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

本文深入探讨编译期常量与运行期常量的区别,通过实例分析指出编译期间无法确定值的常量会导致运行时类的初始化。同时,文章详细阐述了数组创建的过程,包括一维和二维数组,揭示了数组实例的类型由JVM在运行期动态生成的特性,并通过反编译代码解析数组创建的底层指令。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、编译期常量与运行期常量

引例:判断MyParent3会不会初始化?

import java.util.UUID;

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

class MyParent3 {

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

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

我的想法:
我的知识只能支撑我这样想:这个例子跟上节课的例子没有区别。静态常量会放在引用他的那个方法所在的类的常量池,所以Myparent3不会初始化。
实际输出:

MyParent3 static code
0e1b29c7-bd80-4b9c-ba7c-58c5de10cd9a

静态代码块执行了,脸又被打肿了。


分析:在本例中,str的值在编译期间是不知道的,只有运行的时候才知道(即str是运行期常量)。所以MyParents会初始化。这时候,如果把MYParent的class文件删除掉,则会抛出异常。
当一个常量的值并非编译期间可以确定的,那么其值就不会被放到调用类的常量池中,这时在程序运行时,会导致主动使用这个常量所在的类,显然会导致这个类被初始化。

2、创建实例引起的主动使用

引例1:判断MyParent4中的静态代码块会不会被执行?

public class MyTest4 {
    public static void main(String[] args) {
        MyParent4 myParent4 = new MyParent4();
    }
}

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

我的想法:会被执行,因为这里创建了类的实例啊,肯定会被执行的。
实际结果:

MyParent4 static block

引例2: 了解首次主动调用

public class MyTest4 {
    public static void main(String[] args) {
        MyParent4 myParent4 = new MyParent4();
        System.out.println("---");
        MyParent4 myParent5 = new MyParent4();
    }
}

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

实际结果:

MyParent4 static block
---

对比引例1,说明只有在首次主动调用的时候才会初始化。

3、数组

引例:判断静态代码块会不会执行?

public class MyTest4 {
    public static void main(String[] args) {
        MyParent4[] myParent4s = new MyParent4[1];
    }
}

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

我的想法:
不知道,没想法。
实际结果:无输出。

分析:说明数组那行代码不代表对类的主动使用。
疑问:new数组的那行代码应该是创建实例了,但是静态代码块没执行,说明肯定MyParent4的实例没创建,那new的是啥玩意?
在数组初始化代码下面加上如下代码:

System.out.println(myParent4s.getClass());

输出如下:

class [Lcom.jvm.classloader.MyParent4;

说明是[Lcom.jvm.classloader.MyParent4类,但是这个类我没创建过。这个类型是java虚拟机在运行期帮我们创建出来的。


引申:二维数组呢?

public class MyTest4 {
    public static void main(String[] args) {
        MyParent4[] myParent4s = new MyParent4[1];
        System.out.println(myParent4s.getClass());
        System.out.println(myParent4s.getClass().getSuperclass());
        MyParent4[][] myParent4s1 = new MyParent4[1][1];
        System.out.println(myParent4s1.getClass());
        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());

        char[] chars = new char[1];
        System.out.println(chars.getClass());
        System.out.println(chars.getClass().getSuperclass());

        boolean[] booleans = new boolean[1];
        System.out.println(booleans.getClass());
        System.out.println(booleans.getClass().getSuperclass());

        short[] shorts = new short[1];
        System.out.println(shorts.getClass());
        System.out.println(shorts.getClass().getSuperclass());

        byte[] bytes = new byte[1];
        System.out.println(bytes.getClass());
        System.out.println(bytes.getClass().getSuperclass());

    }
}

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

运行结果:

class [Lcom.jvm.classloader.MyParent4;
class java.lang.Object
class [[Lcom.jvm.classloader.MyParent4;
class java.lang.Object
======
class [I
class java.lang.Object
class [C
class java.lang.Object
class [Z
class java.lang.Object
class [S
class java.lang.Object
class [B

分析:静态代码块依旧没有执行,类型变了。

结论:对于数组实例来说,其类型是由JVM在运行期动态生成的,表示为[Lcom.jvm.classloader.MyParent4这种形式。动态生成的类型,其父类型就是Object。
对于数组来说,JavaDoc经常将构成数组的元素为Component,实际上就是将数组降低一个维度后的类型。

4、数组类型解密

反编译MyTest4:javap -c target/classes/com/jvm/classloader/MyTest4.class
结果如下:

public class com.jvm.classloader.MyTest4 {
  public com.jvm.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/jvm/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: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
      18: aload_1
      19: invokevirtual #4                  // Method java/lang/Object.getClass:()Ljava/lang/Class;
      22: invokevirtual #6                  // Method java/lang/Class.getSuperclass:()Ljava/lang/Class;
      25: invokevirtual #5                  // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
      28: iconst_1
      29: iconst_1
      30: multianewarray #7,  2             // class "[[Lcom/jvm/classloader/MyParent4;"
      34: astore_2
      35: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
      38: aload_2
      39: invokevirtual #4                  // Method java/lang/Object.getClass:()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 #6                  // 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 #6                  // Method java/lang/Class.getSuperclass:()Ljava/lang/Class;
      90: invokevirtual #5                  // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
      93: iconst_1
      94: newarray       char
      96: astore        4
      98: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
     101: aload         4
     103: invokevirtual #4                  // Method java/lang/Object.getClass:()Ljava/lang/Class;
     106: invokevirtual #5                  // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
     109: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
     112: aload         4
     114: invokevirtual #4                  // Method java/lang/Object.getClass:()Ljava/lang/Class;
     117: invokevirtual #6                  // Method java/lang/Class.getSuperclass:()Ljava/lang/Class;
     120: invokevirtual #5                  // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
     123: iconst_1
     124: newarray       boolean
     126: astore        5
     128: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
     131: aload         5
     133: invokevirtual #4                  // Method java/lang/Object.getClass:()Ljava/lang/Class;
     136: invokevirtual #5                  // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
     139: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
     142: aload         5
     144: invokevirtual #4                  // Method java/lang/Object.getClass:()Ljava/lang/Class;
     147: invokevirtual #6                  // Method java/lang/Class.getSuperclass:()Ljava/lang/Class;
     150: invokevirtual #5                  // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
     153: iconst_1
     154: newarray       short
     156: astore        6
     158: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
     161: aload         6
     163: invokevirtual #4                  // Method java/lang/Object.getClass:()Ljava/lang/Class;
     166: invokevirtual #5                  // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
     169: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
     172: aload         6
     174: invokevirtual #4                  // Method java/lang/Object.getClass:()Ljava/lang/Class;
     177: invokevirtual #6                  // Method java/lang/Class.getSuperclass:()Ljava/lang/Class;
     180: invokevirtual #5                  // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
     183: iconst_1
     184: newarray       byte
     186: astore        7
     188: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
     191: aload         7
     193: invokevirtual #4                  // Method java/lang/Object.getClass:()Ljava/lang/Class;
     196: invokevirtual #5                  // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
     199: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
     202: aload         7
     204: invokevirtual #4                  // Method java/lang/Object.getClass:()Ljava/lang/Class;
     207: invokevirtual #6                  // Method java/lang/Class.getSuperclass:()Ljava/lang/Class;
     210: invokevirtual #5                  // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
     213: return
}

分析:
1、iconst_1表示的是数组长度的那个1
2、anewarray 表示的新的这个数组
3、newarray表示创建一个指定的原始类型(int,float,char等)的数组,并将其引用值压入栈顶

助记符:

1、anewarray :表示创建一个引用类型的(类、接口、数组)的数组,并将其引用值压入栈顶。
2、newarray:表示创建一个指定的原始类型(int,float,char等)的数组,并将其引用值压入栈顶
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值