聊聊JVM——类的加载(二)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/weixin_41622183/article/details/81266355

前言:

        本篇博客接续上一篇中未写到的部分,分为类的主动加载和被动加载,可能涉及到一些其他内容。依旧使用先思考做题后在看答案的策略。

阅读须知:

        此次博客以启发性代码和解释进行学习。在阅读时,按照代码和提示进行思考分析为什么,请思考过后在看答案来验证自己的思考。(此博客为个人观点且本人水平有限,如有错,请批评指正)

  

小题测试:(先思考,并写出自己的答案后在往下看)

package com.wen.demo.test.MyClass;

class Parent {
    static {
        System.out.println("父亲初始化");
    }
}
class Childen extends Parent {

    static {
        System.out.println("儿子初始化");
    }
}
public class MyTest {

    public static void main(String[] args) {
        new Childen();

    }

}

                                            对于以上代码,请先思考,输出的是什么。然后才往下面看。

                                                                                       。

                                                                                       。

                                                                                       。

                                                                                       。

概念:

    主动使用:

           在类的初始化阶段,虚拟机规范严格规定了有且只有只有五种情况必须对类进行“初始化”,这里我在细分一些,分为6种情况。

    (1)创建类的示例。例如:new 一个对象

    (2)访问某个类的静态变量或给静态变量赋值

    (3)调用一个类的静态方法

    (4)反射

    (5)初始化一个子类(继承了父类)

    (6)虚拟机启动时,用户指定一个启动类(即含有main()的那个类)会优先初始化

   被动使用:

         被动引用不会导致类的初始化,这里先介绍几种被动引用的方法。如下代码:

         第一种被动引用情况:

package com.wen.demo.test.MyClass;

/**
 * 被动使用简单示例
 */
class Parent {

    static {
        System.out.println("父亲初始化");
    }
    /**
     * 第一种,父类定义静态变量,被子类引用,不会导致子类初始化
     */
    public static  int A=10;

}
class Childen extends Parent {

    static {
        System.out.println("儿子初始化");
    }
}
public class MyTest {

    public static void main(String[] args) {
        System.out.println(Childen.A);
    }

}

          第一种情况,我们要理解上面所说的主动使用,访问某个类的静态变量或给静态变量赋值,父类拥有这个静态字段,通过子类引用是不会导致子类初始化的。

我们只要认定,对于静态字段,只有直接定义这个字段的类才会被初始化,就可以了~!

          第二种被动引用情况:

package com.wen.demo.test.MyClass;

/**
 * 被动使用简单示例
 */
class Parent {

    static {
        System.out.println("父亲初始化");
    }

}

public class MyTest {

    public static void main(String[] args) {
        //注意看这里,看清楚是个数组
        Parent[] parents= new Parent[10];
    }

}

             对于这种情况,大家可能懵逼了~不是用了new关键字吗?不是对象吗?

             运行后,我们会没有发现有“父亲初始化”这几个字输出来。其实,在运行时,其实数组已经不是Parent类型了,Parent的数组jvm在运行期,会动态生成一个新的类型,新类型为为L+包名+类名。这个类型是由虚拟机自动生成的,直接继承自java.lang.Object的子类。已经不是原本我们自己写的包+类名了。

         第三种被动引用情况:

package com.wen.demo.test.MyClass;

/**
 * 被动引用简单示例
 */
class Parent {
    static {
        System.out.println("父亲初始化");
    }
    public static final int value=10;

}

public class MyTest {

    public static void main(String[] args) {
        System.out.println(Parent.value); 
    }

}

           这里直接用类名.静态字段来调用,如果是变量当然是会初始化了。但是final修饰后已经在编译期存入了调用类的常量池中。这个和上一篇博客中有介绍,这里就不多介绍了。

测试题结果:

                                             

       我们实例化了子类,可是却先初始化了父类,才初始化子类。为什么?这个就要从JVM的双亲委派模型说起了。先看下图:

    

      先介绍双亲委派模型的工作流程:

          如果一个类加载器收到了类的加载请求,他首先不会自己去尝试去加载这个类,而是将这个请求委派给父类加载器去完成。每一层加载器都是这样委派,因此所有的加载请求都会到最顶层的启动了加载器中,假如启动类加载器加载不了,反馈信息给子类加载器,子类加载器在尝试加载,最终可能又回到最开始的自定义类加载器来加载。如果都加载不了了。那就会抛出异常,这就是我们平时见到的ClassNotFoundException异常的由来了。

          简单来说就是:今天吃完饭要洗碗的,我们那么懒肯定不洗就丢给爸妈去洗,他们也不肯,就丢给爷爷奶奶去洗,以此类推,当最顶的都不想洗碗,就告诉下一级子类,依次传递最终自己来洗也是有可能的,当然,父母大发慈悲另说。(这里只是随便说说,大致了解一下就好~当真你就输了)

         

额外补充一下:(类加载器)

  • 启动类加载器,由C++实现,没有父类。

  • 拓展类加载器(Extension ClassLoader),由Java语言实现,父类加载器为null

  • 系统类加载器(Application  ClassLoader),由Java语言实现,父类加载器为Extension ClassLoader

         上述是我们应用程序常用的类加载器,如果有必要,还可以加入我们自己定义的类加载器~!每个加载器负责的东西这里就不介绍了,想了解的自己百度一下~!

总结:

        一天又结束了,看了书才会知道自己的基础到底有多差,整个大学快过去了,也许打基础的时间久剩下一年了。出去工作后想要打地基就更加难了。不管出去工作还是在学习,还是需要时刻充电了。最后祝大家学习进步,工作愉快~!

程序人生,与君共勉~!

猜你喜欢

转载自blog.csdn.net/weixin_41622183/article/details/81266355