1. 静态变量所在类加载过程



/**
 * @author ztkj-hzb
 * @Date 2019/11/1 11:46
 * @Description
 */
public class Test1 {

    public static void main(String[] args) {

        System.out.println(MyChild1.str);
        //System.out.println(MyChild1.str2);

    }

}


class MyParent1 {

    public static String str = "hello world";

    static {
        System.out.println("MyParent1 block");
    }

}


class MyChild1 extends MyParent1 {
    public static String str2 = "welcome";

    static {
        System.out.println("MyChild1 block");
    }

}

执行以上代码,得到的输出结果是

MyParent1 block
hello world

为什么调用了 MyChild1 类,却没有初始化MyChild1,执行MyChild1的static代码块呢

依然是上述例子,如果这样配置,得出什么结果呢?

//System.out.println(MyChild1.str);
System.out.println(MyChild1.str2);

执行以上代码,得到的输出结果是

MyParent1 block
MyChild1 block
welcome

过程分析:

2. 针对静态常量的类加载过程分析(这里是指在编译期就能获取值的情况)

package jvmtest;

/**
 * @author ztkj-hzb
 * @Date 2019/11/4 10:42
 * @Description
 */
public class Test3 {

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

}

class MyTest3{

    public static final String str = "hello world";

    static {
        System.out.println("MyTest3...");
    }

}

执行以上代码,得到的输出结果是:

hello world

分析其过程:

使用javap -c反编译工具可以看到结果:

javap -c Test3
警告: 二进制文件Test3包含jvmtest.Test3
Compiled from "Test3.java"
public class jvmtest.Test3 {
  public jvmtest.Test3();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
       3: ldc           #4                  // String hello world
       5: invokevirtual #5                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
       8: return
}

可以看到main中的 ldc(助记符),结果是个已知值,hello world

3. 针对静态常量的类加载过程分析(这里是指在编译期不能获取值的情况)

package jvmtest;

import java.util.UUID;

/**
 * @author ztkj-hzb
 * @Date 2019/11/4 15:54
 * @Description 针对静态常量,如果其值是在编译期间可以得出结果的,则会在调用该常量的所在方法的所在类的常量池中存储该值,并不会引用常量所在类。
 *                          如果其值在编译期间不能得出结果的,则会从声明该常量的类中获取,导致其引用时初始化。
 */
public class Test4 {

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

class MyTest4{

    public static final String str = UUID.randomUUID().toString();

    static {
        System.out.println("MyTest4....");
    }
}

执行以上代码,得到的输出结果是:

MyTest4....
61f48050-a502-492e-8678-cf82d708253d

分析其过程:

4. 数组类型本质分析

package jvmtest;

/**
 * @author ztkj-hzb
 * @Date 2019/11/4 17:34
 * @Description 针对数组而言,jvm会自动生成一个jvm识别的类型,这里不会去主动调用MyTest5这个类(虽然这个类会被加载)
 */
public class Test5 {


    public static void main(String[] args) {

        MyTest5[] myTest5s = new MyTest5[1];
        System.out.println(myTest5s.getClass());

        System.out.println("====================================");

        MyTest5[][] myTest5s1 = new MyTest5[1][2];
        System.out.println(myTest5s1.getClass());
    }

}


class MyTest5 {
    static {
        System.out.println("MyTest5...");
    }
}

执行以上代码,得到的输出结果是:

class [Ljvmtest.MyTest5;
====================================
class [[Ljvmtest.MyTest5;

分析,为什么初始化MyTest5这个类(如何看出?:因为MyTest5这个类的静态代码块没有执行)

5. 针对接口类型,运行时分析

package jvmtest;

/**
 * @author ztkj-hzb
 * @Date 2019/11/5 11:33
 * @Description
 */
public class Test6 {
    public static void main(String[] args) {
        System.out.println(MyChild6.b);
    }
}

interface MyParent6{
    public static final int a = 5;
}

interface MyChild6 extends MyParent6{
    public static final int b = 6;
}

执行以上代码,结果如下:

6

分析过程:

02-12 17:51