java 啥是拆箱和装箱,拆箱和装箱 嘛用啊???
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java 啥是拆箱和装箱,拆箱和装箱 嘛用啊???相关的知识,希望对你有一定的参考价值。
详解Java的自动装箱与拆箱(Autoboxing and unboxing)
一、什么是自动装箱拆箱
很简单,下面两句代码就可以看到装箱和拆箱过程
//自动装箱Integer total = 99;
//自定拆箱
int totalprim = total;
简单一点说,装箱就是自动将基本数据类型转换为包装器类型;拆箱就是自动将包装器类型转换为基本数据类型。
下面我们来看看需要装箱拆箱的类型有哪些:
这个过程是自动执行的,那么我们需要看看它的执行过程:
public class Mainpublic 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函数
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对象,就会给这个变量初始化。第二个传入的是一个String变量,它会先把它转换成一个int值,然后进行初始化。
下面看看SMALL_VALUES[i + 128]是什么东西:
private static final Integer[] SMALL_VALUES = new Integer[256];它是一个静态的Integer数组对象,也就是说最终valueOf返回的都是一个Integer对象。
所以我们这里可以总结一点:装箱的过程会创建对应的对象,这个会消耗内存,所以装箱的过程会增加内存的消耗,影响性能。
2、接着看看intValue函数
@Overridepublic int intValue()
return value;
这个很简单,直接返回value值即可。
二、相关问题
上面我们看到在Integer的构造函数中,它分两种情况:
1、i >= 128 || i < -128 =====> new Integer(i)
2、i < 128 && i >= -128 =====> SMALL_VALUES[i + 128]
SMALL_VALUES本来已经被创建好,也就是说在i >= 128 || i < -128是会创建不同的对象,在i < 128 && i >= -128会根据i的值返回已经创建好的指定的对象。
说这些可能还不是很明白,下面我们来举个例子吧:
public class Main public static void main(String[] args)Integer i1 = 100;
Integer i2 = 100;
Integer i3 = 200;
Integer i4 = 200;
System.out.println(i1==i2); //true
System.out.println(i3==i4); //false
代码的后面,我们可以看到它们的执行结果是不一样的,为什么,在看看我们上面的说明。
1、i1和i2会进行自动装箱,执行了valueOf函数,它们的值在(-128,128]这个范围内,它们会拿到SMALL_VALUES数组里面的同一个对象SMALL_VALUES[228],它们引用到了同一个Integer对象,所以它们肯定是相等的。
2、i3和i4也会进行自动装箱,执行了valueOf函数,它们的值大于128,所以会执行new Integer(200),也就是说它们会分别创建两个不同的对象,所以它们肯定不等。
下面我们来看看另外一个例子:
public class Main public static void main(String[] args)Double i1 = 100.0;
Double i2 = 100.0;
Double i3 = 200.0;
Double i4 = 200.0;
System.out.println(i1==i2); //false
System.out.println(i3==i4); //false
看看上面的执行结果,跟Integer不一样,这样也不必奇怪,因为它们的valueOf实现不一样,结果肯定不一样,那为什么它们不统一一下呢?
这个很好理解,因为对于Integer,在(-128,128]之间只有固定的256个值,所以为了避免多次创建对象,我们事先就创建好一个大小为256的Integer数组SMALL_VALUES,所以如果值在这个范围内,就可以直接返回我们事先创建好的对象就可以了。
但是对于Double类型来说,我们就不能这样做,因为它在这个范围内个数是无限的。
总结一句就是:在某个范围内的整型数值的个数是有限的,而浮点数却不是。
所以在Double里面的做法很直接,就是直接创建一个对象,所以每次创建的对象都不一样。
public static Double valueOf(double d)return new Double(d);
下面我们进行一个归类:
Integer派别:Integer、Short、Byte、Character、Long这几个类的valueOf方法的实现是类似的。
Double派别:Double、Float的valueOf方法的实现是类似的。每次都返回不同的对象。
下面对Integer派别进行一个总结,如下图:
下面我们来看看另外一种情况:
public class Main public static void main(String[] args)Boolean i1 = false;
Boolean i2 = false;
Boolean i3 = true;
Boolean i4 = true;
System.out.println(i1==i2);//true
System.out.println(i3==i4);//true
可以看到返回的都是true,也就是它们执行valueOf返回的都是相同的对象。
public static Boolean valueOf(boolean b)return b ? Boolean.TRUE : Boolean.FALSE;
可以看到它并没有创建对象,因为在内部已经提前创建好两个对象,因为它只有两种情况,这样也是为了避免重复创建太多的对象。
public static final Boolean TRUE = new Boolean(true);public static final Boolean FALSE = new Boolean(false);
上面把几种情况都介绍到了,下面来进一步讨论其他情况。
Integer num1 = 400;int num2 = 400;
System.out.println(num1 == num2); //true
说明num1 == num2进行了拆箱操作
Integer num1 = 100;int num2 = 100;
System.out.println(num1.equals(num2)); //true
我们先来看看equals源码:
@Overridepublic boolean equals(Object o)
return (o instanceof Integer) && (((Integer) o).value == value);
我们指定equal比较的是内容本身,并且我们也可以看到equal的参数是一个Object对象,我们传入的是一个int类型,所以首先会进行装箱,然后比较,之所以返回true,是由于它比较的是对象里面的value值。
Integer num1 = 100;int num2 = 100;
Long num3 = 200l;
System.out.println(num1 + num2); //200
System.out.println(num3 == (num1 + num2)); //true
System.out.println(num3.equals(num1 + num2)); //false
1、当一个基础数据类型与封装类进行==、+、-、*、/运算时,会将封装类进行拆箱,对基础数据类型进行运算。
2、对于num3.equals(num1 + num2)为false的原因很简单,我们还是根据代码实现来说明:
public boolean equals(Object o)
return (o instanceof Long) && (((Long) o).value == value);
它必须满足两个条件才为true:
1、类型相同
2、内容相同
上面返回false的原因就是类型不同。
Ingeger num2 = 200;
Long num3 = 300l;
System.out.println(num3 == (num1 + num2)); //true
我们来反编译一些这个class文件:javap -c StringTest
可以看到运算的时候首先对num3进行拆箱(执行num3的longValue得到基础类型为long的值300),然后对num1和mum2进行拆箱(分别执行了num1和num2的intValue得到基础类型为int的值100和200),然后进行相关的基础运算。
我们来对基础类型进行一个测试:
int num1 = 100;int num2 = 200;
long mum3 = 300;
System.out.println(num3 == (num1 + num2)); //true
上面就说明了为什么最上面会返回true.
所以,当 “==”运算符的两个操作数都是 包装器类型的引用,则是比较指向的是否是同一个对象,而如果其中有一个操作数是表达式(即包含算术运算)则比较的是数值(即会触发自动拆箱的过程)。
通过上面的分析我们需要知道两点:
1、什么时候会引发装箱和拆箱
2、装箱操作会创建对象,频繁的装箱操作会消耗许多内存,影响性能,所以可以避免装箱的时候应该尽量避免。
如果你需要的话,可以用微信搜索“Java团长”或者“javatuanzhang”关注。 参考技术A Java中的包装类 装箱和拆箱
在java中,数据类型总共可分为两大种,基本数据类型(值类型)和类类型(引用数据类型)。基本类型的数据不是对象,不能作为对象调用其toString()、hashCode()、getClass()、equals()等等方法。
所以在java中,针对8种基本数据类型,提供了针对每个基本数据类型的包装类。如下:
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
所谓装箱,就是把基本类型用它们相对应的引用类型包起来,使它们可以具有对象的特质,如我们可以把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=10
Integer 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
将字符串参数作为有符号的十进制整数进行解析。除了第一个字符可以是用来表示负值的 ASCII 减号 '-' ('\u002D') 外,字符串中的字符都必须是十进制数字。返回得到的整数值,就好像将该参数和基数 10 作为参数赋予 parseInt(java.lang.String, int) 方法一样。
参数:
s - 包含要解析的 int 表示形式的 String。
返回:
用十进制参数表示的整数值。
抛出:
NumberFormatException - 如果字符串不包含可解析的整数。
总结在包装类中将String转换为包装类所对应的基本数据类型的方法如下,所有的方法都将抛出NumberFormatException异常,需要在调用时候使用try ..catch 进行捕获
index 包装类 返回的基本数据类型 方法名
1 Integer int static int Integer.parseInt(Sting s)
2 Character char static char Character.parseChar(String s)
3 Float float static float Float.parseFloat(String s)
4 Dobule double static double Double.parseDouble(String s)
5 Byte byte static byte Byte.parseByte(String s)
6 Short short static short Short.parseShort(String s)
7 Long long static long Long.parseLong(String s)
8 Boolean boolean static boolean Boolean.parseBoolean(String s)
类似的一个方法为 parseXXX(String s,int radix),对于Integer包装类,这个radix表示进制. parseInt(String s)默认为十进制。 其他包装类的radix参数意义,请参见API文档 参考技术B 装箱就是把基础类型封装成一个类。比如把int封装成Integer,这时你就不能把他当成一个数了,而是一个类了,对他的操作就需要用它的方法了。拆箱就是把类转换成基础类型。比如你算个加法什么的是不能用类的,就得把它转换成基本类型,jdk5以后已经可以自动转换了貌似。基本类型你应该懂的吧,就那八种本回答被提问者采纳 参考技术C 拆箱是把“引用”类型转换成“值”类型;
装箱是把“值”类型转换成“引用类型”;
原因是有时某些方法的参数要求使用“引用”类型,而你想把“值”类型的变量通过这个参数传入,就需要使用这个操作。追问
不是很懂的,能不能说的详细点呢???
谢谢!!!
假如码头上有很多货物 这些货物需要装到集装箱才能装船运走(这个可以比作把自定义变量打包) 把这个步骤比作装箱
那么 当卸载货物时候 需要打开集装箱 然后发往各处(这个可以比作 值得调用) 把这个步骤比作拆箱
以上是关于java 啥是拆箱和装箱,拆箱和装箱 嘛用啊???的主要内容,如果未能解决你的问题,请参考以下文章