zoukankan      html  css  js  c++  java
  • 分享一道关于类、实例加载和初始化顺序的基础面试题

    一 题目

      二话不说,直接上题

    public class Son extends Father{
    
        private int i = test();
        private static int j = method();
    
        //静态代码块
        static {
            System.out.println("(6)");
        }
    
        //构造方法
        Son(){
    //        super();
            System.out.println("(7)");
        }
    
        //非静态代码块
        {
            System.out.println("(8)");
        }
    
        //非静态方法
        public int test(){
            System.out.println("(9)");
            return 0;
        }
    
        //静态方法
        public static int method(){
            System.out.println("(10)");
            return 0;
        }
    
        //main方法
        public static void main(String[] args) {
            Son son = new Son();
            System.out.println();
        }
    }
    
    class Father{
        private int i = test();
        private static int j = method();
    
        //静态代码块
        static {
            System.out.println("(1)");
        }
    
        //构造器
        Father(){
            System.out.println("(2)");
        }
    
        //非静态代码块
        {
            System.out.println("(3)");
        }
    
        //public 非静态方法
        public int test() {
            System.out.println("(4)");
            return 0;
        }
    
        //public 静态方法
        public static int method() {
            System.out.println("(5)");
            return 0;
        }
    }

     main方法执行后,输出顺序是什么? 答案是:

    5 ===> 1 ===> 10 ===> 6 ===> 9 ===> 3 ===> 2 ===> 9 ===> 8 ===> 7
      
    与你的答案是否一致呢?如果一致,说明你的这方面的知识过关了。

    二 分析

      2.1 考点

          1.类的加载和初始化

        2.实例的创建和初始化

        3.方法的重写

      2.2 步骤分析

        步骤1:

          程序从main方法开始,首先会加载和初始化main方法所在的类Son,Son类的初始化,要执行类中的静态代码块、为静态成员变量赋值 (这两者按照代码从上到下的顺序执,谁在前面,谁先执行)。

          但是,当类Son被加载后,发现Son类还有一个父类-----Father类,这个时候会先对Father类进行加载和初始化,初始化Father类,需要执行静态代码块、为静态成员变量赋值。

            所以,执行顺序为:5 ===> 1 ===> 10 ===> 6 (先初始化父类,然后在初始化子类)

            这一点也可以验证,main方法中没有其他代码,只有一个main方法,也会出现这个执行顺序。

     

        步骤2:

          类加载和初始化完成后,main方法继续执行包含的代码,Son son = new Son();执行到这一步,会调用子类Son的无参构造方法,这里有一点需要注意:无论,
         子类有没有显示调用super();即调用父类的构造方法,实际上都会去调用父类的构造方法。
         也就是你无论写不写super(),都会去先调用父类的构造方法,上面的题目代码中,我已经标记了出来。
         所以,此时会先去调用父类的构造方法,创建父类的实例,并且初始化。
         实例的创建和初始化的过程会先进行非静态成员变量的赋值、执行非静态代码块(这两者也是按照代码从上到下的顺序,谁在前面,谁先被执行),最后执行构造方法里面的代码。
         整个过程就是先执行父类实例的初始化,然后执行子类实例的初始化。
         所以顺序是:
    4 ===> 3 ===> 2 ===> 9 ===> 8 ===> 7 (先实例化父类,然后实例化子类)
         但是,这个顺序并不是程序运行结果的顺序。
    所以接下来有步骤3


       步骤3:
        这个知识点是关于方法重写的,首先我们要知道,有三种情况下子类是不能对父类的方法进行重写的:
           1.父类方法被 private 默认包权限修饰 由于封装性的问题导致不能重写。
           2.父类方法被final修饰,final修饰的方法,不能被子类重写;
           3.父类的方法被static修饰,静态方法是不能被重写的。
         上面题目中涉及到了两个方法,一个public 非静态的 test(),另一个是 public static 静态方法method(); 而子类中也提供了一模一样的两个方法。
         根据上面介绍的重写规则,子类中的test()方法时重写方法,而method则不是重写。
         所以,返回到步骤2父类实例初始化的过程,首先会为非静态字段赋值,也就是会调用test()方法,问题就出现在这,这个test()方法是调用父类的呢,还是子类的呢?
         此时调用的是子类重写的方法.
         所以步骤2正确的顺序是:
    9 ===> 3 ===> 2 ===> 9 ===> 8 ===> 7
    
    

    三 总结

      所以,最后的执行顺序就是:

      5 ===> 1 ===> 10 ===> 6 ===> 9 ===> 3 ===> 2 ===> 9 ===> 8 ===> 7

      一句话就是: 由父及子,静态先行,构造后行。
    
    
  • 相关阅读:
    DI 之 3.4 Bean的作用域(捌)
    DI 之 3.3 更多DI的知识(柒)
    MySQL中基本的多表连接查询教程
    DI 之 3.3 更多DI的知识(陆)
    字符串(string)转json
    如何查看连接mysql的ip地址
    DI 之 3.2 循环依赖 (伍)
    DI 之 3.1 DI的配置使用(肆)
    IoC 之 2.3 IoC的配置使用(叁)
    MySQL for Windows 解压缩版配置安装
  • 原文地址:https://www.cnblogs.com/BoildWater/p/11377017.html
Copyright © 2011-2022 走看看