静态代码块,构造代码块,构造函数及其执行顺序和逻辑

静态代码块:

  1. 随着类的加载而执行,只执行一次,并优先于主函数。具体说,静态代码块是由类调用的。类调用时,先执行静态代码块,然后才执行主函数的。
  2. 静态代码块其实就是给类初始化的,而构造代码块是给对象初始化的。
  3. 静态代码块中的变量是局部变量,与普通函数中的局部变量性质没有区别。
  4. 一个类中可以有多个静态代码块(顺序由上至下)。

构造代码块

  1. 构造代码块的作用是给对象进行初始化。
  2. 对象一建立就运行构造代码块了,而且优先于构造函数执行。这里要强调一下,有对象建立,才会运行构造代码块,类不能调用构造代码块的,而且构造代码块与构造函数的执行顺序是前者先于后者执行。
  3. 构造代码块与构造函数的区别是:构造代码块是给所有对象进行统一初始化,而构造函数是给对应的对象初始化,因为构造函数是可以多个的,运行哪个构造函数就会建立什么样的对象,但无论建立哪个对象,都会先执行相同的构造代码块。也就是说,构造代码块中定义的是不同对象共性的初始化内容。

构造函数

  1. 对象一建立,就会调用与之相应的构造函数,也就是说,不建立对象,构造函数是不会运行的。
  2. 构造函数的作用是用于给对象进行初始化。
  3. 一个对象建立,构造函数只运行一次,而一般方法可以被该对象调用多次。

Java类初始化顺序

  1. 单一类:静态变量,静态代码块 > 变量,构造代码块 > 构造函数
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    public class Test06 {

    public static String x = "静态变量";
    public String y = "变量";

    public Test06() {
    System.out.println("构造函数");
    }

    static {
    System.out.println(x);
    System.out.println("静态代码块");
    }

    {
    System.out.println(y);
    System.out.println("构造代码块");
    }

    public static void main(String[] args) {
    /* 输出:静态变量
    静态代码块
    */

    Test06 test06 = new Test06();
    /* 输出:变量
    构造代码块
    构造函数
    */
    }
    }
  2. 继承情况:父类静态 > 子类静态 > 父类构造代码块 > 父类构造函数 > 子类构造代码块 > 子类构造函数

静态变量是属于类的,和继承无关!!!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
public class Test06 {

public Test06() {
System.out.println("父类构造函数");
}

static {
System.out.println("父类静态代码块");
}

{
System.out.println("父类构造代码块");
}
}
class Test06_1 extends Test06 {

public Test06_1() {
System.out.println("子类构造函数");
}

static {
System.out.println("子类静态代码块");
}

{
System.out.println("子类构造代码块");
}

public static void main(String[] args) {
/* 输出:父类静态代码块
子类静态代码块
*/

Test06_1 test06_1 = new Test06_1();
/* 输出:父类构造代码块
父类构造函数
子类构造代码块
子类构造函数
*/
}
}

练习

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public class StaticTest {

public static void main(String[] args) {
staticFunction();
}

// 静态变量(有实例化的过程,这就是本题的重点)
static StaticTest st = new StaticTest();
static {
//System.out.println(b); // 编译报错:因为b在构造代码块后边,此处不能引用。因此Java代码是从上到下的顺序
System.out.println("1");
}
{
System.out.println("2");
}
StaticTest() {
System.out.println("3");
System.out.println("a=" + a + ",b=" + b);
}
public static void staticFunction() {
System.out.println("4");
}

// 这两个变量写在最后面
int a = 110;
static int b = 112;
}

/* 输出:2
3
a=110,b=0
1
4
*/
  1. 先初始化静态变量,也就是执行new StaticTest(),先执行构造代码块,输出 2
  2. 再执行构造函数,输出 3 和 a=110,b=0
    • 在执行构造函数前,必须初始化实例属性,故 a = 110
    • 静态变量从上到下初始化,还没有轮到,因此 b = 0
  3. 执行静态代码块,输出 1
  4. 最后进入main函数,执行静态方法staticFunction,输出 4

可以发现:static变量并不一定在实例化变量前被初始化

父类和子类有同名属性时

public class Test07 {
    public static void main(String[] args) {
        // 使用多态
        Parent chidParent = new Child();
        System.out.println("Parent:" + chidParent.getAge()); //40
        System.out.println("Parent:" + chidParent.age); //18
        System.out.println("Parent:" + chidParent.id); //08

        // 直接使用原本类型
        Child child = new Child();
        System.out.println("Child:" + child.getAge()); //40
        System.out.println("Child:" + child.age); //40
        System.out.println("Child:" + child.id); //8
    }
}

class Child extends Parent {
    public Integer age = 40;
    public Integer id = 8;

    public int getAge() {
        return age;
    }
}

class Parent {
    public Integer age = 18;
    public String id = "08";

    public int getAge() {
        return age;
    }
}

对于输出 18 的解释(Java的继承机制):

  1. 属性属于实例自己的,所以Parent的age属性值是18
  2. 属性不存在覆盖(即使同名),而方法是实实在在的覆盖(复写)
作者

Benboby

发布于

2020-07-17

更新于

2021-01-28

许可协议

Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×