Prueba práctica del proceso de carga de clases JVM
-
-
-
- 1. Prueba de valor constante
- 2. Prueba de valor de variable estática
- 3. Crear prueba de objeto
- 4. Crear prueba de datos de matriz de objetos
- 5. Cree un tipo básico de prueba de datos de matriz
- 6. Utilice el ejemplo del patrón singleton para simular el proceso de carga de clases.
- 7. Inicializar al probar la herencia de la interfaz
- 8. Inicialice una herencia de clase de prueba
- 9. Inicialice dos al probar la herencia de clases
-
-
1. Prueba de valor constante
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
La conclusión es:
- El valor constante se almacenará en el grupo constante de la clase llamada, por lo que la clase llamada no se utilizará activamente, por lo que la clase llamada no se inicializará. El
UUID.randomUUID().toString()
valor de esta declaración no se fija durante la compilación, por lo que no será como la mencionada anteriormente.(public static final String str = "I Am ClassLoaderTest1";)
- El valor constante se almacenará en el grupo constante de la clase que llama, por lo que la clase llamada no se usará activamente, por lo que la clase llamada no se inicializará
2. Prueba de valor de variable estática
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
La conclusión es:
- Acceder a las variables estáticas de una determinada clase o interfaz es equivalente al uso activo de la clase (explicado en el artículo anterior), por lo que la clase llamada se usará activamente, por lo que la clase llamada se inicializará y generará
FinalTest static block
- Tenga en cuenta que las variables estáticas no son lo mismo que las constantes
3. Crear prueba de objeto
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
------------------------------
}
La conclusión es:
- Una clase usará activamente la clase llamada (ClassLoaderTest2) cuando sea nuevo un objeto, y una clase solo se inicializará una vez cuando sea nuevo un objeto
4. Crear prueba de datos de matriz de objetos
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
}
La conclusión es:
- La
ClassLoaderTest2 Block
palabra no aparece como se esperaba , por lo que todas las clases, excepto la clase de matriz, son creadas por el cargador de clases. El tipo de datos es generado dinámicamente por el tiempo de ejecución de jvm, y***[LclassLoader.ClassLoaderTest2t;
las palabras expresadas como una matriz de dos dígitos son [[** (Nota: hay un punto y coma después). El tipo generado dinámicamente, el super tipo es.java.lang.Object
Para las matrices, JavaDoc a menudo se refiere a los elementos que componen la matriz como Componente, que en realidad es el tipo después de que la matriz se reduce en una dimensión.
5. Cree un tipo básico de prueba de datos de matriz
@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
}
En conclusión:
- El súper tipo es
java.lang.Object
para matrices. JavaDoc a menudo se refiere a los elementos que constituyen la matriz como Componente, que en realidad es el tipo después de que la matriz se reduce en una dimensión.
6. Utilice el ejemplo del patrón singleton para simular el proceso de carga de clases.
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
*/
}
7. Inicializar al probar la herencia de la interfaz
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
* 所以得出结论是:
* 只有在真正使用到父接口的时候(如引用接口中定义的常量时),才会初始化。注意如果是类与类继承的时候如果子类进行初始化的时候
* 父类必须进行初始化
*/
}
En conclusión:
- Es decir, la interfaz secundaria no tiene nada que ver con la interfaz principal, por lo que se concluye que la inicialización de la interfaz secundaria no requiere necesariamente que la interfaz principal complete la inicialización.
- Solo se inicializará cuando se utilice realmente la interfaz principal (como cuando se hace referencia a una constante definida en la interfaz).
- Tenga en cuenta que si la clase se hereda de la clase, la clase principal debe inicializarse cuando se inicializa la subclase
8. Inicialice una herencia de clase de prueba
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
}
La conclusión es:
- Cuando un objeto es nuevo, se inicializará la clase actual, pero la premisa es que la clase actual no se ha inicializado (presentado en el capítulo anterior)
- Acceda activamente a las variables estáticas de esta clase, y la clase llamada también se inicializará activamente (descrito anteriormente)
9. Inicialice dos al probar la herencia de clases
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
}
La conclusión es:
-
Acceda activamente a las variables estáticas de esta clase, y la clase llamada también se inicializará activamente (descrito anteriormente)
-
Llamar al método estático de la clase también es un uso activo de la clase, pero la clase ya se ha inicializado una vez cuando se llama a la variable estática, por lo que la segunda vez que la clase no se inicializará, no se emitirá
Parent static block
En este punto, todos deberían comprender el proceso general de carga de clases. Si tiene alguna pregunta, espero dejar un mensaje
Présteme atención, seguiré publicando ... Espero con ansias el próximo número de aprendizaje de JVM.
Animo con usted ...