静态代码块、静态变量、构造代码块、构造方法的执行顺序

时间:2021-02-24 19:32:15

最近项目经理要我整理一下java基础,找个重点讲一下,刚好最近在研究关于静态static的东西,所以拿出来先分享一下。

首先讲一下java中有关静态代码块、静态变量、构造代码块、构造方法的定义。

静态代码块:直接在类中定义的并且加了static关键字的代码块{},静态代码块是在类加载时自动执行的一段语句。形如:

public class Test
{
static {
//.......
}
}

静态变量:被static修饰的变量。形如:

static int num = 1;
构造代码块:直接在类中定义的没有加static关键字的代码块{}。   
public class Test
{
{
//.......
}
}

构造方法:

public class Test
{
Test()
{
//无参构造方法
}
Test(int i)
{
//有参构造方法
}
}
然后开始我们的实验。

1、对于一个类里的静态变量和静态代码块,执行的顺序是谁在前先执行谁

测试代码:

Test4.java

public class Test4 
{
public static int no = 1;
public static int num1 = 6;
static {
if(num1 == 6)
System.out.println("第" + (no++) + "次加载静态变量");
System.out.println("第" + (no++) + "次加载静态代码块");
}
public static int num2 = 10;
static {
if(num2 == 10)
System.out.println("第" + (no++) + "次加载静态变量");
System.out.println("第" + (no++) + "次加载静态代码块");
}

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

}

测试结果:

第1次加载静态变量
第2次加载静态代码块
第3次加载静态变量
第4次加载静态代码块

2、对于一个类里的静态代码块(静态变量)、构造代码块、构造方法的执行顺序为:
静态代码块(静态变量)>实例化[构造代码块]>实例化[构造方法]
测试代码: Test5.java
public class Test5 
{
public static int no = 1;

{
System.out.println("第" + (no++) + "次加载构造代码块");
}

static {
System.out.println("第" + (no++) + "次加载静态代码块");
}

Test5()
{
System.out.println("第" + (no++) + "次加载构造方法");
}

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

}
测试结果:
第1次加载静态代码块
第2次加载构造代码块
第3次加载构造方法

3、对于有继承关系的父类子类中的静态代码块(静态变量)、构造代码块、构造方法的执行顺序为:
父类静态代码块(静态变量)>子类的静态代码块(静态变量)>父类构造代码块>父类构造方法>子类构造代码块>子类构造方法
测试代码: TestParent.java
public class TestParent 
{
{
System.out.println("这是父类的构造代码块");
}

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

TestParent()
{
System.out.println("这是父类的构造方法");
}
}
TestChild.java
public class TestChild extends TestParent
{
{
System.out.println("这是子类的构造代码块");
}

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

TestChild()
{
System.out.println("这是子类的构造方法");
}
}
Test6.java
public class Test6 
{
public static void main(String args[])
{
new TestChild();
}
}
测试结果:
这是父类的静态代码块
这是子类的静态代码块
这是父类的构造代码块
这是父类的构造方法
这是子类的构造代码块
这是子类的构造方法
其中静态代码块(静态变量)和静态方法的区别在于静态代码块是自动执行,而静态方法则是被调用的时候才执行。
4、对于静态变量和静态代码块,自动执行且只执行一次。
构造代码块和构造方法是实例化几次执行几次。注意是实例化,不是声明,声明不会执行构造代码块和构造方法
测试代码: Test7.java
public class Test7 
{
public static int no = 1;
static {
if(no == 1)
System.out.println("第" + (no++) + "次加载静态变量");
System.out.println("第" + (no++) + "次加载静态代码块");
}

{
System.out.println("第" + (no++) + "次加载构造代码块");
}

Test7()
{
System.out.println("第" + (no++) + "次加载构造方法");
}

public static void main(String args[])
{
new Test7();
new Test7();
}
}
测试结果:
第1次加载静态变量
第2次加载静态代码块
第3次加载构造代码块
第4次加载构造方法
第5次加载构造代码块
第6次加载构造方法

特殊情况1、当类里面声明并实例化一个自己的静态类,对于当前的静态变量而言,如果静态变量在静态类之前,则先执行静态变量;如果静态变量在静态类之后,则绕开静态变量,执行静态类里面的构造代码块和构造方法:
静态类[构造代码块]>静态类[构造方法]>当前类[静态代码块(静态变量)]>实例化[构造代码块]>实例化[构造方法]
测试代码: Test8.java
public class Test8   
{  
public static int no = 1; 
    public static Test8 t8 = new Test8();  
    
    static {  
        if(no == 1)  
            System.out.println("第" + (no++) + "次加载静态变量");  
        System.out.println("第" + (no++) + "次加载静态代码块");  
    }  
      
    {  
        System.out.println("第" + (no++) + "次加载构造代码块");  
    }  
      
    Test8()  
    {  
        System.out.println("第" + (no++) + "次加载构造方法");  
    }  
      
    public static void main(String args[])  
    {  
        new Test8();  
    }  
}  
测试结果:
第1次加载构造代码块
第2次加载构造方法
第3次加载静态代码块
第4次加载构造代码块
第5次加载构造方法

Test8.java(1)
public class Test8   
{

public static Test8 t8 = new Test8();
public static int no = 1;

static {
if(no == 1)
System.out.println("第" + (no++) + "次加载静态变量");
System.out.println("第" + (no++) + "次加载静态代码块");
}

{
System.out.println("第" + (no++) + "次加载构造代码块" );
}

Test8()
{
System.out.println("第" + (no++) + "次加载构造方法");
}

public static void main(String args[])
{
new Test8();
}
}
测试结果:
第0次加载构造代码块
第1次加载构造方法
第1次加载静态变量
第2次加载静态代码块
第3次加载构造代码块
第4次加载构造方法

注意:这里之所以加载构造代码块的序号为0,是因为静态变量在静态类之后,而在静态类执行的时候,是不执行静态变量、静态代码块的,但是,会对静态变量进行声明,虽然能访问到no这个变量,但是没有初始化,所以为0。当加载构造方法时因为 no自增加了1,所以值变为1。但是当执行完静态类以后,no变量又被初始化为1。这也进一步验证了静态变量、静态代码块只执行一次。
特殊情况2、当类里面声明并实例化一个具有父类的自己的静态类,则该类的执行顺序为:
父类的静态代码块(静态变量)>父类的构造代码块>父类的构造方法>子类的构造代码块>子类的构造方法>子类的静态代码块(静态变量)
测试代码: TestParent.java
public class TestParent 
{
{
System.out.println("这是父类的构造代码块");
}

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

TestParent()
{
System.out.println("这是父类的构造方法");
}
}
TestChild1.java
public class TestChild1 extends TestParent
{
public static TestChild1 tc1 = new TestChild1();
{
System.out.println("这是子类的构造代码块");
}

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

TestChild1()
{
System.out.println("这是子类的构造方法");
}

public static void main(String args[])
{

}
}
测试结果:
这是父类的静态代码块
这是父类的构造代码块
这是父类的构造方法
这是子类的构造代码块
这是子类的构造方法
这是子类的静态代码块

特殊情况3、当类里面声明并实例化一个不是当前类的静态类,则该类的执行顺序为:
静态类[静态代码块(静态变量)]>静态类[构造代码块]>静态类[构造方法]>当前类[静态代码块(静态变量)]
测试代码: Test9.java
public class Test9 {
public static Test10 t10;
public static Test10 t11 = new Test10();
public static int no = 1;
static {
if(no == 1)
System.out.println("第" + (no++) + "次加载静态变量");
System.out.println("第" + (no++) + "次加载静态代码块");
}

{
System.out.println("第" + (no++) + "次加载构造代码块");
}

Test9()
{
System.out.println("第" + (no++) + "次加载构造方法");
}

public static void main(String args[])
{

}

}
Test10.java
public class Test10 
{
public static int no = 1;
static {
if(no == 1)
System.out.println("第" + (no++) + "次加载静态变量");
System.out.println("第" + (no++) + "次加载静态代码块");
}

{
System.out.println("第" + (no++) + "次加载构造代码块");
}

Test10()
{
System.out.println("第" + (no++) + "次加载构造方法");
}

}
测试结果:
第1次加载静态变量
第2次加载静态代码块
第3次加载构造代码块
第4次加载构造方法
第1次加载静态变量
第2次加载静态代码块
这里可以看出,如果实例化的静态类不是当前类,那么该静态类的执行顺序还是正常的,也就是说不会绕开静态代码块和静态变量。注意,这里如果没有实例化而只是声明了静态类,那么该静态类不会执行任何语句。

总结: 1、不考虑特殊情况,类的执行顺序为静态代码块(静态变量)>构造代码块>构造方法 2、特殊情况中,在类里面实例化自己的静态类这种情况在实际编码中极少出现,所以也不太需要考虑这种情况。