ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

类型转换与基本类型包装类

2022-06-07 21:32:15  阅读:160  来源: 互联网

标签:类型转换 包装 float int 自动 类型 Integer 装箱


类型转换与基本类型包装类

基本数据类型转换

在实际应用中,经常需要在不同类型的值之间进行操作,这时就需要进行数据类型的转换。 数据类型转换有两种:

  • 自动类型转换:编译器自动完成类型转换,不需要在程序中编写代码;
    规则:从存储范围小的类型到存储范围大的类型。
    具体规则:byte(8b)→short(char)(16b)→int(32b)→long(64b)→float(32b)→double(64b)
    注意:对于short、char两种类型而言,他们是相同级别的,因此,不能相互自动转换,但是可以强制类型转换
  • 强制类型转换 :强制类型转换,也称显式类型转换,是指必须书写代码才能完成的类型转换。该类类型转换很可能存在精度的损失,所以必须书写相应的代码,并且能够忍受该种损失时才进行该类型的转换。
    转换规则:从存储范围大的类型到存储范围小的类型。
    具体规则为:double→float→long→int→short(char)→byte

包装类

  • 基本类型在java.lang包中都有一个相应的包装类,如:Byte,Short,Character,Integer,Long,Float,Double,Boolean,通过这些包装类,我们就可以将基本数据类型包装成类对象。
  • 包装类的构造方法:
    所有包装类都可将与之对应的基本数据类型作为参数,来构造它们的实例,例如:Integer i=new Integer(1);
    除Character类外,其他包装类可将一个字符串作为参数构造它们的实例,例如:Integer i=new Integer(“123”);
  • 注意事项:
    • Boolean类构造方法参数为String类型时,若该字符串内容为true(不考虑大小写),则该Boolean对象表示true,否则表示false。
    • Number包装类构造方法(上面的8种包装类中除了Character和Boolean,都继承了Number)参数为String 类型时,字符串不能为null,且该字符串必须可解析为相应的基本数据类型的数据,否则编译通过,运行时NumberFormatException异常。
    • 包装类内部都是final类型(以Integer为例,内部为private final int value;),因此包装类是引用传递,但是值修改后会生成新的内存地址,从表现方式可以理解为”值传递”。
  • 所有的包装类内部都是final类型的基本数据,所以都是不可变的

自动装箱与拆箱

自动装箱和拆箱从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 or False

            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);
            }
    

【进阶】long如何自动转化为float?

long占八个字节,float只占四位,如何能自动转化呢?

对于byte,short,int,long四个整数类型而言,它们在内存中无一例外都是直接换算成二进制存储的,所以我们可以直接计算出它们的最大值。
二进制的第一位是符号位不计算入数值,所以Byte的最大值是01111111,即127

而浮点型在内存中的存储结构是不同的:
float在内存中占4个字节,共32位,但是浮点数在内存中是这样的:
  V=(-1)^s ∗ M ∗ 2^E
浮点数的32位不是简单的直接表示大小,而是按照一定的标准分配的。

  • 其中第1位,符号位,即S。【所以符号位1表示负数,0表示正数】
  • 接下来的8位,指数域,即E。
  • 剩下的23位,小数域,即M,M的取值范围为[0,1)或【1,2)。

所以这就是为什么float虽然只用到了4个字节,但是浮点数表述范围却比长整型long的表述范围要大的原因了。由此就可以就解释的通为什么小范围的long能自动转型成为大范围float了。

简单计算一下float的范围

2^-127 ~ 2^128-1,很明显比long的 [2^63 - 1 , -2^63]范围大

为什么long类型转float可能存在精度丢失的问题

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的存储结构是很不一样的

156795497950633
以这一例子来说明,由图可知,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位是表示小数位的。

156795497950633
就以图的例子来讲。

符号位我想不用我讲了
指数位就跟我们算十进制一样的方法,可以算出指数位是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】

标签:类型转换,包装,float,int,自动,类型,Integer,装箱
来源: https://www.cnblogs.com/faetbwac/p/16353467.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有