Java 类的实例变量初始化的过程 静态块、非静态块、构造函数的加载顺序

来源:互联网 发布:中国新闻自由 知乎 编辑:程序博客网 时间:2024/05/24 11:13
先看一道Java面试题:

 

复制代码
 1 public class Baset { 2     private String baseName = "base"; 3     // 构造方法 4     public Baset() { 5         callName(); 6     } 7     // 成员方法 8     public void callName() { 9         // TODO Auto-generated method stub10         System.out.println("basename:" + baseName);11     }12   //静态内部类13     static class Sub extends Baset {//static必须写在开头14         // 静态字段15         private String baseName = "sub";16         public Sub() {17             callName();18         }19         // 重写父类的方法20         public void callName() {21             System.out.println("subname:" + baseName);22         }23     }24 25     public static void main(String[] args) {26         Baset base = new Sub();27     }28 }
复制代码

 

求这段程序的输出。

解答此题关键在于理解和掌握类的加载过程以及子类继承父类后,重写方法的调用问题:

一、从程序的执行顺序去解答:

1.编译;当这个类被编译通知后,会在相应的目录下生成两个.class 文件。一个是 Base.class,另外一个就是Base$Sub.class。这个时候类加载器将这两个.class  文件加载到内存

2、Base base= new Sub():

  声明父类变量base对子类的引用,JAVA类加载器将Base,Sub类加载到JVM(Java虚拟机);

3、JVM为Base,Sub 的的成员开辟内存空间

  此时,Base 和Sub类中的值为null;

4、new Sub();

  这个时候会调用Sub类的隐式构造方法,

     Sub的构造方法本质为:

     public Sub(){

  super();//  调用父类的构造方法必须在构造方法中的第一行,为什么呢?这是因为在一些程序的升级中,要兼容旧版本的一些功能,父类即原先的一些初始化信息也要保证  //被执行到,然后执行当前

  baseName = "sub";//子类字段初始化

      }

    new Sub()执行到super()这行代码也就是跑到父类中去执行了,我们跳转到父类中的无参构造方法中执行,最后执行Sub()中的baseName = "sub"

5、public Base() ;

      父类无参构造方法的本质为:

  public Base(){

  baseName= "base";//父类字段初始化

  callName();

    }

  即将父类的baseName赋值为“base”,赋值后调用callName();

6、callName()方法在子类中被重写,因此调用子类的callName(),子类的callName方法执行,打印输出的是子类的baseName 字段的值,而这个时候子类的构造函数中字段的赋值还未执行。

7、父类的构造函数执行完毕,这个时候又回到子类当中,从super()的下一行继续执行,这个时候才为子类字段baseName 分配好存储空间,随后为其赋值:

 可见,在baseName = "sub"执行前,子类的callName()已经执行,所以子类的baseName为默认值状态null;

 、另写两个程序,进行更具体的分析

 1、第一个程序:

复制代码
 1 public class InitialOrderTest { 2 //    变量 3     public String field = "变量"; 4 //  静态变量 5     public  static  String staticField="静态变量"; 6 //父类静态方法     7     public static void Order(){ 8         System.out.print("父类静态方法"); 9         System.out.println("staticField:"+staticField);10     }11 12 //  静态初始代码块13     static{14         System.out.println("静态初始化块");15            System.out.println("staticField:"+staticField);16        }17 //   初始化代码块  18       {19           System.out.println("初始化代码块");20           System.out.println("field:"+field);21        }22 //   构造函数23    public InitialOrderTest(){24        System.out.println("构造器");25        26    }27  28    public static void main(String[] args) {29        System.out.println("-----[[-------");30        System.out.println(InitialOrderTest.staticField);31        InitialOrderTest.Order();32        System.out.println("------]]------");33        InitialOrderTest i = new InitialOrderTest();34        System.out.println("-----[[-------");35        System.out.println(InitialOrderTest.staticField);36        InitialOrderTest.Order();37        System.out.println("------]]------");38       39    }40 }
复制代码

执行结果为:

  第一个程序总结:

     1)、java中的块分为静态块(static{})和非静态块({}),这两种的执行是有区别的:

        非静态块的执行时间是:在执行构造函数之前。   静态块的执行时间是:class文件加载时执行。

        static类型的属性也是在类加载时执行的。

    2)可见Java类的实例变量初始化的过程:

       static类型的成员属性执行,静态块(static{})按顺序执行,然后非静态成员变量初始化,非静态代码块({})执行,最后执行构造方法。

       static类型与static块按先后顺序执行。

---------------------------------------------------------------------------------------------------------------------------------------------------

  2、第二个程序:

   

复制代码
 1 public class BaseTest { 2     // 父类变量 3     private String baseName = "base"; 4     // 父类静态变量 5     private static String staticField = "父类静态变量"; 6     // 父类静态方法 7     public static void Order() { 8         System.out.println("父类静态方法-"); 9         System.out.println("staticField:" + staticField);10     }11     // 父类静态初始代码块12     static {13         System.out.println("父类静态初始化代码块-");14         System.out.println("staticField:" + staticField);15     }16     // 初始化代码块17     {18         System.out.println("父类非静态初始化代码块-");19         System.out.println("baseName:" + baseName);20     }21     // 构造函数22     public BaseTest() {23         System.out.println("父类构造方法");24         callName();25     }26     // 成员方法27     public void callName() {28         System.out.println("父类callName方法-");29         System.out.println("baseName:" + baseName);30     }31 32     // 静态内部类33     static class Sub extends BaseTest {34         // 子类变量35         private String baseName = "sub";36         // 子类 静态变量37         private static String staticField = "子类静态变量";38 39         // 子类静态方法40         public static void Order() {41             System.out.println("子类静态方法-");42             System.out.println("staticField:" + staticField);43         }44 45         // 子类静态初始化代码块46         static {47             System.out.println("子类静态初始化代码块-");48             System.out.println("staticField:" + staticField);49         }50         // 子类非静态初始化代码块51         {52             System.out.println("子类非静态初始化代码块-");53             System.out.println("baseName:" + baseName);54         }55 56         public Sub() {57             System.out.println("子类构造方法");58             callName();59         }60 61         public void callName() {62             System.out.println("子类重写父类callName方法-");63             System.out.println("baseName:" + baseName);64         }65     }66 67     public static void main(String[] args) {68         69         BaseTest b = new Sub();70 71         System.out.println("-----[[-------");72         Sub.Order();73         System.out.println(Sub.staticField);74         System.out.println(BaseTest.staticField);75         BaseTest.Order();76         System.out.println("------]]------");77 78     }79 }
复制代码

 

  执行结果:

  

第二个程序总结:

    1)可见Java初始化的顺序:   

复制代码
 1  1 父类静态变量初始化 2  2 父类静态代码块-static{ } 3  3 子类静态变量初始化 4  4 子类静态代码块-static{ } 5  5 父类非静态变量初始化 6  6 父类非静态代码块-{} 7  7 父类构造方法 8  8 子类非静态变量初始化 9  9 子类非静态代码块-{}10 10 子类构造方法
复制代码

 


2 0