在实际应用中,经常需要在不同类型的值之间进行操作,这时就需要进行数据类型的转换。 数据类型转换有两种:
自动装箱和拆箱从Java 1.5开始引入,目的是将原始类型值转自动地转换成对应的对象。
在JDK1.5之前,基本类型转换为对象需要程序显示用包装类进行处理,有了装箱和拆箱后就自动了,程序就不需要显示处理了。例如:Integer num = 1;
什么是自动装箱和拆箱?
自动装箱就是Java自动将原始类型值转换成对应的对象,比如将int的变量转换成Integer对象,这个过程叫做装箱,反之将Integer对象转换成int类型值,这个过程叫做拆箱。因为这里的装箱和拆箱是自动进行的非人为转换,所以就称作为自动装箱和拆箱。
自动装箱拆箱要点
自动装箱时编译器调用valueOf将原始类型值转换成对象,同时自动拆箱时,编译器通过调用类似intValue(),doubleValue()这类的方法将对象转换成原始类型值。
自动装箱是将boolean值转换成Boolean对象,byte值转换成Byte对象,char转换成Character对象,float值转换成Float对象,int转换成Integer,long转换成Long,short转换成Short,自动拆箱则是相反的操作。
何时发生自动装箱和拆箱
自动装箱和拆箱在Java中很常见,比如我们有一个方法,接受一个对象类型的参数,如果我们传递一个原始类型值,那么Java会自动讲这个原始类型值转换成与之对应的对象。
ArrayList intList = new ArrayList(); intList.add(1); //自动装箱
自动拆箱(unboxing),也就是将对象中的基本数据从对象中自动取出。
Integer i = 10; //装箱 int t = i; //拆箱,实际上执行了 int t = i.intValue();
注意事项:
对于–128到127(默认是127)之间的值,Integer.valueOf(int i) 返回的是缓存的Integer对象(并不是新建对象)
包装类型的缓存机制
Byte
,Short
,Integer
,Long
这 4 种包装类默认创建了数值 [-128,127] 的相应类型的缓存数据,Character
创建了数值在 [0,127] 范围的缓存数据,Boolean
直接返回True
orFalse
。
public static Integer valueOf(int i) { assert IntegerCache.high >= 127; if (i >= IntegerCache.low && i <= IntegerCache.high) return IntegerCache.cache[i + (-IntegerCache.low)]; return new Integer(i); }
对于byte,short,int,long四个整数类型而言,它们在内存中无一例外都是直接换算成二进制存储的,所以我们可以直接计算出它们的最大值。
二进制的第一位是符号位不计算入数值,所以Byte的最大值是01111111,即127
而浮点型在内存中的存储结构是不同的:
float在内存中占4个字节,共32位,但是浮点数在内存中是这样的:
V=(-1)^s ∗ M ∗ 2^E
浮点数的32位不是简单的直接表示大小,而是按照一定的标准分配的。
所以这就是为什么float虽然只用到了4个字节,但是浮点数表述范围却比长整型long的表述范围要大的原因了。由此就可以就解释的通为什么小范围的long能自动转型成为大范围float了。
简单计算一下float的范围
2^-127 ~ 2^128-1,很明显比long的 [2^63 - 1 , -2^63]范围大
V=(-1)^s ∗ M ∗ 2^E
M区域(小数域),M的取值范围为[1,2)或[0,1)。
所以尽管float的表数范围很大,但是不是区间内所有的值都一定能精确表示的。所以在long转float时可能存在精度丢失。
【范例】
int a = 123456789; float f = a; double d = a; System.out.println("原始int值a: " + a); System.out.println("a转换为float精度损失:" + f); System.out.println("a转换为double精度未损失:" + d); long l = 1234567899999999l; f = l; System.out.println("long类型" + 1 + "转换为float,精度损失: " + f);
输出为:
原始int值a: 123456789 a转换为float精度损失:1.23456792E8 a转换为double精度未损失:1.23456789E8 long类型1转换为float,精度损失: 1.23456795E15
【备注】在进行货币等精确计算时应使用BigDecimal。
程序中如果对精度要求不是很高的情况,可以使用float。但精度要求高的情况,要尽量使用double。如果要求更高的精度,则应使用BigDecimal
在百度上搜到一个很形象的答复,就是int是准确值,而float是精确值,准确转精确当然会精度丢失。
Int是4字节32位来表示的,而float虽然也是4字节32位,但是float的存储结构是很不一样的
以这一例子来说明,由图可知,float的存储结构是1个符号位,8个指数位,23个尾数。
符号位,表述浮点数的正或者负,0代表正,1代表负。
指数位,实际也是有正负的,但是没有单独的符号位,在计算机的世界里,进位都是二进制的,指数表示的也是2的N次幂,8位指数表达的范围是0到255,而对应的实际的指数是-127到128。也就是说实际的指数等于指数位表示的数值减127。这里特殊说明,-127和+128这两个指数数值在IEEE当中是保留的用作多种用途的,这里就不多做介绍了,有兴趣的可以查阅其他资料。【其实这个就是IEEE的定义,因为公式中是2的N次幂-127,这个跟JAVA中byte值范围不一样】
尾数位,只代表了二进制的小数点后的部分,小数点前的那位被省略了,当指数位全部为0时省略的是0否则省略的是1。
由此我们可以明白,实际上尾数确定了浮点数的精度,而数的大小主要是靠指数位,尾数只有23位,加上省略的那一位便是24位,所以如果int类型的值在2^24以内,float是可以精确表示的,但是当超过这个数的时候就不一定能精确表示了。这里最重要的一点便是要理解确定精度的有效位数,不管是什么基本类型转换实际上都要明白这一点。就如int与float,都是32位,但是内存结构既存储结构是不一样的,float只能有24位来确定精度,而int是32位。其他类型也如此进行理解即可。
在讲这公式之前讲一下我在理解过程中遇到的难题,就是尾数的23位值为什么是介于1.0和2.0之间,当时看到一直想不明白,后来才了解到这23位是用来表示小数位的,而省略的那一位是1,因为0是没有意义的(因为如果是0,一个小数的不管乘以多少都是小数)。再次强调这里的尾数23位是表示小数位的。
就以图的例子来讲。
符号位我想不用我讲了
指数位就跟我们算十进制一样的方法,可以算出指数位是124(算法我就不在这里说了,网上自己查一下),套入以上的表达式的最后一个,既是2^(124-127) = 2^(-3)
然后尾数部分,看它的公式就可以看出来,b23-i就是尾数部分的哪一位数,i取1的时候就b22,既最左边的部分,然后再乘以2的负i次方。从上面这一公式也可以看出,尾数部分是介于1.0和2.0之间。【尾数部分:1+2的-2次方,值为1.25;再乘以指数部分的2的-3次方,相当于除以8,值为0.15625】