Java自动装箱与拆箱及其陷阱分析
- 格式:doc
- 大小:16.85 KB
- 文档页数:6
Java中的包装类装箱和拆箱在java中,数据类型总共可分为两大种,基本数据类型(值类型)和类类型(引用数据类型)。
基本类型的数据不是对象,不能作为对象调用其toString()、hashCode()、getClass()、equals()等等方法。
所以在java中,针对8种基本数据类型,提供了针对每个基本数据类型的包装类。
如下:所谓装箱,就是把基本类型用它们相对应的引用类型包起来,使它们可以具有对象的特质,如我们可以把int型包装成Integer类的对象,或者把double包装成Double,等等。
所谓拆箱,就是跟装箱的方向相反,将Integer及Double这样的引用类型的对象重新简化为值类型的数据。
//JDK1.5 之前使用手动方式进行装箱和拆箱的操作public class IntegerDemo{public static void main (String []agrs){int i=10;Integer j=new Integer(i); //手动装箱操作int i1=j.intValue();//手动拆箱操作System.out.println(i1*i1);}}; //output:100//JDK1.5之后可自动进行的装箱和拆箱的操作,比较方便public class IntegerDemo02{public static void main (String []agrs){int i=10Integer j=i;int k=j;System.out.println(++k);}}; //output:11包装类的另外一个比较有用的是可以将String 字符串类型转换为相应的基本数据类型。
下面以将String转为int为例:public class IntegerDemo04{public static void main (String[] args){String str="1500";try{int i=Integer.parseInt(str);System.out.println(++i);}catch (Exception e){e.printStackTrace(); //打印异常信息}}};// output: 1501如果在上述方法中str="15er00";则在调用parseInt方法时候会产生NumberFormatException异常,见JDK API中对该方法的描述:public static int parseInt(String s) throws NumberFormatException将字符串参数作为有符号的十进制整数进行解析。
自动装箱和自动拆箱原理今天咱们来唠唠编程世界里一个挺有趣的话题——自动装箱和自动拆箱。
这俩概念听起来是不是有点神秘兮兮的?别担心,让我用大白话给您讲明白。
咱们先来说说自动装箱。
想象一下,您正在编程,就好像在搭建一个神奇的数字城堡。
有时候您想把一个基本数据类型,比如说一个小小的整数 5 ,当成一个对象来处理。
这时候,自动装箱就像一个神奇的魔法,它能把这个简单的整数 5 瞬间变成一个 Integer 对象。
是不是很神奇?就好像给这个数字穿上了一件华丽的外衣,让它从一个“小透明”变成了一个有身份有地位的“贵族”。
那这个魔法是怎么实现的呢?其实呀,在幕后有一套聪明的机制在运作。
当您写下类似 `Integer num = 5;` 这样的代码时,编译器就会悄悄地施展魔法,帮您把这个基本类型的 5 包装成一个 Integer 对象,然后再赋值给 num 。
这一切都发生得如此自然,您甚至都感觉不到魔法的存在。
再来说说自动拆箱。
这就像是把那个穿上华丽外衣的数字又脱下来,变回原来简单朴素的样子。
比如说,您有一个 Integer 对象 `num` ,然后您要进行一些数学运算,像 `int result = num + 5;` 。
这时候,自动拆箱就登场啦!它会悄悄地把`num` 这个对象变回基本数据类型的整数,然后再进行加法运算。
是不是感觉特别贴心?就好像有个小精灵在背后默默地为您服务,让您的编程变得轻松又愉快。
自动装箱和自动拆箱可给咱们编程带来了不少方便呢!以前没有它们的时候,咱们得自己手动去做这些转换,麻烦得要死。
现在有了这两个小魔法,代码写起来那叫一个流畅,效率也提高了不少。
比如说,在集合框架里,像 ArrayList 这种东西,它可只能装对象。
要是没有自动装箱,您想把一个整数放进去,那可就得自己先手动把它变成对象,多累呀!有了自动装箱,您直接把整数往里扔就行,简单又省事。
不过呢,虽然自动装箱和自动拆箱很方便,但也不是完全没有缺点的。
Java自动装箱与自动拆箱弄清楚了关于String的比较之后,昨天突发奇想地冒出一些问题。
Java里面的每个基本类型(primitive type)都有对应的引用类型(包装类wrapper),哈,问题就是与这个有关滴~打开eclipse写了个Integer i=1; 没有报错(呃,俺不了解autoboxing...)。
然后又陆续写了一些乱七八糟的,出现了一些意外的结果。
纠结了很久,想起autoboxing,有的问题想清楚了有的还没有;回寝室的路上又想起autounboxing,今天上网查了查相关资料,算是弄明白了。
开始写的是:public class EqualsDemo{public static void main(String[] args) {int i1=1,i2=1;Integer iv1=1,iv2=1;Integer in1=new Integer(1),in2= new Integer(1);print("(1) " + (i1==i2)); //(1)print("(2) " + (iv1==iv2)); //(2)print("(3) " + (in1==in2)); //(3)print("(4) " + (i1==iv1)); //(4)print("(5) " + (i1==in1)); //(5)print("(6) " + (iv1==in1)); //(6)}static void print(Object obj){System.out.println(obj);}}输出结果:(1) true(2) true(3) false(4) true(5) true(6) false(1) true:明白,==比较基本类型的值,i1、i2都是int类型,值相等。
java装箱和拆箱的方法Java中的装箱和拆箱是指将基本数据类型转换为对应的包装类类型和将包装类类型转换为对应的基本数据类型的过程。
在Java中,基本数据类型是int、float、double、boolean等,而包装类类型是Integer、Float、Double、Boolean等。
装箱和拆箱的方法如下:1. 装箱装箱是将基本数据类型转换为对应的包装类类型。
Java中提供了两种装箱的方法:(1)使用包装类的构造方法进行装箱例如,将int类型的数值装箱为Integer类型:int i = 10;Integer integer = new Integer(i);(2)使用valueOf()方法进行装箱例如,将int类型的数值装箱为Integer类型:int i = 10;Integer integer = Integer.valueOf(i);2. 拆箱拆箱是将包装类类型转换为对应的基本数据类型。
Java中提供了两种拆箱的方法:(1)使用包装类的xxxValue()方法进行拆箱例如,将Integer类型的数值拆箱为int类型:Integer integer = new Integer(10);int i = integer.intValue();(2)使用自动拆箱进行拆箱自动拆箱是指在需要基本数据类型的地方,自动将包装类类型转换为对应的基本数据类型。
例如:Integer integer = new Integer(10);int i = integer;需要注意的是,在使用自动拆箱时,如果包装类对象为null,则会抛出NullPointerException异常。
总结:Java中的装箱和拆箱是将基本数据类型和包装类类型相互转换的过程。
装箱可以使用包装类的构造方法或valueOf()方法,拆箱可以使用包装类的xxxValue()方法或自动拆箱。
在使用自动拆箱时,需要注意包装类对象是否为null。
自动装箱拆箱的作用
自动装箱和拆箱是Java语言中的概念,它们的作用是方便程序
员在基本数据类型和对应的包装类之间进行转换。
在Java中,基本
数据类型(如int、double等)和对应的包装类(如Integer、Double等)之间可以相互转换,而自动装箱和拆箱就是为了简化这
一过程而引入的概念。
自动装箱指的是将基本数据类型自动转换为对应的包装类对象,而自动拆箱则是将包装类对象自动转换为对应的基本数据类型。
这样,程序员在使用基本数据类型和包装类时就无需手动进行类型转换,使得代码更加简洁和易读。
举个例子,当我们需要将一个int类型的变量赋值给Integer
类型的对象时,可以直接进行赋值操作,而不需要调用Integer类
的构造函数进行手动转换。
这就是自动装箱的作用。
同样地,当我
们需要将一个Integer类型的对象赋值给int类型的变量时,也可
以直接进行赋值操作,而不需要调用intValue()方法进行手动转换,这就是自动拆箱的作用。
除了赋值操作,自动装箱和拆箱还可以在方法参数传递、方法
返回值等场景中发挥作用,使得代码编写更加便捷和简单。
总的来说,自动装箱和拆箱的作用是简化基本数据类型和包装类之间的转换过程,提高代码的可读性和易用性。
它们使得程序员在使用基本数据类型和包装类时更加方便,同时也减少了一些不必要的代码编写。
自动装箱(boxing)和自动拆箱(unboxing)首先了解下Java的四类八种基本数据类型自动装箱Java中所谓的装箱通俗点就是:八种基本数据类型在某些条件下使用时,会自动变为对应的包装器类型。
如下清单1:输出:解释下清单1第11句输出true的原因:当包装器类型进行“==”比较时,i3会调用Integer.valueOf自动装箱基本数据类型为包装器类型。
从源码中可以看出,Integer对象自动缓存int值范围在low~high(-128~127),如果超出这个范围则会自动装箱为包装类。
Note:1.Integer、Short、Byte、Character、Long这几个包装类的valueOf方法的实现是类似的;2.Double、Float的valueOf方法的实现是类似的。
3.Boolean的valueOf方法的实现是个三目运算,形如` return (b ? TRUE : FALSE); `自动拆箱Java中所谓的拆箱通俗点就是:八种包装器类型在某些条件下使用时,会自动变为对应的基本数据类型。
清单2:输出:解释下清单2第10句输出true的原因:当程序执行到第10句时,i4会调用Integer.intValue方法自动拆箱包装器类型为基本数据类型。
从源码可以看出,当包装器类型和基本数据类型进行“==”比较时,包装器类型会自动拆箱为基本数据类型。
清单3内容如下:输出:解释第15句为什么会输出true:因为在Integer包装类实现的equals方法中,只要比较的当前对象是Integer实例,那么就会自动拆箱为基本数据类型。
从以下Integer类的equals方法的源码就可看出:Note:1.Integer、Short、Byte、Character、Long这几个包装类的intValue方法的实现是类似的;2.Double、Float的intValue方法的实现是类似的。
3.Boolean的booleanValue方法的实现和intValue方法的实现也是类似的。
在Java中,装箱和拆箱是指将基本类型和对应的包装类之间进行相互转换的过程。
装箱(Boxing)是指将基本类型转换为对应的包装类的过程。
在Java中,包装类通常是以大写字母开头的类,如Integer、Double、Boolean等。
通过装箱,我们可以将基本类型值转换为相应的包装类对象,从而可以调用包装类中的方法或使用包装类中的属性。
拆箱(Unboxing)是指将包装类转换为对应的基本类型的过程。
通过拆箱,我们可以将包装类对象转换为相应的基本类型值,从而可以对其进行操作。
下面是一个示例,演示了装箱和拆箱的过程:
java复制代码
int num = 10;
Integer i = num; // 装箱
double result = i.doubleValue(); // 调用包装类方法
int num2 = (int) result; // 拆箱
在上面的示例中,将基本类型int转换为对应的包装类Integer,这是装箱过程。
然后,调用包装类方法doubleValue(),将包装类对象转换为double类型,这是拆箱过程。
最后,将double类型的result转换为int类型,这是再次拆箱的过程。
需要注意的是,在Java中,自动装箱和自动拆箱可以简化装箱和拆箱的过程。
自动装箱是指将基本类型转换为对应的包装类对象,自动拆箱是指将包装类对象转换为对应的基本类型值。
详解Java 的自动装箱与拆箱(Autoboxing and unboxing) 2015/09/22 5010 一、什么是自动装箱拆箱很简单,下面两句代码就可以看到装箱和拆箱过程//自动装箱Integer total = 99;//自定拆箱int totalprim = total; 简单一点说,装箱就是自动将基本数据类型转换为包装器类型;拆箱就是自动将包装器类型转换为基本数据类型。
下面我们来看看需要装箱拆箱的类型有哪些:这个过程是自动执行的,那么我们需要看看它的执行过程:public class Main { public static void main(String[] args) { //自动装箱Integer total = 99; //自定拆箱int totalprim = total; }} 反编译class 文件之后得到如下内容:javap -c StringTestInteger total = 99; 执行上面那句代码的时候,系统为我们执行了:Integer total = Integer.valueOf(99);int totalprim = total; 执行上面那句代码的时候,系统为我们执行了:int totalprim= total.intValue();我们现在就以Integer 为例,来分析一下它的源码:1、首先来看看Integer.valueOf 函数public static Integer valueOf(int i) { return i = 128 || i -128 ? new Integer(i) : SMALL_VALUES[i + 128];} 它会首先判断i 的大小:如果i 小于-128 或者大于等于128,就创建一个Integer 对象,否则执行SMALL_VALUES[i + 128]。
首先我们来看看Integer 的构造函数:private final int value;public Integer(int value) { this.value = value;}public Integer(String string) throws NumberFormatException { this(parseInt(string));} 它里面定义了一个value 变量,创建一个Integer 对象,就会给这个变量初始化。
java自动拆箱和自动装箱原理自动装箱和自动拆箱是Java语言提供的两种语法糖,使得Java编程更加简洁方便。
以下是自动拆箱和装箱的原理和使用方法。
自动装箱的原理:自动装箱是指在使用基本数据类型时,不需要手动将基本数据类型转化为对应的包装类,Java编译器自动将基本类型转化为包装类,这个过程也就是自动装箱。
例如:int类型的数据直接赋值给Integer类型的变量```int a = 10;Integer b = a;//这就是Java提供的自动装箱机制```自动装箱的原理是借助了Java自动调用valueOf方法的特性,将基本数据类型直接转化为对应的包装类,实现了基本数据类型向包装类的自动转换。
自动拆箱的原理:自动拆箱是指在使用包装类时,不需要手动将包装类转化为对应的基本数据类型,Java编译器自动将包装类转化为基本类型,这个过程也就是自动拆箱。
例如:Integer类型的数据直接赋值给int类型的变量```Integer a = 10;int b = a;//这就是Java提供的自动拆箱机制```自动拆箱的原理是借助了Java自动调用intValue方法的特性,将包装类直接转化为对应的基本数据类型,实现了包装类向基本数据类型的自动转换。
自动拆箱和自动装箱的使用方法:1. 自动拆箱和自动装箱在变量的赋值、方法传参和返回值方面,和直接使用基本数据类型和包装类没有任何区别,只是Java编译器自动执行了数据类型的转换。
例如:```public static void main(String[] args) {Integer a = 100;Integer b = 200;System.out.println(a + b);//自动拆箱和装箱}```2. 自动拆箱和自动装箱也可以手动执行,在一些特殊场景下可以起到更好的效果。
例如:手动拆箱和自动装箱可以很好地解决包装类的空指针异常问题。
```public static void main(String[] args) {Integer a = null;int b = a == null ? 0 : a;//手动拆箱和装箱System.out.println(b);}```总结:通过对自动拆箱和自动装箱的原理和使用方法的分析,我们可以发现自动拆箱和自动装箱机制很好地将基本数据类型和包装类有机地结合在一起,大大简化了 Java 编程的复杂度。
自动拆箱和自动装箱原理一、概述自动拆箱和自动装箱是Java语言中的两个特性,可以使得程序员在进行基本数据类型和其对应的包装类之间的转换时更加方便快捷。
本文将从以下几个方面详细介绍自动拆箱和自动装箱的原理。
二、基本概念1. 基本数据类型:Java中的基本数据类型有8种,分别是byte、short、int、long、float、double、char和boolean。
2. 包装类:Java中为每一种基本数据类型都提供了对应的包装类,用于在需要使用对象而不是基本数据类型的场合下使用。
例如,Integer 类是int类型的包装类。
3. 自动拆箱:将一个包装类对象转换为其对应的基本数据类型。
4. 自动装箱:将一个基本数据类型转换为其对应的包装类对象。
三、自动拆箱原理1. 概述自动拆箱是指将一个包装类对象转换为其对应的基本数据类型。
例如,将Integer对象转换为int类型。
在Java 5之前,需要手动进行这种转换;但是从Java 5开始,Java引入了自动拆箱机制,可以使得程序员在进行这种操作时更加方便快捷。
2. 实现原理当程序员使用一个包装类对象调用一个需要传入基本数据类型的方法时,Java虚拟机会自动将包装类对象转换为其对应的基本数据类型。
这个过程可以分为以下几个步骤:(1)获取包装类对象的值;(2)将该值转换为其对应的基本数据类型;(3)将基本数据类型作为参数传递给方法。
四、自动装箱原理1. 概述自动装箱是指将一个基本数据类型转换为其对应的包装类对象。
例如,将int类型转换为Integer对象。
在Java 5之前,需要手动进行这种转换;但是从Java 5开始,Java引入了自动装箱机制,可以使得程序员在进行这种操作时更加方便快捷。
2. 实现原理当程序员使用一个基本数据类型赋值给一个需要传入包装类对象的变量时,Java虚拟机会自动将基本数据类型转换为其对应的包装类对象。
这个过程可以分为以下几个步骤:(1)创建一个对应的包装类对象;(2)将基本数据类型赋值给该包装类对象;(3)使用该包装类对象进行后续操作。
Java 自动装箱与拆箱什么是自动装箱拆箱基本数据类型的自动装箱(autoboxing)、拆箱(unboxing)是自J2SE 5.0开始提供的功能。
一般我们要创建一个类的对象的时候,我们会这样:Class a = new Class(parameter);当我们创建一个Integer对象时,却可以这样:Integer i = 100; (注意:不是int i = 100; )实际上,执行上面那句代码的时候,系统为我们执行了:Integer i = new Integer(100); 此即基本数据类型的自动装箱功能。
基本数据类型与对象的差别基本数据类型不是对象,也就是使用int、double、boolean等定义的变量、常量。
基本数据类型没有可调用的方法。
eg:int t = 1;t. 后面是没有方法滴。
Integer t =1;t. 后面就有很多方法可让你调用了。
什么时候自动装箱例如:Integer i = 100;相当于编译器自动为您作以下的语法编译:Integer i = new Integer(100);什么时候自动拆箱自动拆箱(unboxing),也就是将对象中的基本数据从对象中自动取出。
如下可实现自动拆箱:1 Integer i = 10; //装箱2int t = i; //拆箱在进行运算时,也可以进行自动装箱与拆箱。
1 Integer i = 10;2 System.out.println(i++);Integer的自动装箱//在-128~127 之外的数Integer i1 = 200;Integer i2 = 200;System.out.println("i1==i2: "+(i1==i2)); //在-128~127 之内的数Integer i3 = 100;Integer i4 = 100;System.out.println("i3==i4: "+(i3==i4));输出的结果是:i1==i2: falsei3==i4: true说明:equals() 比较的是两个对象的值(内容)是否相同。
java陷阱之自动装箱下面一段代码会得到什么样的结果呢?public static void main(String[] args) {Integer a=1;Integer b=2;Integer c=3;Integer d=3;Integer e=127;Integer f=127;Integer e2=128;Integer f2=128;Long g=3L;System.out.println(c==d);System.out.println(e==f);System.out.println(e2==f2);System.out.println(c==(a+b));System.out.println(c.equals(a+b));System.out.println(g==(a+b));System.out.println(g.equals(a+b));}结果是truetruefalsetruetruetruefalse有些出乎意料吧,有以下几点说明我就大概明白了:1. java在对int型数据自动装箱为Integer对象的时候,如果值在-128-127之间,则会在内存中公用相同对象。
比如前面的Integer e=127;Integer f=127;中,e和f其实是引用了同一个对象。
JVM这样做的原因其实很好理解,就如同设置了字符串池一样,可能是他们通过统计发现java对int的定义很大部分都是在-128-127之间,并且通过性能分析发现装箱操作在这部分的开销占据很大一部分。
2. 包装类的“==”运算在没有遇到算术运算的时候是不会自动拆箱的。
3. Object.equals(Object obj)方法会先判断obj对象是否是该Object类型,如果不是,直接返回false;如果是则通过类型转换再进行比较其值。
可以参考下面的Integer类的equals代码public boolean equals(Object obj) {if (obj instanceof Integer) {return value == ((Integer)obj).intValue();}return false;}。
详解Java的⾃动装箱与拆箱(Autoboxingandunboxing)⼀、什么是⾃动装箱拆箱很简单,下⾯两句代码就可以看到装箱和拆箱过程1//⾃动装箱2 Integer total = 99;34//⾃动拆箱5int totalprim = total;简单⼀点说,装箱就是⾃动将基本数据类型转换为包装器类型;拆箱就是⾃动将包装器类型转换为基本数据类型。
下⾯我们来看看需要装箱拆箱的类型有哪些:这个过程是⾃动执⾏的,那么我们需要看看它的执⾏过程:1 public class Main {2 public static void main(String[] args) {3//⾃动装箱4 Integer total = 99;56//⾃定拆箱7int totalprim = total;8 }9 }反编译class⽂件之后得到如下内容:1 javap -c StringTestInteger total = 99;执⾏上⾯那句代码的时候,系统为我们执⾏了:Integer total = Integer.valueOf(99);int totalprim = total;执⾏上⾯那句代码的时候,系统为我们执⾏了:int totalprim = total.intValue();我们现在就以Integer为例,来分析⼀下它的源码:1、⾸先来看看Integer.valueOf函数1public static Integer valueOf(int i) {2return i >= 128 || i < -128 ? new Integer(i) : SMALL_VALUES[i + 128];3 }它会⾸先判断i的⼤⼩:如果i⼩于-128或者⼤于等于128,就创建⼀个Integer对象,否则执⾏SMALL_VALUES[i + 128]。
⾸先我们来看看Integer的构造函数:1private final int value;23public Integer(int value) {4this.value = value;5 }67public Integer(String string) throws NumberFormatException {8this(parseInt(string));9 }它⾥⾯定义了⼀个value变量,创建⼀个Integer对象,就会给这个变量初始化。
javamap使用数字类型作为key的坑在使用Java的Map时,使用数字类型作为键可能会遇到以下坑:
1. 自动装箱和自动拆箱:数字类型会自动装箱成对应的包装类,例如int会自动装箱为Integer。
在使用数字类型作为键时,会导致频繁的自动装箱和拆箱操作,影响性能。
2.精度损失:数字类型作为键时,可能会因为精度问题导致相等的数字被认为是不同的键,造成数据不一致。
3. 散列冲突:数字类型作为键时,由于数字空间有限,可能导致散列冲突,影响Map的性能。
解决这些问题的方法是使用适当的包装类作为键,例如使用Long作为键来避免自动装箱和拆箱操作,或使用BigDecimal来避免精度损失。
另外,可以考虑使用其他数据结构来代替Map,如IntMap或LongMap,它们专门用于处理数字类型作为键的情况,可以提供更好的性能和准确性。
Java JDK:自动装箱和拆箱浏览次数:8次字体:[增加减小]类型:转载基本数据(Primitive)类型的自动装箱(autoboxing)、拆箱(unboxing)是自J2SE 5.0开始提供的功能。
虽然为您打包基本数据类型提供了方便,但提供方便的同时表示隐藏了细节,建议在能够区分基本数据类型与对象的差别时再使用。
autoboxing和unboxing在Java中,所有要处理的东西几乎都是对象(Object),例如之前所使用的Scanner 是对象,字符串(String)也是对象,之后还会看到更多的对象。
然而基本(Primitive)数据类型不是对象,也就是您使用int、double、boolean等定义的变量,以及您在程序中直接写下的字面常量。
在前一个小节中已经大致看到了操作对象的方便性,而使用Java有一段时间的人都知道,有时需要将基本数据类型转换为对象。
例如使用Map对象要操作put()方法时,需要传入的参数是对象而不是基本数据类型。
要使用打包类型(Wrapper Types)才能将基本数据类型包装为对象,前一个小节中您已经知道在J2SE 5.0之前,要使用以下语句才能将int包装为一个Integer对象:Integer i nteger=new Integer(10);在J2SE 5.0之后提供了自动装箱的功能,您可以直接使用以下语句来打包基本数据类型:Integer integer=10;在进行编译时,编译器再自动根据您写下的语句,判断是否进行自动装箱动作。
在上例中integer参考的会是Integer类的实例。
同样的动作可以适用于boolean、byte、short、char、long、float、double等基本数据类型,分别会使用对应的打包类型(Wrapper Types)Boolean、Byte、Short、Character、Long、Float或Double。
下面直接使用自动装箱功能来改写范例4.4。
详解Java⾃动装箱与拆箱的实现原理什么是⾃动装箱和拆箱⾃动装箱就是Java⾃动将原始类型值转换成对应的对象,⽐如将int的变量转换成Integer对象,这个过程叫做装箱,反之将Integer对象转换成int类型值,这个过程叫做拆箱。
因为这⾥的装箱和拆箱是⾃动进⾏的⾮⼈为转换,所以就称作为⾃动装箱和拆箱。
原始类型byte, short, char, int, long, float, double 和 boolean 对应的封装类为Byte, Short, Character, Integer, Long, Float, Double, Boolean。
下⾯例⼦是⾃动装箱和拆箱带来的疑惑public class Test {public static void main(String[] args) {test();}public static void test() {int i = 40;int i0 = 40;Integer i1 = 40;Integer i2 = 40;Integer i3 = 0;Integer i4 = new Integer(40);Integer i5 = new Integer(40);Integer i6 = new Integer(0);Double d1=1.0;Double d2=1.0;System.out.println("i=i0\t" + (i == i0));System.out.println("i1=i2\t" + (i1 == i2));System.out.println("i1=i2+i3\t" + (i1 == i2 + i3));System.out.println("i4=i5\t" + (i4 == i5));System.out.println("i4=i5+i6\t" + (i4 == i5 + i6));System.out.println("d1=d2\t" + (d1==d2));System.out.println();}}请看下⾯的输出结果跟你预期的⼀样吗?输出的结果:i=i0 truei1=i2 truei1=i2+i3 truei4=i5 falsei4=i5+i6 trued1=d2 false为什么会这样?带着疑问继续往下看。
装箱与拆箱及其性能损失问题⾸先我想了解⼀下什么是装箱和拆箱在类型系统中,任何值类型和引⽤类型都可以和object类型进⾏转化,装箱转化是指将⼀个值类型显式或者隐式的转化为⼀个object类型,或者是转化成⼀个被该值类型应⽤的接⼝类型,将⼀个值类型装箱,就创建了⼀个object实例,并且将这个值赋值给了object,object对象的数据位于堆中,在栈上有对该对象的引⽤,⽽被装箱的类型的值是被作为⼀个复制的⽂件赋给对象的,所谓拆箱,就是装箱操作的反操作,复制堆中的对象⾄栈中,并且返回其值。
性能损失:相⽐于简单的赋值操作,装箱和拆箱需要进⾏⼤量的计算,对值类型进⾏装箱时,需要分配并构造⼀个全新的对象。
为了解决装箱与拆箱带来的性能损失问题,微软公司在.NET中提供了泛型。
下⾯就让我们看⼀段代码:将5000000条记录写⼊到ArrayList并将数据读出来5遍,其中写⼊和读取ArrayList的⼯作就是装箱和拆箱,最后计算出执⾏这些⽅法所消耗的时间。
class Unbox{public static void RunUnbox(){int count;DateTime startTime = DateTime.Now;ArrayList MyArrayList = new ArrayList();for (int i = 0; i < 5; i++){MyArrayList.Clear();for (count = 0; count < 5000000; count++){MyArrayList.Add(count); //装箱}int j;for (count = 0; count < 5000000; count++){j = (int)MyArrayList[count];//拆箱}}DateTime endTime = DateTime.Now;Console.WriteLine("使⽤装箱和拆箱共花时间:{0}",endTime-startTime);}public static void RunNoBox(){int count;DateTime startTime = DateTime.Now;List<int> MyTList = new List<int>();for (int i = 0; i < 5; i++){MyTList.Clear();for (count = 0; count < 5000000; count++){MyTList.Add(count);}int j;for (count = 0; count < 5000000; count++){j = MyTList[count];}}DateTime endTime = DateTime.Now;Console.WriteLine("使⽤泛型共花时间:{0}", endTime - startTime);}}class Test{static void Main(string[] args){Unbox.RunUnbox();Unbox.RunNoBox();Console.ReadLine();}}下⾯是运⾏结果:使⽤装箱和拆箱共花时间:00:00:04.2500000使⽤泛型共花时间:00:00:00.4687500由运⾏结果可以发现,即使是进⾏同样的操作,使⽤装箱和拆箱所使⽤的时间是使⽤泛型所花时间的10倍。
Java自动装箱、自动拆箱一、前言Java自动装箱和自动拆箱是JDK5.0版本提供的新特性,所以在JDK5.0后的版本中都可以使用,之前的版本则不支持该特性。
理解自动装箱和自动拆箱需要先对java中的8种原始数据类型和相对应的封装类有所了解。
二、8种原始数据类型及其对应的封装类byte ---> Byteshort ---> Shortint ---> Integerlong ---> Longfloat ---> Floatdouble ---> Doublechar ---> Characterboolean ---> Boolean注:原始数据类型的封装类都在ng包下,可以直接使用三、什么是自动装箱自动装箱指的是:可以将一个原始数据类型直接赋值给相应的封装类,其类型转换工作由java编译器自动处理,如:Byte b = 1;Short s = 2000;Integer i = 300000;Float f = 2.5f;Double d = 2.555;Character c = '中';Boolean bln = true;在没有自动装箱时(JDK5.0前的版本),想要创建一个原始数据类型封装类对象,需要改成下面这种形式(以Integer为例): //以下三种方式都可创建一个Integer对象Integer i = new Integer(1);Integer j = new Integer("1");Integer k = new Integer.valueOf(1);注:自动装箱可以简单理解为原始数据类型可以自动装载成封装类四、什么是自动拆箱自动拆箱指的是:原始数据类型的封装类对象可以当作原始数据类型来处理,其中的转换工作由java编译器自动处理,如:Integer i = 2000;Character c = '中';int value = i + c + 100;在没有自动拆箱前(JDK5.0前的版本),上述操作需要改成下面这种形式:Integer i = 2000;Character c = '中';int value = i.intValue() + c.charValue() + 100;注:自动拆箱可以简单理解为原始数据类型的封装类可以自动转换成原始数据类型五、自动装箱、自动拆箱带来的好处在实际开发中,我们经常会操作原始数据类型的封装类,也经常需要对原始数据类型与封装类相互转换或做一些运算,如果没有自动装箱和自动拆箱,这些操作装会很烦琐。
Java自动装箱与拆箱及其陷阱分析
定义
大家在平时编写Java程序时,都常常以以下方式来定义一个Integer对象:
Integeri=100;
从上面的代码中,大家可以得知,i为一个Integer类型的引用,100为Java中的基础数据类型(primitivedatatype)。
而这种直接将一个基础数据类型传给其相应的封装类(wrapperclass)的做法,便是自动装箱(Autoboxing)。
在jdk1.5中,自动装箱首次被引入。
而在jdk1.5之前,如果你想要定义一个value为100的Integer对象,则需要这样做:
Integeri=newInteger(100);
原理
我们在以上代码“Integeri=100;”处打一个断点,跟踪一下。
接下来,我们可以看到,程序跳转到了Integer类的
valueOf(inti)方法中
/**
*Returnsa Integer instancerepresentingthespecified
*int value.
*Ifanew Integer instanceisnotrequired,thismethod
*shouldgenerallybeusedinpreferencetotheconstructor
*{@link#Integer(int)},asthismethodislikelytoyield
*significantlybetterspaceandtimeperformancebycaching *frequentlyrequestedvalues.
*
*@paramian int value.
*@returna Integer instancerepresenting i.
*@since1.5
*/
publicstaticIntegervalueOf(inti){
if(i>=-128&&i<=IntegerCache.high)
returnIntegerCache.cache[i+128];
else
returnnewInteger(i);
}
换句话说,装箱就是jdk自己帮你完成了调用
Integer.valueOf(100)。
定义
Integerinteger100=100;
intint100=integer100;
从上面的代码中,大家可看出integer100为一个Integer类型的引用,int100为一个int类型的原始数据类型。
但是,我们可以将一个Integer类型的对象赋值给其相应原始数据类型的变量。
这便是拆箱。
拆箱与装箱是相反的操作。
装箱是将一个原始数据类型赋值给相应封装类的变量。
而拆箱则是将一个封装类的变量赋值给相应原始数据类型的变量。
装箱、拆箱的名字也取得相当贴切。
原理
笔者相信大家也都猜到了,拆箱过程中jdk为我们做了什么。
我们还是通过实验来证明我们的猜想吧。
在以上代码的第二行代码打上断点,即在
“intint100=integer100;”上打上断点,跟踪一下。
我们可以看到,程序跳转到了Integer的intValue()方法。
/**
*Returnsthevalueofthis Integer asan
*int.
*/
publicintintValue(){
returnvalue;
}
也就是,jdk帮我们完成了对intValue()方法的调用。
对于以上的实验而言,便是调用integer100的intValue()方法,将其返回值赋给了int100。
实验1
Integerinteger400=400;
intint400=400;
System.out.println(integer400==int400);
在以上代码的第三行中,integer400与int400执行了==运行。
而这两个是不同类型的变量,到底是integer400拆箱了,还是int400装箱了呢?运行结果是什么呢?
==运算是判断两个对象的地址是否相等或者判断两个基础数据类型的值是否相等。
所以,大家很容易推测到,如果integer400拆箱了,则说明对比的是两个基础类型的值,那此时必然相等,运行结果为true;如果int400装箱了,则说明对比的是两个对象的地址是否相等,那此时地址必然不相等,运行结果为false。
(至于为什么笔者对它们赋值为400,就是后面将要讲到的陷阱有关)。
我们实际的运行结果为true。
所以是integer400拆箱了。
对代码跟踪的结果也证明这一点。
实验2
Integerinteger100=100;
intint100=100;
System.out.println(integer100.equals(int100));
在以上代码的第三行中,integer100的方法equals的参数为int100。
我们知道equals方法的参数为Object,而不是基础数据类型,因而在这里必然是int100装箱了。
对代码跟踪的结果也证明了这一点。
其实,如果一个方法中参数类型为原始数据类型,所传入的参数类型为其封装类,则会自动对其进行拆箱;相应地,如果一个方法
中参数类型为封装类型,所传入的参数类型为其原始数据类型,则会自动对其进行装箱。
实验3
Integerinteger100=100;
intint100=100;
Longlong200=200l;
System.out.println(integer100+int100);
System.out.println(long200==(integer100+int100));
System.out.println(long200.equals(integer100+int100));
在第一个实验中,我们已经得知,当一个基础数据类型与封装类进行==运算时,会将封装类进行拆箱。
那如果+、-、*、/呢?我们在这个实验中,就可知道。
如果+运算,会将基础数据类型装箱,那么:
?第4行中,integer100+int100就会得到一个类型为Integer 且value为200的对象o,并执行这个对象的toString()方法,并输出”200”;
?第5行中,integer100+int100就会得到一个类型为Integer 且value为200的对象o,==运算将这个对象与long200对象进行对比,显然,将会输出false;
?第6行中,integer100+int100就会得到一个类型为Integer 且value为200的对象o,Long的equals方法将long200与o对比,因为两都是不同类型的封装类,因而输出false;
如果+运算,会将封装类进行拆箱,那么:
?第4行中,integer100+int100就会得到一个类型为int且value为200的基础数据类型b,再将b进行装箱得到o,执行这个对象的toString()方法,并输出”200”;
?第5行中,integer100+int100就会得到一个类型为int且value为200的基础数据类型b1,==运算将long200进行拆箱得到b2,显然b1==b2,输出true;
?第6行中,integer100+int100就会得到一个类型为int且value为200的基础数据类型b,Long的equals方法将b进行装箱,但装箱所得到的是类型为Integer的对象o,因为o与long200为不同的类型的对象,所以输出false;
程序运行的结果为:
200
true
false
因而,第二种推测是正确,即在+运算时,会将封装类进行拆箱。