Java基本数据类型
声明:
本文章为笔者个人总结,仅用于分享和讨论,不提供转载权限,不作为最终正确信息,如有错误和讨论可以进行留言。
一、存储空间:
在计算机中所有的数据都保存在一组连续的存储基元组成的空间中。通常一个存储基元可以存储1bit的信息量(逻辑电路中的开与关、晶体管的通导或截止、电压的高或低、脉冲信号的有或无等等状态来表示0和1)。我们也可以反过来说计算机的最小存储单位为1bit,它可以表示0和1两种数值或两种状态。
二、数据类型:
在计算机中我们所有存储的数据都是用0和1来表示的,为了告诉计算机程序数据的用途以及占用的存储空间大小,我们在计算机内对各种各样的数据定义了数据类型。如char数据类型在Java中表示一个使用16bit存储的文字。当计算机需要取出一个值的时候只需要知道这个值在存储空间中的开始位置以及数据类型就可以完整的取出这个数据。
而在Java中,数据类型又分为基本数据类型(原始数据类型/primitive value)和引用数据类型(reference type)。本文主要针对Java基本数据类型进行讲述。
三、基本数据类型:
在Java中基本数据类型分为为数值类型(numeric type)、boolean类型、char类型和void(此类型为特殊类型,绝大部分书籍未将此类型列为基本数据类型,但通过JavaApi Class类中的方法isPrimitive()注释来看,此类型也是基本数据类型的一种,在这里仅供参考),其中数值类型又分为整数和浮点数2个大类6个子类如下图所示:
1、数值类型:
1.1、整数类型:
整数类型,顾名思义就是表示整数的一种类型,该类型有4种不同的子类型用来表示不同范围内的整数。它们分别为byte、short、int、long。其中它们占用的存储空间和数值表示范围如下表所示:
整数类型定义表:
数据类型 | 占用的存储空间 | 数值表示范围 |
---|---|---|
byte | 8 | -128( − 2 7 -2^7 −27)至127( 2 7 − 1 2^7-1 27−1) |
short | 16 | -32768( − 2 15 -2^{15} −215)至32767( 2 15 − 1 2^{15}-1 215−1) |
int | 32 | –2,147,483,648( − 2 31 -2^{31} −231)至2,147,483,647( 2 31 − 1 2^{31}-1 231−1) |
long | 64 | -9,223,372,036,854,775,808( − 2 63 -2^{63} −263)至9,223,372,036,854,775,807( 2 63 − 1 2^{63}-1 263−1) |
在Java中所有的的整数类型都是以有符号的二进制补码形式表示的。即一个整型(整数类型)的Java变量占用的存储空间中,最高位的bit为符号位,用来表示数值符号(0表示正号、1表示负号)。其余的bit为数值位,以二进制补码的方式表示值。下图为byte类型的+1(上)和-1(下)在内存中的表示,其中红色部分为符号位,其余部分为数值位(注意-1使用了补码):
如整数类型定义表中所示一样byte和short分别需要使用8个和16个bit的空间进行存储,但是在Java虚拟机(Java运行环境)的定义中byte和short类型的数据,在编译器的编译期或运行期将带符号扩展为int类型数据。
1.2、浮点数类型
在Java中浮点数类型float和double,它们在概念上与IEEE 754标准中定义的32位单精度和64位双精度取值和操作是一致的。在存储空间中都是由数符位、阶码和尾数组成,它们在存储空间中占用的空间大小,如下表所示。其中阶码为被表示数值的二进制指数的移码(原码加一个固定的偏移值)表示,尾数(在IEEE 754中尾数的整数部分为1,如果不为1则通过对指数加减来移动小数点的位置,将整数部分设为1)为被表示数值的二进制原码的小数部分。
数据类型 | 数符位 | 阶码 | 尾数 | 偏移值 | 总位数(占用的存储空间) |
float | 1 | 8 | 23 | 127 | 32 |
double | 1 | 11 | 52 | 1023 | 64 |
- Java虚拟机中的浮点操作在遇到非法操作,如被零除、上限溢出、下限溢出和非精确时,不会抛出exception、trap或者IEEE 754异常情况中定义的其他信号。Java虚拟机页没有信号NaN值。
- Java虚拟机不支持IEEE 754中信号浮点比较。
- 在Java虚拟机中,舍入操作永远使用IEEE 754标准中定义的向最接近数舍入模式,无法精确表示的结果将会舍入为最接近的可表示值,如果最接近的值有两个,那就舍入到最低有效位为0的那个值。这种模式也是IEEE 754中的默认模式。不过在Java虚拟机里面,将浮点数值转化为整型数值使用向零舍入。Java虚拟机并不给出改变浮点运算舍入模式的手段。
- Java虚拟机不支持IEEE 754的单精度扩展和双精度扩展格式。
2、布尔类型
布尔类型只有两个值true(真)和false(假),在Java虚拟机中虽然定义了这种基本类型,但对其的支持非常有限,在编译器编译之后,通常会将true转换为数值1,false转换为数值0,并使用int类型来代替存储。
3、字符类型
字符类型其实本质上是一个无符号的整数类型,它是通过Unicode编码方式将数值与字符进行相对应,在取出数值之后将数值转换为最终的字符或在存储时将字符转换为相应的数值。如中国的"中"字在存储时实际存储的是数值20013,不过字符类型在存储时会进行零位扩展为int类型来代替存储。字符类型的表示范围如下表所示:
数据类型 | 占用的存储空间 | 数值表示范围 |
char | 16 | 0至65535(2^16-1) |
4、void
此类型是无意义的,仅用在方法体上,用于表示该方法没有返回值。
四、基本数据类型运算的特殊情况
本段用于罗列基本数据类型在运算过程中可能出现的特殊的容易忽视的情况。
1、整数类型的运算和转换
在Java中进行整数运算时,如果运算结果超出该类型表达的最大范围则会计算出错,下面这段代码的运算结果为:2147483647。
int i = -2147483648;
System.out.println(i-1);
这是因为整数是以二进制补码(负数的数值位为数值的绝对值的二进制取反+1)的形式进行表示的,当进行运算的时候数值的符号位也会参与运算。以上代码的运算就会变为下图所示:
其中左边红色的部分为超过int存储空间的部分,会被舍去。所以运算的最终结果为0111 1111 1111 1111 1111 1111 1111 1111,转换为十进制即2147483647。
2、浮点数精度问题
下面这段代码的输出结果为:0.9,好像没什么不对本来就应该为0.9,但真的这样吗?
float f = 0.9F;
System.out.println(f);
0.9的二进制表示为:0.111001100110011…无限循环下去,是二进制不能表示的,根据IEEE 754规范标准中的定义将0.9的二进制转换为1.11001100110011…* 2 − 1 2^{-1} 2−1那么这个值在内存中的表示应该为0011 1111 0110 0110 0110 0110 0110 0110,多于尾数空间的小数部分会被舍去。所以在存储空间中存储的值是接近于0.9的值并不是0.9。为什么最终结果会显示为0.9呢?这是因为在Java浮点数中会对无法精确表示的浮点数进行舍入到最接近的数值,所以最终展示效果为0.9但存储和计算并不是使用的0.9,所以当需要进行精确计算时,我们应该使用其他可以精确计算的方案,而不是使用浮点数,不然可能会发生意料之外的计算结果。
3、浮点数运算的特殊结果
在Java运算中任意正数除以零结果为正无穷,任意负数除以零为负无穷,零除以零为NaN。它们的表示分别如下表所示:
形式 | 符号位 | float类型下指数 | double类型下指数 | 小数部分 |
---|---|---|---|---|
正无穷 | 0 | 127 | 1023 | 0 |
负无穷 | 1 | 127 | 1023 | 0 |
NaN | 0 | 127 | 1023 | 非零 |
五、各数据类型在数组中表示
数组是一组连续存储相同数据类型数据的空间,在数组中byte和short类型不会在编译期间隐性的转换为int类型,而是保留其本身类型和存储占用空间。如下图为存储两个byte类型数据的数组,共占用16个bit。
在数组中boolea类型也同样不会隐性的转换为int类型,但因为Java虚拟机对其的支持较少,通常会被隐式的转换为byte类型的数组。
参考书籍:
《Java虚拟机规范(Java SE 8版)》
《计算机组成原理》
其他参考
百度百科:IEEE 754