Java基本类型在 JVM 中的实现

Java 的基本类型

类型 值域 默认值 虚拟机内部符号
boolean {false,true} false Z
byte [-128,127] 0 B
short [-32768,32767] 0 S
char [0,65535] ‘\u0000’ C
int [-2^31, 2^31-1] 0 I
long [-2^63, 2^63-1] 0 J
float ~[-3.4E38,3.4E38] +0.0F F
double ~[-1.8E308,1.8E308] +0.0D D

从表格中可以看到除boolean和char外,从byte到double值域依次扩大,而且前面的值域被后面的值域包含。因此,从前面的基本类型准换到后面的基本类型无需强转,另外,即便他们的默认值看起来是不一样的,但是在内存中都是0。

关于 Java 虚拟机的boolean 类型

在 Java 语言规范中,boolean 类型的值只有两种可能,分别是 “true” 和 “false” 来表示,但是这两个符号是不能被虚拟机直接使用的

在 Java 虚拟机规范中,boolean 类型被映射成 int 类型。即 “true” 映射为 1,而 “false” 被映射为 0。这个编码规则约束了 Java 字节码的具体实现。

public class Demo {
	public static void main(String[] args) {
		boolean 你咋样 = 2; // 直接编译 javac 显然会报错
		if (你咋样) {
			Sysetm.out.println("好");
		}
		if (true == 你咋样) {
			System.out.println("不好");
		}
	}
}

以上代码直接编译自然是会报错,如果采用一个 Java 字节码的会变工具,直接堆字节码进行更改,如果 boolean 变量的值是 2 是,它究竟是 true 还是 false。通过汇编打印后发现,第一个判断会打印好,而第二个却不会。

例如,对于存储 boolean 数组的字节码,Java 虚拟机需保证实际存入的值是整数 1 或 0 。同时也要求 Java 编译器遵守这个编码规则,并且用整数相关的字节码来实现逻辑运算,以及基于 boolean 类型的条件跳转。这样一来,在编译而成的 class 文件中,除了字段和传入参数外,基本看不出 boolean 类型的痕迹。

# Demo.main 编译后的字节码
  iconst_2       // 用 AsmTools 更改了这一指令
  istore_1
  iload_1
  ifeq 14        // 第一个 if 语句,即操作数栈上数值为 0 时跳转
  getstatic java.lang.System.out
  ldc " 好 "
 invokevirtual java.io.PrintStream.println
 iload_1
 iconst_1
 if_icmpne 27   // 第二个 if 语句,即操作数栈上两个数值不相同时跳转
 getstatic java.lang.System.out
 ldc " 不好 "
 invokevirtual java.io.PrintStream.println
 return

可以看到,Java 编译器的确遵守了相同的编码规则。因此,对于 Java 虚拟机来说,它看到的 boolean 类型,早已被映射成为整数类型。因此,将原本声明为 boolean 类型的局部变量,赋值除了 0、1 之外的整数值,在 Java 虚拟机来看是“合法的”。

声明为基本类型的局部白能力能否存储超出其范围的值?

可以,但会出现一些问题,比如声明为 char 类型的局部变量实际上是有可能为负数。当然,在正常使用的 idea之类的 Java 编译器生成的字节码会遵守 Java 虚拟机规范对编译器的约束。

Java 的浮点类型采用 IEEE 754 浮点数格式。以 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)除以 +0.0F 得到的值,而负无穷是任意正浮点数除以 -0.0F 得到的值。在 Java 中,正无穷和负无穷是有确切的值,在内存中分别等同于十六进制整数 0x7F800000 和 0xFF800000。

既然整数 0x7F800000 等同于正无穷,那么 0x7F800001 又对应什么浮点数呢?对应的浮点数是 NaN(Not-a-Number)。

不仅如此,[0x7F800001, 0x7FFFFFFF] 和 [0xFF800001, 0xFFFFFFFF] 对应的都是 NaN。当然,一般我们计算得出的 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 方法便会创建一个栈帧。以解释器使用的解释栈帧为例—这种栈帧由两个主要部分组成,分别是局部变量区以及字节码的操作栈。这里的局部变量是广义的,还包含实例方法的 “this”以及方法所接收的参数。

    在 Java 虚拟机规范中,局部变量区等价于一个数组,并且可以用正整数来索引。除了long、double值需要使用两个数组单元来存储之外,其他基本类型以及引用类型的值均占用一个数组单元。即:boolean、byte、char、short 这四种类型,在栈上占用的空间和 int 是一样的,和引用类型也是一样的。因此,在 32 位的HotSpot中,这些类型在栈上将占用 4 个字节;而在 64 位的 HotSpot 中,他们占 8 个字节。

    这些仅存在于局部变量,并不会出现在存储于堆中的字段或者数组元素。对于 byte、char、short这三种类型的字段或数组单元,在堆上占用的空间分别为一字节、两字节、两字节,也就是跟这些类型的值域相吻合。

    因此,将一个 int 类型的值,存储到这些类型的字段或者数组中,相当于做了一次隐式的掩码操作。如,当我们把 0xFFFFFFFF(-1)存储到一个声明为 char 类型的字段里时,由于该字段仅占两字节,所以高两位的字节便会被截取掉,最终存入“\uFFFF”。

    boolean 字段和 boolean 数组则比较特殊。在 HotSpot 中,boolean 字段占用一字节,而 boolean 数组则直接用 byte 数组来实现。为了保证堆中的 boolean 值是合法的,HotSpot 在存储时显式地进行掩码操作,也就是说,只取最后一位的值存入 boolean 字段或数组中。

  • 从加载来看

    Java 虚拟机的算数运算几乎全部依赖于操作数栈。也就是说,我们需要将堆中的 boolean、byte、char 以及 short 加载到操作数栈上,而后将栈上的值当成 int 类型来运算。

    对于 boolean、char 这两个无符号类型来说,加载伴随着零扩展。举个例子,char 的大小为两个字节。在加载时 char 的值会被复制到 int 类型的低二字节,而高二字节则会用 0 来填充。

    对于 byte、short 这两个类型来说,加载伴随着符号扩展。举个例子,short 的大小为两个字节。在加载时 short 的值同样会被复制到 int 类型的低二字节。如果该 short 值为非负数,即最高位为 0,那么该 int 类型的值的高二字节会用 0 来填充,否则用 1 来填充。

小结

  • Java 里的基本类型
    boolean 类型在 Java 虚拟机中被映射为整数类型:“true”被映射为 1,而“false”被映射为 0。Java 代码中的逻辑运算以及条件跳转,都是用整数相关的字节码来实现的。

    另外 7 个基本类型。它们拥有不同的值域,但默认值在内存中均为 0。这些基本类型之中,浮点类型比较特殊。基于它的运算或比较,需要考虑 +0.0F、-0.0F 以及 NaN 的情况。

    除 long 和 double 外,其他基本类型与引用类型在解释执行的方法栈帧中占用的大小是一致的,但它们在堆中占用的大小不同。在将 boolean、byte、char 以及 short 的值存入字段或者数组单元时,Java 虚拟机会进行掩码操作。在读取时,Java 虚拟机则会将其扩展为 int 类型。

猜你喜欢

转载自blog.csdn.net/qq_40488936/article/details/106339747
今日推荐