文章标题

1.Java数据类型
在介绍Java的自动装箱和拆箱之前,我们先来了解一下Java的基本数据类型。
在Java中,数据类型可以分为两大种,Primitive Type(基本类型)和Reference Type(引用类型)。基本类型的数值不是对象,不能调用对象的toString()、hashCode()、getClass()、equals()等方法。所以Java提供了针对每种基本类型的包装类型。
2.Java自动装箱和拆箱定义
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 i = new Integer(2);//这是拆箱
编译器内部会调用int intValue()返回该Integer对象的int值
注意:自动装箱和拆箱是由编译器来完成的,编译器会在编译期根据语法决定是否进行装箱和拆箱动作。

3.一个简单的例子
代码如下:

[java] view plain copy 在CODE上查看代码片派生到我的代码片
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   
          反编译一下看得更清楚:   
[java] view plain copy 在CODE上查看代码片派生到我的代码片
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源码中是怎么在内部缓存数值的。   

[java] view plain copy 在CODE上查看代码片派生到我的代码片
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=.
*/
getAndRemoveCacheProperties方法,用于获取或移除JDK对Integer设置的缓存属性,同时可以通过调整虚拟机-XX:AutoBoxCacheMax=选项,调整“自动装箱池”的大小
[java] view plain copy 在CODE上查看代码片派生到我的代码片
// 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=选项再次测试<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的缓存大小。   

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范围(一般是这个范围)内是取缓存内对象用,所以相等,该范围外是两个不同对象引用比较,所以不等。
以上是我个人学习和研究结论,如有不正确之处,欢迎大家指正。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值