Java中自动类型转换溢出问题引发的探究


语言类型的强弱并没有好坏之分

编程鼻祖C语言是有类型的语言,在C语言中的变量必须满足:“在使用前定义,并确定类型”。在C语言之后的编程语言向着两个方向发展:

  • C++、Java为代表的强类型语言
    更加强调类型,对类型的检查比C语言更严格。
  • JavaScript、Python、PHP为代表的弱类型语言
    不看重类型,甚至不需要事先定义。

一般来说,面向底层的语言更加强调类型;而越是面向应用的语言会越忽视类型,而去重视事务逻辑的处理。

Java中所有的数值类型所占据的字节数量与平台无关

C/C++中int和long等类型的大小与目标平台相关,因为程序需要针对不同的处理器选择最为高效的整型,例如,在32位处理器上long值为4字节;在64位处理器上则为8字节。这样就有可能造成一个在64位处理器上运行的很好的C程序在32位处理器上运行却发生整数溢出。这就为编写跨平台程序带来了很大的难度。

而在Java中,所有的数值类型所占据的字节数量与平台无关,即数值类型的范围与运行Java代码的机器无关,这也是Java跨平台的一个表现。

另外,Java中的int、long、short、byte类型都是有符号的。底层的二进制形式中,首位如果是 0,就是正的;首位是1就是负的。另外,char则是0-65535的无符号数,从char转换到int类型时,缺少的高16位字节用0扩展。

Java - Type 默认值 大小 取值范围
byte 0 1字节 -128 - 127
short 0 2字节 -32768 - 32767
int 0 4字节 -2147483648 to 2147483647(21亿多)
long 0L 8字节 -232-1 to (232-1-1)
float 0.0f 4字节 ±1.4E-45 to ±3.4028235E+38(有效位数为6~7位)
double 0.0d 8字节 ±4.9E-324 to ±1.7976931348623157E+308(有效位数为15位)
char ‘\u0000’ 2字节 0-65535
boolean false 取决于Java虚拟机 true 或 false

在这里插入图片描述
输出如下:

在这里插入图片描述

  • 不同类型的变量在内存中的表现形式是不同的,整型变量在内存中表现为2进制数,浮点型变量在内存中表现为编码形式

数据类型之间的转换

在这里插入图片描述
上图中,6个实心箭头的转换表示无信息丢失的转换;3个虚箭头的转换表示可能有精度损失的转换。如果两个不同的数值类型进行二元操作时,先要将两个操作数转换为同一种类型,然后再进行计算,计算规则如下:

  • 如果两个操作数中,有一个是double类型,另一个操作数就会转换为double类型。
  • 否则,如果其中一个操作数是float类型,另一个操作数就会转换为float类型。
  • 否则,如果其中一个操作数是long类型,另一个操作数就会转换为long类型。
  • 否则,两个操作数都将被转换为int类型。

特别的:整型向字符型转换时,JVM会把数字当成字符的ASCII编码来处理。

        //将整型类型强制转换为字符型时,JVM会把数字当成字符的ASCII编码来处理.
        char i = (char)97;
        System.out.println(i);//输出:a

数据溢出问题

数据溢出问题就是:当某一种类型的数值已经达到了此类型能够保存的最大值之后,再继续扩大,或者达到了最小值后再继续缩小,就会出现数据溢出问题。

整型类型溢出

下面的代码以int类型为例,测试对int类型进行溢出测试:

package com.gql;

/**
 int类型的溢出测试
 @author Hudie
 @date 2020/10/2 - 11:53
 */
public class Test {
    
    
    public static void main(String[] args) {
    
    
        //int类型的最小值为:-2^31
        int a = -2147483648;
        System.out.println("int类型的最小值为:"+a);
        System.out.println("int类型的最小值减1为:"+(a-1));
        System.out.println("int类型的最小值减2为:"+(a-2));
        System.out.println("int类型的最小值减3为:"+(a-3));
        System.out.println("int类型的最小值减4为:"+(a-4));
        System.out.println("//总结:超过最小值的数据会向下溢出变成正数.\n");

        //int类型的最大值为2^31 - 1
        a = 2147483647;
        System.out.println("int类型的最大值为:"+a);
        System.out.println("int类型的最大值加1为:"+(a+1));
        System.out.println("int类型的最大值加2为:"+(a+2));
        System.out.println("int类型的最大值加3为:"+(a+3));
        System.out.println("int类型的最大值加4为:"+(a+4));
        System.out.println("总结:超过最大值的数据会向上溢出变成负数.\n");
    }
}

在这里插入图片描述

上面的代码符合了整型的数据溢出规则:即超过最大值的整型数值会向上溢出变成负数;超过最小值的整型数值会向下溢出变成正数。

前文提到过,Java中的int、long、short、byte类型都是有符号的。而在C/C++语言中,溢出规则分为两种:

  • 有符号的整型溢出:“undefined behavior”,即由编译器实现,通常的编译器都是按照上面测试的整型数据溢出规则。
  • 无符号的整型溢出:溢出后的数会以2^(8*sizeof(type))作模运算

整型转换为字符型的溢出

字符型数据在内存中一般情况下下占据一个字节(如今有些Unicode字符需要用两个char来描述),其存放的就是这个字符对应的ASCLL码的整数值,所以字符型数据和整型数据之间可以通用。

在这里插入图片描述
ASCCLL码表毕竟范围有限,在转换时也可能会出现字符型的溢出:

下面的代码演示整型向字符型转换时出现的溢出情况。

        int j = (char)0;
        System.out.println(j);
        j = (char)-1;
        System.out.println(j);
        j = (char)-2;
        System.out.println(j);
        j = (char)-3;
        System.out.println(j);
        System.out.println("总结:超过最小值的数据会向下溢出变成正数");

        System.out.println("-------");
        j = (char)65535;

        System.out.println(j);
        j = (char)65536;
        System.out.println(j);
        j = (char)65537;
        System.out.println(j);
        System.out.println("总结:超过最大值的数据会向下溢出仍变成正数(因为char类型的符号位永远是0,即正数)");

在这里插入图片描述

可以发现整型转换为字符型时的溢出规律:

  • 当超过最小值的数据时会向下溢出,变成正数,所以直接取了char类型的最大值65535。
  • 当超过最大值的数据时会向上溢出,由于字符型不能变成负数,所以直接取了最小值0。
  • 可以将上面两条概括理解为,超过最大值后,取最小值;超过最小值后,取最大值。

猜你喜欢

转载自blog.csdn.net/weixin_43691058/article/details/108901673
今日推荐