After writing java code for so many years, do you really know how classes are loaded?


Insert picture description here

1. Constant value test

class ClassLoaderTest {
    
    
  public static final String str = UUID.randomUUID().toString();
  static {
    
    
    System.out.println("ClassLoaderTest  Block");
  }
}

class ClassLoaderTest1 {
    
    
  public static final String str = "I Am ClassLoaderTest1";
  static {
    
    
    System.out.println("ClassLoaderTest1  Block");
  }
}
 @Test
 public void test() {
    
    
   System.out.println(ClassLoaderTest.str);
   System.out.println("-------------------------");
   System.out.println(ClassLoaderTest1.str);
 }
 输出结果:
	ClassLoaderTest  Block
    6799db17-6d67-4195-9b13-b2c4c082d86c
    -------------------------
    I Am ClassLoaderTest1

The conclusion is :

  1. The constant value will be stored in the constant pool of the called class, so the called class will not be used actively, so the called class will not be initialized. The
    UUID.randomUUID().toString()value of this statement is not fixed during compilation, so it will not be like the one mentioned before(public static final String str = "I Am ClassLoaderTest1";)
  2. The constant value will be stored in the constant pool of the calling class, so the called class will not be used actively, so the called class will not be initialized
    Insert picture description here

2. Static variable value test

class ClassLoaderStatic {
    
    
    public static int x = 3;

    static {
    
    
        System.out.println("FinalTest static block");
    }
}
@Test
public void testStatic() {
    
    
  System.out.println(ClassLoaderStatic.x);
}
输出结果:
   FinalTest static block
   3

The conclusion is:

  1. Accessing the static variables of a certain class or interface is equivalent to the active use of the class (explained in the previous article), so that the called class will be actively used, so the called class will be initialized and outputFinalTest static block
  2. Note that static variables are not the same as constants
    Insert picture description here

3. Create object test

class ClassLoaderTest2 {
    
    
  public static final String str = "I Am ClassLoaderTest2";

  static {
    
    
    System.out.println("ClassLoaderTest2  Block");
  }
}
@Test
public void testCreateNewObject() {
    
    
  ClassLoaderTest2 c = new ClassLoaderTest2();
  System.out.println("------------------------------");
  ClassLoaderTest2 c1 = new ClassLoaderTest2();
  输出结果:
	 ClassLoaderTest2  Block
	 ------------------------------
  }

The conclusion is:

  • A class will actively use the called class (ClassLoaderTest2) when new an object, and a class will only be initialized once when new an object
    Insert picture description here

4. Create object array data test

class ClassLoaderTest2 {
    
    
  public static final String str = "I Am ClassLoaderTest2";

  static {
    
    
    System.out.println("ClassLoaderTest2  Block");
  }
}
@Test
 public void testCreateObjectArray() {
    
    
   ClassLoaderTest2[] cArray = new ClassLoaderTest2[1];
   System.out.println(cArray.getClass());
   System.out.println(cArray.getClass().getSuperclass());
   System.out.println("------------------");
   /** 二维数组*/
   ClassLoaderTest2[][] c2Array = new ClassLoaderTest2[1][];
   System.out.println(c2Array.getClass());
   System.out.println(c2Array.getClass().getSuperclass());
   输出结果:
      class [LclassLoader.ClassLoaderTest2;
   	  class java.lang.Object
      ------------------
      class [[LclassLoader.ClassLoaderTest2;
      class java.lang.Object
  }

The conclusion is:

  • The ClassLoaderTest2 Blockword does not appear as expected , so all classes except the array class are created by the class loader. The data type is dynamically generated by the jvm runtime, and ***[LclassLoader.ClassLoaderTest2t;the words expressed as a two-digit array are [[ ** (Note: there is a semicolon after it). The dynamically generated type, the super type is. java.lang.ObjectFor arrays, JavaDoc often refers to the elements that make up the array as Component, which is actually the type after the array is reduced by one dimension

5. Create a basic type of array data test

 @Test
  public void testCreateBaseArray() {
    
    
    int[] ints = new int[1];
    System.out.println(ints.getClass());
    System.out.println(ints.getClass().getSuperclass());
	输出结果:
       class [I
       class java.lang.Object
  }

in conclusion:

  • The super type is java.lang.Objectfor arrays. JavaDoc often refers to the elements constituting the array as Component, which is actually the type after the array is reduced by one dimension
    Insert picture description here

6. Use the example of the singleton pattern to simulate the process of class loading

class SingletonDemo1 {
    
    
  public static int counter1;
  public static int counter2 = 0;
  private static SingletonDemo1 singleton = new SingletonDemo1();
  
  private SingletonDemo1() {
    
    
    counter1++;
    counter2++;
  }
  public static SingletonDemo1 getInstance() {
    
    
    return singleton;
  }
}

@Test
 public void testSingletonLoad() {
    
    
   SingletonDemo1 singleton1 = SingletonDemo1.getInstance();
   System.out.println("SingletonDemo1 counter1: " + singleton1.counter1);
   System.out.println("SingletonDemo1 counter2: " + singleton1.counter2);
   输出结果:
   	  SingletonDemo1 counter1: 1
      SingletonDemo1 counter2: 1
    /* 流程分析:
    *   加载:执行 SingletonDemo1.getInstance() 第一步进行 SingletonDemo1的类加载
    *   连接:
    *        1.验证:字节码文件的正确性
    *        2.准备:将类的静态变量进行赋值(counter1,counter2 都进行空间分配再赋予初始的值 0 0)
    *        3.解析:将符号引用变成直接引用
    *   初始化:
    *        counter1++; 变成 1
    *        counter2++; 变成 1
    */
}
class SingletonDemo2 {
    
    
  public static int counter1;
  private static SingletonDemo2 singleton = new SingletonDemo2();

  private SingletonDemo2() {
    
    
    counter1++;
    counter2++;
  }
  public static int counter2 = 0;
  public static SingletonDemo2 getInstance() {
    
    
    return singleton;
  }
@Test
 public void testSingletonLoad() {
    
    
   SingletonDemo2 singleton2 = SingletonDemo2.getInstance();
   System.out.println("SingletonDemo2 counter1: " + singleton2.counter1);
   System.out.println("SingletonDemo2 counter2: " + singleton2.counter2);
   输出结果:
   	  SingletonDemo2 counter1: 1
      SingletonDemo2 counter2: 0
   /**
    * 流程分析:
    *   加载:执行 SingletonDemo1.getInstance() 第一步进行 SingletonDemo2
    *   连接:
    *        1.验证:字节码文件的正确性
    *        2.准备:将类的静态变量进行赋值(counter1,counter2 都进行空间分配再赋予初始的值 0 0)
    *        3.解析:将符号引用变成直接引用
    *   初始化:
    *        counter1++; 变成 1
    *        counter2++; 变成 1
    *   重点: public static int counter2 = 0;
    *   再一次的将 counter2 进行 0 赋值所以 counter2 的值变成了 0
    */
 }

Insert picture description here


7. Initialize when testing interface inheritance

interface ClassLoaderTest3 {
    
    
  /**
   * public static final 接口中定义的变量默认就是常量
   */
  public static final String str = "i am interface parent";

}

interface ClassLoaderChild extends ClassLoaderTest3 {
    
    
  public static final String str = "i am interface child";
}
/**
	在运行之前将编译后的 ClassLoaderTest3.class 文件删除
*/
@Test
 public void testInterface() {
    
    
   System.out.println(ClassLoaderChild.str);
   System.out.println("------------------");
   System.out.println(ClassLoaderTest3.str);
   输出结果:
   	  i am interface child
   	  ------------------
   	  Error:(95, 24) java: 找不到符号
      符号:   变量 ClassLoaderTest3
      位置: 类 classLoader.JuintTestTest
    * 所以得出结论是:
    *   只有在真正使用到父接口的时候(如引用接口中定义的常量时),才会初始化。注意如果是类与类继承的时候如果子类进行初始化的时候
    *   父类必须进行初始化
    */
 }

in conclusion:

  1. That is, the child interface has nothing to do with the parent interface, so it is concluded that the initialization of the child interface does not necessarily require the parent interface to complete the initialization.
  2. Only when the parent interface is actually used (such as when referencing a constant defined in the interface) will it be initialized.
  3. Note that if the class is inherited from the class, the parent class must be initialized when the subclass is initialized

8. Initialize a test class inheritance

class ClassLoaderStaticParent{
    
    
    public static final int x = 1;

    static {
    
    
        System.out.println("i am ClassLoaderStaticParent");
    }
}

class ClassLoaderStaticChild extends ClassLoaderStaticParent{
    
    
    public static int x = 2;
    static {
    
    
        System.out.println("i am ClassLoaderStaticChild");
    }
}
@Test
public void ClassLoaderStaticParentAndChild() {
    
    
  ClassLoaderStaticParent parent;
  System.out.println("-------------------");
  parent = new ClassLoaderStaticParent();
  System.out.println("------------------");
  System.out.println(parent.x);
  System.out.println("------------------");
  System.out.println(ClassLoaderStaticChild.x);
  输出结果: 
     -------------------
     i am ClassLoaderStaticParent
     ------------------
     1
     ------------------
     i am ClassLoaderStaticChild
     2
}

The conclusion is:

  1. When an object is new, the current class will be initialized, but the premise is that the current class has not been initialized (introduced in the previous chapter)
  2. Actively access the static variables of this class, and the called class will also be actively initialized (described above)

9. Initialize two when testing class inheritance

class ClassLoaderPorpertyAndMethodParent{
    
    
   public static int a = 3;
   static{
    
    
       System.out.println("Parent static block");
   }
   static void doSomeThing(){
    
    
       System.out.println("I am doSomeThing methed");
   }
}

class ClassLoaderPorpertyAndMethodChild extends ClassLoaderPorpertyAndMethodParent{
    
    
    static {
    
    
        System.out.println("i am ClassLoaderPorpertyAndMethodChild block");
    }
}
@Test
public void ClassLoaderPorpertyAndMethodParent() {
    
    
  System.out.println(ClassLoaderPorpertyAndMethodChild.a);
  System.out.println("-------------------------");
  ClassLoaderPorpertyAndMethodChild.doSomeThing();
  输出结果: 
     Parent static block
     3
     -------------------------
     I am doSomeThing methed
}

The conclusion is:

  1. Actively access the static variables of this class, and the called class will also be actively initialized (described above)

  2. Calling the static method of the class is also an active use of the class, but the class has already been initialized once when the static variable is called, so the second time the class will not be initialized, it will not be output Parent static block

    Insert picture description here
    At this point, everyone should understand the general process of class loading. If you have any questions, I hope to leave a message

Pay attention to me, I will continue to output... Looking forward to the next issue of JVM learning.
Encourage with you...

Guess you like

Origin blog.csdn.net/weixin_38071259/article/details/105893659