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

最近项目经理要我整理一下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、特殊情况中,在类里面实例化自己的静态类这种情况在实际编码中极少出现,所以也不太需要考虑这种情况。

你可能感兴趣的:(java)