作者:程序员库森
链接:https://www.nowcoder.com/discuss/601272
来源:牛客网
自动装箱:将基本数据类型重新转化为对象
1 2 3 4 5 6 |
|
9是属于基本数据类型的,原则上它是不能直接赋值给一个对象Integer的。但jdk1.5 开始引入了自动装箱/拆箱机制,就可以进行这样的声明,自动将基本数据类型转化为对应的封装类型,成为一个对象以后就可以调用对象所声明的所有的方法。
自动拆箱:将对象重新转化为基本数据类型
1 2 3 4 5 6 7 8 9 |
|
因为对象时不能直接进行运算的,而是要转化为基本数据类型后才能进行加减乘除。
9. int 和 Integer 有什么区别?
- Integer是int的包装类;int是基本数据类型;
- Integer变量必须实例化后才能使用;int变量不需要;
- Integer实际是对象的引用,指向此new的Integer对象;int是直接存储数据值 ;
- Integer的默认值是null;int的默认值是0。
10. 两个new生成的Integer变量的对比
由于Integer变量实际上是对一个Integer对象的引用,所以两个通过new生成的Integer变量永远是不相等的(因为new生成的是两个对象,其内存地址不同)。
1 2 3 |
|
11. Integer变量和int变量的对比
Integer变量和int变量比较时,只要两个变量的值是向等的,则结果为true(因为包装类Integer和基本数据类型int比较时,java会自动拆包装为int,然后进行比较,实际上就变为两个int变量的比较)
1 2 3 4 5 |
|
12. 非new生成的Integer变量和new Integer()生成变量的对比
非new生成的Integer变量和new Integer()生成的变量比较时,结果为false。(因为非new生成的Integer变量指向的是java常量池中的对象,而new Integer()生成的变量指向堆中新建的对象,两者在内存中的地址不同)
1 2 3 |
|
13. 两个非new生成的Integer对象的对比
对于两个非new生成的Integer对象,进行比较时,如果两个变量的值在区间-128到127之间,则比较结果为true,如果两个变量的值不在此区间,则比较结果为false
1 2 3 4 5 6 7 |
|
当值在 -128 ~ 127之间时,java会进行自动装箱,然后会对值进行缓存,如果下次再有相同的值,会直接在缓存中取出使用。缓存是通过Integer的内部类IntegerCache来完成的。当值超出此范围,会在堆中new出一个对象来存储。
给一个Integer对象赋一个int值的时候,会调用Integer类的静态方法valueOf,源码如下:
1 2 3 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
IntegerCache是Integer的内部类,源码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
|
Integer x = new Integer(123);
Integer y = new Integer(123);
System.out.println(x == y); // false
Integer z = Integer.valueOf(123);
Integer k = Integer.valueOf(123);
System.out.println(z == k); // true
在使用这些基本类型对应的包装类型时,如果该数值范围在缓冲池范围内,就可以直接使用缓冲池中的对象。
在 jdk 1.8 所有的数值类缓冲池中,Integer 的缓冲池 IntegerCache 很特殊,这个缓冲池的下界是 - 128,上界默认是 127,但是这个上界是可调的,在启动 jvm 的时候,通过 -XX:AutoBoxCacheMax=<size> 来指定这个缓冲池的大小,该选项在 JVM 初始化的时候会设定一个名为 java.lang.IntegerCache.high 系统属性,然后 IntegerCache 初始化的时候就会读取该系统属性来决定上界
-
装箱:将基本类型用包装器类型包装起来
-
拆箱:将包装器类型转换为基本类型
这个地方有很多易混淆的地方,但在面试中问到的频率一般,笔试的选择题中经常出现,还有一个
String
创建对象和这个比较像,很容易混淆,在下文可以看到 -
编译器会在自动装箱过程调用 valueOf() 方法,因此多个值相同且值在缓存池范围内的 Integer 实例使用自动装箱来创建,那么就会引用相同的对象。
Integer m = 123; Integer n = 123; System.out.println(m == n); // true
new Integer(123) 与 Integer.valueOf(123) 的区别在于:
- new Integer(123) 每次都会新建一个对象;
- Integer.valueOf(123) 会使用缓存池中的对象,多次调用会取得同一个对象的引用。
-
基本类型对应的缓冲池如下:
- boolean values true and false
- all byte values
- short values between -128 and 127
- int values between -128 and 127
- char in the range \u0000 to \u007F
-
下面这段代码的输出结果是什么?public class Main { public static void main(String[] args) { Integer a = 100; Integer b = 100; Integer c = 128; Integer d = 128; System.out.println(a==b); System.out.println(c==d); } }
true false
很多人看到这个结果会很疑惑,为什么会是一个
true
一个flase
.其实从源码中可以很容易找到原因.首先找到Integer
方法中的valueOf
方法public static Integer valueOf(int i) { if (i >= IntegerCache.low && i <= IntegerCache.high) return IntegerCache.cache[i + (-IntegerCache.low)]; return new Integer(i); }
可以看到当不满足
if
语句中的条件,就会重新创建一个对象返回,那结果必然不相等。继续打开IntegerCache
可以看到private static class IntegerCache { static final int low = -128; static final int high; static final Integer cache[]; static { // high value may be configured by property int h = 127; String integerCacheHighPropValue = sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high"); if (integerCacheHighPropValue != null) { try { int i = parseInt(integerCacheHighPropValue); i = Math.max(i, 127); // Maximum array size is Integer.MAX_VALUE h = Math.min(i, Integer.MAX_VALUE - (-low) -1); } catch( NumberFormatException nfe) { // If the property cannot be parsed into an int, ignore it. } } high = h; cache = new Integer[(high - low) + 1]; int j = low; for(int k = 0; k < cache.length; k++) cache[k] = new Integer(j++); // range [-128, 127] must be interned (JLS7 5.1.7) assert IntegerCache.high >= 127; } private IntegerCache() {} }
代码挺长,大概说的就是在通过
valueOf
方法创建Integer
对象的时候,如果数值在[-128,127]之间,便返回指向IntegerCache.cache
中已经存在的对象的引用;否则创建一个新的Integer
对象。所以上面代码中a
与b
相等,c
与d
不相等。 -
再看下面的代码会输出什么
public class Main { public static void main(String[] args) { Double a = 1.0; Double b = 1.0; Double c = 2.0; Double d = 2.0; System.out.println(a==b); System.out.println(c==d); } }
flase flase
采用同样的方法,可以看到
Double
的valueOf
方法,每次返回都是重新new
一个新的对象,所以上面代码中的结果都不想等。public static Double valueOf(double d) { return new Double(d); }
-
最后再看这段代码的输出结果
public class Main { public static void main(String[] args) { Boolean a = false; Boolean b = false; Boolean c = true; Boolean d = true; System.out.println(a==b); System.out.println(c==d); } }
true true
老方法继续看
valueOf
方法public static Boolean valueOf(boolean b) { return (b ? TRUE : FALSE); }
再看看
TRUE
和FALSE
是个什么东西,是两个静态成员属性。public static final Boolean TRUE = new Boolean(true); public static final Boolean FALSE = new Boolean(false);
**说下结论 **:Integer
、Short
、Byte
、Character
、Long
这几个类的valueOf
方法的实现是类似的。Double
、Float
的valueOf
方法的实现是类似的。然后是Boolean
的valueOf
方法是单独一组的。
-
Integer i = new Integer(xxx)
和Integer i =xxx
的区别这两者的区别主要是第一种会触发自动装箱,第二者不会
最后看看下面这段程序的输出结果
public class Main { public static void main(String[] args) { Integer a = 1; Integer b = 2; Integer c = 3; Long g = 3L; int int1 = 12; int int2 = 12; Integer integer1 = new Integer(12); Integer integer2 = new Integer(12); Integer integer3 = new Integer(1); System.out.println("c==(a+b) ->"+ (c==(a+b))); System.out.println("g==(a+b) ->" + (g==(a+b))); System.out.println( "c.equals(a+b) ->" + (c.equals(a+b))); System.out.println( "g.equals(a+b) ->" + (g.equals(a+b))); System.out.println("int1 == int2 -> " + (int1 == int2)); System.out.println("int1 == integer1 -> " + (int1 == integer1)); System.out.println("integer1 == integer2 -> " + (integer1 == integer2)); System.out.println("integer3 == a1 -> " + (integer3 == a)); } }
c==(a+b) ->true g==(a+b) ->true c.equals(a+b) ->true g.equals(a+b) ->false int1 == int2 -> true int1 == integer1 -> true integer1 == integer2 -> false integer3 == a1 -> false
下面简单解释这些结果。
1.当 "=="运算符的两个操作数都是包装器类型的引用,则是比较指向的是否是同一个对象,而如果其中有一个操作数是表达式(即包含算术运算)则比较的是数值(即会触发自动拆箱的过程)。所以
c==a+b
,g==a+b
为true
。2.而对于
equals
方法会先触发自动拆箱过程,再触发自动装箱过程。也就是说a+b,会先各自调用intValue
方法,得到了加法运算后的数值之后,便调用Integer.valueOf
方法,再进行equals
比较。所以c.equals(a+b)
为true
。而对于g.equals(a+b)
,a+b
会先拆箱进行相加运算,在装箱进行equals
比较,但是装箱后为Integer
,g
为Long
,所以g.equals(a+b)
为false
。3.
int1 == int2
为true
无需解释,int1 == integer1
,在进行比较时,integer1
会先进行一个拆箱操作变成int
型在进行比较,所以int1 == integer1
为true
。4.
integer1 == integer2
->false
。integer1
和integer2
都是通过new
关键字创建的,可以看成两个对象,所以integer1 == integer2
为false
。integer3 == a1
->false
,integer3
是一个对象类型,而a1
是一个常量它们存放内存的位置不一样,所以integer3 == a1
为false
,具体原因可学习下java的内存模型。