目录
一、this引用
1.为什么会有this引用,它可以来干嘛?
先让我们看一个Data的例子:
public class Date {
public int year;
public int month;
public int day;
public void setDay(int y, int m, int d){
year = y;
month = m;
day = d;
}
public void printDate(){
System.out.println(year + "/" + month + "/" + day);
}
public static void main(String[] args) {
// 构造三个日期类型的对象 d1 d2 d3
Date d1 = new Date();
Date d2 = new Date();
Date d3 = new Date();
// 对d1,d2,d3的日期设置
d1.setDay(2020,9,15);
d2.setDay(2020,9,16);
d3.setDay(2020,9,17);
// 打印日期中的内容
d1.printDate();//打印结果:2020/9/15
d2.printDate();//打印结果:2020/9/16
d3.printDate();//打印结果:2020/9/17
}
}
以上定义了一个Data类,如何创建三个对象,传值,打印。 代码整体逻辑非常简单,没有任何问题。但是你仔细思考,如果出现下面的情况呢:
形参名不小心与成员变量名相同:
public void setDay(int year, int month, int day){
year = year;
month = month;
day = day;
}
那函数体中到底是谁给谁赋值?成员变量给成员变量?参数给参数?参数给成员变量?成员变量参数?估计 自己都搞不清楚了。这时候就需要this引用来解决这个问题。
2.this引用是什么
this引用指向当前的对象,可以通过this对这个方法(形参变量什么的)里面的东西进行操作。可以类似于C语言的指针,指向这个对象,通过地址来修改里面的内容。
3.this引用怎么用?
让我们先看一段代码:
public class Date {
public int year;
public int month;
public int day;
public void setDay(int year, int month, int day){
this.year = year;
this.month = month;
this.day = day;
}
public void printDate(){
System.out.println(this.year + "/" + this.month + "/" + this.day);
}
}
用法:this.成员变量,输出时也需要使用this.
注意:this引用的是调用成员方法的对象。也就是说在下面的代码中,我通过使用d的方法,调用的是d这个对象,可以理解为this里面存着的是d的地址,这样可以精准的找到d这个对象然后去修改里面的内容。
二、对象的初始化及构造
1.什么是初始化
int a;是创建一个变量,而int a=0;就是初始化变量,在创建它的时候顺便给它赋了个值,着就是初始化。
public static void main(String[] args) {
int a;
System.out.println(a);
}
// Error:(26, 28) java: 可能尚未初始化变量a
看上面这个代码,如果不初始化,在后续去输出它肯定会报错,为什么?
因为它只管分配空间不包括给值,里面本来就没值,你还去打印它,所以会报错!
2.如何初始化对象
但是对于对象来说,就不会报错,我们看下面的代码:
public static void main(String[] args) {
Date d = new Date();
d.printDate();
d.setDate(2021,6,9);
d.printDate();
}
// 代码可以正常通过编译
那我们可以得到两个问题:
1.为什么a会报错,而上面的代码不会报错呢?
2.并且每次对象创建好后调用SetDate方法设置具体日期,比较麻烦,那对象该如何初始化?
那么这时候我们的构造方法就出现了,让我们一起往下看:
3.构造方法
3.1.什么是构造方法
构造方法是一个特殊的成员方法,名字必须与类名相同,在创建对象时,由编译器自动调用,并且在整个对象的生命周期内只调用一次。简单来讲:它是给对象的变量初始化的方法。
public class Date {
public int year;
public int month;
public int day;
// 构造方法:
// 名字与类名相同,没有返回值类型,设置为void也不行
// 一般情况下使用public修饰
// 在创建对象时由编译器自动调用,并且在对象的生命周期内只调用一次
public Date(int year, int month, int day){
this.year = year;
this.month = month;
this.day = day;
System.out.println("Date(int,int,int)方法被调用了");
}
public void printDate(){
System.out.println(year + "-" + month + "-" + day);
}
public static void main(String[] args) {
// 此处创建了一个Date类型的对象,并没有显式调用构造方法
Date d = new Date(2021,6,9); // 输出Date(int,int,int)方法被调用了
d.printDate(); // 2021-6-9
}
}
注意:构造方法的作用就是对对象中的成员进行初始化,并不负责给对象开辟空间。
注意事项:1. 名字必须与类名相同
2. 没有返回值类型,设置为void也不行
3. 创建对象时由编译器自动调用,并且在对象的生命周期内只调用一次(相当于人的出生,每个人只能出生一次)
4. 构造方法可以重载(用户根据自己的需求提供不同参数的构造方法)
public class Date {
public int year;
public int month;
public int day;
// 无参构造方法
public Date(){
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;
}
public void printDate(){
System.out.println(year + "-" + month + "-" + day);
}
public static void main(String[] args) {
Date d = new Date();
d.printDate();//打印1900-1-1
}
}
但是当你自己写出了一个构造方法的话,编译器就不会再给你提供。(简单来说:救急不救穷,你没有我就给你,有了就不给)
public class Date {
public int year;
public int month;
public int day;
public Date(int year, int month, int day) {
this.year = year;
this.month = month;
this.day = day;
}
public void printDate(){
System.out.println(year + "-" + month + "-" + day);
}
public static void main(String[] args) {
// 如果编译器会生成,则生成的构造方法一定是无参的
// 则此处创建对象是可以通过编译的
// 但实际情况是:编译期报错
Date d = new Date();
d.printDate();
}
}
/*
Error:(26, 18) java: 无法将类 extend01.Date中的构造器 Date应用到给定类型;
需要: int,int,int
找到: 没有参数
原因: 实际参数列表和形式参数列表长度不同
*/
构造方法中,可以通过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;
}
}
3.2.什么是重载呢?
上述代码中,两个Data构造方法:名字相同,参数列表不同,因此构成了方法重载。
三、默认初始化
在上文中提出的第一个问题:为什么局部变量在使用时必须要初始化,而成员变量可以不用呢?
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);
}
}
要搞清楚这个过程,就需要知道 new 关键字背后所发生的一些事情:
Date d = new Date(2021,6,9);
在程序层面只是简单的一条语句,在JVM层面需要做好多事情,下面简单介绍下:
1. 检测对象对应的类是否加载了,如果没有加载则加载
2. 为对象分配内存空间
3. 处理并发安全问题 比如:多个线程同时申请对象,JVM要保证给对象分配的空间不冲突
4. 初始化所分配的空间 即:对象空间被申请好之后,对象中包含的成员已经设置好了初始值,比如:
5. 设置对象头信息
6. 调用构造方法,给对象中各个成员赋值