1、构造函数是用来干什么的

构造函数是用来初始化对象的成员属性的。
举个例子:

  1. public class MyClass {
  2. private String name;
  3. private int id;
  4. public MyClass(String name, int id)
  5. {
  6. this.name = name;
  7. this.id = id;
  8. }
  9. }

在main方法里new一个MyClass对象的过程如下:

public class Main {
    public static void main(String[] args) {
        String name = "Jerry";
        int id = 1;
        MyClass myClass = new MyClass(name, id);
    }
}
  1. 通过new关键字创建一个MyClass对象,并在堆区为这个新创建的对象分配内存空间;
  2. 调用MyClass类的构造函数为这个新创建的对象初始化其成员属性name和id;
  3. 声明一个指向MyClass类型的对象引用myClass,令其指向1中新创建的对象(即myClass变量的值为1中新创建对象的内存地址)。

注意一个对象建立的过程中,构造函数仅执行一次,后面想对对象的成员做改变等操作可以调用getter/setter方法。

2、构造函数特点

构造函数也是类里的一个函数,跟普通函数不同,构造函数主要有以下几个特点:

  • 构造函数的名称必须与类名相同,区分大小写;
  • 构造函数没有返回值,也不能用void修饰;
  • 构造函数可以用任何访问修饰符(public、protected和private)修饰;
  • 构造函数不能用static、final、abstract和synchronized等关键字修饰;
  • 构造函数不能被覆写(override);
  • 构造函数可以被重载(overload),以参数的个数、类型及顺序区分;
  • 重载意味着一个类里可以有多个构造函数,构造函数1可以调用构造函数2完成对象初始化,通过this关键字:this(参数1,参数2…)实现;
  • 接口里没有构造函数,抽象类里可以有构造函数,也可以没有,抽象类的构造函数在创建继承自抽象类的子类的对象时会被调用;

    3、构造函数分类

    构造函数分为有参构造函数、无参构造函数和默认构造函数。

    3.1 有参构造函数

    带入参的构造函数叫有参构造函数,上面例子里就是有参构造函数。

    3.2 无参构造函数

    不带入参的构造函数叫无参构造函数。无参构造函数对类的成员初始化有下面两种方法:
    (1)在类成员变量声明时进行初始化

    public class MyClass {
      private String name = "Jerry";
      private int id = 1;
      public MyClass()
      {
      }
    }
    

    (2)在无参构造函数的方法体里对类成员变量初始化

    public class MyClass {
      private String name;
      private int id;
      public MyClass()
      {
          this.name = "Jerry";
          this.id = 1;
      }
    }
    

    3.3 默认构造函数

    当类里没有显式地定义代码来实现任何构造函数时,Java编译器将会在编译的字节码(.class文件)里面为这个类插入默认构造函数。由于是在编译阶段插入,因此在源文件(.java)里是不会找到默认构造函数的代码的。
    默认构造函数形式上类似无参构造函数(都没有入参),但是二者并不能划等号,区别如下:

  • 默认构造函数方法体为空,无参构造函数方法体可以为空也可以不为空;

  • 只要类里显式的创建了一个构造函数,比如一个无参构造函数,编译器都不会再为这个类创建默认构造函数了。

    4、子类中调用父类的构造函数

    在Java中,子类的构造函数必须调用父类的构造函数,这个调用可以是隐式地调用,即代码里没有直接调用父类构造函数,但是编译器会默认调用父类的无参构造函数或者默认构造函数;也可以显示地通过super关键字调用父类的构造函数,总之一句话:子类的构造函数必须调用父类的构造函数。
    **
    子类中调用父类的构造函数分为以下两个场景:

  • 当父类定义了无参构造函数,或者父类源码里没有显式定义构造函数而是编译器插入了一个默认构造函数,则子类构造函数里可以不显式地调用父类构造函数,此时编译器会在子类的构造函数里默认调用父类的无参构造函数或者默认构造函数;

  • 当父类没有定义无参构造函数,且定义了有参构造函数,则在子类的构造函数里必须通过super语句显式地调用父类的有参构造函数。

(1)对于第一个场景,且父类里包含无参构造函数,举例如下:
父类:

public class Dog {
    private String name;
    public Dog()
    {
        System.out.println("调用Dog类的无参构造函数");
    }
}

子类:

public class YellowDog extends Dog {
    private String name;
    public YellowDog()
    {
        System.out.println("调用YellowDog类的无参构造函数");
    }
}

main函数:

public class Main {
    public static void main(String[] args) {
        Dog dog = new YellowDog();
    }
}

结果如下:

调用Dog类的无参构造函数
调用YellowDog类的无参构造函数

此时创建子类对象时,会先调用父类的无参构造函数,再调用子类的无参构造函数。
如果把父类的无参构造函数去掉,即父类里不定义任何构造函数,此时运行结果如下:

调用YellowDog类的无参构造函数

此时创建子类对象时,会先调用父类的默认构造函数,再调用子类的无参构造函数。
(2)对于第二个场景,举例如下:
父类:

public class Dog {
    private String name;
    public Dog(String name)
    {
        this.name= name;
        System.out.println("调用Dog类的有参构造函数");
    }
}

父类里不包含无参构造函数,仅有有参构造函数,此时子类如果不显式地调用父类的有参构造函数会报错,如图:
Java构造函数 - 图1
提示父类里没有默认构造函数,那是因为子类里没有显式地调用父类构造函数,编译器会去父类里寻找无参构造函数,无参构造函数也没有就看能否找到默认构造函数,默认构造函数也找不到就会报上面的错误。
正确的做法是在子类里通过super语句显式地调用父类的构造函数,如下:
子类:

public class YellowDog extends Dog {
    private String name;
    public YellowDog(String name)
    {
        super(name);
        System.out.println("调用YellowDog类的有参构造函数");
    }
}

Main函数:

public class Main {
    public static void main(String[] args) {
        String name = "Jerry";
        Dog dog = new YellowDog(name);
    }
}

执行main函数结果如下:

调用Dog类的有参构造函数
调用YellowDog类的有参构造函数

此时创建子类对象时,会先调用父类的有参构造函数,再调用子类的有参构造函数。

参考

lea:Java中的构造函数——通过示例学习Java编程(14) zhuanlan.zhihu.com
https://blog.csdn.net/yuyan1988nb/article/details/78133738 blog.csdn.net
Java里的构造函数(构造方法) - LivterJA - 博客园 www.cnblogs.com