5.对象的构造及初始化
5.1 如何初始化对象
我们知道局部变量必须先赋值在使用,如果不赋值直接使用则会编译失败 。
对于实例化对象来说如果不初始化成员变量,成员变量里面存放的是默认值。
①局部变量先赋值后使用,则编译成功
②局部变量不赋值直接使用,则编译失败
③上面我们讲了对于实例化对象来说如果不初始化成员变量,成员变量里面存放的是默认值
class Data { public int year; public int month; public int day; public void print() { System.out.println(year+"-"+month+"-"+day); } } public class Test { public static void main(String[] args) { Data data = new Data(); data.print(); } }
运行结果:
那么现在就出现了两个问题:
那对象究竟如何初始化?
为什么局部变量必须先赋值后使用,而对象中的成员变量就不需要赋值可以直接使用?
注:实例化对象里面存的就是成员变量,初始化也就是对成员变量进行初始化
那接下来我们就带着以上两个问题进入构造方法的学习
5.2 构造方法
1.概念
构造方法的概念:构造方法又称为构造器,它是一个特殊的成员方法,名字必须要与类名相同,在创建对象时,由编译器自动调用,并且在整个对象的生命周期内只调用一次
2.特性
它是一个特殊的成员方法
没有返回值类型,设置为void也不行
名字与类名相同
一般情况下使用public修饰
创建对象的时候编译器自动调用,且在整个对象的生命周期内只会调用一次
构造方法可以重载
class Data { public int year; public int month; public int day; public Data(int year,int month,int day) { this.year = year; this.month = month; this.day = day; System.out.println("编译器自动调用了构造方法"); } } public class Test { public static void main(String[] args) { Data data = new Data(2022,9,10); } }
运行结果:
上述代码中主方法里面就只有一条实例化 Data 类的语句,通过实例化语句编译器自动调用了对象构造方法,并在构造方法中给成员变量初始化了
注意:构造方法的作用就是对对象中的成员进行初始化,并不负责给对象开辟空间。开辟空间是实例化的时候开辟的也就是new的时候开辟的
3.构造方法的重载
构造方法可以重载:
class Data { public int year; public int month; public int day; public Data() { System.out.println("编译器自动调用了没有形参的构造方法"); } public Data(int year,int month,int day) { this.year = year; this.month = month; this.day = day; System.out.println("编译器自动调用了有形参的构造方法"); } } public class Test { public static void main(String[] args) { Data data = new Data(2022,9,10); Data data1 = new Data(); } }
运行结果:
上述两个构造方法:名字相同,参数列表不同,因此构成了方法重载
当构造方法重载时,根据实例化对象后面的参数进行匹配构造方法
4.编译器什么时候自动生成一个无参的构造方法
如果用户没有自己定义构造方法,那么编译器将会自动生成一份默认无参的构造方法:
class Data { public int year; public int month; public int day; } public class Test { public static void main(String[] args) { Data data = new Data(); } }
我们看不到编译器自动生成的构造方法,但是不代表它没有生成
如果用户自己定义了一个构造方法那么,编译器将不会自动生成:
运行结果:
我们说如果用户不自己定义一个构造方法,那么编译器将会自动生成一个无参构造方法。上述代码自定义了一个有参的构造方法,那么编译器将不会自动生成了,所以当 data1 在编译时就直接报错了,原因就是用户自定义了一个有参的构造方法,编译器无法自动生成一个无参的构造方法了,如果想解决这个问题那么用户在定义一个无参的构造方法即可
5. 构造方法中,可以通过 this 调用其他构造方法来简化代码
class Data { public int year; public int month; public int day; public Data() { this(2022,9,10); } public Data(int year,int month,int day) { this.year = year; this.month = month; this.day = day; } public void print() { System.out.println(year+"-"+month+"-"+day); } } public class Test { public static void main(String[] args) { Data data = new Data(); data.print(); } }
运行结果:
this(...)必须在构造方法中的第一条
不能形成环
不能形成环:不能在无参构造方法中调用有参构造方法,在有参构造方法中调用无参构造方法 ,这样就容易形成环
6.默认初始化
学习了上面的构造方法我们也就解决了第一个问题,就是对象如何初始化。
那我们接下来看第二个问题, 为什么局部变量必须先赋值后使用,而对象中的成员变量就不需要赋值可以直接使用?
要搞清楚第二个问题,就要了解 new 关键字背后所发生的一些事情
Data data = new Data();
在程序层面只是简单的一条语句,在JVM层面需要做好多事情,下面简单介绍一下:
检测对象对应的类是否加载了,如果没有加载则加载
为对象分配内存空间
处理并发安全问题。比如:多个线程同时申请对象,JVM要保证给对象分配的空间不冲突
初始化所分配的空间
设置对象头信息
调用构造方法,给对象中各个成员赋值
所以总结下来,为什么对象中的成员变量就不需要赋值可以直接使用,因为它调用了构造方法完成了初始化
7.就地初始化
class Data { public int year = 2022; public int month = 9; public int day = 10; public void print() { System.out.println(year+"-"+month+"-"+day); } } public class Test { public static void main(String[] args) { Data data = new Data(); data.print(); } }
在声明成员变量时,就直接给出了初始值,就叫做就地初始化