类和对象的内存结构
person
是一个变量,只是这个变量里储存的是地址,所以这个变量也被叫做引用
注意事项👇
- 使用 . 访问对象的字段.
- “访问” 既包含读, 也包含写.
- 对于一个对象的字段如果没有显式设置初始值, 那么会被设置一个默认的初值.
默认值规则
- 对于各种数字类型, 默认值为 0.
- 对于 boolean 类型, 默认值为 false.
- 对于引用类型(String, Array,以及自定制类), 默认值为 null
注意事项:
- null 在 Java 中为 “空引用”, 表示不引用任何对象. 类似于 C 语言中的空指针. 如果对 null 进行 . 操作就会引发异常
- 引用多次指向对象,引用仅指向最后一个对象
- 引用不一定在栈上
类的成员
字段/属性/成员变量
在类内,在方法外部
定义的变量我们称为 “字段” 或 “属性” 或 “成员变量”,用于描述一个类中包含哪些数据
代码一:
代码二:(访问普通成员变量)
static关键字
**方法区:**保存在着被加载过的每一个类的信息,这些信息由类加载器在加载类的时候,从类的源文件中抽取出来,static变量和方法的信息也保存在方法区中
代码3:(访问静态成员变量)
注意事项1(能不能在方法中 创建一个 被 static修饰的变量)
无论是 普通成员方法还是静态成员方法,都不能在其内部创建一个被static修饰的变量
因为 被static修饰了的变量,该变量就属于类了(类变量/静态变量)。
而你把一个(类变量/静态成员变量)写在方法里,就意味着属于方法(是一个局部变量,不再是 类变量了),而不属于类
所以冲突了,不合适,导致编译器报错。
注意事项 2(能不能在方法中调用方法)
普通成员方法 调用动态成员方法
静态成员方法/类方法 调用 普通成员方法
静态成员方法 是无法调用 普通成员方法的
原因也很简单,静态成员方法不需要对象,而普通成员方法需要对象
静态成员通过类名来调用,因此不需要new对象,那么 eat() 谁调?
没有对象。不能调,所以编译器会报错。
引用 一定是在栈上吗?? ? ?
不是
注意&总结:
- 1.普通的方法内部,不能够定义静态的变量
- 2.静态方法的内部不可以调用普通方法
- 3.我们曾经说写方法都要统一加上了static,是因为我们之前写的方法只有被static修饰后才能直接调用而不依赖于对象
- 4.一个对象存储到哪里,和是否被final修饰没有任何关系
- 5.使用方法的时候,为什么都用static, 因为被static修饰的方法是可以直接调用的,不需要new对象
总结 static 关键字
- 修饰属性
修饰属性,Java静态属性和类相关, 和具体的实例无关. 换句话说, 同一个类的不同实例共用同一个静态属性- 修饰方法
如果在任何方法上应用 static 关键字,此方法称为静态方法。
静态方法属于类,而不属于类的对象。
可以直接调用静态方法,而无需创建类的实例。
静态方法可以访问静态数据成员,并可以更改静态数据成员的值。
注意事项1: 静态方法和实例无关, 而是和类相关. 因此这导致了两个情况:
- 静态方法不能直接使用非静态数据成员或调用非静态方法(非静态数据成员和方法都是和实例相关的).
- this和super两个关键字不能在静态上下文中使用(this 是当前实例的引用, super是当前实例父类实例的引用, 也是和当前实例相关).
注意事项2
- 我们曾经写的方法为了简单(不用new对象), 都统一加上了 static. 但实际上一个方法具体要不要带 static, 都需要是情形而定.
- main 方法为 static 方法.
注意事项3
被final(使其具有常量属性)修饰的成员变量,与成员变量是不是存储在方法区或者堆上是无关的
简单来说: 一个对象存储到哪里 和 你是否被final修饰无关
还是那句话,凡是被 static 修饰的成员变量或者成员方法(又称静态成员变量和静态成员方法),都是存储在方法区中
没有被static修饰的成员变量 或者 方法(又称:普通成员变量 和 普通成员方法)
都需要通过new对象,来实体化对象,通过指向对象的引用来调用我们普通成员变量和方法。
在代码中尽量少使用 static,这样的代码称为祖传代码,容易存在一些不合理的代码,代码不好修改
重写toString
程序解流程图
点击 println 》 Ctrl+点击,进入println函数
点击 valueOf 》 Ctrl+点击,进入valueOf函数
点击 toString 》 Ctrl+点击,进入toString函数
通过我们层层解析,我发现它最后是通过 toString 来转换
那么我们可不可以 自己写一个 toString 方法
答案是可以的
代码如下:
toString
是Object类提供的方法, 我们自己创建的Test类默认继承自Object类,可以重写 toString 方法实
现我们自己版本的转换字符串方法。
我们在打印数据时,如果我们写了一个toString的方法
编译器,就会执行我们所写的 toString 方法,而不是系统默认的toString
执行我们所写的 toString方法 的 前提是 toString 方法 的名字不能变,是定死了的,缺胳膊少腿 或者 画蛇添足都是不行的
同理:也就是说我们可以不用调用方法,就能输出一样的效果
自动生成Generate方法
toString函数 可以 通过快捷键和鼠标来让编译器自动生成
代码如下:
@Override在Java中称为 “注解”,此处的@Override表示下面实现的toString
方法是重写了父类的方法
封装
访问限定修饰符
private
/ public
这两个关键字称为访问限定修饰符
- public修饰的成员变量或者成员方法,可以直接被类的调用者使用
- private修饰的成员变量或者成员方法,不能被类的调用者使用
当我们将变量使用public修饰时,在类外访时需要了解Test类内部的实现,才能够使用这个类,学习成本较高,一旦类的实现者修改了代码(把count
改成num
),那么类的使用者就需要大规模的修改代码,这使得代码的可维护性较低,维护难度较大,维护成本较高。为了解决这些问题,提高我们代码的可维护性,我们提出了封装这个概念。
封装可以被认为是一个保护罩,防止该类的代码和数据被外部类定义的代码随机访问。要访问该类的代码和数据,必须通过严格的接口控制,我们的getter和setter方法来源就是如此。
getter和setter方法
我们使用private封装属性, 并提供public的getter和setter方法供类的调用者去访问这些属性。
this表示当前对象引用(注意不是当前对象)
. 可以借助 this 来访问对象的字段和方法,稍后我们详细介绍
注意&总结:
1.getName
即为getter
方法,表示获取这个成员的值,setName
即为setter
方法,表示修改这个成员的值.
2.当set
方法的形参名字和类中的成员属性的名字一样的时候,如果不使用this
,就相当于自己给自己赋值,this
表示当前对象的引用
3.不是所有的字段都一定要提供 setter / getter
方法,要根据实际情况决定提供哪种方法
IDEA生成getter / setter
方法快捷方法:
构造方法
一个对象的产生要有两个过程:
1.为对象分配内存
2.调用合适的构造方法
什么是构造方法?
构造方法是一种特殊方法, 使用关键字new实例化新对象时会被自动调用, 用于完成初始化操作
,就算我们在类中没有实现构造方法,那么编译器也会自动帮我们生成一个无参的构造方法,也就是说:一个类至少有一个构造方法
基本语法
构造类型:
方法名和类名是相同的,且构造方法比较特殊,没有返回值
语法规则
1.方法名称必须与类名称相同
2.构造方法没有返回值
3.每一个类中一定至少存在一个构造方法(没有明确定义,则系统自动生成一个无参构造)
new 执行过程
1.为对象分配内存空间
2.调用合适的构造方法
当上面这两步完成之后,我们的对象才真正产生了。(意味着调用完构造方法之后,我们的对象才真正产生了)
注意 合适 这两个字,意味着构造方法不止一个。就好比鞋子很多,但要挑合适的
现在我们来写一个构造方法,代码如下
很多人可能会问,你有输出,怎么说是不带参数的构造方法?这只是让你理解 在new对象的过程中,会调用构造方法。
现在我们来写一写 多个构造方法,加深对 “合适” 的理解
注意
构造方法支持重载. 规则和普通方法的重载一致
构造方法重载规则:类名相同,参数的类型和个数,两者中,至少有一个不同项。
调用构造方法时,编译器会自动筛选调用合适的构造方法
注意: 若类中定义了构造方法,则默认的无参构造将不再生成.
this关键字
- this.data 调用当前的对象的 属性/字段/成员变量
- this.func() 调用当前的对象的方法
- this() 调用当前对象的其他构造方法
第一种我就不说了,前面已经见过它的应用了,我来讲第二种
第三种 this() 调用当前对象的构造方法
那么 this() 在上述情况下 该怎么使用?
调用 其他 有参数的构造方法
代码如下:
但是 注意一点 this()在构造方法中 去调用 其他构造方法时,只能放在该构造方法的第一句的位置,this()才能使用( 图41 )。而且 this() 这种方法,只能用于构造方法中。