java的类和对象详解

一、java是面向对象的编程语言

首先一般的编程语言有两种,一种是面向对象,一种是面向过程。前者更加关注代码中对象与对象之间关系与协作,而后者更加注重代码的执行过程。
举个例子
在这里插入图片描述传统的方式:注重的是洗衣服的过程,少了一个环节可能都不行。而且不同衣服洗的方式,时间长度,拧干方式都不同,处理起来就比较麻烦。如果将来要洗鞋子,那就是另一种放方式。
按照该种方式来写代码,将来扩展或者维护起来会比较麻烦。
而现代洗衣服大家都用洗衣机,我们将洗衣服的步骤交给了洗衣机,我们更关心的是我们与洗衣机之间的交互的过程。
在这里插入图片描述以面向对象方式来进行处理,就不关注洗衣服的过程,具体洗衣机是怎么来洗衣服,如何来甩干的,用户不用去关心,只需要将衣服放进洗衣机,倒入洗衣粉,启动开关即可,通过对象之间的交互来完成的。

面相对象程序设计关注的是对象,而对象是现实生活中的实体,比如:洗衣机。但是洗衣机计算机并不认识,需要开发人员告诉给计算机什么是洗衣机。
比如
在这里插入图片描述
上图就是对洗衣机简单的描述,该过程称为对洗衣机对象(实体)进行抽象(对一个复杂事物的重新认知),但是这些简化的抽象结果计算机也不能识别,开发人员可以采用某种面相对象的编程语言来进行描述,比如:Java语言。

二、类的定义

上述对于洗衣机对象的一些属性的描述就是在定义一个类
类是用来对一个实体(对象)来进行描述的,主要描述该实体(对象)具有哪些属性(外观尺寸等),哪些功能(用来干啥),描述完成后计算机就可以识别了。

java中定义类时需要用到class关键字,具体语法如下

//创建类
classClassName{ field;    //字段(属性)或者成员变量  method;    //行为或者成员方法
}

类中包含的内容称为类的成员。属性主要是用来描述类的,称之为类的成员属性或者类成员变量。方法主要说明类具有哪些功能,称为类的成员方法

比如说定义人这样一个类(在Person.java文件里)

public class person{public String name;//定义一个名字属性public int age;//定义一个年龄属性public void eat(){System.out.println("干饭!");}public void sleep(){//定义了一个睡觉行为(方法)System.out.println("睡觉");}
}

采用Java语言将洗衣机类在计算机中定义完成,经过javac编译之后形成.class文件,在JVM的基础上计算机就可以识别了。

注意:
一般来说一个java文件只写一个类(只能有一个public类)。
public修饰的类必须要和文件名相同。比如创建时的文件名是helloworld.java文件,那么这个文件的类在创建的时候就是public class helloworld{}
类的命名采用大驼峰定义的风格
成员前写法统一为public
不要轻易去修改public修饰的类的名称,如果要修改文件名,通过开发工具修改
在这里插入图片描述

2.1类的实例化

定义了一个类,就相当于在计算机中定义了一种新的类型,与int,double类似,只不过int和double是java语言自带的内置类型,而类是用户自定义了一个新的类型,比如上述的:Person类。有了这些自定义的类型之后,就可以使用这些类来定义实例(或者称为对象)。
类的实例化需要通关键字new,并且类实例化化后就是我们需要的对象了。
比如在helloworld.java 文件中

public class helloworld {public static void main(String[] args) {Person person1 = new Person();//通过new来实例化类,创建了对象person1person1.name = "白线";//对类的属性进行访问赋值person1.age = 15;Person person2 = new Person();//通过new来实例化类,创建了对象person2person2.name = "黑线";//对类的属性进行访问赋值person2.age = 16;}
}

注意
1.new 关键字用于创建一个对象的实例.
2.使用 . 来访问对象中的属性和方法.
3.同一个类可以创建多个个实例.

2.2类的存储

再来说明一下类创建后分别是存在哪一片内存区域的

首先类在创建后只是一些信息,存在JDK内存的方法区,然后在实例化时,执行Person person1 = new Person();,会在栈区创建一个局部变量person1(又叫对象名),这个变量的类型是Person类型(其实就是结构体类型)在堆区开辟一片存储空间,这片存储空间的大小就是这个类定义的时候的大小。然后person1里面存的就是堆中那片区域的地址。在java 中这叫做该引用指向这个对象。person1.name = "白线"; person1.age = 15;就是在堆区里面对name属性(成员)赋值白线,对age属性(成员)赋值15。后面person2也是一样。

实际是我们在创建一个字符串并给字符串赋值的时候,其实底层也是类的实例化

public class helloworld {public static void main(String[] args) {String str1 = "abcde";String str2 = new String("abcde");//这两个是一样的}
}

此外,还有一点要说明,在定义类的时候,成员变量是并没有赋值的,但是java并么有报错,这是因为编译器给了他们默认的值,如果是引用类型默认为null,如果是int float long等默认是0,boolean的默认值是false,char 的默认值是’\u0000’.

2.3this引用

SE.09.00.28.55
我们先来 看一个代码

public class Day_Date {public int year;public int month;public int day;//定义三个成员变量public  void set_Date(int y,int m,int d){year = y;month = m;day = d;}public void print(){System.out.println(year+"年"+month+"月"+day+"日");}public static void main(String[] args) {Day_Date date1  = new Day_Date();date1.set_Date(2022,4,22);date1.print();Day_Date date2  = new Day_Date();date2.set_Date(2022,4,22);date2.print();Day_Date date3  = new Day_Date();date3.set_Date(2022,4,22);date3.print();}
}

结果是
在这里插入图片描述
但是如果我现在将set_date中的形参改为year month day 这时会出现 什么情况呢?

public class Day_Date {public int year;public int month;public int day;//定义三个成员变量public  void set_Date(int year,int month,int day){//形参变量名变成year,month,dayyear = year;month = month;day = day;}public void print(){System.out.println(year+"年"+month+"月"+day+"日");}public static void main(String[] args) {Day_Date date1  = new Day_Date();date1.set_Date(2022,4,22);date1.print();Day_Date date2  = new Day_Date();date2.set_Date(2022,4,22);date2.print();Day_Date date3  = new Day_Date();date3.set_Date(2022,4,22);date3.print();}
}

在这里插入图片描述
此时会发现,打印出来全是0,这是因为

public  void set_Date(int year,int month,int day){//形参变量名变成year,month,dayyear = year;month = month;day = day;}

编译器没法分清year,month ,day到底是什么了,是形参还是成员变量。

同时我们要知道这个代码的执行顺序是现在方法区里创建好Day_Date类,然后在栈区创建第一个对象(引用)date1并指向堆区里面的存储空间,按里面有初始化过的成员变量。但是我们在调用成员方法set_Date的时候,我们没有传任何有关于第一个对象的引用或者是地址,编译器怎么知道第一个set_Date是里面传输的实参在第一个对象的堆区空间里。后面的对象调用成员方法也是这样。

这是因为编译器省略了一个东西就是this引用

this引用指向当前对象(成员方法运行时调用该成员方法的对象),在成员方法中所有成员变量的操作,都是通过该引用去访问。只不过所有的操作对用户是透明的,即用户不需要来传递,编译器自动完成。

所以我们写代码时,遇到成员方法需要用到调用成员变量时,最好在成员变量前加上this.这个更好理解

public class Day_Date {public int year;public int month;public int day;//定义三个成员变量public  void set_Date(int year,int month,int day){//形参变量名变成year,month,daythis.year = year;this.month = month;this.day = day;}public void print(){System.out.println(this.year+"年"+this.month+"月"+this.day+"日");}public static void main(String[] args) {Day_Date date1  = new Day_Date();date1.set_Date(2022,4,22);date1.print();Day_Date date2  = new Day_Date();date2.set_Date(2022,4,22);date2.print();Day_Date date3  = new Day_Date();date3.set_Date(2022,4,22);date3.print();}
}

至此我们在梳理一下这个内存的过程,创建类的过程就不说了,在执行Day_Date date1 = new Day_Date();时,系统会在栈区创建一个局部变量(或者叫对象)命名为data1,在堆区开辟一块存储空间用于存放输入data1对象所有变量,data1是引用类型,他存的是一个地址,指向的就是这块堆区。然后开始执行 date1.set_Date(2022,4,22);调用方法区的方法,然后将2022,4,22以及data1所在的堆区地址(也就是this这个引用类型)作为实参传入set_Date方法中,执行ste_Date方法。进入这个方法后,编译器区分俩个year的方法是编译器知道等号左边的year的是被this这个引用所指向的,也就是data1这个成员的year,而等号右边的year是临时开辟的形参,用于接收2022的year。

下面这个图就很好的说明了这一点

publicstaticvoidmain(String[]args){Date d=newDate();d.setDay(2020,9,15);d.printDate();
}

在这里插入图片描述注意:

  1. this的类型:对应类类型引用,即哪个对象调用就是哪个对象的引用类
  2. this只能在"成员方法"中使用,同时不能用于静态的成员方法(这个后面会说)
  3. 在"成员方法"中,this只能引用当前对象,不能再引用其他对象
  4. this是“成员方法”第一个隐藏的参数,编译器会自动传递,在成员方法执行时,编译器会负责将调用成员方法类也是可以通过编译的
    在这里插入图片描述

2.4构造方法

构造方法是java中一类比较特殊的方法。
我们在上面的代码中初始化用的是一个set_Date的成员方法。但是要知道我要是每次生成一个对象都要调用一个成员方法,这多麻烦啊!所以有没有简单一点的的方法呢!答案是有的,就是构造方法

构造方法(也称为构造器)是一个特殊的成员方法,名字必须与类名相同,在创建对象时,由编译器自动调用,并且在整个对象的生命周期内只调用一次
具体来说
在执行Day_Date date1 = new Day_Date();时,也就是实例化对象时,就会调用data1对象的构造方法。

构造方法非常特殊,没有返回值,(不是返回值为void,单纯就是 没有返回值,定义的时候也不写)方法名也必须和类名一致,参数列表可有可没有。

public class Day_Date {public int year;public int month;public int day;//定义三个成员变量public  void set_Date(int year,int month,int day){//形参变量名变成year,month,daythis.year = year;this.month = month;this.day = day;}public void print(){System.out.println(this.year+"年"+this.month+"月"+this.day+"日");}public Day_Date(){//定义一个构造函数,这个函数不用在主函数里面写明调用,java也会调用,// 如果你不写构造函数,java也会直接提供一个不带任何参数和函数体的构造方法System.out.println("这是构造函数!");}public static void main(String[] args) {Day_Date date1  = new Day_Date();date1.set_Date(2022,4,22);date1.print();}
}

构造方法的特性

  1. 名字必须与类名相同
  2. 没有返回值类型,设置为void也不行
  3. 创建对象时由编译器自动调用,并且在对象的生命周期内只调用一次(相当于人的出生,每个人只能出生一次)
  4. 构造方法可以重载(用户根据自己的需求提供不同参数的构造方法)

所以我们初始化对象,完全可以从构造方法的角度,直接传参就像下面这个例子一样

public class Day_Date {public int year;public int month;public int day;//定义三个成员变量//    public  void set_Date(int year,int month,int day){//形参变量名变成year,month,day
//        this.year = year;
//        this.month = month;
//        this.day = day;
//    }public void print(){System.out.println(this.year+"年"+this.month+"月"+this.day+"日");}public Day_Date(int year,int month,int day){//定义一个构造函数,这个函数不用在主函数里面写明调用,java也会调用,// 如果你不写构造函数,java也会直接提供一个不带任何参数和函数体的构造方法this.year = year;this.month = month;this.day = day;System.out.println("这是构造函数!");}public static void main(String[] args) {Day_Date date1  = new Day_Date(2022,4,22);//构造方法定义了形参和函数体后,直接赋值初始化就可以。date1.print();}
}

一定要注意,只要我们自己写了构造方法,那么java就会执行我们写的构造方法,只有我们没有写任何的构造方法时,java才会执行一个没有任何参数、返回值、函数体的构造方法。

这里有个问题需要解释一下
我们在调用构造函数是使用里this这个引用类型,但是this这个引用类型指向的是这个对象,但是我调用构造函数正是为了生成这个对象啊!这好像变成了我为了生成这个对象而引用了这个对象?
实际上,构造方法的作用就是对对象中的成员进行初始化,并不负责给对象开辟空间。也就是说对象的生成分为两步,第一步是开辟内存空间,这个是new来完成的,内存空间一旦开辟了就可以引用了,而第二步才是调用构造方法进行初始化,实际就是对内存空间内的成员变量进行赋值。

构造方法中,可以通过this调用其他构造方法来简化代码

public class Date {public int year;public int month;public int day;
// 无参构造方法--内部给各个成员赋值初始值,该部分功能与三个参数的构造方法重复
// 此处可以在无参构造方法中通过this调用带有三个参数的构造方法
// 但是this(1900,1,1);必须是构造方法中第一条语句public Date(){
//System.out.println(year); 注释取消掉,编译会失败this(1900, 1, 1);
//this.year = 1900;
//this.month = 1;
//this.day = 1;}
// 带有三个参数的构造方法public Date(int year, int month, int day) {this.year = year;this.month = month;this.day = day;}
}

这里背后的逻辑在主函数初始化对象先调用了无参的函数构造方法,然后执行this(1900, 1, 1);此时系统会自动调用另外一个重载函数:带有三个参数的构造方法
从而完成初始化。
但是需要注意的是
1.必须放到this(1900, 1, 1);第一行
2.只能在构造方法内部用
3.不能形成环

public Date(){
this(1900,1,1);
}
public Date(int year, int month, int day) {
this();
}
/*
无参构造器调用三个参数的构造器,而三个参数构造器有调用无参的构造器,形成构造器的递归调用
编译报错:Error:(19, 12) java: 递归构造器调用
*/

2.5 默认初始化

public class Date {
public int year;
public int month;
public int day;
public Date(int year, int month, int day) {
// 成员变量在定义时,并没有给初始值, 为什么就可以使用呢?
System.out.println(this.year);
System.out.println(this.month);
System.out.println(this.day);
}
public static void main(String[] args) {
// 此处a没有初始化,编译时报错:
// Error:(24, 28) java: 可能尚未初始化变量a
// int a;
// System.out.println(a);
Date d = new Date(2021,6,9);
}
}

在程序层面只是简单的一条语句,在JVM层面需要做好多事情,下面简单介绍下:

  1. 检测对象对应的类是否加载了,如果没有加载则加载

  2. 为对象分配内存空间

  3. 处理并发安全问题
    比如:多个线程同时申请对象,JVM要保证给对象分配的空间不冲突

  4. 初始化所分配的空间
    即:对象空间被申请好之后,对象中包含的成员已经设置好了初始值,比如:
    在这里插入图片描述

  5. 设置对象头信息(关于对象内存模型后面会介绍)

  6. 调用构造方法,给对象中各个成员赋值

在说完构造方法后,我们来说一说面向对象程序的三大特性


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/68967.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

linux系统部署jenkins详细教程

一、Linux环境 1、下载war包 官网下载地址: https://get.jenkins.io/war-stable/2.332.4/jenkins.war 2、将war包上传至服务器 创建目录/home/ubuntu/jenkins 上传war包至该目录 3、将jenkins添加到环境变量 进入环境变量文件 vim /etc/profile # 文件下方追加…

云原生 AI 工程化实践之 FasterTransformer 加速 LLM 推理

作者:颜廷帅(瀚廷) 01 背景 OpenAI 在 3 月 15 日发布了备受瞩目的 GPT4,它在司法考试和程序编程领域的惊人表现让大家对大语言模型的热情达到了顶点。人们纷纷议论我们是否已经跨入通用人工智能的时代。与此同时,基…

【D3.js 01】

D3.js 01 说在前面1 概述2 配置Web环境3 HTML4 SVG5 DOM6 JS7 常用接口8 D3语法基础9 使用D3查询SVG10 使用D3设置SVG中属性11 修改整组属性12 使用D3添加与删除SVG元素13 数据读取 —— CSV数据14 D3.js的数值计算15 比例尺Scale - LinearScale - Band 16 引入坐标轴17 DATA-J…

opencv进阶02-在图像上绘制多种几何图形

OpenCV 提供了方便的绘图功能,使用其中的绘图函数可以绘制直线、矩形、圆、椭圆等多种几何图形,还能在图像中的指定位置添加文字说明。 OpenCV 提供了绘制直线的函数 cv2.line()、绘制矩形的函数 cv2.rectangle()、绘制圆的函数cv2.circle()、绘制椭圆的…

SQL-每日一题【1341. 电影评分】

题目 表:Movies 表:Users 请你编写一个解决方案: 查找评论电影数量最多的用户名。如果出现平局,返回字典序较小的用户名。查找在 February 2020 平均评分最高 的电影名称。如果出现平局,返回字典序较小的电影名称。 …

利用ViewModel和LiveData进行数据管理

利用ViewModel和LiveData进行数据管理 1. 引言 在当今移动应用开发的世界中,数据管理是一个至关重要的方面。随着应用的复杂性不断增加,需要有效地管理和维护应用中的数据。无论是从服务器获取数据、本地数据库存储还是用户界面的状态,数据…

openpnp - 吸嘴是否吸取了元件的检测

文章目录 openpnp - 吸嘴是否吸取了元件的检测概述笔记补充 - 2023/8/11有bug, 元件底部视觉检测有时检测不到正确的元件外形END openpnp - 吸嘴是否吸取了元件的检测 概述 设备本身调试好了, 现在进行试贴. 发现一个奇怪的问题, 不管是否吸嘴吸取到了元件, 都会往板子上贴元…

【Windows 常用工具系列 8 -- 修改鼠标光标(指针)大小和颜色的快速方法方法】

文章目录 修改方法 上篇文章:Windows 常用工具系列 7 – 禁用win10自带的微软输入法 修改方法 Win键 i 快捷键进入设置页面,然后输入光标... 就会跳出修改鼠标大小与光标颜色的选项。 Win键是在计算机键盘左下角Ctrl和Alt键之间的按键 根据自己的需求…

SQL | 排序检索的数据

3-排序检索的数据 使用order by语句排序检索到的数据。 3.1-排序数据 使用SQL语句返回一个数据表的列。 select prod_id from products; --------------------- | prod_name | --------------------- | 8 inch teddy bear | | 12 inch teddy bear | | 18 inch teddy bear |…

高并发数据抓取实战:使用HTTP爬虫ip提升抓取速度

又到每天一期学习爬虫的时间了,作为一名专业的爬虫程序员,今天要跟你们分享一个超实用的技巧,就是利用HTTP爬虫ip来提升高并发数据抓取的速度。听起来有点高大上?别担心,我会用通俗易懂的话来和你们说,让你…

【论文阅读】基于深度学习的时序预测——FEDformer

系列文章链接 论文一:2020 Informer:长时序数据预测 论文二:2021 Autoformer:长序列数据预测 论文三:2022 FEDformer:长序列数据预测 论文四:2022 Non-Stationary Transformers:非平…

【C++】stack容器

1.stack基本概念 英stk 美stk n.&#xff08;整齐的&#xff09;一堆&#xff1b;<英> 垛&#xff0c;堆&#xff1b;大量&#xff0c;许多&#xff1b;&#xff08;尤指工厂的&#xff09;大烟囱&#xff1b;&#xff08;图书馆的&#xff09;藏书架&#xff0c;双面书架…