5.2.2 特性
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(); } }
上述两个构造方法:名字相同,参数列表不同,因此构成了方法重载
🍩构造方法可以有多个
🍩构造方法之间构成重载
5.如果用户没有显式定义,编译器会生成一份默认的构造方法,生成的默认构造方法一定是无参的。
public class Date { public int year; public int month; public int day; public void printDate(){ System.out.println(year + "-" + month + "-" + day); } public static void main(String[] args) { Date d = new Date(); d.printDate(); } }
🍤 运行结果:
注: 一旦用户定义,编译器则不再生成。
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(); } }
报错:
6.构造方法中,可以通过 this 调用用其他构造方法来简化代码。
public class Date { public int year; public int month; public int day; // 无参构造方法--内部给各个成员赋值初始值,该部分功能与三个参数的构造方法重复 // 此处可以在无参构造方法中通过this调用带有三个参数的构造方法 // 但是this(2023,8,1);必须是构造方法中第一条语句 public Date(){ //System.out.println(year); 注释取消掉,编译会失败 this(2023, 8, 1); //this.year = 2023; //this.month = 8; //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(); } }
🍤 运行结果:
注:
- this(…)必须是构造方法中第一条语句
- 不能形成环
例如:
public Date(){ this(1900,1,1); } public Date(int year, int month, int day) { this(); }
7.绝大多数情况下使用public来修饰,特殊场景下会被private修饰。
5.3 默认初始化
在上文中提出的第二个问题:为什么局部变量在使用时必须要初始化,而成员变量可以不用呢?
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) { //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.初始化所分配的空间
即:对象空间被申请好之后,对象中包含的成员已经设置好了初始值,比如:
数据类型 | 默认值 |
byte | 0 |
char | ‘\u0000’ |
short | 0 |
int | 0 |
long | 0L |
boolean | false |
float | 0.0f |
double | 0.0 |
reference | null |
5.设置对象头信息
6.调用构造方法,给对象中各个成员赋值
5.4 就地初始化
在声明成员变量时,就直接给出了初始值
public class Date { public int year = 1900;//赋初值 public int month = 1; public int day = 1; public Date(){ } 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 d1 = new Date(2023,8,1); Date d2 = new Date(); d1.printDate(); d2.printDate(); } }
🍤 运行结果:
🍩代码编译完成后,编译器会将所有给成员初始化的这些语句添加到各个构造函数中