Java引进了八个基本类型,在执行效率以及内存使用两方面提升软件性能。
Java虚拟机的boolean类型
在Java语言规范中,boolean类型的值只有两种可能,分别用符号”true”和”false”来表示。这两个符号是不能被虚拟机直接使用的。
在Java虚拟机规范中,boolean类型则被映射成int类型。”true”被映射为整数1,”false”被映射为整数0。这个编码规则约束了Java字节码的具体实现:如对于存储boolean数组的字节码,Java虚拟机需要保证实际存入的值是整数1或者0。
Java虚拟机规范同时要求Java编译器遵守这个编码规则,并且用整数相关的字节码来实现逻辑运算,以及基于boolean类型的条件跳转。在编译而成的class文件中,除了字段和传入参数外,基本看不出boolean类型的痕迹。
对于Java虚拟机来说,它看到的boolean类型,早已被映射为整数类型。将原本声明为boolean类型的局部变量,赋值为除了0、1之外的整数值,在Java虚拟机看来是合法的。
Java的基本类型
Java的基本类型还包括整数类型byte、short、char、int和long,以及浮点类型float和double。
类型 | 字节 | 位 | 值域 | 默认值 | 虚拟机内部符号 |
---|---|---|---|---|---|
boolean | {false,true} | false | Z | ||
byte | 1byte | 8bit | [-128,127] | 0 | B |
short | 2byte | 16bit | [-32768,32767] | 0 | S |
char | 2byte | 16bit | [0,65525] | ‘\u0000’ | C |
int | 4byte | 32bit | [-2^31,2^31-1] | 0 | I |
long | 8byte | 64bit | [-2^63,2^63-1] | 0L | J |
float | 4byte | 32bit | ~[-3.4E38,3.4E38] | +0.0F | F |
double | 8byte | 64bit | ~[-1.8E308,1.8E308] | +0.0D | D |
基本类型从上到下的值域依次扩大,而且前面的值域被后面的值域所包含。从前面的基本类型转换至后面的基本类型,无需强制转换。尽管默认值看起来不一样,但在内存中都是0。
boolean和char是唯二的无符号类型。在不考虑违反规范的情况下,boolean类型的取值范围是0或者1。char类型的取值范围则是[0,65535]。通常可以认定char类型的值为非负数。这种特性可以用作数组索引。
声明为boolean、byte、char、short的局部变量,能够存储超出它们取值范围的数值。
Java浮点类型采用IEEE754浮点数格式。以float为例,浮点类型通常有两个0,+0.0F以及-0.0F。前者在Java里是0,后者是符号为1、其他位均为0的浮点数,在内存中等同于十六进制整数0x8000000(即-0.0F可通过Float.intBitsToFloat(0x8000000)求得)。它们的内存数值不同,但是在Java中+0.0F==-0.0F会返回真。
正无穷是任意正浮点数(不包括+0.0F)除以+0.0F得到的值;负无穷是任意正浮点数除以-0.0F得到的值。在Java中,正无穷和负无穷是有确切的值,在内存中分别等同于十六进制整数0x7F800000和0xFF800000。
[0x7F800001,0x7FFFFFFF]和[0xFF800001,0xFFFFFFFF]对应的都是NaN(Not-a-Number)。一般计算得出的NaN,如+0.0F/+0.0F=0x7FC00000。称之为标准的NaN,其他的称之为不标准的NaN。NaN:除了”!=”始终返回true之外,所有其他比较结果都会返回false。
NaN<1.0F返回false,NaN>=1.0F返回false。对于任意浮点数f(包括0,NaN),f!=NaN始终返回true,f==NaN始终返回false。
Java基本类型的大小
Java虚拟机每调用一个Java方法,便会创建一个栈帧。以解释器使用的解释栈帧(interpreted frame)为例。
这种栈帧有两个主要的组成部分,分别是局部变量区、字节码的操作数栈。局部变量是广义的,除了普遍意义下的局部变量之外,还包含实例方法的”this指针”以及方法所接受的参数。
存储-掩码操作
在Java虚拟机规范中,局部变量区等价于一个数组,并且可以用正整数来索引。除了long、double值需要用两个数组单元来存储之外,其他基本类型以及引用类型的值均占用一个数组单元。boolean、byte、char、short这四种类型,在栈上占用的空间和int是一样的,和引用类型也是一样的。在32为的HotSpot中,这些类型在栈上将占用4个字节(一个数组单位4字节);在64位的HotSpot中,将占8个字节(一个数组单位8字节)。
变长数组不好控制,浪费一些空间转换成定长,以便访问时直接通过下标来计算地址。
存储于堆中的字段或者数组元素上,对于byte、char、short这三种类型的字段或者数组单元,他们在堆上占用的空间分别为一字节、两字节、两字节,跟这些类型的值域相吻合(在栈上跟值域不吻合)。
将一个int类型的值,存储到这些类型的字段或者数组时,相当于做了一次隐式的掩码操作。如,把0xFFFFFFFF(-1)存储到一个声明为char类型的字段里时,由于该字段仅占两字节,所以高位的字节便会被截取掉,最终存入”\uFFFF”。(32位)
在HotSpot中,boolean字段占用一字节(存储到堆中),而boolean数组则直接用byte数组实现。为了保证堆中的boolean值是合法的,HotSpot在存储时显式的进行掩码操作,只取最后一位的值存入boolean字段或者数组中。
加载-扩展操作
Java虚拟机的算术运算几乎全部依赖于操作数栈。需要将堆中的boolean、byte、char、short加载到操作数栈上,而后将栈上的值当成int类型来运算。
其他long、double、float加载至操作数栈上,当成各自类型来计算,不用转换。
对于boolean、char这两个无符号类型,加载伴随着零扩展。char的大小为两个字节,在加载char的值会被复制到int类型的低二字节,高二字节则会用0来填充。
对于byte、short这两个类型来说,加载伴随着符号扩展。short的大小为两个字节。在加载时short的值同样会被复制到int类型的低二字节。如果该short值为非负数,即最高位为0,那么该int类型的值的高二字节会用0来填充,否则用1来填充。