• 分享一道关于类、实例加载和初始化顺序的基础面试题


    一 题目

      二话不说,直接上题

    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

      一句话就是: 由父及子,静态先行,构造后行。
    
    
  • 相关阅读:
    首次使用随便写点哦
    js中call、apply和bind的区别
    前端的事件流以及事件处理程序
    javascript中数组的深拷贝的方法
    我的第一篇博客
    圆盘转动按钮-react native
    鼠标拖拽删除
    js基础 -----鼠标事件(按下 拖拽)
    清除浮动的几种常用方法
    VUE常见问题解决
  • 原文地址:https://www.cnblogs.com/BoildWater/p/11377017.html
Copyright © 2020-2023  润新知