1.Java数据类型

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

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

Java基本数据类型
INDEX 基本类型  大小 数值范围 默认值 包装类型
1 boolean    --- true,false false Boolean
2 byte 8bit -2^7 -- 2^7-1 0 Byte
3 char 16bit
\u0000 - \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

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.一个简单的例子

         代码如下:  
  1. Integer integer1 = 100;
  2. Integer integer2 = 100;
  3. System.out.println("integer1==integer2: " + (integer1 == integer2));// true  自动装箱的两个缓存中的 Integer对象的引用比较
  4. System.out.println("integer1.equals(integer2): " + (integer1.equals(integer2)));// true
  5. System.out.println("integer1.compare(integer2): " + integer1.compareTo(integer2));// 0
  6. Integer integer3 = 200;
  7. Integer integer4 = 200;
  8. System.out.println("integer3==integer4: " + (integer3 == integer4));// false 自动装箱的两个new Integer的引用比较
  9. System.out.println("integer3>integer4: " + (integer3 > integer4)); // false 将两个对象拆箱,再比较大小
  10. System.out.println("integer3.equals(integer4): " + (integer3.equals(integer4)));// true
  11. System.out.println("integer3.compare(integer4): " + integer3.compareTo(integer4));// 0
  12. Integer integer5 = new Integer(100);
  13. Integer integer6 = new Integer(100);
  14. System.out.println("integer5==integer6: " + (integer5 == integer6)); // false 两个不同的Integer对象引用的比较
  15. System.out.println("integer5.equals(integer6): " + (integer5.equals(integer6)));// true
  16. System.out.println("integer5.compare(integer6): " + integer5.compareTo(integer6));// 0
  17. int int1 = 100;
  18. System.out.println("integer1==int1: " + (integer1 == int1));// true  Integer缓存对象拆箱后与int比较
  19. System.out.println("integer1.equals(int1): " + (integer1.equals(int1)));// true
  20. System.out.println("integer1.compare(int1): " + integer1.compareTo(int1));// 0
  21. int int2 = 200;
  22. System.out.println("integer3==int2: " + (integer3 == int2));// true  Integer对象拆箱后与int比较
  23. System.out.println("integer3.equals(int2): " + (integer3.equals(int2)));// true
  24. System.out.println("integer3.compare(int2): " + integer3.compareTo(int2));// 0

反编译一下看得更清楚:

  1. Integer localInteger1 = Integer.valueOf(100);
  2. Integer localInteger2 = Integer.valueOf(100);
  3. System.out.println(new StringBuilder().append("integer1==integer2: ").append(localInteger1 == localInteger2).toString());
  4. System.out.println(new StringBuilder().append("integer1.equals(integer2): ").append(localInteger1.equals(localInteger2)).toString());
  5. System.out.println(new StringBuilder().append("integer1.compare(integer2): ").append(localInteger1.compareTo(localInteger2)).toString());
  6. Integer localInteger3 = Integer.valueOf(200);
  7. Integer localInteger4 = Integer.valueOf(200);
  8. System.out.println(new StringBuilder().append("integer3==integer4: ").append(localInteger3 == localInteger4).toString());
  9. System.out.println(new StringBuilder().append("integer3>integer4: ").append(localInteger3.intValue() > localInteger4.intValue()).toString());
  10. System.out.println(new StringBuilder().append("integer3.equals(integer4): ").append(localInteger3.equals(localInteger4)).toString());
  11. System.out.println(new StringBuilder().append("integer3.compare(integer4): ").append(localInteger3.compareTo(localInteger4)).toString());
  12. Integer localInteger5 = new Integer(100);
  13. Integer localInteger6 = new Integer(100);
  14. System.out.println(new StringBuilder().append("integer5==integer6: ").append(localInteger5 == localInteger6).toString());
  15. System.out.println(new StringBuilder().append("integer5.equals(integer6): ").append(localInteger5.equals(localInteger6)).toString());
  16. System.out.println(new StringBuilder().append("integer5.compare(integer6): ").append(localInteger5.compareTo(localInteger6)).toString());
  17. int i = 100;
  18. System.out.println(new StringBuilder().append("integer1==int1: ").append(localInteger1.intValue() == i).toString());
  19. System.out.println(new StringBuilder().append("integer1.equals(int1): ").append(localInteger1.equals(Integer.valueOf(i))).toString());
  20. System.out.println(new StringBuilder().append("integer1.compare(int1): ").append(localInteger1.compareTo(Integer.valueOf(i))).toString());
  21. int j = 200;
  22. System.out.println(new StringBuilder().append("integer3==int2: ").append(localInteger3.intValue() == j).toString());
  23. System.out.println(new StringBuilder().append("integer3.equals(int2): ").append(localInteger3.equals(Integer.valueOf(j))).toString());
  24. System.out.println(new StringBuilder().append("integer3.compare(int2): ").append(localInteger3.compareTo(Integer.valueOf(j))).toString());

4.源码分析

4.1 valueOf工厂方法

  1. public static Integer valueOf(inti) {
  2. if(i >= -128 &&i <=IntegerCache.high)
  3. //如果i在-128~high之间,就直接在缓存中取出i的Integer类型对象
  4. return IntegerCache.cache[i + 128];
  5. else
  6. return new Integer(i); //否则就在堆内存中创建
  7. }

4.2 IntegerCache内部类

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

  1. private static class IntegerCache {//内部类,注意它的属性都是定义为static final
  2. static final inthigh; //缓存上界
  3. static final Integer cache[];//cache缓存是一个存放Integer类型的数组
  4. static {//静态语句块
  5. final int low = -128;//缓存下界,值不可变
  6. // high value may beconfigured by property
  7. int h = 127;// h值,可以通过设置jdk的AutoBoxCacheMax参数调整(参见(3))
  8. if (integerCacheHighPropValue !=null) {
  9. // Use Long.decode here to avoid invoking methods that
  10. // require Integer's autoboxing cache to be initialized
  11. // 通过解码integerCacheHighPropValue,而得到一个候选的上界值
  12. int i = Long.decode(integerCacheHighPropValue).intValue();
  13. // 取较大的作为上界,但又不能大于Integer的边界MAX_VALUE
  14. i = Math.max(i, 127);//上界最小为127
  15. // Maximum array size is Integer.MAX_VALUE
  16. h = Math.min(i, Integer.MAX_VALUE - -low);
  17. }
  18. high = h; //上界确定,此时high默认一般是127
  19. // 创建缓存块,注意缓存数组大小
  20. cache =new Integer[(high - low) + 1];
  21. int j = low;
  22. for(int k = 0; k <cache.length; k++)
  23. cache[k] =new Integer(j++);// -128到high值逐一分配到缓存数组
  24. }
  25. private IntegerCache() {}//构造方法,不需要构造什么
  26. }

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

*/

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

  1. // value of java.lang.Integer.IntegerCache.high property(obtained during VMinit)
  2. private static String integerCacheHighPropValue;
  3. static void getAndRemoveCacheProperties() {
  4. if (!sun.misc.VM.isBooted()) {
  5. Properties props= System.getProperties();
  6. integerCacheHighPropValue=
  7. (String)props.remove("java.lang.Integer.IntegerCache.high");
  8. if (integerCacheHighPropValue!=null)
  9. System.setProperties(props); // remove from system props
  10. }
  11. }

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的缓存大小。   

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自动装箱和自动拆箱操作的更多相关文章

  1. java 中的自动装箱和拆箱操作

    在前面的文章中提到,Java为每种基本数据类型都提供了对应的包装器类型,至于为什么会为每种基本数据类型提供包装器类型在此不进行阐述,有兴趣的朋友可以查阅相关资料.在Java SE5之前,如果要生成一个 ...

  2. Java语法糖2:自动装箱和自动拆箱

    前言 一开始想学学自动拆箱和自动装箱是被这个名字吸引到,听上去好像很高端的样子,其实自动拆箱.自动装箱是很简单的内容. 自动拆箱和自动装箱 Java为每种基本数据类型都提供了对应的包装器类型.举个例子 ...

  3. java基础40 可变参数、自动装箱和自动拆箱

    一.可变参数 可变参数是jdk1.5新特性 1.1.可变参数的格式 数据类型...变量名 // 数据类型...变量名public static void sum(int...arr){ } 1.2.可 ...

  4. Java连载77-Integer常用方法、Integer、int、String三者相互转化、自动装箱、自动拆箱

    一.关于Integer中常用的方法 package com.bjpowernode.java_learning; ​ public class D77_1_ { public static void ...

  5. java 自动装箱、自动拆箱

    /** * @描述:自动装箱,自动拆箱 * @date 2017年1月10日下午1:30:01 * 结论:包装类的"=="运算在不遇到算术运算的情况下不会自动拆箱, * 以及他们的 ...

  6. JavaSE的包装类,自动装箱和自动拆箱 ,字符窜转换,toString(),equals(), hashCode()的区别

    一.基本数据类型和包装类 包装类均位于Java.lang包,包装类和基本数据类型的对应关系如下表所示: Primitive-Type   Wrapper-Class        byte       ...

  7. 24-从零玩转JavaWeb-包装类、自动装箱、自动拆箱

    一.什么是包装类 二.对基本数据类型包装的好处 三.装箱操作 四.拆箱操作 五.自动装箱 六.自动拆箱 七.字符串与基本数据类型和包装类的转换   八.包装类的缓存设计

  8. jdk1.5新特性之-----自动装箱与自动拆箱

    import java.util.ArrayList; /* jdk1.5新特性之-----自动装箱与自动拆箱. java是面向对象 的语言,任何事物都可以使用类进行描述,sun就使用了 一些类描述j ...

  9. 自动装箱与自动拆箱——JavaSE基础

    自动装箱与自动拆箱 自动装箱与拆箱就是编译器蜜糖(Compiler Sugar) Integer a = 234; // 自动装箱,实际上是Integer a = Integer.valueOF(23 ...

  10. 再谈C#装箱和拆箱操作

    1. 使用非泛型集合时引发的装箱和拆箱操作 看下面的一段代码: 1 2 3 4 5 6 7 8 var array = new ArrayList(); array.Add(1); array.Add ...

随机推荐

  1. 分享最近写的 两条sql语句

    1. 搭建基本环境 插入测试数据 insert into jgdm (jgdm,jgmc)  values('12300000000','河南省');insert into jgdm (jgdm,jg ...

  2. POJ 2186.Popular Cows (强连通)

    强连通缩点,统计入度为1的缩点后的点的个数 个数1的话输出这个强连通分量的点的数量 否则输出0: code /* Kosaraju算法,无向图的强连通分量,时间复杂度O(n+m) 思路: 按照图G的深 ...

  3. PHP中定义常量的几种方式与区别

    [问]在php中定义常量时,const与define的区别? [答]使用const使得代码简单易读,const本身就是一个语言结构,而define是一个函数.另外const在编译时要比define快很 ...

  4. pojo和JavaBean的区别

    javabean可以处理业务,pojo不可以. pojo就是get 和set 例如: Student{ id; name; get();... set();...} javabean可以实现业务逻辑 ...

  5. sphinx (coreseek)——3、区段查询 与 增量索引实例

    首先本文测试数据100多万的域名的wwwtitle 信息  检索数据: 首先建立临时表格: CREATE TABLE `sph_counter` ( `index_id` ) NOT NULL, `m ...

  6. Ubuntu14.04 安装QQ(国际版)

    1.在/etc/apt/source.list文件中添加: deb http://packages.linuxdeepin.com/deepin trusty main non-free univer ...

  7. 在Ubuntu12.0.4下搭建TFTP服务器

    一.安装相关安装包 tftpd(服务端),tftp(客户端) sudo apt-get install tftp-hpa tftpd-hpa 安装xinetd sudo apt-get install ...

  8. Python3 time()

    在<Python基础教程(第二版)>一书中, if time % 60 == 0 : print 'on the hour! '在3.3.2版本中显示错误.于是自己查了一下帮助文档,也在网 ...

  9. 转:FIFO和DMA

    FIFO SPI端口增加了FIFO,使得传输数据有了缓冲区间. FIFO存储器是一个先入先出的双口缓冲器,即第一个进入其内的数据第一个被移出,其中一个存储器的输入口,另一个口是存储器的输出口.主要有三 ...

  10. RMQ with Shifts

    uva12299:http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_prob ...