Java学习笔记之自动装箱与拆箱
来源:互联网 发布:淘宝页面布局怎么设置 编辑:程序博客网 时间:2024/05/15 09:18
更多博文可参考我的个人独立博客:贱贱的梦想
什么是自动装箱与拆箱
自动装箱就是Java自动将原始类型值转换成对应的对象,比如将int的变量转换成Integer对象,这个过程叫做装箱,反之将Integer对象转换成int类型值,这个过程叫做拆箱。
自动装箱时编译器调用valueOf()
将原始类型值转换成对象;自动拆箱时,编译器通过调用类似xxxValue()
这类方法(如intValue()
,doubleValue()
)将对象转换成原始类型值。
什么情况下会发生自动装箱和拆箱
赋值时
在Java1.5之前,需要手动地进行类型转换,而现在所有的转换都是有编译器来完成。
//before autoboxingInteger iObject = Integer.valueOf(3);int iPrimitive = iObject.intValue()//after java5Integer iObject = 3; //autobxing - primitive to wrapper conversionint iPrimitive = iObject; //unboxing - object to primitive conversion
方法调用时
当在进行方法调用时,可以传入原始数据值或对象,编译器同样会自动进行转换。
public static Integer show(Integer iParam){ System.out.println("autoboxing example - method invocation i: " + iParam); return iParam;}//autoboxing and unboxing in method invocationshow(3); //autoboxingint result = show(3); //unboxing because return type of method is Integer
自动装箱引起的性能问题
如果有人告诉你:“只要修改一个字符,下面这段代码的运行速度就能提高5倍。”,你觉得可能么?
long t = System.currentTimeMillis();Long sum = 0L;for (long i = 0; i < Integer.MAX_VALUE; i++) { sum += i;}System.out.println("total:" + sum);System.out.println("processing time: " + (System.currentTimeMillis() - t) + " ms");
输出结果:
total:2305843005992468481processing time: 63556 ms
将Long
修改为long
,再来看一下运行结果
long t = System.currentTimeMillis();long sum = 0L;for (long i = 0; i < Integer.MAX_VALUE; i++) { sum += i;}System.out.println("total:" + sum);System.out.println("processing time: " + (System.currentTimeMillis() - t) + " ms");
输出结果:
total:2305843005992468481processing time: 12229 ms
事实证明,仅仅修改了一个字符,性能提高了不止一倍两倍。那,就究竟是什么原因导致的呢?
因为,+
这个操作符不适用Integer
对象,在进行数值相加操作之前会发生自动拆箱操作,转换成int
,相加之后还会发生自动拆箱操作,装换成Integer
对象。其内部变化如下:
sum = sum.longValue() + i;Long sum = new Long(sum);
很明显,在上面的循环中会创建2147483647个”Long“类型实例,在这样庞大的循环中,会降低程序的性能并且加重了垃圾回收的工作量。
说明:包含在包装器中的内容不会改变。即
Long
对象是不可变的。
重载与自动装箱
在java 5之前,value(int)
和value(Integer)
是完全不相同的方法,开发者不会因为传入是int
还是Integer
调用哪个方法困惑,但是由于自动装箱和拆箱的引入,处理重载方法时会不会有什么变化呢?可通过下面一个例子进行探讨:
public void test(int num){ System.out.println("method with primitive argument");}public void test(Integer num){ System.out.println("method with wrapper argument");}//calling overloaded methodAutoboxingTest autoTest = new AutoboxingTest();int value = 3;autoTest.test(value); //no autoboxing Integer iValue = value;autoTest.test(iValue); //no autoboxing/** Output:method with primitive argumentmethod with wrapper argument*/
从输出结果可以看出,在重载的情况下,不会发生自动装箱操作。(有关重载和重写的基础知识可参考Java学习笔记之重写(Overriding)与重载(Overloading))
在Java中使用自动装箱与拆箱应当注意的问题
自动装箱与拆箱在编程过程中给我们带来了极大的方便,但也存在一些容易让人出错的问题。
对象相等比较
"=="
既可用于原始值的比较,也可用于对象间的比较。当进行对象间的比较时,实质上比较的是对象的引用是否相等,而不是比较对象代表的值。如果要比较对象的值,应当使用对象对应的equals
方法。可通过以下例子进行探讨:
public class AutoboxingTest { public static void main(String args[]) { // Example 1: == comparison pure primitive – no autoboxing int i1 = 1; int i2 = 1; System.out.println("i1==i2 : " + (i1 == i2)); // true // Example 2: equality operator mixing object and primitive Integer num1 = 1; // autoboxing int num2 = 1; System.out.println("num1 == num2 : " + (num1 == num2)); // true // Example 3: special case - arises due to autoboxing in Java Integer obj1 = 1; // autoboxing will call Integer.valueOf() Integer obj2 = 1; // same call to Integer.valueOf() will return same cached Object System.out.println("obj1 == obj2 : " + (obj1 == obj2)); // true // Example 4: equality operator - pure object comparison Integer one = new Integer(1); // no autoboxing Integer anotherOne = new Integer(1); System.out.println("one == anotherOne : " + (one == anotherOne)); // false }}/** Output:i1==i2 : truenum1 == num2 : trueobj1 == obj2 : trueone == anotherOne : false*/
值得注意的是,在Example 2
中,比较是一个对象和一个原始值,出现这种情况比较的应该是对象的值。
让人感到困惑的Example 3
,在一开始我们说过,"=="
用于对象间的比较时,比较的是它们的引用,那么为什么obj1 == obj2
返回的结果却是true
?这是一种极端情况,处于节省内存的考虑,JVM
会缓存-128
到127
的Integer
对象。也就是说,在创建obj1
对象时,会进行自动装箱操作,并且将其对象保存至缓存中,在创建obj2
对象时,同样会进行自动装箱操作,然后在缓存中查找是否有相同值的对象,如果有,那么obj2
对象就会指向obj1
对象。obj1
和obj2
实际上是同一个对象。所以使用"=="
比较返回true
。
而Example 4
,是通过使用构造器来创建对象的,而没有发生自动装箱操作,不会执行缓存策略,故one
和anotherOne
是指向不同的引用的。
说明:这种 Integer 缓存策略仅在自动装箱(autoboxing)的时候有用,使用构造器创建的 Integer 对象不能被缓存。
容易混乱的对象和原始数据值
一个很容易犯错的问题,就是忽略对象与原始数据值之间的差异,在进行比较操作时,对象如果没有初始化或者为null
,在自动拆箱过程中obj.xxxValue
,则会抛出NullPointerException
,可通过以下例子进行探讨:
private static Integer count;//NullPointerException on unboxingif( count <= 0){ System.out.println("Count is not started yet");}
生成无用对象增加GC压力
因为自动装箱会隐式地创建对象,像前面提到的那样,如果在一个循环体中,会创建无用的中间对象,这样会增加GC压力,拉低程序的性能。所以在写循环时一定要注意代码,避免引入不必要的自动装箱操作。
参考资料:
1.What is Autoboxing and Unboxing in Java
2.Java Integer Cache
- Java学习笔记之自动装箱与拆箱
- Java学习笔记 --- 自动装箱与自动拆箱
- java学习笔记-自动装箱,自动拆箱
- Java学习笔记之JDK1.5新特性(四):自动装箱和自动拆箱
- java中自动拆箱与自动装箱学习总结
- Java进阶之自动拆箱与自动装箱
- java学习之拆箱与装箱
- 黑马训练营java学习笔记:自动装箱与拆箱(AutoBoxing/unboxing)
- JavaWeb学习笔记-java基础-3-自动装箱拆箱
- Java自动装箱与自动拆箱
- java-自动装箱与自动拆箱
- java 之自动装箱自动拆箱
- Java编程之自动装箱与拆箱
- JAVA自动装箱与拆箱
- java的自动装箱与拆箱
- java自动装箱与拆箱
- Java 自动装箱与拆箱
- java 自动拆箱与装箱
- Hyper-V网络配置
- SSM之创建一个Maven工程
- 决策树
- jdk annotation发布webservice服务 及生成客户端调用代码
- 设计模式之禅-里氏替换原则
- Java学习笔记之自动装箱与拆箱
- hdu1710 Binary Tree Traversals ----- 二叉树前序中序推后序
- linux初学笔记
- C基础规则
- 图像的中值滤波
- Android禁止旋转屏幕
- lucene学习
- Android通用流行框架大全
- java基础知识总结(面试篇)