zoukankan      html  css  js  c++  java
  • 【类型转换】 隐式转换 自动提升 强制转换

    基本数据类型的类型转换

    Java中基本数据类型共有8种,分别是:布尔型boolean,字符型char和数值型byte/short/int/long/float/double。由于字符型char所表示的【单个字符】与Ascii码中相应【整形】对应,因此,有时也将其划分到数值型中

    基本数据类型中,布尔类型boolean占有一个字节(或一个二进制位,关于布尔类型的长度,详见另一篇笔记),由于其本身所代表的特殊含义,boolean类型与其他基本类型不能进行类型的转换(既不能进行自动类型的提升,也不能强制类型转换), 否则,将编译出错。

    数值类型在内存中直接存储其本身的值,对于不同的数值类型,内存中会分配相应的大小去存储。相应的,不同的数值类型会有与其存储空间相匹配的取值范围。具体如下所示:
    byte->【char->short->int->long->float->double
    图中依次表示了各数值类型的字节数和相应的取值范围。
    在Java中,整数类型(byte/short/int/long)中,对于未声明数据类型的整形,其默认类型为int型浮点类型(float/double)中,对于未声明数据类型的浮点型,默认为double型

    强制类型转换

    当我们需要将【数值范围较大】的数值类型赋给【数值范围较小】的数值类型变量时,由于此时可能会丢失精度或者超界溢出(注:下面说的隐式转换除外),为了保证程序的可靠性、严谨性、健壮性......计算机不会自动帮我们进行转换,而是要求我们人为进行转换。我们称这种为强制类型转换。
    float b = 1.5;  // 编译出错 Type mismatch: cannot convert from double to float,1.5默认是一个double型,不能自动转换为float类型
    float b2 = (float) 1.5;  //  编译正确。进行强制类型转换,转换后精度变低了
    float b3 = 1.5f;  //  编译正确。直接用一个float类型的数值给float变量b3初始化
    
    double d = 0;//这里的0其实是0D,而不是int 0
    float f = (float) d;
    int i2 = (int) f;//由于float类型的数值范围远大于int类型,所以强转后可能因超出int边界而导致数值异常
    long l = (long) f;//注意,虽然long有64位,float只有32位,但float类型的数值范围仍是远大于long类型的,所有float->>long时也需要强制类型转换
    强制类型转换时,如果超出范围较小的数据类型的数值范围(溢出),很可能出现一些意外情况,如下:
    System.out.println((byte) 127 + "   " + (byte) 128 + "   " + (byte) 255 + "   " + (byte) 256); //127   -128   -1   0
    这种情况在编程语言设计层面是不可避免的,因为即使是同一数据类型的两个数进行简单的加减运算,都可能溢出,如下:
    System.out.println(Integer.toBinaryString(Integer.MAX_VALUE));//[0]1111111111111111111111111111111,注意最前面的符号位0省略了
    System.out.println(Integer.toBinaryString(Integer.MIN_VALUE));//10000000000000000000000000000000,32位,注意这里都是用原码表示的
    
    System.out.println(Integer.toBinaryString(1));//1,前面所有的0都被省略了
    System.out.println(Integer.toBinaryString(-1));//11111111111111111111111111111111,32位
    
    System.out.println((Integer.MAX_VALUE + 1) == Integer.MIN_VALUE);//true,溢出了,提示:Comparing identical相同的 expressions
    System.out.println((Integer.MIN_VALUE + (-1) == Integer.MAX_VALUE));//true,溢出了
    所以,在进行强制类型转换时,只能由我们自己去保证代码的可靠性。

    隐式类型转换

    JVM在编译过程中,对于默认为int类型的数值,当赋给一个比int型数值范围小的数值类型变量时(在此统一称为数值类型k,k可以是byte/char/short类型),会进行判断,如果此int类型的数值超过k类型的数值范围,那么会直接编译出错;但是如果此int型数值尚在类型k的数值范围内,则JVM会自动进行一次隐式类型转换,将此int类型数值转换成k类型,而不需要我们手动强制类型转换。如上图中的虚线箭头。
    接下来我们看看如下一个较为经典例子:
    byte b1 = 3;      //编译正确。如果此int类型的数值在byte类型的数值范围内,则jvm会自动进行一次隐式类型转换,将此int数值数值转换成byte类型
    byte b2 = 1000;  //编译出错 Type mismatch: cannot convert from int to byte,如果此int类型的数值超过k类型的数值范围,会直接编译出错
    byte b3 = (byte) 1000;  //编译正确。进行强制类型转换
    我们再看另一个经典例子:
    byte p = 3; // 编译正确:int到byte编译过程中发生了【隐式类型转换】。3是直接量,编译期间可以直接进行判定
    int a = 3;
    byte b = a; // 编译出错:cannot convert from int to byte。a为一变量,需要到运行期间才能确定
    byte c = (byte) a; // 编译正确。强制类型转换
    为什么将一个值为3的int类型变量a赋值给byte类型变量b时发生了编译错误呢?
    区别在于前者3是直接量,编译期间可以直接进行判定;后者a为一变量,需要到运行期间才能确定,也就是说,编译期间为以防万一,当然不可能编译通过了。此时,需要进行强制类型转换。

    自动类型提升

    当将一个【数值范围较小】的类型赋给一个【数值范围大】的数值型变量时,jvm在编译过程中均将此数值的类型进行了自动提升。如上图所示,从byte到double一路都可以自动类型提升。
    byte->【char】->short->int->long->float->double
    float f = 5;//编译正确。整数默认是int类型,此时直接发生了【自动类型提升】
    double d = 1.5f;//编译正确。float类型的浮点数赋值给double类型,此时直接发生了【自动类型提升】
    long l = 'a';//编译正确。字符'a'被当做int类型的97后赋值给long类型,此时直接发生了【自动类型提升】

    不能 byte->char,char->short

    由于字符型char所表示的【单个字符】与Ascii码中相应【整形】对应,因此,有时也将其划分到数值型中,所以上面中的"强转"和"隐转"都无条件包括char类型
    The char data type is a single 16-bit Unicode character. It has a minimum value of 'u0000' (or 0) and a maximum value of 'uffff' (or 65,535 inclusive).
    但是由于char型具有两个字节(2*8=16-bit),且可看做是unsigned型(其他整型默认都是signed型),因此,这直接导致byte型不能自动类型提升到char(byte可能有负数),char型也不能自动类型提升到short(可能超出short边界
    当然,byte型是可以直接提升到short等类型的。
    byte b = 3;//隐式类型转换
    char c1=b;//编译错误:Type mismatch: cannot convert from byte to char。byte型不能自动类型提升到char!
    char c2 = 'a';
    short s1 = c2;//编译错误:Type mismatch: cannot convert from char to short。char型不能自动类型提升到short!
    short s2 = b;//编译正确。自动类型提升。byte型可以直接提升到short等类型!
    实际上,你可以认为"不能 byte->char,char->short"是特例,也可以认为"能char->int,char->long,char->float,char->double"是特例,就看你怎么解释了。

    自动类型提升规则

    注意以下讨论的是二元操作符。
    Java定义了若干适用于表达式的类型提升规则:
    • 所有的byte型、short型和char型将被提升到int型(例外:final修饰的byte、short、char变量相加后不会被自动提升)
    • 如果一个操作数是long型,计算结果就是long型
    • 如果一个操作数是float型,计算结果就是float型
    • 如果一个操作数是double型,计算结果就是double型
    另一种归纳方式,《Java核心技术卷I》P43:  
    • 如果两个操作数其中有一个是double类型,另一个操作就会转换为double类型。
    • 否则,如果其中一个操作数是float类型,另一个将会转换为float类型。
    • 否则,如果其中一个操作数是long类型,另一个会转换为long类型。
    • 否则,两个操作数都转换为int类型【这是最最最最重要的一条】
    如下示例
    byte b = 50;
    char c = 'a';
    short s = 1024;
    int i = 50000;
    float f = 5.67f;
    double d = 0.1234;
    double result = (f * b) + (i / c) - (d * s);
    System.out.println("(f * b)=" + (f * b) + "  (i / c)=" + (i / c) + "  (d * s)=" + (d * s) + "  result=" + result);
    //(f * b)=283.5  (i / c)=515  (d * s)=126.3616  result=672.1384
    //第一个表达式 f * b 中,b被提升为float类型,该子表达式的结果也提升为float类型。
    //第二个表达式 i / c 中,变量c被提升为int类型,该子表达式的结果提升为int类型。
    //第三个表达式 d * s 中,变量s被提升为double类型,该子表达式的结果提升为double型。
    //最后,这三个结果类型分别是float,int和double类型,想减后该表达式的最后的结果就是double类型。

    自动类型提升的一个经典案例

    在分析自动类型提升时,不要被一些虚假的表象欺骗了,如下经典案例:
    long l_maxInt = 21474_83647;//编译正确。
    long l_bigerThanMaxInt = 21474_83648;//编译出错:The literal ** of type int is out of range。由于数值**【超过了int类型范围的边界】,故而其自身已经编译出错
    long l_endWithL = 21474_83648L;//编译正确。如果一个整数以字母L或l结尾,则其类型为long类型;否则为默认的int类型
    int i_outOfRange = Integer.MAX_VALUE + 1;//编译正确。虽然结果溢出,但是程序没有任何问题,所以需要开发中自己留意溢出问题
    如上:定义long类型的 l_bigerThanMaxInt 变量时,将编译出错,原因在于 21474_83648 默认是int类型,而 21474_83648 已经超出int类型范围内的最大值,故而其自身已经编译出错,更谈不上赋值给long型变量 l_bigerThanMaxInt  了。

    自动类型提升后的精度问题

    在数值类型的自动类型提升过程中,数值精度问题要分情况来看
    • 整型-->整型,自动类型提升后精度保持不变
    • float-->double,自动类型提升后精度将变高
    • 整型-->float/double,暂且定义为"精度将变低"
      这个我表示很纠结啊,按上面的节奏来说的话,应该是精度将变高(比如int转为float),但是实际上数值都可能被"消尾",也即数据都已经由"精确值"变成了"近似",那还哪来的"精度变高",很明显是"精度变低"啊?但是,怎么也找不到相应的说法。

    float f = Integer.MAX_VALUE;
    System.out.println(Integer.MAX_VALUE + "  " + f);//21474_83647  2.1474_8365E9(呐,被"消尾"了,已经由"精确值"变成了"近似值")
    等以后找到更多更权威的资料后再补充

    再来一个经典案例

    进行数学运算时的数据类型自动提升与可能需要的强制类型转换
    byte b = 0, b1 = 1, b2 = 2;//【隐式类型转换】
    b = b1 + b2; //编译出错,Type mismatch: cannot convert from int to byte。进行 b1 + b2 运算时会先将b1和b2【自动类型提升】成为int,运算结果也是int,赋给byte类型的b1时需要强制转换(向下转型)
    b = b + 0;//编译出错,同上面的错误一样,运算时会先将b【自动类型提升】成为int,运算结果也是int
    b += b2; //编译正确。【自增】没有类型提升问题。编译器在编译的时候自动进行了强转,相当于【b = (byte) (b + b2)】
    
    //final修饰的byte、short、char
    final byte b3 = 3, b4 = 4;
    b = b3 + b4;//编译正确。有【final】修饰的byte、short、char变量相加后【不会被自动提升】为int类型
    
    //一个奇葩的情况
    final byte b6 = Byte.MAX_VALUE, b7 = 0, b8 = 1;
    b = b6 + b7;//编译正确。
    b = b6 + b8;//编译出错,Type mismatch: cannot convert from int to byte。注意,错误的原因和【long l = 21474_83648】一样,是因为由于数值**【超过了byte类型的范围】,故而其自身已经编译出错,而不是因为需要强制转换(向下转型)
    b = 128;//编译出错,但是这里出错的原因我感觉和上面的原因是不一样的
    2017-8-27




  • 相关阅读:
    分布式session管理解决方案
    RabbitMQ知识汇总
    RabbitMQ之集群模式总结
    Flexbox参数详解
    CSS Lint
    javascript中的defer属性和async属性
    简介BFC
    GIT 牛刀小试 (第二发)
    GIT 牛刀小试 (第一发)
    如何让浏览器支持HTML5标签
  • 原文地址:https://www.cnblogs.com/baiqiantao/p/7442114.html
Copyright © 2011-2022 走看看