编译期常量与运行期常量的区别及数组创建本质分析
助记符补充:
在上一次【http://www.cnblogs.com/webor2006/p/8849520.html】中接触到了一些字节码的助记符,其中说到了“iconst助记符”,如下:
对它的总结如下:
也就是iconst包含iconst_1到iconst_5,其实还落了两个,一个是0,一个是-1,那下面实验一下:
那如果是-2呢?
很显然就不是iconst的啦,所以补充一下。
而其实对于这些助记符在定义在JDK中的rt.jar中,这里以iconst助记符为例,在工具中搜一下:
打开可以发现该类位于一个我们不常用的包“com.sun.org.apache.bcel.internal.generic”中,打开看一下它的注释说明:
而看一下它的具体实现就秒懂了:
另外发现它是继承一个"Instruction"这个类的,从英文意思来讲就是指令的意思嘛,可以看一下它的子类有多少:
好多呀,那我们上节还学有其它的助字符是不是都在其中,无聊找找呗:
哟西~~确实如此~~也就说明对于每个助字符都对应具体的实现类的。
编译期常量与运行期常量的区别:
开始这次的正题,先编写一个新的例子,如下:
那运行结果不跟之前举的例子结果一样嘛,因为打印常量不会导致类的初始化,是不是这样呢?看结果:
呃~~为啥呢?好奇怪的呢,其实这里就涉及到编译期的常量与运行期的常量是有区别的,这里再解释一下什么是编译期的常量和运行期的常量,所以编译期的常量就是其常量值在编译就能确定的,也就是咱们之前的那种,如:
而运行期常量指的当然就是在编译期其常量值是不确定的,回到咱们的这个例子,编译期对于UUID的值我们是完全不知的,那就属于运行期常量了。而如果将build的Myparent3的字节文件删除掉就不会像之前可以正常运行了,如下:
所以这里做一个总结:
当一个常量的值并非编译期间可以确定,那么其值就不会被放到调用类的常量池中,这时程序运行时,会导致主动使用这个常量所在的类,显然会导致这个类的初始化。
数组创建本质:
继续新建例子:
那它导致MyParent4的初始化么?编译运行:
很显然是导致了,为啥呢?还得回到导致类的主动使用的七种类型,具体是哪种呢,如下:
接下来改造代码:
那会导致MyParent4的主动使用么?
很显然木有主动使用,那又为什么呢?因为它木有出现在七种主动使用的情况之列嘛,那很显然这是创建实例了呀,都用了new了,那下面打印一下这个实例是什么类型:
这个类型很显然咱们木有去定义,那它是从何而来的呢?不难想象肯定是JVM在运行期创建的嘛,这个类型就叫做“数组类型”, 接着再改造:
那数组类型的父类是什么呢?打印一下:
所以总结如下:
对于数组实例来说,其类型是由JVM在运行期动态生成的,表示为“[Lcom.jvm.classloader.MyParent4”这种形式,动态生成的类型,其父类型就是Obejct,对于数组来说,JavaDoc经常将构成数组的元数称之为Component,实际上就是将数组降低为一个维度后的类型。
下面继续改造代码:
助记符介绍:
anewarray:
好,接下來反编译一下字节码文件看一下对应的助记符,具体内容如下:
xiongweideMacBook-Pro:classes xiongwei$ javap -c com.jvm.classloader.MyTest4 Compiled from "MyTest4.java" 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: iconst_1 16: iconst_1 17: multianewarray #6, 2 // class "[[Lcom/jvm/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等)的数组,并将其引用值压入栈顶。
【总结】:anewarray是新建的引用类型的数组,而newarray是新建的原生类型的数组。
那下面再多打印几个原生数组类型都长啥样,有个印象: