一段代码看 Java 引用类型
Java 中的操作数(不知道叫什么,相对于 bytecode 而言,类似 CPU 的操作码和操作数)分为值类型和引用类型:
值类型就是直接存储最终数值的,如 char, int, float, double...
引用类型包括了数组,以及其它所有用 class 定义的数据类型,这种数据类型由引用和对象实例连部分组成。
虽然 Java 中一切都是类,但是对于值类型,因为不需要存储其‘对象’特征,所以,编译器只为他们分配了 stack 上的空间,存储单个的值就可以了。而引用类型则不同,他们的变量存储分为两部分:在 stack 中的引用(指针),以及在 heap 中的对象实例。大概长这样(图借别人的,看最后的链接):
使用下面一段代码来理解这个问题,体会下区别(String.equals() 的标准实现)。
1 public boolean equals(Object anObject) { 2 if (this == anObject) { 3 return true; 4 } 5 if (anObject instanceof String) { 6 String anotherString = (String)anObject; 7 int n = value.length; 8 if (n == anotherString.value.length) { 9 char v1[] = value; 10 char v2[] = anotherString.value; 11 int i = 0; 12 while (n-- != 0) { 13 if (v1[i] != v2[i]) 14 return false; 15 i++; 16 } 17 return true; 18 } 19 } 20 return false; 21 }
一起从这段代码来看看值类型和引用类型的区别:
1. 第 2 行,首先使用 == 判断,两个引用指向的是否为同一个对象实例。
2. 第 5 行,判断传入的 object 是否是 String 的实例。
3. 第 6 行,将传入的 object 强制转换为 String 类。
4. 第 7、8 行,取 this.value.length 与 anotherString.value.length 作比较。value 的类型是 char[],因为数组是一个有限的数据集合,其定义时就已经固定了长度。所以这个操作被编译后,只是两个内存单元的内容比较。
5. 第 9、10 行,创建新的数组引用,分别指向 this.value 和 anotherString.value。
6. 第 11-16 行,逐个比较两个 char 数组中的元素。直接比较两个数组各个元素的效率,是要比比较 String 元素的效率更高的(转化为 Char 对象的比较,再比较 Char value,显然是要耗费更多资源的...)。
关于数组
数组是 JVM 提供的基本数据类型之一。
JVM 直接提供的数组类型有 T_BOOLEAN([Z, 1byte)、T_CHAR([C, 2bytes)、T_FLOAT([F, 4bytes)、T_DOUBLE([D,8bytes)、T_BYTE([B, 1bytes)、T_SHORT([S, 2bytes)、T_INT([I, 4bytes)、T_LONG([J, 8bytes)。
我们可以轻松验证,一个数组是 instanceof Object,同时,数组调用方法 getClass().getName(),也是可以正确输出的。
public class arrayTest { public static void main(String[] args) { int[] a = {1,2,3,4}; System.out.println(a.getClass().getName()); System.out.println("a is instanceof Object? "+(a instanceof Object)); } }
当然,数组的实现方法是在 JVM 中的,但这并不妨碍我们像使用其它类/对象一样类使用数组。
一个 array 在内存中的 layout 大概长这样:
从上面的图中,可以看到两个信息,第一个,数组在内存中是连续存放的,第二个,header 之后的位置存储着数组的长度。这也是为什么我们可以直接获取数组的 length 属性,而其它一些类,必须使用 length() 方法。
关于 length 存储空间的大小,这个显示是和 JVM 实现有关的。查阅文档发现,sun 的 JVM (java7) 版本:32位系统中 size_t 是4字节的,在64位系统中,size_t 是8字节的。
另外,好奇对象数组是怎样存的,打印了一下 String[] b 的 classNage,显示是“[Ljava.lang.String;”。其中 “[” 表示这是个数组,“L/java/lang.String;” 叫做 JavaNative Interface Field Descriptors,大概就是说这是个 String 类型的数组了。
创建数组的 bytecode 是 JAVA_NEWARRAY 和 JAVA_ANEWARRAY,暂作记录,改天有空再看看 JVM 中是怎么来操作的。