6. 编译期常量与运行期常量的区别及数组创建本质分析

编译期常量与运行期常量的区别

例1

package com.study.classloader;

public class MyTest2 {

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

class MyParent2{

    public static final String content = "parent";

    public static final short s = 127;

    public static final int i = 128;

    public static final int m = 5;

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

执行结果:

5

分析:

静态代码块不会被执行,因为在常量m在编译阶段会被保存在调用常量m的方法所在的类MyTest2的常量池中(MyTest2的class文件中),所以执行时没有对MyParent2进行主动使用,不会触发其初始化,而静态代码块又是要在初始化阶段被执行的。所以这里不会被执行

例2

package com.study.classloader;

import java.util.UUID;

public class MyTest3 {

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

}

class MyParent3 {

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

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

执行结果:

MyParent3 static block
2d759b96-5ba8-4be2-87fe-9eeb29949d61

分析:

为什么例1不会打印静态代码块,而例2会打印静态代码块?

从本质上来说两个例子都是在打印的常量,但区别在于常量的值能否在编译器确定下来。例1可以,例2不行,只能在运行期确定值

当一个常量的值并非编译期间可以确定的,那么其值就不会被放到调用类的常量池中,这时候在程序运行时,会导致主动使用这个常量所在的类,显然会导致这个类被初始化。

数组创建本质分析

例3

package com.study.classloader;

public class MyTest4 {

    public static void main(String[] args) {
        MyParent4[] myParent4s = new MyParent4[1];
        System.out.println(myParent4s.getClass());
        MyParent4[][] myParent4s1 = new MyParent4[1][1];
        System.out.println(myParent4s1.getClass());
        System.out.println(myParent4s.getClass().getSuperclass());
        System.out.println(myParent4s1.getClass().getSuperclass());
    }
}

class MyParent4 {

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

结果:

class [Lcom.study.classloader.MyParent4;

class [[Lcom.study.classloader.MyParent4;

class java.lang.Object
class java.lang.Object

分析:

没有执行MyParent4的初始化,并不属于主动使用的7种类型的任何一种。

[Lcom.study.classloader.MyParent4为数组的类型,我们并没有在自己的代码中定义过这个类型,这是JVM帮助我们在运行期生成出来数组的类型

例4

基本数据类型数组

package com.study.classloader;

public class MyTest4 {

    public static void main(String[] args) {
        MyParent4[] myParent4s = new MyParent4[1];
        System.out.println(myParent4s.getClass());
        MyParent4[][] myParent4s1 = new MyParent4[1][1];
        System.out.println(myParent4s1.getClass());
        System.out.println(myParent4s.getClass().getSuperclass());
        System.out.println(myParent4s1.getClass().getSuperclass());
        System.out.println("=====");
        int[] ints = new int[1];
        System.out.println(ints.getClass());
        System.out.println(ints.getClass().getSuperclass());
    }
}

class MyParent4 {

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

结果:

class [Lcom.study.classloader.MyParent4;
class [[Lcom.study.classloader.MyParent4;
class java.lang.Object
class java.lang.Object
=====
class [I
class java.lang.Object

对于数组实例来说,其类型是由JVM在运行期动态生成的,表示为[Lcom.study.classloader.MyParent4这种形式。动态生成的类型,其父类型就是Object。

对于数组来说,JavaDoc经常将构成数组的元素为Component,实际上就是将数组降低一个维度后的类型。

反编译分析

➜  jvm javap -c target.classes.com.study.classloader.MyTest4 
警告: 文件 ./target/classes/com/study/classloader/MyTest4.class 不包含类 target.classes.com.study.classloader.MyTest4
Compiled from "MyTest4.java"
public class com.study.classloader.MyTest4 {
  public com.study.classloader.MyTest4();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: iconst_1
       1: anewarray     #2                  // class com/study/classloader/MyParent4
       4: astore_1
       5: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
       8: aload_1
       9: invokevirtual #4                  // Method java/lang/Object.getClass:()Ljava/lang/Class;
      12: invokevirtual #5                  // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
      15: iconst_1
      16: iconst_1
      17: multianewarray #6,  2             // class "[[Lcom/study/classloader/MyParent4;"
      21: astore_2
      22: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
      25: aload_2
      26: invokevirtual #4                  // Method java/lang/Object.getClass:()Ljava/lang/Class;
      29: invokevirtual #5                  // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
      32: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
      35: aload_1
      36: invokevirtual #4                  // Method java/lang/Object.getClass:()Ljava/lang/Class;
      39: invokevirtual #7                  // Method java/lang/Class.getSuperclass:()Ljava/lang/Class;
      42: invokevirtual #5                  // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
      45: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
      48: aload_2
      49: invokevirtual #4                  // Method java/lang/Object.getClass:()Ljava/lang/Class;
      52: invokevirtual #7                  // Method java/lang/Class.getSuperclass:()Ljava/lang/Class;
      55: invokevirtual #5                  // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
      58: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
      61: ldc           #8                  // String =====
      63: invokevirtual #9                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      66: iconst_1
      67: newarray       int
      69: astore_3
      70: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
      73: aload_3
      74: invokevirtual #4                  // Method java/lang/Object.getClass:()Ljava/lang/Class;
      77: invokevirtual #5                  // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
      80: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
      83: aload_3
      84: invokevirtual #4                  // Method java/lang/Object.getClass:()Ljava/lang/Class;
      87: invokevirtual #7                  // Method java/lang/Class.getSuperclass:()Ljava/lang/Class;
      90: invokevirtual #5                  // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
      93: return
}

助记符:

anewarray:表示创建一个引用类型的(如类、接口、数组)数组,并将其引用值压入栈顶

newarray:表示创建一个指定的原始类型(如int、float、char等)的数组,并将其引用值压入栈顶

猜你喜欢

转载自blog.csdn.net/Cheng_Kohui/article/details/93298919