[转]JAVA自动装箱和拆箱
http://www.cnblogs.com/dolphin0520/p/3780005.html
1.Java数据类型
装箱和拆箱之前,我们先来了解一下Java的基本数据类型。
在Java中,数据类型可以分为两大种,Primitive Type(基本类型)和Reference Type(引用类型)。基本类型的数值不是对象,不能调用对象的toString()、hashCode()、getClass()、equals()等方法。所以Java提供了针对每种基本类型的包装类型。如下:
| INDEX | 基本类型 | 大小 | 数值范围 | 默认值 | 包装类型 |
| 1 | boolean | --- | true,false | false | Boolean |
| 2 | byte | 8bit | -2^7 -- 2^7-1 | 0 | Byte |
| 3 | char | 16bit |
\uffff |
\u0000 | Character |
| 4 | short | 16bit | -2^15 -- 2^15-1 | 0 | Short |
| 5 | int | 32bit | -2^31 -- 2^31-1 | 0 | Integer |
| 6 | long | 64bit | -2^63 -- 2^63-1 | 0 | Long |
| 7 | float | 32bit | IEEE 754 | 0.0f | Float |
| 8 | double | 64bit | IEEE 754 | 0.0d | Double |
| 9 | void | --- | --- | --- | Void |
Java 1.5中引入了自动装箱和拆箱机制:
(1)把基本类型用它们对应的引用类型包装起来,使它们具有对象的特质,可以调用toString()、hashCode()、getClass()、equals()等方法。 如下:
Integer a=3;//这是自动装箱
其实编译器调用的是static Integer valueOf(int i)这个方法,valueOf(int i)返回一个表示指定int值的Integer对象,那么就变成这样:
Integer a=3; => Integer a=Integer.valueOf(3);
(2)跟的方向相反,将Integer及Double这样的引用类型的对象重新简化为基本类型的数据。
如下://这是拆箱
编译器内部会调用int intValue()返回该Integer对象的int值
代码如下:
- Integer integer1 = 100;
- Integer integer2 = 100;
- System.out.println("integer1==integer2: " + (integer1 == integer2));// true 自动装箱的两个缓存中的 Integer对象的引用比较
- System.out.println("integer1.equals(integer2): " + (integer1.equals(integer2)));// true
- System.out.println("integer1.compare(integer2): " + integer1.compareTo(integer2));// 0
- Integer integer3 = 200;
- Integer integer4 = 200;
- System.out.println("integer3==integer4: " + (integer3 == integer4));// false 自动装箱的两个new Integer的引用比较
- System.out.println("integer3>integer4: " + (integer3 > integer4)); // false 将两个对象拆箱,再比较大小
- System.out.println("integer3.equals(integer4): " + (integer3.equals(integer4)));// true
- System.out.println("integer3.compare(integer4): " + integer3.compareTo(integer4));// 0
- Integer integer5 = new Integer(100);
- Integer integer6 = new Integer(100);
- System.out.println("integer5==integer6: " + (integer5 == integer6)); // false 两个不同的Integer对象引用的比较
- System.out.println("integer5.equals(integer6): " + (integer5.equals(integer6)));// true
- System.out.println("integer5.compare(integer6): " + integer5.compareTo(integer6));// 0
- int int1 = 100;
- System.out.println("integer1==int1: " + (integer1 == int1));// true Integer缓存对象拆箱后与int比较
- System.out.println("integer1.equals(int1): " + (integer1.equals(int1)));// true
- System.out.println("integer1.compare(int1): " + integer1.compareTo(int1));// 0
- int int2 = 200;
- System.out.println("integer3==int2: " + (integer3 == int2));// true Integer对象拆箱后与int比较
- System.out.println("integer3.equals(int2): " + (integer3.equals(int2)));// true
- System.out.println("integer3.compare(int2): " + integer3.compareTo(int2));// 0
反编译一下看得更清楚:
- Integer localInteger1 = Integer.valueOf(100);
- Integer localInteger2 = Integer.valueOf(100);
- System.out.println(new StringBuilder().append("integer1==integer2: ").append(localInteger1 == localInteger2).toString());
- System.out.println(new StringBuilder().append("integer1.equals(integer2): ").append(localInteger1.equals(localInteger2)).toString());
- System.out.println(new StringBuilder().append("integer1.compare(integer2): ").append(localInteger1.compareTo(localInteger2)).toString());
- Integer localInteger3 = Integer.valueOf(200);
- Integer localInteger4 = Integer.valueOf(200);
- System.out.println(new StringBuilder().append("integer3==integer4: ").append(localInteger3 == localInteger4).toString());
- System.out.println(new StringBuilder().append("integer3>integer4: ").append(localInteger3.intValue() > localInteger4.intValue()).toString());
- System.out.println(new StringBuilder().append("integer3.equals(integer4): ").append(localInteger3.equals(localInteger4)).toString());
- System.out.println(new StringBuilder().append("integer3.compare(integer4): ").append(localInteger3.compareTo(localInteger4)).toString());
- Integer localInteger5 = new Integer(100);
- Integer localInteger6 = new Integer(100);
- System.out.println(new StringBuilder().append("integer5==integer6: ").append(localInteger5 == localInteger6).toString());
- System.out.println(new StringBuilder().append("integer5.equals(integer6): ").append(localInteger5.equals(localInteger6)).toString());
- System.out.println(new StringBuilder().append("integer5.compare(integer6): ").append(localInteger5.compareTo(localInteger6)).toString());
- int i = 100;
- System.out.println(new StringBuilder().append("integer1==int1: ").append(localInteger1.intValue() == i).toString());
- System.out.println(new StringBuilder().append("integer1.equals(int1): ").append(localInteger1.equals(Integer.valueOf(i))).toString());
- System.out.println(new StringBuilder().append("integer1.compare(int1): ").append(localInteger1.compareTo(Integer.valueOf(i))).toString());
- int j = 200;
- System.out.println(new StringBuilder().append("integer3==int2: ").append(localInteger3.intValue() == j).toString());
- System.out.println(new StringBuilder().append("integer3.equals(int2): ").append(localInteger3.equals(Integer.valueOf(j))).toString());
- System.out.println(new StringBuilder().append("integer3.compare(int2): ").append(localInteger3.compareTo(Integer.valueOf(j))).toString());
4.源码分析
4.1 valueOf工厂方法
- public static Integer valueOf(inti) {
- if(i >= -128 &&i <=IntegerCache.high)
- //如果i在-128~high之间,就直接在缓存中取出i的Integer类型对象
- return IntegerCache.cache[i + 128];
- else
- return new Integer(i); //否则就在堆内存中创建
- }
4.2 IntegerCache内部类
下面我们看看Integer源码中是怎么在内部缓存数值的。
- private static class IntegerCache {//内部类,注意它的属性都是定义为static final
- static final inthigh; //缓存上界
- static final Integer cache[];//cache缓存是一个存放Integer类型的数组
- static {//静态语句块
- final int low = -128;//缓存下界,值不可变
- // high value may beconfigured by property
- int h = 127;// h值,可以通过设置jdk的AutoBoxCacheMax参数调整(参见(3))
- if (integerCacheHighPropValue !=null) {
- // Use Long.decode here to avoid invoking methods that
- // require Integer's autoboxing cache to be initialized
- // 通过解码integerCacheHighPropValue,而得到一个候选的上界值
- int i = Long.decode(integerCacheHighPropValue).intValue();
- // 取较大的作为上界,但又不能大于Integer的边界MAX_VALUE
- i = Math.max(i, 127);//上界最小为127
- // Maximum array size is Integer.MAX_VALUE
- h = Math.min(i, Integer.MAX_VALUE - -low);
- }
- high = h; //上界确定,此时high默认一般是127
- // 创建缓存块,注意缓存数组大小
- cache =new Integer[(high - low) + 1];
- int j = low;
- for(int k = 0; k <cache.length; k++)
- cache[k] =new Integer(j++);// -128到high值逐一分配到缓存数组
- }
- private IntegerCache() {}//构造方法,不需要构造什么
- }
4.3 用integerCacheHighPropValue变量设置自动装箱池大小
/**
* Cache to support theobject identity semantics of autoboxing for values between
* -128 and 127(inclusive) as required by JLS.
*
* The cache isinitialized on first usage. During VM initialization the
* getAndRemoveCachePropertiesmethod may be used to get and remove any system
* properites thatconfigure the cache size. At this time, the size of the
* cache may be controlledby the vm option -XX:AutoBoxCacheMax=<size>.
*/
- // value of java.lang.Integer.IntegerCache.high property(obtained during VMinit)
- private static String integerCacheHighPropValue;
- static void getAndRemoveCacheProperties() {
- if (!sun.misc.VM.isBooted()) {
- Properties props= System.getProperties();
- integerCacheHighPropValue=
- (String)props.remove("java.lang.Integer.IntegerCache.high");
- if (integerCacheHighPropValue!=null)
- System.setProperties(props); // remove from system props
- }
- }
4.4 设置-XX:AutoBoxCacheMax=<size>选项再次测试<3>中代码
在eclipse中,选中源文件,右键Run as—>RunConfiguratio--->Arguments,在VM arguments中做以下设置:

运行,控制台报错:Unrecognized VM option 'AutoBoxCacheMax=256'
网上找到http://rednaxelafx.iteye.com/blog/680746这篇文章,里面解释的很清楚,使用Oracle/SunJDK
6,在server模式下,使用-XX:AutoBoxCacheMax=NNN参数即可将Integer的自动缓存区间设置为[-128,NNN]。这个参数是server模式专有的。而我使用的是HotSpot
Client VM,那怎么切换成server模式呢?网上找到http://www.iteye.com/topic/857587这篇文章也很好的解决了我的问题,网上牛人好多啊,学习了。
虚拟机版本与模式查看:
java -version //查看JVM默认的环境
java -client -version //查看JVM的客户端环境,针对GUI优化,启动速度快,运行速度不如server
java -server -version //查看JVM的服务器端环境,针对生产环境优化,运行速度快,启动速度慢
我的虚拟机默认版本如下:

需要切换到

切换方法如下:
找到JAVA_HOME/jre/lib/i386/jvm.cfg,这就是JVM默认的查找顺序,内容如下
-client KNOWN
-server KNOWN
-hotspot ALIASED_TO -client
-classic WARN
-native ERROR
-green ERROR
只需要把-server和-clent换个位置就行了.如下
-server KNOWN
-client KNOWN
-hotspot ALIASED_TO -client
-classic WARN
-native ERROR
-green ERROR
最后运行,得到如下结果:
Integer integer1 = 100;
Integer integer2 = 100;
System.out.println("integer1==integer2:
" + (integer1 == integer2));// true 自动装箱的两个缓存中的 Integer对象的引用比较
System.out.println("integer1.equals(integer2):
" + (integer1.equals(integer2)));// true
System.out.println("integer1.compare(integer2):
" + integer1.compareTo(integer2));// 0
Integer integer3 = 200;
Integer integer4 = 200;
System.out.println("integer3==integer4:
" + (integer3 ==integer4));
// true 自动装箱的两个new Integer的引用比较
System.out.println("integer3>integer4:
" + (integer3 > integer4)); // false将两个对象拆箱,再比较大小
System.out.println("integer3.equals(integer4):
" + (integer3.equals(integer4)));// true
System.out.println("integer3.compare(integer4):
" + integer3.compareTo(integer4));// 0
Integer integer5 =new Integer(100);
Integer integer6 =new Integer(100);
System.out.println("integer5==integer6:
" + (integer5 == integer6));// false两个不同的Integer对象引用的比较
System.out.println("integer5.equals(integer6):
" + (integer5.equals(integer6)));// true
System.out.println("integer5.compare(integer6):
" + integer5.compareTo(integer6));// 0
intint1 = 100;
System.out.println("integer1==int1: "
+ (integer1 == int1));// true Integer缓存对象拆箱后与int比较
System.out.println("integer1.equals(int1):
" + (integer1.equals(int1)));// true
System.out.println("integer1.compare(int1):
" + integer1.compareTo(int1));// 0
intint2 = 200;
System.out.println("integer3==int2: "
+ (integer3 == int2));// true Integer对象拆箱后与int比较
System.out.println("integer3.equals(int2):
" + (integer3.equals(int2)));// true
System.out.println("integer3.compare(int2): "
+ integer3.compareTo(int2));// 0
可以看到黄色那行和3的测试结果相比,结果变成了true,说明-XX:AutoBoxCacheMax=<size>这个选项真的可以改变Integer的缓存大小。
在很多很是时候会遇到“==”问题,这时候也是与自动拆装箱是联系的,具体原则是:当 “==”运算符的两个操作数都是 包装器类型的引用时,则是比较指向的是否是同一个对象,而如果其中有一个操作数是表达式(即包含算术运算)则比较的是数值(即会触发自动拆箱的过程)。用 下面代码验证一下:
Integer a = 1;
Integer b = 2;
Integer c = 3;
Integer d = 3;
Integer e = 364;
Integer f = 364;
Long g = 3L;
Long h = 2L;
System.out.println(c==d);//true
System.out.println(e==f);//false
System.out.println(c==(a+b));//true
System.out.println(c.equals(a+b));//true
System.out.println(g==(a+b));//true
System.out.println(g.equals(a+b));//false
System.out.println(g.equals(a+h));//true
c.equals(a+b) 会先触发自动拆箱过程,再触发自动装箱过程,也就是说a+b会先各自调用intValue方法,得到了加法运算后的数值之后,便调用 Integer.valueOf方法,再进行equals比较。注意一下:g.equals(a+h),先拆箱是a与h相加后装箱后的调用的是 Long.valueof方法。这个过程是先拆箱后装箱的操作顺序。
5.疑问解答
5.1 看IntegerCache的源码可以知道Integer的缓存至少要覆盖[-128, 127]的范围,为什么?
参见《The Java™Language Specification Java SE 7Edition 5.1.7》Boxing Conversion的描述
If the valuepbeing boxed is true, false, a byte, or a char inthe range \u0000 to \u007f,or an int or short numberbetween -128 and 127 (inclusive),then letr1andr2be the results of any two boxing conversions ofp. It is always the case thatr1==r2.
5.2 其它基本数据类型对应的包装类型的自动装箱池大小
Byte,Short,Long对应的是-128~127
Character对应的是0~127
Float和Double没有自动装箱池
6.总结
Java使用自动装箱和拆箱机制,节省了常用数值的内存开销和创建对象的开销,提高了效率。通过上面的研究和测试,结论如下:
(1)Integer和 int之间可以进行各种比较;Integer对象将自动拆箱后与int值比较
(2)两个Integer对象之间也可以用>、<等符号比较大小;两个Integer对象都拆箱后,再比较大小
(3) 两个Integer对象最好不要用==比较。因为:-128~127范围(一般是这个范围)内是取缓存内对象用,所以相等,该范围外是两个不同对象引用比较,所以不等。
以上是我个人学习和研究结论,如有不正确之处,欢迎大家指正。
[转]JAVA自动装箱和拆箱的更多相关文章
- Java 自动装箱与拆箱
Java 自动装箱与拆箱(Autoboxing and unboxing) 什么是自动装箱拆箱 基本数据类型的自动装箱(autoboxing).拆箱(unboxing)是自J2SE 5.0开始提供 ...
- 【转】java 自动装箱与拆箱
java 自动装箱与拆箱 这个是jdk1.5以后才引入的新的内容,作为秉承发表是最好的记忆,毅然决定还是用一篇博客来代替我的记忆: java语言规范中说道:在许多情况下包装与解包装是由编译器自行完成的 ...
- Java进阶(三十七)java 自动装箱与拆箱
Java进阶(三十七)java 自动装箱与拆箱 前言 这个是jdk1.5以后才引入的新的内容.java语言规范中说道:在许多情况下包装与解包装是由编译器自行完成的(在这种情况下包装称为装箱,解包装称为 ...
- JAVA基础之——三大特征、接口和抽象类区别、重载和重写区别、==和equals区别、JAVA自动装箱和拆箱
1 java三大特征 1)封装:即class,把一类实体定义成类,该类有变量和方法. 2)继承:从已有的父类中派生出子类,子类实现父类的抽象方法. 3)多态:通过父类对象可以引用不同的子类,从而实现不 ...
- 转载:详解Java 自动装箱与拆箱的实现原理
原文:http://www.jb51.net/article/111847.htm 什么是自动装箱和拆箱 自动装箱就是Java自动将原始类型值转换成对应的对象,比如将int的变量转换成Integer对 ...
- [转]java 自动装箱与拆箱
转自:http://www.cnblogs.com/shenliang123/archive/2012/04/16/2451996.html 这个是jdk1.5以后才引入的新的内容,作为秉承发表是最好 ...
- 全面理解java自动装箱和拆箱(转)
自动装箱和拆箱从Java 1.5开始引入,目的是将原始类型值转自动地转换成对应的对象.自动装箱与拆箱的机制可以让我们在Java的变量赋值或者是方法调用等情况下使用原始类型或者对象类型更加简单直接. 如 ...
- Java——Java自动装箱和拆箱
一.什么是自动装箱和拆箱: 我们知道java为8种基本类型分别提供了对应的包装类型,在Java SE5之前,如果要生成一个数值为10的Integer对象,必须这样进行: Integer i=new I ...
- Java自动装箱和拆箱
jdk5.0之后,在基本数据类型封装类之间增加了自动装箱和拆箱的功能,其实“自动”的实现很简单,只是将装箱和拆箱通过编译器,进行了“自动补全”,省去了开发者的手动操作. 而进行封装类与对应基本数据类型 ...
随机推荐
- HTTP协议(二)
一.请求的格式: (一).请求行 (1).请求方法 1.GET 2.POST 3.PUT 4.DELETE 5.TRACE 6.OPTIONS (2).请求路径 (3).所用的协议 (二).请求头信息 ...
- java中的基本jdbc中mvc基本示例
数据库: 包文件: Student.java 1 package com.model; 2 3 public class Student { 4 private int id; 5 private S ...
- 【C++】智能指针详解(一):智能指针的引入
智能指针是C++中一种利用RAII机制(后面解释),通过对象来管理指针的一种方式. 在C++中,动态开辟的内存需要我们自己去维护,在出函数作用域或程序异常退出之前,我们必须手动释放掉它,否则的话就会引 ...
- *** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[ViewController > setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key
*** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[ViewController > ...
- 2.Java集合总结系列:List接口及其实现
在介绍List接口之前,我们先来看看 Collection 接口,因为Collection接口是 List / Set / Queue 接口的父接口,List / Set / Queue 的实现类中很 ...
- AngularJS1.X学习笔记4-内置事件指令及其他
AngularJS为我们定义了一系列事件指令,方便我们对用户的操作作出响应.甚至他还有一个可选模块提供了触摸事件和手势事件的支持,为移动端开发提供了可能.现在开始学习一下AngularJS的事件指令. ...
- C++11 新特性总结
前言 转载请注明出处,感谢! C++11 的新特性 1 变量和基本类型 1.1 long long 类型 扩展精度浮点数,10位有效数字 1.2 列表初始化 初始化的几种不同形式,其中用花括号来初始化 ...
- TCP/IP笔记(五)IP协议相关技术
IP旨在让最终目标主机收到数据包,但是在这一过程中仅仅有IP时无法实现通信的.必须还要又能够解析主机名称和MACdivide功能,以技术包在发送过程中异常情况处理的功能. 这篇主要介绍下DNS.ARP ...
- Python学习_argsparse
# -*- coding: utf-8 -*- import argparse args = "-f hello.txt -n 1 2 3 -x 100 -y b -z a -q hello ...
- SQL入门之条件表达式
where子句和having子句主要是用来筛选符合条件的元组,其后紧跟的即为条件表达式. 0.and, or条件的连接 用法和一般编程语言一样,主要用于条件的拼接.and两边都为真,则结果为真.or两 ...