关于java类加载的一个面试点分析

首先明确一点,类初始化加载static修饰的属性/代码块的时候是按照从上到下加载的,

 

实例:

   

package com.wm.jasypt.service;

/**
 * @author 半卷流年
 * @date 2020-6-3 15:56
 */
public class Singleton {

    private static Singleton singleton = new Singleton();
    public static int counter1;
    public static int counter2 = 0;

    private Singleton() {
        counter1++;
        counter2++;
    }

    public static Singleton getSingleton() {
        return singleton;
    }


    public static void main(String[] args) {
        Singleton singleton = Singleton.getSingleton();
        System.out.println("counter1="+singleton.counter1); //输出什么?
        System.out.println("counter2="+singleton.counter2);  //输出什么?
    }
}

    输出的结果如下:

    

counter1=1
counter2=0

原因分析:

   1.执行main方法的时候,由于Singleton 类没有被加载,首先触发类加载器,初始化该类,注意类的初始化是只会初始化一次的

singleton = null;

counter1 = 0;

counter2 = 0;

    2.按照static顺序加载,先进入new Singleton()初始化,进入构造函数的加载, 执行完成之后

counter1 = 1;
counter2 = 1;

     3.在执行  public static int counter1;

      由于没有指定初始值,故 counter1位之前初始化的值:

   

counter1 = 1;

4.执行 public static int counter2 = 0;

所以counter2的值为:

counter2 = 0;

综合得出结果:  counter1 = 1; counter2 = 0;

下面debug如下:  依次的顺序如下:

1.

关于java类加载的一个面试点分析_第1张图片

2.

关于java类加载的一个面试点分析_第2张图片

 

3.

关于java类加载的一个面试点分析_第3张图片

 

下面将上面的例子更换一个顺序,输出的是什么:

package com.wm.jasypt.service;

/**
 * @author 半卷流年
 * @date 2020-6-3 15:56
 */
public class Singleton {

    public static int counter1;
    public static int counter2 = 0;
    private static Singleton singleton = new Singleton();

    private Singleton() {
        counter1++;
        counter2++;
    }

    public static Singleton getSingleton() {
        return singleton;
    }


    public static void main(String[] args) {
        Singleton singleton = Singleton.getSingleton();
        System.out.println("counter1="+singleton.counter1); //输出什么?
        System.out.println("counter2="+singleton.counter2);  //输出什么?
    }
}

 

结果为:

counter1=1
counter2=1

 

原因:

 是先初始化counter1,2赋值,在进入构造,故输出都是1

 

类加载机制,主动初始化化的几种情况:

主动初始化的6种方式
(1)创建对象的实例:我们new对象的时候,会引发类的初始化,前提是这个类没有被初始化。
(2)调用类的静态属性或者为静态属性赋值
(3)调用类的静态方法
(4)通过class文件反射创建对象
(5)初始化一个类的子类:使用子类的时候先初始化父类
(6)java虚拟机启动时被标记为启动类的类:就是我们的main方法所在的类
只有上面6种情况才是主动使用,也只有上面六种情况的发生才会引发类的初始化。

 

 

关于类加载器的双亲委派机制:

         关于类加载器,我们不得不说一下双亲委派机制。听着很高大上,其实很简单。比如A类的加载器是AppClassLoader(其实我们自己写的类的加载器都是AppClassLoader),AppClassLoader不会自己去加载类,而会委ExtClassLoader进行加载,那么到了ExtClassLoader类加载器的时候,它也不会自己去加载,而是委托BootStrap类加载器进行加载,就这样一层一层往上委托,如果Bootstrap类加载器无法进行加载的话,再一层层往下走。
 

为什么使用双亲委派机制:

       判断两个类相同的前提是这两个类都是同一个加载器进行加载的,如果使用不同的类加载器进行加载同一个类,也会有不同的结果。
如果没有双亲委派机制,会出现什么样的结果呢?比如我们在rt.jar中随便找一个类,如java.util.HashMap,那么我们同样也可以写一个一样的类,也叫java.util.HashMap存放在我们自己的路径下(ClassPath).那样这两个相同的类采用的是不同的类加载器,系统中就会出现两个不同的HashMap类,这样引用程序就会出现一片混乱。


 

 

 

 

 

 

 

你可能感兴趣的:(Java,java)