Java自动拆箱装箱

1.Java数据类型

在介绍Java自动装箱和拆箱之前,先来了解一下Java的数据类型。

Java中,数据类型可以分为两大种,Primitive Type(基本类型)和Reference Type(引用类型)。基本类型的数值不是对象,不能作为对象调用其toString()hashCode()getClass()equals()等等方法。所以针对8种基本数据类型,Java提供了针对每个基本数据类型的包装类。如下:

INDEX

基本数据类型

包装类

1

int

Integer

2

char

Character

3

float

Float

4

double

Double

5

byte

Byte

6

short

Short

7

long

Long

8

boolean

Boolean

2.Java自动装箱和自动拆箱定义

J2SE 5.0推出了自动装箱和拆箱的功能,以下是它们的定义:

自动装箱把基本类型用它们相对应的引用类型包装起来,使它们可以具有对象的特质,可以调用toString()hashCode()getClass()equals()等等方法。

自动拆箱跟装箱的方向相反,将IntegerDouble这样的引用类型的对象重新简化为基本类型的数据。

自动装箱和自动拆箱其实是由编译器来完成的,编译器会在编译时期根据你所写的语法决定是否进行装箱和拆箱动作。

2.1自动装箱

Integer a=3;//这是自动装箱

其实编译器调用的是static Integer valueOf(int i)这个方法,valueOf(inti)返回一个表示指定的int值的Integer对象,那么就变成这样:

Integer a=3; => Integer a=Integer.valueOf(3);

2.2自动拆箱

inti = new Integer(2);//这是自动拆箱

编译器内部自动调用 int intValue()返回该Integer对象的int

3.关于大小的比较(以Integer为例)

3.1测试代码

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 自动装箱的两个newInteger的引用比较

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));// trueInteger缓存对象自动拆箱后与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));// trueInteger对象自动拆箱后与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(newStringBuilder().append("integer1==integer2 : ").append(localInteger1== localInteger2).toString());

System.out.println(newStringBuilder().append("integer1.equals(integer2) :").append(localInteger1.equals(localInteger2)).toString());

System.out.println(newStringBuilder().append("integer1.compare(integer2) :").append(localInteger1.compareTo(localInteger2)).toString());

Integer localInteger3 = Integer.valueOf(200);

Integer localInteger4 = Integer.valueOf(200);

System.out.println(newStringBuilder().append("integer3==integer4 : ").append(localInteger3== localInteger4).toString());

System.out.println(newStringBuilder().append("integer3>integer4 :").append(localInteger3.intValue() >localInteger4.intValue()).toString());

System.out.println(newStringBuilder().append("integer3.equals(integer4) :").append(localInteger3.equals(localInteger4)).toString());

System.out.println(newStringBuilder().append("integer3.compare(integer4) :").append(localInteger3.compareTo(localInteger4)).toString());

Integer localInteger5 = new Integer(100);

Integer localInteger6 = new Integer(100);

System.out.println(newStringBuilder().append("integer5==integer6 : ").append(localInteger5== localInteger6).toString());

System.out.println(newStringBuilder().append("integer5.equals(integer6) :").append(localInteger5.equals(localInteger6)).toString());

System.out.println(newStringBuilder().append("integer5.compare(integer6) :").append(localInteger5.compareTo(localInteger6)).toString());

int i = 100;

System.out.println(newStringBuilder().append("integer1==int1 :").append(localInteger1.intValue() == i).toString());

System.out.println(newStringBuilder().append("integer1.equals(int1) :").append(localInteger1.equals(Integer.valueOf(i))).toString());

System.out.println(newStringBuilder().append("integer1.compare(int1) :").append(localInteger1.compareTo(Integer.valueOf(i))).toString());

int j = 200;

System.out.println(newStringBuilder().append("integer3==int2 :").append(localInteger3.intValue() == j).toString());

System.out.println(newStringBuilder().append("integer3.equals(int2) :").append(localInteger3.equals(Integer.valueOf(j))).toString());

System.out.println(newStringBuilder().append("integer3.compare(int2) :").append(localInteger3.compareTo(Integer.valueOf(j))).toString());

3.2源码分析

3.2.1 IntegerCache

下面我们看看Integer源码中是怎么在内部缓存数值的。

private staticclass IntegerCache {//内部类,注意它的属性都是定义为staticfinal

static finalinthigh; //缓存上界

static final Integer cache[];//cache缓存是一个存放Integer类型的数组

static {//静态语句块

final int low = -128;//缓存下界,值不可变

// high value may beconfigured by property

int h = 127;// h值,可以通过设置jdkAutoBoxCacheMax参数调整(参见3.2.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++);//-128high值逐一分配到缓存数组

}

private IntegerCache() {}//构造方法,不需要构造什么

}

3.2.2 valueOf工厂方法

publicstatic Integer valueOf(inti){

if(i >= -128 &&i <=IntegerCache.high)

//如果i-128~high之间,就直接在缓存中取出iInteger类型对象

returnIntegerCache.cache[i + 128];

else

returnnew Integer(i);//否则就在堆内存中创建

}

3.2.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>.

*/

getAndRemoveCacheProperties方法,用于获取或移除JDKInteger设置的缓存属性,同时可以通过调整虚拟机-XX:AutoBoxCacheMax=<size>选项,调整自动装箱池的大小

// value of java.lang.Integer.IntegerCache.high property(obtained during VMinit)

privatestatic String integerCacheHighPropValue;

staticvoid 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

}

}

3.3设置-XX:AutoBoxCacheMax=<size>选项再次测试3.1中的代码

在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

int int1 = 100;

System.out.println("integer1==int1: " + (integer1 == int1));// trueInteger缓存对象自动拆箱后与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));// trueInteger对象自动拆箱后与int比较

System.out.println("integer3.equals(int2): " + (integer3.equals(int2)));// true

System.out.println("integer3.compare(int2): " + integer3.compareTo(int2));// 0

可以看到黄色那行和3.1节测试结果相比,结果变成了true,说明-XX:AutoBoxCacheMax=<size>这个选项真的可以改变Integer的缓存大小。

4.疑问解答

4.1看IntegerCache的源码可以知道Integer的缓存至少要覆盖[-128, 127]的范围,为什么?

参见《The Java™Language Specification Java SE 7Edition 5.1.7》Boxing Conversion的描述

If the value p being 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 letr1and r2 be the results of any two boxing conversions of p. It is always the case thatr1==r2.

4.2其它基本数据类型对应的包装类型的自动装箱池大小

Byte,Short,Long对应的是-128~127

Character对应的是0~127

FloatDouble没有自动装箱池

5.总结

Java使用自动装箱和自动拆箱机制,节省了常用数值的内存开销和创建对象的开销,提高了效率。通过上面的研究和测试,结论如下:

(1) Integer int之间可以进行各种比较;Integer对象将自动拆箱后与int值比较

(2) 两个Integer对象之间也可以用><等符号比较大小;两个Integer对象都自动拆箱后,再比较大小

3) 两个Integer对象最好不要用==比较。因为:-128~127范围(一般是这个范围)内是取缓存内对象用,所以相等,该范围外是两个不同对象引用比较,所以不等。

以上是我个人学习和研究结论,如有不正确之处,欢迎大家指正。

你可能感兴趣的:(java)