构造器、初始化块

构造器

  每个类都默认有一个无参的构造器,在使用类创建的对象时,会优先调用构造器,如果手动添加了构造器,那么默认的无参构造器将会消失

无参构造器

  创建如下类

1
2
3
4
5
6
7
8
9
public class Root {
public void print() {
System.out.println("Root的print方法");
}

public Root() {
System.out.println("Root的构造器");
}
}

  使用如下方式调用,将会输出

1
2
3
4
// 将会输出下面两行
// Root的构造器
// Root的print方法
new Root().print();

构造器重载

  再给Root添加一个有参数的构造器,然后在无参构造器中调用它,如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Root {
public void print() {
System.out.println("Root的print方法");
}

public Root() {
this("Root无参构造器调用");
System.out.println("Root的构造器");
}

public Root(String value) {
System.out.println("Root的构造器,参数 = " + value);
}
}

  使用如下方式调用,将会输出

1
2
3
4
5
// 将会输出下面两行
// Root的构造器,参数 = Root无参构造器调用
// Root的构造器
// Root的print方法
new Root().print();

  这是因为在Root的无参构造器中使用this()调用了另外一个构造器,需要注意的是,使用this()对构造器的重载中,必须在构造器的第一行调用

调用父类构造器

  创建一个Mid类,使他继承上面的Root类,并给它添加一个构造器

1
2
3
4
5
public class Mid extends Root {
public Mid() {
System.out.println("Mid的构造器");
}
}

  使用如下方式调用,将会输出

1
2
3
4
5
// 将会输出下面三行
// Root的构造器,参数 = Root无参构造器调用
// Root的构造器
// Mid的构造器
new Mid();

  由上可以得出,父类的构造器要比子类构造器先执行,这是因为如果没有手动使用super()调用父类构造器的话,在子类构造器执行前,会隐式调用父类的无参构造器

  如果手动使用super()调用父类构造器,则必须要写在子类构造器执行体的第一行,这注定了this()和super()不会同时出现在同一个构造方法中

  如下,当使用this()重载构造器时,super()就只能写在最后一个被重载的构造器的第一行

1
2
3
4
5
6
7
8
9
10
11
public class Mid extends Root {
public Mid() {
this("Mid无参构造器调用");
System.out.println("Mid的构造器");
}

public Mid(String value) {
super("Mid有参构造器调用");
System.out.println("Mid的构造器,参数 = " + value);
}
}

  使用Mid创建对象,将会输出

1
2
3
4
// Root的构造器,参数 = Mid有参构造器调用
// Mid的构造器,参数 = Mid无参构造器调用
// Mid的构造器
new Mid();

  上面代码中的调用顺序,Mid无参构造器 -> Mid有参构造器 -> Root有参构造器

初始化块

  在Root和Mid中分别创建初始化块

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Root {
{
System.out.println("Root的初始化块");
}

public Root() {
this("Root无参构造器调用");
System.out.println("Root的构造器");
}

public Root(String value) {
System.out.println("Root的构造器,参数 = " + value);
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Mid extends Root {
{
System.out.println("Mid的初始化块");
}

public Mid() {
this("Mid无参构造器调用");
System.out.println("Mid的构造器");
}

public Mid(String value) {
System.out.println("Mid的构造器,参数 = " + value);
}
}

  通过如下代码调用,将会输出

1
2
3
4
5
6
7
// Root的初始化块
// Root的构造器,参数 = Root无参构造器调用
// Root的构造器
// Mid的初始化块
// Mid的构造器,参数 = Mid无参构造器调用
// Mid的构造器
new Mid();

  由上可以得出,该类的初始化块总是在该类的构造方法执行前、在super()执行后被执行

静态初始化块

  在Root和Mid中分别创建静态初始化块

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class Root {
static {
System.out.println("Root的静态初始化块");
}

{
System.out.println("Root的初始化块");
}

public Root() {
this("Root无参构造器调用");
System.out.println("Root的构造器");
}

public Root(String value) {
System.out.println("Root的构造器,参数 = " + value);
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class Mid extends Root {
static {
System.out.println("Mid的静态初始化块");
}

{
System.out.println("Mid的初始化块");
}

public Mid() {
this("Mid无参构造器调用");
System.out.println("Mid的构造器");
}

public Mid(String value) {
System.out.println("Mid的构造器,参数 = " + value);
}
}

  通过如下代码调用,将会输出

1
2
3
4
5
6
7
8
9
// Root的静态初始化块
// Mid的静态初始化块
// Root的初始化块
// Root的构造器,参数 = Root无参构造器调用
// Root的构造器
// Mid的初始化块
// Mid的构造器,参数 = Mid无参构造器调用
// Mid的构造器
new Mid();

  由上可知,父类的静态初始化块先被执行,然后是子类的静态初始化块,接着是父类初始化块、父类构造器、子类初始化块、子类构造器。
  这是因为静态初始化块与类相关联,而不是与对象相关联,所以当类被加载到内存中时,静态初始化块被执行。
  而父类要先于子类加载到内存中,所以先执行了父类中的静态初始化块,然后是子类的静态初始化块。在创建Mid对象时,又执行了父类初始化块、父类构造器、子类初始化块、子类构造器。