原文地址:http://zangweiren.javaeye.com/blog/208122作者:臧圩人 
网址:http://zangweiren.javaeye.com 大家在去参加面试的时候,经常会遇到这样的考题:给你两个类的代码,它们之间是继承的关系,每个类里只有构造器方法和一些变量,构造器里可能还有一段代码对变量值进行了某种运算,另外还有一些将变量值输出到控制台的代码,然后让我们判断输出的结果。这实际上是在考查我们对于继承情况下类的初始化顺序的了解。 我们大家都知道,对于静态变量、静态初始化块、变量、初始化块、构造器,它们的初始化顺序以此是静态变量>静态初始化块>变量>初始化块>构造器。我们也可以通过下面的测试代码来验证这一点: 
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();   
    }   
}  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代码 
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();   
    }   
}  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();
        }
}运行一下上面的代码,结果马上呈现在我们的眼前: 父类--静态变量 
父类--静态初始化块 
子类--静态变量 
子类--静态初始化块 
父类--变量 
父类--初始化块 
父类--构造器 
子类--变量 
子类--初始化块 
子类--构造器 现在,结果已经不言自明了。大家可能会注意到一点,那就是,并不是父类完全初始化完毕后才进行子类的初始化,实际上子类的静态变量和静态初始化块的初始化是在父类的变量、初始化块和构造器初始化之前就完成了。 了解了继承情况下类的初始化顺序之后,如何判断最终输出结果就迎刃而解了。

解决方案 »

  1.   

    此回复为自动发出,仅用于显示而已,并无任何其他特殊作用
    楼主【zangweiren】截止到2008-06-26 10:40:09的历史汇总数据(不包括此帖):
    发帖数:0                  发帖分:0                  
    结贴数:0                  结贴分:0                  
    未结数:0                  未结分:0                  
    结贴率:-------------------结分率:-------------------
    如何结贴请参考这里:http://topic.csdn.net/u/20080501/09/ef7ba1b3-6466-49f6-9d92-36fe6d471dd1.html
      

  2.   

    // 静态初始化块
    static
    {
    System.out.println(staticField);
    System.out.println("静态初始化块");
    }
    //静态变量
    public static String staticField = "静态变量";
    /* 这样的话,就报错了,如果是先初始化静态变量,那就不会报错了*/
      

  3.   

    楼主所说的初始化顺序是:
    静态变量>静态初始化块>变量>初始化块>构造器。// 静态初始化块
    static
    {
    System.out.println(staticField);//这里报错的
    System.out.println("静态初始化块");
    }
    //静态变量
    public static String staticField = "静态变量";
    /*如果是先初始化staticField,那为何报错呢*/
    /*再看下面*/
            {
    System.out.println(field);
    System.out.println("初始化块");
    }

    public static String field = "变量";
    /*这个就没错,因为在执行初始化块之前,所有static属性都初始化完成了,所以没报错*/
    /*我的观点还是:static属性  是根据代码顺序去加载的*/
      

  4.   


    class Initiaalization
    {
    public static void main(String[] args)
    {
    new Initiaalization( );
    } public static String staticField = "静态变量";
    //1 static A a = new A();
    static
    {
    System.out.println(staticField);
    System.out.println("静态初始化块");
    } static A a = new A();}
    class A
    { public static String staticField = "A的静态变量"; static
    {
    System.out.println(staticField);
    System.out.println("A的静态初始化块");
    }}
    /*楼主不说静态变量先初始化吗?  为什么先执行的静态初始化块*/
      

  5.   


    先re再看,是 thinking  in  java的吗?
      

  6.   

    试了一下,确实如jingulang所说,偶更正一下错误。感谢jingulang!
      

  7.   

    原文地址:http://zangweiren.javaeye.com/blog/208122作者:臧圩人 
    网址:http://zangweiren.javaeye.com 大家在去参加面试的时候,经常会遇到这样的考题:给你两个类的代码,它们之间是继承的关系,每个类里只有构造器方法和一些变量,构造器里可能还有一段代码对变量值进行了某种运算,另外还有一些将变量值输出到控制台的代码,然后让我们判断输出的结果。这实际上是在考查我们对于继承情况下类的初始化顺序的了解。 我们大家都知道,对于静态变量、静态初始化块、变量、初始化块、构造器,它们的初始化顺序以此是(静态变量、静态初始化块)>(变量、初始化块)>构造器。我们也可以通过下面的测试代码来验证这一点: 
    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代码 
    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代码 
    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以及静态初始化块的前后位置,就会发现输出结果随着它们在类中出现的前后顺序而改变,这就说明静态变量和静态初始化块是依照他们在类中的定义顺序进行初始化的。同样,变量和初始化块也遵循这个规律。 了解了继承情况下类的初始化顺序之后,如何判断最终输出结果就迎刃而解了。 
      

  8.   

    很好的例子,think in java的开头几章重也有类似的例子。谢谢
      

  9.   

    呵呵,偶的这篇浅显的文章上了javaeye首页了,有点意外
      

  10.   

    回复bestseal:jingulang说的是对的,静态变量和静态初始化快的初始化顺序是依据他们在类中定义的先后顺序的。
    请看我最新的文章:http://zangweiren.javaeye.com/blog/208122
      

  11.   


    System.out.println("子类--静态初始化块"); 
    System.out.println(s_StaticField); 
    调换一下顺序就相反了 
    子类--静态初始化块
    子类的静态变量
      

  12.   

    http://www.zzGps.cn 在线技术视频 http://bbs.ZzGps.Cn 技术视频下载 技术视频提供,分类清晰,技术覆盖面广,几千视频助您轻松学习 
      

  13.   

      顶...初始化顺序以此是静态变量->静态初始化块->变量->初始化块->构造器
        
      

  14.   

    thinking in java 关于这个问题的确写得好详细啊 
      

  15.   

    欢迎大家来http://zangweiren.javaeye.com交流
      

  16.   

    现在,结果已经不言自明了。大家可能会注意到一点,那就是,并不是父类完全初始化完毕后才进行子类的初始化,实际上子类的静态变量和静态初始化块的初始化是在父类的变量、初始化块和构造器初始化之前就完成了。 
    ===========================================================================
    我觉得LZ这个说法不正确。LZ没有搞清楚Class的初始化和Instance的初始化之间的区别
    对于static的部分,是Class的信息被加载的时候JVM进行处理的,其次序是先于所有Instance生成的。所以,即使是子类的static属性也是在Instance形成之前进行处理(或者说初始化的)。
    对于继承,简单一点的理解就是“代码Copy”,也就是说在子类当中存在着一部分我们看不见但是又的确存在的来自父类的代码。这部分代码(如果没有被Override)的执行顺序是先于子类当中同级别的代码。所以父类当中的Static代码最先执行。但是由于static代码的级别高于Instance级别的代码,所以子类当中的Static代码在生成Instance之前被JVM处理了。这也就是为什么子类static的输出在父类的“非static代码块”之前输出。
    非static代码块的输出属于Instance级别,也就是说,如果没有生成具体的Instance,这个代码块和Constructor一样是不会被执行的。
    一旦生成Instance,那么执行顺序就是父类的代码块--父类Constructor--子类代码块--子类Constructor,这样一个过程。
    结论:把Instance的生成分为两个过程,一个是Class加载过程,一个是Instance生成的过程,其实每个过程父类的动作都优先于子类。不信的话,LZ在多new几个子类看看结果如何。或者使用Class.forName方法只加载Class信息,不生成Instance看看结果如何。
    PS,Method被Override然后在父类被调用这样的动态邦定不在这个讨论范围之内。
      

  17.   

    接上,Class的信息只加载一次,所以一个Instance的生成过程当中有可能不包含Class的load。因为在此之前,Class可能已经被Load了。
      

  18.   

    回复:williamzhougps
    你说得对。这里我进行了简化,为了简单易懂,否则要引入不少概念,越说越复杂了
      

  19.   

    我自己总结下吧   个人观点阿  有问题别骂我
    前两天也看了篇帖子  
    java中的final关键字详解
    主要是讲final关键字的  然后对这些初始化的顺序也有提到
     http://topic.csdn.net/u/20080713/11/8BD9A46F-B0DC-4DDD-8DD6-666565735F00.html那篇帖子里说道 在类中 以static final关键字修饰的变量  是直接放在常量池里的  基本和这个类"无关"了
    就是说 用到那个常量的时候  不需要加载定义它的类 (有几位大大还把反编译的CLASS文件的内容解释了下,小小的佩服下)
    就是不知道static final修饰的方法用的时候需不需要加载类了然后  加载类时  先执行静态初始化块  因为这是属于类的初始化内容  相当于类的"构造方法"
    注意  不是对象的  是类的
    然后类的初始化块是在实例化类时 ,先于构造方法被执行
    就是说  实例化一个类  如果有初始化块  先执行初始化块  再执行构造方法对于继承的情况   没有验证  看楼主的输出结果
    是先加载父类  父类的静态初始化块被执行  再加载子类  子类的静态初始化块被执行
    然后执行父类的构造方法   在这之前  父类的初始化块被执行
    然后执行子类的初始化块  子类的构造方法这是我的一点分析  罗嗦了点
    如果有错误的地方 欢迎大家指出
      

  20.   

      原来如此.一方面跟代码编译有关,另一方面跟代码的顺序和java语言本身的特点有关~~~