如何初始化对象

我们知道再Java方法内部定义一个局部变量的时候,必须要初始化,否则就会编译失败
image.png
要让这串代码通过编译,很简单,只需要在正式使用a之前,给a设置一个初始值就好
那么对于创造好的对象来说,我们也要进行相对应的初始化
我们先写一个Mydate的类

  1. public class MyDate {
  2. public int year;
  3. public int month;
  4. public int day;
  5. /**
  6. * 设置日期:
  7. */
  8. public void setDate(int year,int month,int day) {
  9. this.year = year;
  10. this.month = month ;
  11. this.day = day;
  12. }
  13. public void printDate() {
  14. System.out.println("年:"+this.year+"月:"+this.month+"日:"+this.day);
  15. }
  16. public static void main(String[] args) {
  17. // this.printDate();
  18. MyDate myDate = new MyDate();
  19. myDate.setDate(2022,3,25);
  20. myDate.printDate();
  21. MyDate myDate2 = new MyDate();
  22. myDate2.setDate(2022,3,28);
  23. myDate2.printDate();
  24. }
  25. }

我们可以看到,在Mydate类中,要在调用之前实现写好setDate才能将具体的日期设置到对象当中
image.png
我们通过这个例子就可以发现两个问题:

  1. 每次对象创建好之后调用setDate方法设置具体时期,比较麻烦,那么对象该如何初始化呢?
  2. 局部变量必须要初始化之后才能使用,那么为什么字段声明之后没有给初值,它依旧可以使用呢?

答案:

  1. 我们可以运用构造函数来进行初始化
  2. 因为这里和main函数中定义的局部变量不同,编译器会自动为你的字段声明的局部变量赋初始零值。

构造方法

当我们实例化一个对象的时候:必须会有这两步,但并不是一定只有这两步

  1. 为对象分配内存
  2. 调用合适的构造方法

构造方法(也称为构造器)是一个特殊的成员方法,名字必须与类名相同,在创建对象的时候,由编译器自动调用,并且在整个对象的生命周期内只调用一次

  1. class Person {
  2. public String name;
  3. public int age;
  4. //构造方法:
  5. //名字与类名相同,且没有返回值类型,void也不行
  6. //一般情况下使用public修饰
  7. //在创建对象的时候由编译器自动调用,并且在对象的声明周期内只调用一次
  8. public Person(String name,int age){
  9. this.name = name;
  10. this.age = age;
  11. System.out.println("构造方法被调用了")
  12. }
  13. public void eat() {
  14. System.out.println("吃饭!");
  15. }
  16. public void sleep() {
  17. System.out.println("睡觉!");
  18. }
  19. public static void main(String[] args){
  20. //在此处创建了一个Date类型的对象,并没有显式调用构造函数
  21. Person p = new Person("xiaohong",18);
  22. p.eat();
  23. }
  24. }

⚠️:构造方法的作用就是对对象中的成员进行初始化,并不负责给对象开辟空间
image.png

特性

  1. 名字必须和类名相同
  2. 没有返回值类型,void也不行
  3. 创建对象时由编译器自动调用,并且在对象的生命周期内只调用一次(相当于人的出生)
  4. 构造方法可以重载(用户根据自己的需求提供不同参数的构造方法) ```java class Student { //属性:成员变量-》类的内部 方法的外部的 public String name; public int age; public double score ; public String sex;
  1. public Student() {
  2. //调用本类中 带有2个参数的构造方法,第一个参数的类型是String,第2个参数的类型是int
  3. this.age = 18;
  4. System.out.println("这个是不带参数的构造方法!");
  5. }
  6. public Student(String name,int age) {
  7. //this();
  8. this.age = age;
  9. this.name = name;
  10. System.out.println("这个是带2个参数的构造方法!");
  11. }
  12. public Student(String name, int age, double score, String sex) {
  13. this.name = name;
  14. this.age = age;
  15. this.score = score;
  16. this.sex = sex;
  17. System.out.println("带4个参数的构造方法!");
  18. }
  19. public void doClass() {
  20. System.out.println(name+"正在上课!");
  21. //this.doHomeWork();
  22. }
  23. public void doHomeWork(){
  24. System.out.println(name+"正在写作业");
  25. }
  26. public void show() {
  27. System.out.println("姓名:"+name+" 年龄:"+age+" 学分:"+score+" 性别:"+sex);
  28. }

} //重载的时候

  1. 上述方法中:名字相同,参数列表不同,因此构成了方法重载<br />如果用户没有显式定义,编译器会生成一份默认的构造方法,生成的默认构造方法一定是无参的。
  2. ```java
  3. class MyGirlFired {
  4. public String name;
  5. public int age;
  6. public String eyes;
  7. public void eat() {
  8. System.out.println("吃火锅!");
  9. }
  10. }

在上面的代码中,没有定义任何构造方法,编译器就会默认为我们生成一个不带参数的构造方法
⚠️:一旦用户定义了,编译器则不再生成任何构造函数
image.png

  1. class MyGirlFired {
  2. public String name;
  3. public int age;
  4. public String eyes;
  5. public MyGirlFired(String name,int age){
  6. this.name = name;
  7. this.age = age;
  8. }
  9. public void eat() {
  10. System.out.println("吃火锅!");
  11. }
  12. public static void main(String[] args) {
  13. //如果编译器会生成,则生成的构造方法一定是无参数的
  14. //则此处创建对象是可以通过编译的
  15. //但实际上会报错
  16. MyGirlFired xHong = new MyGirlFired();
  17. }
  18. }

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

  1. class Student {
  2. //属性:成员变量-》类的内部 方法的外部的
  3. public String name;
  4. public int age;
  5. public double score ;
  6. public String sex;
  7. public Student() {
  8. //调用本类中 带有2个参数的构造方法,第一个参数的类型是String,第2个参数的类型是int
  9. this("yumi",18);
  10. //this("bit3",99,98.9,"女");
  11. System.out.println("这个是不带参数的构造方法!");
  12. }
  13. public Student(String name,int age) {
  14. //this();
  15. this.age = age;
  16. this.name = name;
  17. System.out.println("这个是带2个参数的构造方法!");
  18. }
  19. public Student(String name, int age, double score, String sex) {
  20. this.name = name;
  21. this.age = age;
  22. this.score = score;
  23. this.sex = sex;
  24. System.out.println("带4个参数的构造方法!");
  25. }
  26. public void doClass() {
  27. System.out.println(name+"正在上课!");
  28. //this.doHomeWork();
  29. }
  30. public void doHomeWork(){
  31. System.out.println(name+"正在写作业");
  32. }
  33. public void show() {
  34. System.out.println("姓名:"+name+" 年龄:"+age+" 学分:"+score+" 性别:"+sex);
  35. }
  36. }

⚠️注意:

  • this()必须是构造方法中的第一条语句,且只能放在构造函数中
  • 不能形成“环”

例如
image.png
绝大多数情况我们都用public来修饰,特殊场景下会被private修饰

默认初始化

上面我们提到了一个问题:为什么局部变量在使用时必须要用初始化,而成员变量可以不用呢?

  1. public class Date {
  2. public int year;
  3. public int month;
  4. public int day;
  5. public Date(int year,int month,int day){
  6. //成员变量在定义之前,并没有给出初始值,那为什么就可以使用呢?
  7. System.out.println(this.year);
  8. System.out.println(this.month);
  9. System.out.println(this.day);
  10. }
  11. public static void main(String[] args) {
  12. //此处a并没有初始化,编译器报错;
  13. //Error:(24,28)Java:可能尚未初始化变量a
  14. //int a;
  15. //System.out.println(a);
  16. Date d = new Date(2022,3,29);
  17. }
  18. }

image.png
要搞清楚这个过程,我们需要知道new关键字背后所发生的一些事情

  1. Date d = new Date(2021,6,9);

在程序层面只是简单的一条语句,而在JVM层面则需要做好多事情

  1. 检测对象对应的类是否加载了,如果没有加载则加载
  2. 为对象分配内存空间
  3. 处理并发安全问题

👉比如:多个线程同时申请对象,JVM要保证给对象分配的空间不冲突

  1. 初始化所分配的空间

即:对象空间被申请好了之后,对象中包含的成员已经设置好了初始值
image.png

数据类型 默认值
byte 0
char ‘\u0000’
short 0
int 0
long 0L
boolean false
float 0.0f
double 0.0
reference null
  1. 设置对象头信息(关于对象内存模型后面再说)
  2. 调用构造方法,是给对象中的各个成员赋值

    就地初始化

    在声明成员变量时,就直接给出了初始值 ```java public class Date { public int year; public int month; public int day;

    public Date(){

    1. System.out.println(this.year);
    2. System.out.println(this.month);
    3. System.out.println(this.day);

    } public Date(int year,int month,int day){

    1. //成员变量在定义之前,并没有给出初始值,那为什么就可以使用呢?
    2. System.out.println(this.year);
    3. System.out.println(this.month);
    4. System.out.println(this.day);

    }

    public static void main(String[] args) {

    1. //此处a并没有初始化,编译器报错;
    2. //Error:(24,28)Java:可能尚未初始化变量a
    3. //int a;
    4. //System.out.println(a);
    5. Date d1 = new Date(2022,3,29);
    6. Date d2 = new Date();

    } }

//运行结果: //0 //0 //0 //0 //0 //0

``` ⚠️注意:代码编译完成之后,编译器会将所有成员初始化的这些语句添加到各个构造函数中
image.png
image.png
5.一个类至少会有1个构造函数,就算你没有写!
6.构造方法 本质 就是来实例化对象的时候调用
(1)分配内存
(2)调用合适的构造方法
7.this可以用来调用本类中的其他构造方法【构造方法当中使用】
且必须放在第一行!所以,只能在当前构造方法当中,调用一次
8.this的用法:

  • this.data 访问属性
  • this.func(); 访问方法
  • this(); //用来调用本类中的其他构造方法

9.this不能形成环。

有错误请大家批评指正
感谢阅读