zoukankan      html  css  js  c++  java
  • Java面试中经常遇到的类执行顺序

    Java面试中经常遇到的类执行顺序

    版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
    本文链接:https://blog.csdn.net/u012403290/article/details/68954928

    引言:

    记得以前面试的时候,会直接给一段代码,这段代码很多地方都有print,所以让我写出现打印出什么,后打印出什么。今天我整理一下单类的情况,继承的情况和一些特殊情况,希望大家看了之后,在面试过程中举一反三,成功规避错误。笔者目前整理的一些blog针对面试都是超高频出现的。大家可以点击链接:http://blog.csdn.net/u012403290

    单类执行顺序:

    下面是我写的一个demo:

    package com.bw;
    
    /**
     * @author brickworker
     * 关于类Color的描述:测试单个类的执行顺序问题
     */
    public class Color {
    
        //构造函数
        public Color() {
            System.out.println("构造函数执行");
        }
    
        //静态代码块
        static{
            System.out.println("静态代码块执行");
        }
    
        //非静态代码块
        {
            System.out.println("非静态代码块执行");
        }
    
    
        //一般方法
        void run(){
            System.out.println("一般方法执行");
        }
    
    
        public static void main(String[] args) {
            System.out.println("main方法执行");//这个一定要写在最上面,程序一进入马上就执行,不然会导致结果不准确
            Color color = new Color();
            color.run();
        }
    
    
    
    }
    //执行结果:
    //静态代码块执行
    //main方法执行
    //非静态代码块执行
    //构造函数执行
    //一般方法执行
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45

    真的觉得是对的么?因为我们main的入口写在了类本身之中。导致结果其实是不准确的。我们换个方式,信建立一个Rundemo来触发执行:

    package com.bw;
    
    public class RunDemo {
    
        public static void main(String[] args) {
            System.out.println("main方法执行");//这个一定要写在最上面,程序一进入马上就执行,不然会导致结果不准确
            Color color = new Color();
            color.run();
        }
    }
    //执行结果:
    //main方法执行
    //静态代码块执行
    //非静态代码块执行
    //构造函数执行
    //一般方法执行
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    是吧,是不是main方法执行顺序跑到最前面去了呢?前面一种情况是因为main方法放在了目标类中,如果要执行这个main方法,虚拟机会先初始化这个主类,所以会先执行static静态代码块,关于这部分,以后我会写一篇关于虚拟机类加载机制的博文。总之,在看面试题的时候一定要看清楚main方法到底是放在哪里。

    继承类执行顺序:

    接下来,我们看看继承的情况是如何的,以下是我写的一个继承的demo,这里要强调一点,有写小伙伴为了看的更清楚,父类用system.err.println来打印,导致结果不同,两种输出方式是存在很大差别的,而且实验一定要建立在公平公正的前提下:

    
    //父类
    package com.bw;
    
    /**
     * 
     * @author brickworker
     * 关于类Father的描述:测试继承执行顺序父类
     */
    public class Father {
    
        //构造函数
        public Father() {
            System.out.println("父类构造函数执行");
        }
    
        //静态代码块
        static{
            System.out.println("父类静态代码块执行");
        }
    
        //非静态代码块
        {
            System.out.println("父类非静态代码块执行");
        }
    
        //一般方法
        void run(){
            System.out.println("父类一般方法");
        }
    }
    
    
    //子类
    package com.bw;
    
    
    
    /**
     * 
     * @author brickworker
     * 关于类Son的描述:测试继承执行顺序子类
     */
    public class Son extends Father{
    
        //构造函数
        public Son() {
            System.out.println("子类构造方法");
        }
    
        //静态代码块
        static{
            System.out.println("子类静态代码块");
        }
    
        //非静态代码块
        {
            System.out.println("子类非静态代码块");
        }
    
        //子类重写一般方法
        @Override
        void run() {
            System.out.println("子类重写一般方法执行");
        }
    
        public static void main(String[] args) {
            System.out.println("main方法执行");
            Son son = new Son();
            son.run();
        }
    
    
    }
    
    
    //执行顺序:
    //父类静态代码块执行
    //子类静态代码块
    //main方法执行
    //父类非静态代码块执行
    //父类构造函数执行
    //子类非静态代码块
    //子类构造方法
    //子类重写一般方法执行
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85

    现在,我们把main方法单独拿出来,和上面一样,看看执行结果:

    main方法执行
    父类静态代码块执行
    子类静态代码块
    父类非静态代码块执行
    父类构造函数执行
    子类非静态代码块
    子类构造方法
    子类重写一般方法执行
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    总结一下:标准的执行顺序是:当前主程序>父类静态代码块>子类静态代码块>父类非静态代码块>父类构造函数>子类非静态代码块>子类构造方法>子类一般方法。那么把上面的顺序中关于父类执行的去掉,其实也符合我们前面讨论的单个类执行顺序。

    注意了!注意了!(敲黑板),上面的也许大家在别的文章中看了不止一遍,那么下面的特殊情况你可能就不太清楚了,因为出现的少,但是我们一次看个透彻。

    特殊执行顺序:

    其实在类加载机制中,类加载存在主动加载和被动加载(可以不用知道,对下面文章理解不妨碍,只是希望知其然更要知其所以然),在被动加载过程中很多并不会触发初始化,所以在判断被动引用的时候,执行顺序会难很多,主要分为3中情况来具体说明:

    一、通过子类调用了父类的静态字段,子类不会被初始化

    //父类
    package com.bw;
    
    /**
     * 
     * @author brickworker
     * 关于类Father的描述:测试继承执行顺序父类
     */
    public class Father {
    
    
        public static int father = 100;
        //静态代码块
        static{
            System.out.println("父类静态代码块执行");
        }
    
    }
    //子类
    package com.bw;
    /**
     * 
     * @author brickworker
     * 关于类Son的描述:测试继承执行顺序子类
     */
    public class Son extends Father{
    
    
        static{
            System.out.println("子类静态代码块");
        }
    
    }
    
    //测试类:
    package com.bw;
    
    public class RunDemo {
    
        public static void main(String[] args) {
            System.out.println("main方法执行");
            System.out.println(Son.father);//用子类去调去父类的静态字段
        }
    
    }
    //执行结果:
    //main方法执行
    //父类静态代码块执行
    //100
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51

    从上面可以看出,子类的静态代码都没有执行,说明子类完全没有初始化。

    二、类作为数组的组件类型不会触发类初始化:

    借用上面单类的Color类:

    package com.bw;
    
    public class RunDemo {
    
        public static void main(String[] args) {
            System.out.println("main方法执行");
            Color[] colors = new Color[5];
        }
    
    }
    //执行结果:
    //main方法执行
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    上面用Color做为数组的组件,没有执行color的静态方法,说明也没有被初始化。

    三、常量池引用也会导致不初始化类

    package com.bw;
    
    /**
     * @author brickworker
     * 关于类Color的描述:测试单个类的执行顺序问题
     */
    public class Color {
        public static final String color = "red";//新增一段常量,编译阶段户直接放入常量池
        //静态代码块
        static{
            System.out.println("静态代码块执行");
        }
    }
    
    //
    package com.bw;
    
    public class RunDemo {
    
        public static void main(String[] args) {
            System.out.println("main方法执行");
            System.out.println(Color.color);
        }
    
    }
    //执行结果:
    //main方法执行
    //red
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    从上面的结果可以看出,虽然调用了red,但是并有执行color的静态代码块,说明它没有被初始化。

    综上所述,如果把类加载时机和顺序进行结合,那么这样的面试题可能就会难倒一大批人,所以合理分析和总结是非常重要的。关于为什么后面类没有初始化,我想后面如果写java虚拟机类加载机制的时候我们再一起探究下。

    好啦,如果你看到了这里,谢谢你对我辛苦劳动肯定,同时祝愿你学习进步,事业顺心。

  • 相关阅读:
    一起谈.NET技术,.NET 4九大新特性 狼人:
    一起谈.NET技术,重新认识C#: 玩转指针 狼人:
    一起谈.NET技术,.NET 3.x新特性之自动属性及集合初始化 狼人:
    一起谈.NET技术,从WPF想开去 狼人:
    [置顶] 第十七章——配置SQLServer(1)——为SQLServer配置更多的处理器
    [置顶] 第十七章——配置SQLServer(3)——配置“对即时负载的优化”
    JDBC for rdf3x
    表达式判断 帅呆了的题目
    《数学之美》读书感想
    [置顶] 第十七章——配置SQLServer(4)——优化SQLServer实例的配置
  • 原文地址:https://www.cnblogs.com/SanguineBoy/p/11363821.html
Copyright © 2011-2022 走看看