java类静态域、块,非静态域、块,构造函数的初始化顺序

原文:http://ini.iteye.com/blog/2007835

面试的时候,经常会遇到这样的考题:给你两个类的代码,它们之间是继承的关系,每个类里只有构造器方法和一些变量,

构造器里可能还有一段代码对变量值进行了某种运算,另外还有一些将变量值输出到控制台的代码,然后让我们判断输出的
结果。这实际上是在考查我们对于继承情况下类的初始化顺序的了解。 
我们大家都知道,对于静态变量、静态初始化块、变量、初始化块、构造器,它们的初始化顺序以此是 
(静态变量、静态初始化块)>(变量、初始化块)>构造器。 
我们也可以通过下面的测试代码来验证这一点:  
Java代码

Java代码  

public class InitialOrderTest {
    // 静态变量
    public static String staticField = "静态变量";
    // 变量
    public String field = "变量";
    // 静态初始化块
    static {
        System.out.println(staticField);
        System.out.println("静态初始化块");
    }
    // 初始化块
    {
        System.out.println(field);
        System.out.println("初始化块");
    }
    // 构造器
    public InitialOrderTest() {
        System.out.println("构造器");
    }
    public static void main(String[] args) {
        new InitialOrderTest();
    }
}  

运行以上代码,我们会得到如下的输出结果: 
静态变量  
静态初始化块  
变量  
初始化块  
构造器 
这与上文中说的完全符合。 
那么对于继承情况下又会怎样呢?我们仍然以一段测试代码来获取最终结果: 
Java代码

Java代码  

class Parent {
    // 静态变量
    public static String p_StaticField = "父类--静态变量";
    // 变量
    public String p_Field = "父类--变量";
    // 静态初始化块
    static {
        System.out.println(p_StaticField);
        System.out.println("父类--静态初始化块");
    }
    // 初始化块
    {
        System.out.println(p_Field);
        System.out.println("父类--初始化块");
    }
    // 构造器
    public Parent() {
        System.out.println("父类--构造器");
    }
}
public class SubClass extends Parent {
    // 静态变量
    public static String s_StaticField = "子类--静态变量";
    // 变量
    public String s_Field = "子类--变量";
    // 静态初始化块
    static {
        System.out.println(s_StaticField);
        System.out.println("子类--静态初始化块");
    }
    // 初始化块
    {
        System.out.println(s_Field);
        System.out.println("子类--初始化块");
    }
    // 构造器
    public SubClass() {
        System.out.println("子类--构造器");
    }
    // 程序入口
    public static void main(String[] args) {
        new SubClass();
    }
}  

运行一下上面的代码,结果马上呈现在我们的眼前:

父类--静态变量  
父类--静态初始化块  
子类--静态变量  
子类--静态初始化块  
父类--变量  
父类--初始化块  
父类--构造器  
子类--变量  
子类--初始化块  
子类--构造器 
现在,结果已经不言自明了。大家可能会注意到一点,那就是,并不是父类完全初始化完毕后才进行子类的初始化, 
实际上子类的静态变量和静态初始化块的初始化是在父类的变量、初始化块和构造器初始化之前就完成了。 
那么对于静态变量和静态初始化块之间、变量和初始化块之间的先后顺序又是怎样呢? 
是否静态变量总是先于静态初始化块,变量总是先于初始化块就被初始化了呢?实际上这取决于它们在类中出现的先后顺序。 
我们以静态变量和静态初始化块为例来进行说明。 同样,我们还是写一个类来进行测试:

Java代码

Java代码  

public class TestOrder {
    // 静态变量
    public static TestA a = new TestA();
    // 静态初始化块
    static {
        System.out.println("静态初始化块");
    }
    // 静态变量
    public static TestB b = new TestB();
    public static void main(String[] args) {
        new TestOrder();
    }
}
class TestA {
    public TestA() {
        System.out.println("Test--A");
    }
}
class TestB {
    public TestB() {
        System.out.println("Test--B");
    }
}  

运行上面的代码,会得到如下的结果:

Test--A  
静态初始化块  
Test--B 
大家可以随意改变变量a、变量b以及静态初始化块的前后位置,就会发现输出结果随着它们在类中出现的前后顺序而改变, 
这就说明静态变量和静态初始化块是依照他们在类中的定义顺序进行初始化的。同样,变量和初始化块也遵循这个规律。 
了解了继承情况下类的初始化顺序之后,如何判断最终输出结果就迎刃而解了。

测试函数:

Java代码  

public class TestStaticCon {
    public static int a = 0;
      static {
        a = 10;
          System.out.println("父类的静态代码块在执行a=" + a);
    }
    {
        a = 8;
          System.out.println("父类的非静态代码块在执行a=" + a);
    }
      public TestStaticCon() {   

        this("a在父类带参构造方法中的值:" + TestStaticCon.a); // 调用另外一个构造方法
        System.out.println(a);
        System.out.println("父类无参构造方法在执行a=" + a);
    }
      public TestStaticCon(String n) {
        System.out.println(n);
        System.out.println(a);
      }
      public static void main(String[] args) {
        TestStaticCon tsc = null;
          System.out.println("!!!!!!!!!!!!!!!!!!!!!");
        tsc = new TestStaticCon();
    }
}  

运行结果:

父类的非静态代码块在执行a=10 
!!!!!!!!!!!!!!!!!!!!! 
父类的非静态代码块在执行a=8 
a在父类带参构造方法中的值:10 


父类无参构造方法在执行a=8

结论:静态代码块是在类加载时自动执行的,非静态代码块是在创建对象时自动执行的代码,不创建对象不执行该类的非静态代码块。且执行顺序为静态代码块------非静态代码块----构造函数。 
扩展:静态代码块  与  静态方法: 
一般情况下,如果有些代码必须在项目启动的时候就执行的时候,需要使用静态代码块,这种代码是主动执行的; 
需要在项目启动的时候就初始化,在不创建对象的情况下,其他程序来调用的时候,需要使用静态方法,这种代码是被动执行的.  
两者的区别就是:静态代码块是自动执行的;  静态方法是被调用的时候才执行的.

作用:静态代码块可用来初始化一些项目最常用的变量或对象;静态方法可用作不创建对象也可能需要执行的代码

阿里笔试题:

求下面这段代码的输出:

Java代码  

public class Test1 {  

    public static int k = 0;
    public static Test1 t1 = new Test1("t1");
    public static Test1 t2 = new Test1("t2");
    public static int i = print("i");
    public static int n = 99;
    public int j = print("j");
    {
        print("构造块");
    }  

    static{
        print("静态块");
    }  

    public Test1(String str){
        System.out.println((++k)+":"+str+"    i="+i+"    n="+n);
        ++i;++n;
    }  

    public static int print(String str){
        System.out.println((++k)+":"+str+"    i="+i+"    n="+n);
        ++n;
        return ++i;
    }  

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        Test1 t = new Test1("init");
    }  

}  

运行结果:

1:j    i=0    n=0
2:构造块    i=1    n=1
3:t1    i=2    n=2
4:j    i=3    n=3
5:构造块    i=4    n=4
6:t2    i=5    n=5
7:i    i=6    n=6
8:静态块    i=7    n=99
9:j    i=8    n=100
10:构造块    i=9    n=101
11:init    i=10    n=102

时间: 2024-10-13 15:07:57

java类静态域、块,非静态域、块,构造函数的初始化顺序的相关文章

java 子类、父类中静态代码块、字段,非静态代码块、字段以及构造函数的初始化顺序和次数

一个类中的数据初始化顺序是面试官非常喜欢出的面试题之一,本文用一个实例来介绍java中子类.父类中静态代码块.字段,非静态代码块.字段以及构造函数的执行顺序和次数. 一.包结构 Cat继承Animal Runner里包含main函数,程序入口 Sing类主要用来打印执行顺序的序列号 二.代码 public class Animal { Sing s1=new Sing(1,"animal\tnot static field"); static Sing s2=new Sing(2,&q

Java类静态属性、静态块、非静态属性、非静态块、构造函数在初始化时的执行顺序

前言 今天在看Android ContentProvider实现的时候,突然想到了Java类在new的过程中,静态域.静态块.非静态域.非静态块.构造函数的执行顺序问题.其实这是一个很经典的问题,非常考察对Java基础知识的掌握程度.很多面试过程中相信也有这样的问题,趁着周末有时间复习一下. 结论 这里先把整理好的结论抛给大家,然后我在写个程序来验证我们的结论.在Java类被new的过程中,执行顺序如下: 实现自身的静态属性和静态代码块.(根据代码出现的顺序决定谁先执行) 实现自身的非静态属性和

java中类的静态代码块、构造代码块、构造方法、静态成员的初始化顺序详解

一.描述 java中类的静态代码块.构造代码块.构造方法.静态成员的初始化顺序: 首先,在类加载时,为类中的静态成员变量分配内存空间,并为其赋予默认值(整数型为0,浮点型为0.0,布尔型为false,字符型为'\u0000',引用型为null): 其次,执行静态成员变量的显式初始化操作.静态成员的初始化方法有两种,其一为在声明的时候直接初始化,其二为使用静态代码块进行初始化, 这两种初始化方式会按照在类中的出现顺序来执行,而且只在类加载的时候执行一次. 再次,如果创建了类的对象,便会在堆中为类的

C++成员变量、构造函数的初始化顺序 [转]

C++成员变量.构造函数的初始化顺序 一.C++成员变量初始化 1.普通的变量:一般不考虑啥效率的情况下 可以在构造函数中进行赋值.考虑一下效率的可以再构造函数的初始化列表中进行 2.static 静态变量(本地化数据和代码范围): static变量属于类所有,而不属于类的对象,因此不管类被实例化了多少个对象,该变量都只有一个.在这种性质上理解,有点类似于全局变量的唯一性. 函数体内static变量的作用范围时该函数体,不同于auto变量,该变量内存只被分配一次,因此其值在下次调用时维持上次的值

java 类、方法、代码块修饰式关键字总结

super 关键字 this和super的区别 访问成员的区别 this关键字 this特点 this使用场景 static关键字 例子 访问权限修饰符 特点 总结: 四个修饰符的特点 访问权限修饰符的宽严关系 final关键字 用法 自定义常量 final修饰类 final修饰成员变量 final修饰局部变量 final修饰成员方法 super 关键字 this和super的区别 this代表当前对象,super可以理解为父类对象 既然super理解为一个父类对象,那么super应该可以用访问

静态代码块、构造代码块、构造函数以及Java类初始化顺序

静态代码块:用staitc声明,jvm加载类时执行,仅执行一次构造代码块:类中直接用{}定义,每一次创建对象时执行.执行顺序优先级:静态块,main(),构造块,构造方法. 构造函数 public HelloA(){//构造函数 } 关于构造函数,以下几点要注意:1.对象一建立,就会调用与之相应的构造函数,也就是说,不建立对象,构造函数时不会运行的.2.构造函数的作用是用于给对象进行初始化.3.一个对象建立,构造函数只运行一次,而一般方法可以被该对象调用多次. 构造代码块 {//构造代码块 }

Java类的初始化顺序 (静态变量、静态初始化块、变量、初始化块、构造器)(转)

大家在去参加面试的时候,经常会遇到这样的考题:给你两个类的代码,它们之间是继承的关系,每个类里只有构造器方法和一些变量,构造器里可能还有一段代码对变量值进行了某种运算,另外还有一些将变量值输出到控制台的代码,然后让我们判断输出的结果.这实际上是在考查我们对于继承情况下类的初始化顺序的了解. 我们大家都知道,对于静态变量.静态初始化块.变量.初始化块.构造器,它们的初始化顺序以此是(静态变量.静态初始化块)>(变量.初始化块)>构造器.我们也可以通过下面的测试代码来验证这一点: Java代码 p

【深入理解JVM】:Java类继承关系中的初始化顺序

Java类初始化的顺序经常让人犯迷糊,现在本文尝试着从JVM的角度,对Java非继承和继承关系中类的初始化顺序进行试验,尝试给出JVM角度的解释. 非继承关系中的初始化顺序 对于非继承关系,主类InitialOrderWithoutExtend中包含了静态成员变量(类变量)SampleClass 类的一个实例,普通成员变量SampleClass 类的2个实例(在程序中的顺序不一样)以及一个静态代码块,其中静态代码块中如果静态成员变量sam不为空,则改变sam的引用.main()方法中创建了2个主

Java类继承关系中的初始化顺序

Java类初始化的顺序经常让人犯迷糊,现在本文尝试着从JVM的角度,对Java非继承和继承关系中类的初始化顺序进行试验,尝试给出JVM角度的解释. 非继承关系中的初始化顺序 对于非继承关系,主类InitialOrderWithoutExtend中包含了静态成员变量(类变量)SampleClass 类的一个实例,普通成员变量SampleClass 类的2个实例(在程序中的顺序不一样)以及一个静态代码块,其中静态代码块中如果静态成员变量sam不为空,则改变sam的引用.main()方法中创建了2个主