自动拆箱(unboxing)&自动装箱(boxing)

@author 李东秀|| qq:1028659927

本文主要为自己理解所做的学习笔记,如有不对的地方,

望各位看官不吝指出,代码运行环境:Ubuntu 14.04,jdk1.7版本

在jdk 1.5之前,如果你想要定义一个value为100的Integer对象,则需要如下定义:

          Integer i=new Integer (100);

但有了自动拆装箱之后就可以直接把基本类型赋值给Integer对象。

int intNum1=100;//普通常量
Integer intNum2=intNum1;//自动装箱
int intNum3=intNum2;//自动拆箱
Integer intNum4=100;//自动装箱

上面的代码中,intNum2为一个Integer类型的实例,intNum1为Java中的基础数据类型,

将intNum1赋值给intNum2便是自动装箱;而将intNum2赋值给intNum3则是自动拆箱。

Java为我们提供了八种基本数据类型: boolean  byte  char  shrot  int  long  float  double ,所生成的变量相当于常量;

对应基本类型包装类:Boolean Byte Character Short Integer Long Float Double。

自动拆箱和自动装箱定义:

自动装箱是将一个java定义的基本数据类型赋值给相应封装类的变量。

拆箱与装箱是相反的操作,自动拆箱则是将一个封装类的变量赋值给相应基本数据类型的变量。

自动封箱和自动装箱的原理

自动封箱和自动拆箱是java自动帮我们完成的,通过调试可以看到执行过程,如果调试过程中无法进入源码中的函数,可能因为设置的是JRE运行环境,替换成jdk就可以了,【Window】-【Preference】-【java】-【Installed JREs】

int intNum1=100;//普通常量
Integer intNum2=intNum1;//自动装箱
int intNum3=intNum2;//自动拆箱
Integer intNum4=100;//自动装箱

在每一行代码添加断点,进入调试模式,

第一行代码无法进入函数,说明中间没有调用其他的函数,

第二行代码和第四行代码进入了Integer.valueOf(int);

第三行代码进入了函数Integer.intValue();

第二行代码进入如下代码:

 public static Integer valueOf(int i) {//对于Integer类型变量,如果i在IntegerCache.low-IntegerCache.low之间,
//就直接在缓存中取出i的Integer类型对象  ,而不需要生成新的对象。if (i >= IntegerCache.low && i <= IntegerCache.lowreturn IntegerCache.cache[i + (-IntegerCache.low)];return new Integer(i);//否则在堆上创建新的对象}

此代码会去判断是否在可缓存的范围内(   if (i >= IntegerCache.low && i <= IntegerCache.high)),如果满足缓存条件进入  return IntegerCache.cache[i + (-IntegerCache.low)];否则new 一个新的对象。所以可以看到Integer intNum2=intNum1;并不是每次都会生成新对象。

第三行进入intValue函数直接返回基本类型。

 public int intValue() {return value;
}//其他类型则会调用xxValue()

下面是缓存数据的代码,说明了缓存的范围

 /*** Cache to support the object identity semantics of autoboxing for values between* -128 and 127 (inclusive) as required by JLS.* The cache is initialized on first usage.  The size of the cache* may be controlled by the {@code -XX:AutoBoxCacheMax=<size>} option.* During VM initialization, java.lang.Integer.IntegerCache.high property* may be set and saved in the private system properties in the* sun.misc.VM class.*/private static class IntegerCache {static final int low = -128;static final int high;static final Integer cache[];static {// high value may be configured by propertyint h = 127;String integerCacheHighPropValue =sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");if (integerCacheHighPropValue != null) {try {int i = parseInt(integerCacheHighPropValue);i = Math.max(i, 127);// Maximum array size is Integer.MAX_VALUEh = Math.min(i, Integer.MAX_VALUE - (-low) -1);} catch( NumberFormatException nfe) {// If the property cannot be parsed into an int, ignore it.}}high = h;cache = new Integer[(high - low) + 1];int j = low;for(int k = 0; k < cache.length; k++)cache[k] = new Integer(j++);// range [-128, 127] must be interned (JLS7 5.1.7)assert IntegerCache.high >= 127;}private IntegerCache() {}}

注意此处代码:

 String integerCacheHighPropValue = sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");

说明integerCacheHighPropValue的大小是从VM虚拟机中保存的属性取到的,

所以integerCacheHighPropValue 是可以通过vm命令进行设置的。

可以通过调整虚拟机-XX:AutoBoxCacheMax=<size>选项,调整“自动装箱池”的大小 ,

具体设置方法需要设置虚拟机模式,这里不做讲解。

自动封箱&自动拆箱的使用场景:

(1)赋值过程中:

此过程不在举例

(2)函数参数:

public static int subFun(Integer num){

return num;

}

调用此函数就会发生自动拆箱

public static Integer subFun(int num){

return num;

}

上述两种场景在工作过程中最常见,另外常见的自动拆箱和装箱的场景就是集合操作,集合只能接收对象,但我们传入普通类型也是可以的,它内部就完成了自动装箱。

特别需要注意地方:

1、 是否生成新的对象:

 /*** 基本类型的常量池说明(-127-128)* @author 李东秀(1028659927)*/
Integer num1 = 100;
Integer num2 = 100;
System.out.println("num1==num2: " + (num1 == num2));// true  两个自动装箱的对象,都是对常量池中对象的引用,所以相同。
Integer num3 = 200;
Integer num4 = 200;
System.out.println("num3>num4: " + (num3 > num4)); // false 将两个对象拆箱,再比较大小  ,类似的操作还有加减乘除取余等
Integer num5 = new Integer(100);
Integer num6 = new Integer(100);
System.out.println("num5==num6: " + (num5 == num6)); // false 都会生成新的对象,无论与谁比较都是false
int intNum1 = 100;
System.out.println("num1==int1: " + (num1 == intNum1));// true  Integer缓存对象拆箱后与int比较 ,已经不是对象地址的比较
int intNum2 = 200;
System.out.println("num3==intNum2: " + (num3 == intNum2));// true  Integer对象拆箱后与int比较  

所以new对象的方式一定会产生新对象,但自动装箱的方式则不一定产生新对象,对于Integer对象和基本对象进行运算操作或者逻辑操作,则会首先进行拆箱,之后进行值大小的比较。非new方式生成的Integer这里用缓存解释,深层的原因是jvm方法区的常量池的存在,缓存的对象大小-128-127,基本类型中Byte缓存大小为(-128-127),Long缓存大小为()等:

java中基本类型的包装类的大部分都实现了常量池技术,这些类是Byte,Short,Integer,Long,Character,Boolean,另外两种浮点数类型的包装类则没有实现。Byte,Short,Integer,Long,Character这5种整型的包装类也只是在对应值小于等于127时才可使用常量池,也即对象不负责创建和管理大于127的这些类的对象。  

其它基本数据类型对应的包装类型的自动装箱池大小(可以在各个类型的缓存类中找到范围):

  • Boolean :全部缓存
  • Byte :全部缓存
  • Character : <=127缓存
  • Short : (-128,127)缓存
  • Long : (-128,127)缓存
  • Float : (没有缓存)
  • Double : (没有缓存)
//5种整形的包装类Byte,Short,Integer,Long,Character的对象,//在值小于127时可以使用常量池Integer intdemo1=127;Integer intdemo2=127;System.out.println(intdemo1==intdemo2); //输出true//值大于127时,不会从常量池中取对象Integer intdemo3=128;Integer intdemo4=128;Byte bdemo1=12;Byte bdemo2=12;System.out.println(bdemo1==bdemo2); //输出trueCharacter chdemo1='a';//a=97Character chdemo2='a';System.out.println(chdemo1==chdemo2);//true//.......整形包装类后面就不在实验System.out.println(intdemo3==intdemo4); //输出false//Boolean类也实现了常量池技术Boolean booldemo1=true;Boolean booldemo2=true;System.out.println(booldemo1==booldemo2); //输出true//浮点类型的包装类没有实现常量池技术Double ddemo1=1.0;Double ddemo2=1.0;System.out.println(ddemo1==ddemo2); //输出false


Java利用内存中存储拘留字符串也为String变量提供了缓存特性。

 Integer I1 = 20;Integer I2 = 20;Integer I3 = 0;Integer I4 = 129;Integer I5 = 129;Integer I6 = new Integer("20");Integer I7 = new Integer("20");Integer I8 = new Integer("0");// ==System.out.println(I1 == I2);// 因为小于127所以都是引用的常量池汇总对象,相同System.out.println(I1 == I2 + I3);// 拆箱,不存在新对象的产生System.out.println(I4 == I5);// 无法存入常量池,会生成新的对象System.out.println(I4 == I5 + I3);// 拆箱比较普通值System.out.println(I1 == I6);// 生成新对象System.out.println(I6 == I7);// 生成新对象System.out.println(I6 == I7 + I8);//System.out.println("================");// equals比较值是否相同System.out.println(I1.equals(I2));System.out.println(I1.equals(I2 + I3));System.out.println(I4.equals(I5));System.out.println(I4.equals(I5 + I3));System.out.println(I6.equals(I7));System.out.println(I6.equals(I7 + I8));/*** String 类型常量池*/System.out.println("================");String str1 = "ab";String str2 = "ab";String str3 = "cd";String str6 = "abcd";String str4 = "a";String str5 = "bcd";System.out.println(str1 == str2);// 证明常量池存在没有生成新的对象System.out.println(str6 == str1 + str2);// 没有拆箱过程所以不存在常量池的常量直接比较System.out.println(str6 == "abcd");//// 涉及到变量(不全是常量)的相加,所以会生成新的对象,其内部实现是先new一个StringBuilderSystem.out.println(str2 + str3 == str4 + str5);// 变量的改变,涉及到新建变量System.out.println(str6.equals(str2 + str3));// 下面重新定义,因为常量池中已存在abcdString str10 = new String("AB");String str11 = new String("AB");System.out.println("================");System.out.println(str10 == str11);String str7 = "AB";System.out.println(str10 == str7);String str8 = "CD";String str9 = "ABCD";System.out.println(str9 == str7 + str8);System.out.println(str9 == str10 + str11);String str12 = new String("CD");// 生成新的对象,CD 放入常量池String str13 = new String("ABCD");System.out.println(str9 == str13);System.out.println(str13 == str11 + str12);System.out.println(str13 == "ABCD");String str14 = new String("ABC") + new String("DEF");// 形成的ABCDEF不会被放入常量池str14.intern();// 添加进常量池中(缓存)String str15 = "ABCDEF";System.out.println(str14 == str15);String str16 = new String("AAA");// AAA会被放入常量池中,由于是new产生的对象多以str16不和任何对象相同String str17 = "AAA";System.out.println(str16 == str17);

结果:true true false true false false true
================
true true true true true true
================
true false true false true
================
False false false false false false false true false

除了八种基本类型还有java提供的一种比较特殊的类型String(复合)。Java也为它提供了缓存机制,源代码中所有相同字面值的字符串常量只可能建立唯一 一个拘留字符串对象。 实际上JVM是通过一个记录了拘留字符串引用的内部数据结构来维持这一特性的。在Java程序中,可以调用String的intern()方法来使得一个常规字符串对象成为拘留字符串对象。

String主要使用方法有两种:如果是使用引号声明的字符串都是会直接在字符串常量池中生成,而 new 出来的 String 对象是放在 JAVA Heap 区域。

提到此处就必须得提String.intern方法,intern方法的作用如下:直接使用双引号声明出来的String对象会直接存储在常量池中,如果不是用双引号声明的String对象,可以使用String提供的intern方法。intern 方法会从字符串常量池中查询当前字符串是否存在,若不存在就会将当前字符串放入常量池中。

String s = new String("A");

会创建两个对象一个“A”存储在常量池中,另外一个创建在堆上,由于两个对象所在位置就不相同。所以一定是不相同的。

String s2 = new String("B") + new String("C");//此种方式创建的s2不会被放在常量池中,可以调用intern()方法把s2放入常量池中

Jdk1.7中:

String str14=new String("ABC")+new String("DEF");//此种方式生成的str14不会被放在常量池中String str15="ABCDEF";System.out.println(str14==str15);

结果:false

String str14=new String("ABC")+new String("DEF");str14.intern();String str15="ABCDEF";System.out.println(str14==str15);

结果:true

看过jvm运行时内存分配的都知道,Intern方法的使用在Jdk7中和jdk6中稍有不同:

将String常量池从Perm区移动到了Java Heap区,所以调用intern 方法时,如果存在堆中的对象,会直接保存对象的引用,而不会重新创建对象。(这部分内容还正在看,由于工作比较忙,最近刚开始写blog,会在后面逐渐写一系列文章)

2  Java函数重载

重载的定义为:函数参数类型不同或者参数个数不同则说明两函数重载。

但现在存在自动装箱,自动拆箱功能是否导致重载的函数为一个函数。

public static int add(Integer num){return num;}public static int add(int num){return num;}

这样定义是可以的,使用时相应参数传进函数也不存在混淆问题,所以重载不会受到影响。

3 定义的对象可以为空,但是当自动拆箱赋值给普通类型时或者和基本类型比较时,则会报错误。

Integer num1=null;int num2;num2=num1;System.out.println(num2);//或者比较也会发生此类错误

代码能过通过编译,但运行会报java.lang.NullPointerException错误。

4 循环过程的重复对象创建

Integer num4=0;

for(int i=0;i<10;i++){

num4=num4+i;

}

这么简单的一段程序,打断点查看循环过程,会发现每次循环都要先拆箱再装箱,装箱过程中会生成新的对象,所以会生成很多无用的中间对象,降低程序的性能并且加重了垃圾回收的工作量。因此在我们编程时,需要注意到这一点,正确地声明变量类型,避免因为自动装箱引起的性能问题。

总结:

自动拆箱和自动装箱给变成带来了很多便利,但同时也存在许多弊端,java致力于不让编程者担心内存的分配,由垃圾收集器自动完成内存的管理,这不意味着我们可以随意的使用内存而不加以限制。变成过程中尽量避免可能多次发生的拆箱装箱操作,避免不必要对象的创建。(QQ:1028659927,欢迎指导!)

自动拆箱自动装箱以及String 和基本数据类型封装类生成的对象是否相等相关推荐

  1. java中的自动拆箱和装箱(以及NEP问题)

    java中的自动拆箱和装箱 1.回顾知识点 java中的8种基本数据类型,可以分为三类 字符类型 char 布尔类型 boolean 整数类型 byte , short , int , long 浮点 ...

  2. java double 装箱_Java自动拆箱和装箱

    一.什么是装箱/拆箱 在讲之前,得先提一下为什么两个概念:基本数据类型及其包装类,我们都知道Java是一种面向对象的语言,但是Java中的基本数据类型是不面向对象的,这时在使用中便会存在诸多的不便,为 ...

  3. java 基本数据类型的自动拆箱与装箱

    -->  -128~127之间的特殊性.为什么要这样设计,好处? -->  享元模式(Flyweight Pattern):享元模式的特点是,复用我们内存中已存在的对象,降低系统创建对象实 ...

  4. java装箱和拆箱的意义_java的自动拆箱和装箱是每个程序员都要知道的

    自动装箱和拆箱问题是Java中一个老生常谈的问题了,今天我们就来一些看一下装箱和拆箱中的若干问题.本文先讲述装箱和拆箱最基本的东西. 自动装箱和拆箱从Java 1.5开始引入,目的是将原始类型值转自动 ...

  5. java char的包装对象,Java 从Character和char的区别来学习自动拆箱装箱

    本文结构 1.Character和char 的区别: 2.自动拆箱装箱java 1.Character和char 的区别: Character是类,char基本数据类型.数组 在java中有三个类负责 ...

  6. Java进阶之自动拆箱与自动装箱

    序. java基本类型介绍 java中,基本数据类型一共有8种,详细信息如下表: 类型 大小 范围 默认值 byte 8 -128 - 127 0 short 16 -32768 - 32768 0 ...

  7. java 自动装箱自动拆箱_自动装箱和自动拆箱

    自动装箱和自动拆箱 Java 提供了 8 种基本数据类型,每种数据类型都有其对应的包装类型,包装类是面向对象的类,是一种高级的数据类型,可以进行一些比较复杂的操作,它们是引用类型而不再基本类型了. 基 ...

  8. 对象包装器与自动(拆箱)装箱+参数数量可变的方法+枚举类

    [0]README 0.1) 本文描述+源代码均 转自 core java volume 1, 旨在理解 自动拆箱(装箱).printf的参数数量可变 以及 枚举类的详细说明: 0.2) 源代码: h ...

  9. Java 进阶——自动装箱和自动拆箱

    1.什么是自动装箱拆箱 基本数据类型的自动装箱(autoboxing).拆箱(unboxing)是自J2SE 5.0开始提供的功能. 一般我们要创建一个类的对象实例的时候,我们会这样: Class a ...

最新文章

  1. nginx 转发慢_为啥 Nginx 能轻松淦到几万并发?
  2. device.cpp
  3. 数学图形(2.23)Cylindric sine wave柱面正弦曲线
  4. 网络安全08-虚拟机运行架构(寄居架构+原生架构)、虚拟机产品简单介绍、windows操作系统--屏蔽系统自动更新
  5. 学习template算法(template matching)以及改进(二)
  6. css文本过长如何设置省略号
  7. [导入]你的网站被订阅了吗(浅谈RSS2.0)续
  8. 微信自动选择浏览器打开方式
  9. 【知识点总结】自动控制原理(自控)
  10. BCH智能合约方案正式推出,我们的征途是星辰大海!
  11. 阿里的敏捷组织和中台策略有何不同?
  12. 《看不见的森林:林中自然笔记》书摘一
  13. 从起步到影响世界:漫谈韩国网游发展史
  14. 小米怎么把便签放在手机桌面上
  15. 语义分割-建筑物提取数据集
  16. spring bean的使用范围:singleton,prototype,request,session,application
  17. 使用Matlab对PIV数据中的dat文件进行读取数据并求平均值
  18. python多线程url采集器 + github_利用Python3.5多线程抓取妹子图
  19. 如何看懂电路图之数字逻辑电路
  20. uniapp抖音网页版

热门文章

  1. IE 8兼容:X-UA-Compatible的解释
  2. 【资源】免费好用的在线PS网站【更新中】
  3. 5G网络远未成熟,缓建5G运营4G网络才是赚钱之道,5G为何沦落如斯
  4. 游戏服务端开发浅谈(一)
  5. java安装jdk时显示系统管理员设置了系统策略,禁止进行此安装
  6. 活动抽奖,送出10个OG角色
  7. 一篇学完:王道考研408数据结构(全)
  8. Java 多线程学习一(入门)—— 久违的 Java 多线程终于肝出来了!
  9. 知乎推荐10个黑科技网站,相信你在朋友面前会一鸣惊人
  10. 解决轮播动态加图片效果失效的问题