Java根本数据类型和包装类的声明、编译和初始化
Java基本数据类型和包装类的声明、编译和初始化
在平时的开发中,声明一个数字类型的变量,一般有以下几种形式:
int类型为基本数据类型,a指向的是一个字面量,不是类的对象实例,它是编译期可知的。b、c、d一样,都是指向的类对象实例的引用。
上面这段代码编译后的class文件,用javap命令解析后,可以看到字节码指令如下:
通过分析上面的java字节码,可以印证开始的b/c/d都是指向Integer类的对象实例的论证。
但是又有了下面的问题:
输出是:
true
true
true
false
true
false
可见,a与bcd都相等而bc、cd不相等,bc相等,这是为什么呢?
首先,a与bcd都相等,这是常识了。当和包装类对比的对象是其对应的基本类型时,包装类会自动拆箱成基本数据类型与之对比。而bcd都是对象,相互对比的话,自然就是对比的引用地址了,由此,因为c是new Integer的操作,所以bc、cd不相等也是很显而易见的,那为什么bd是相等的呢?
我们可以在编译后的字节码的分析中看到,b和d的初始化过程是一样的,都是调用了Integer的valueOf静态方法,难道这个方法里有乾坤?
上面是Integer类valueOf方法的源码。
代码中,首先根据参数值和IntegerCache.high值和-128做了对比,如果参数值在此范围内的话,则返回IntegerCache中的一个数组元素,否则,则返回一个新的Integer对象。
那如此看来,b和d初始化时,返回的都是IntegerCache的同一个数组元素值了?
再去看看IntegerCache。
IntegerCache 是Integer类的私有静态内部类,有两个静态元素,int型的high和Integer数组cache[].分析源码可以看到,该类主要是在初始化的时候初始化cache[]数组,其大小是high-low+1,默认情况下,大小是256,元素数值范围是[-128,127],这不也是byte类型的数值范围么?如果在JVM启动的时候设置了java.lang.Integer.IntegerCache.high参数,则cache的大小及数值由integerCacheHighPropValue决定。
由此说明,b和d返回同一个实例论证成功,那么对于如下程序呢?
这个是打印true还是false呢?不用说了吧~~
扩展一下,下面例子的打印呢?
在平时的开发中,声明一个数字类型的变量,一般有以下几种形式:
int a = 1; Integer b = 1; Integer c = new Integer(1); Integer d = Integer.valueOf(1);
int类型为基本数据类型,a指向的是一个字面量,不是类的对象实例,它是编译期可知的。b、c、d一样,都是指向的类对象实例的引用。
上面这段代码编译后的class文件,用javap命令解析后,可以看到字节码指令如下:
Code: Stack=3, Locals=5, Args_size=1 //将int型值1推至栈顶 0: iconst_1 //将栈顶的int型值放入本地变量表中的第二个 1: istore_1 //将int型值1推至栈顶 2: iconst_1 //调用java.lang.Integer的静态方法valueOf(java.lang.Integer),参数是栈顶的元素,并将返回值放入栈顶 3: invokestatic #16; //Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; //将栈顶的引用类型值放入本地变量表中的第三个 6: astore_2 //创建一个Integer对象 7: new #17; //class java/lang/Integer //将该对象的引用压入栈顶 10: dup //将int型值1推至栈顶 11: iconst_1 //调用java.lang.Integer类的初始化方法,该方法参数是栈顶的int型元素,返回值放入栈顶 12: invokespecial #22; //Method java/lang/Integer."<init>":(I)V //将栈顶的引用类型值放入本地变量表中的第四个 15: astore_3 //将int型数值1压制栈顶 16: iconst_1 //调用java.lang.Integer的静态方法valueOf(java.lang.Integer),参数是栈顶的元素,并将返回值放入栈顶 17: invokestatic #16; //Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; //将栈顶的引用类型值放入本地变量表中的第四个 20: astore 4 22: return
通过分析上面的java字节码,可以印证开始的b/c/d都是指向Integer类的对象实例的论证。
但是又有了下面的问题:
System.out.println(a == b); System.out.println(a == c); System.out.println(a == d); System.out.println(b == c); System.out.println(b == d); System.out.println(c == d);
输出是:
true
true
true
false
true
false
可见,a与bcd都相等而bc、cd不相等,bc相等,这是为什么呢?
首先,a与bcd都相等,这是常识了。当和包装类对比的对象是其对应的基本类型时,包装类会自动拆箱成基本数据类型与之对比。而bcd都是对象,相互对比的话,自然就是对比的引用地址了,由此,因为c是new Integer的操作,所以bc、cd不相等也是很显而易见的,那为什么bd是相等的呢?
我们可以在编译后的字节码的分析中看到,b和d的初始化过程是一样的,都是调用了Integer的valueOf静态方法,难道这个方法里有乾坤?
public static Integer valueOf(int i) { if(i >= -128 && i <= IntegerCache.high) return IntegerCache.cache[i + 128]; else return new Integer(i); }
上面是Integer类valueOf方法的源码。
代码中,首先根据参数值和IntegerCache.high值和-128做了对比,如果参数值在此范围内的话,则返回IntegerCache中的一个数组元素,否则,则返回一个新的Integer对象。
那如此看来,b和d初始化时,返回的都是IntegerCache的同一个数组元素值了?
再去看看IntegerCache。
private static class IntegerCache { static final int high; static final Integer cache[]; static { final int low = -128; // high value may be configured by property int h = 127; if (integerCacheHighPropValue != null) { // Use Long.decode here to avoid invoking methods that // require Integer's autoboxing cache to be initialized int i = Long.decode(integerCacheHighPropValue).intValue(); i = Math.max(i, 127); // Maximum array size is Integer.MAX_VALUE h = Math.min(i, Integer.MAX_VALUE - -low); } high = h; cache = new Integer[(high - low) + 1]; int j = low; for(int k = 0; k < cache.length; k++) cache[k] = new Integer(j++); } private IntegerCache() {} }
IntegerCache 是Integer类的私有静态内部类,有两个静态元素,int型的high和Integer数组cache[].分析源码可以看到,该类主要是在初始化的时候初始化cache[]数组,其大小是high-low+1,默认情况下,大小是256,元素数值范围是[-128,127],这不也是byte类型的数值范围么?如果在JVM启动的时候设置了java.lang.Integer.IntegerCache.high参数,则cache的大小及数值由integerCacheHighPropValue决定。
由此说明,b和d返回同一个实例论证成功,那么对于如下程序呢?
Integer e = 200; Integer f = 200; System.out.println(e == f);
这个是打印true还是false呢?不用说了吧~~
扩展一下,下面例子的打印呢?
Integer a = 1; Integer b = 2; Integer c = 3; Integer d = 3; Integer e = 321; Integer f = 321; Long g = 3L; System.out.println(c == d); System.out.println(e == f); System.out.println(c == (a+b)); System.out.println(c.equals(a+b)); System.out.println(g == (a+b)); System.out.println(g.equals(a+b));