8种基本类型

image.png

浮点数

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 虚拟机规范中,局部变量区等价于一个数组,并且可以用正整数来索引。除了 long、double 值需要用两个数组单元来存储之外,其他基本类型以及引用类型的值均占用一个数组单元。
也就是说,boolean、byte、char、short 这四种类型,在栈上占用的空间和 int 是一样的,和引用类型也是一样的。因此,在 32 位的 HotSpot 中,这些类型在栈上将占用 4 个字节;而在 64 位的 HotSpot 中,他们将占 8 个字节。当然,这种情况仅存在于局部变量,而并不会出现在存储于堆中的字段或者数组元素上。对于 byte、char 以及 short 这三种类型的字段或者数组单元,它们在堆上占用的空间分别为一字节、两字节,以及两字节,也就是说,跟这些类型的值域相吻合。
image.png

字段和 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 来填充。

测试:设置布尔类型为整数(借助Unsafe)

  1. public class BooleanTest {
  2. boolean boolValue;
  3. private static final long valueOffset;
  4. private static final Unsafe unsafe = getUnsafe();
  5. static {
  6. try {
  7. valueOffset = unsafe.objectFieldOffset
  8. (BooleanTest.class.getDeclaredField("boolValue"));
  9. Field field = BooleanTest.class.getDeclaredField("boolValue");
  10. long offset = unsafe.objectFieldOffset(field);
  11. System.out.println(offset);
  12. System.out.println(valueOffset);
  13. } catch (Exception ex) { throw new Error(ex); }
  14. }
  15. public static void main(String[] args) {
  16. BooleanTest test = new BooleanTest();
  17. unsafe.putByte(test, valueOffset, (byte)2);
  18. //unsafe.putByte(test, valueOffset, (byte) 3);
  19. //unsafe.putBoolean(test, valueOffset, false);
  20. if (test.boolValue) System.out.println("Hello, Java!");
  21. if (test.boolValue == true) System.out.println("Hello, JVM!");
  22. }
  23. public static Unsafe getUnsafe() {
  24. try {
  25. Class<?> unsafeClass = Class.forName("sun.misc.Unsafe");
  26. Field field = unsafeClass.getDeclaredField("theUnsafe");
  27. field.setAccessible(true);
  28. Unsafe unsafe = (Unsafe) field.get(null);
  29. return unsafe;
  30. } catch (Exception e) {
  31. e.printStackTrace();
  32. }
  33. return null;
  34. }
  35. }

输出:Hello,Java

test.boolValue在内存里已经被修改为2,因此不等于true(编译后为1)