数据类型


一、基本数据类型

Java 中有 8 种基本数据类型,分别为:

  • 6 种数字类型:
    • 4 种整数型:byteshortintlong
    • 2 种浮点型:floatdouble
  • 1 种字符类型:char
  • 1 种布尔型:boolean

这 8 种基本数据类型的默认值以及所占空间的大小如下:

基本类型 位数 字节 默认值 取值范围
byte 8 1 0 -128 ~ 127
short 16 2 0 -32768(-2^15) ~ 32767(2^15 - 1)
int 32 4 0 -2147483648 ~ 2147483647
long 64 8 0L -9223372036854775808(-2^63) ~ 9223372036854775807(2^63 -1)
char 16 2 ‘u0000’ 0 ~ 65535(2^16 - 1)
float 32 4 0f 1.4E-45 ~ 3.4028235E38
double 64 8 0d 4.9E-324 ~ 1.7976931348623157E308
boolean 1 false true、false

为什么负值的范围比正值多一

以 int 为例

0000 0000 ~0111 1111 表示 +0 ~ +127

1111 1111 ~1000 0001 表示 -1 ~ -127

-128的表示

咦,我们好像漏了 1000 0000 这个二进制了,哈哈,到现在为止一切都解决了,其实我们人为规定 1000 0000表示十进制中的-128!当然我们不可能说通过 1000 0000这种二进制求其原码得到某一个数,因为非高位全是0,无法再借位减1了!

其实这种规定是很自然的,1000 0000 -0000 0001=0111 1111,而0111 1111 这个补码表示的是+127,我们可以将 1000 0000这个二进制作为正数和负数之间的一个分水岭(尽管1000 0000本身代表负数),1000 0000再进位就会进入负数范围,若借位就会进入正数范围,就好似1000 0000(-128)将 1000 0001(-127) ~0111 1111(+127)首尾连接了起来形成了一个闭环!!!

原码里面这个是负0,补码里面按照定义他就是-128

码长八位时,各码的范围如下。
原码:-127 ~ +127。
反码:-127 ~ +127。
补码:-128 ~ +127。

-128,没有原码反码,只有补码。

二、包装类型

基本类型和包装类型的区别?

  • 用途:除了定义一些常量和局部变量之外,我们在其他地方比如方法参数、对象属性中很少会使用基本类型来定义变量。并且,包装类型可用于泛型,而基本类型不可以。

  • 存储方式:基本数据类型的局部变量存放在 Java 虚拟机栈中的局部变量表中,基本数据类型的成员变量(未被 static 修饰 )存放在 Java 虚拟机的堆中。包装类型属于对象类型,我们知道几乎所有对象实例都存在于堆中。

  • 占用空间:相比于包装类型(对象类型), 基本数据类型占用的空间往往非常小。

  • 默认值:成员变量包装类型不赋值就是 null ,而基本类型有默认值且不是 null

  • 比较方式:对于基本数据类型来说,== 比较的是值。对于包装数据类型来说,== 比较的是对象的内存地址。所有整型包装类对象之间值的比较,全部使用 equals() 方法。

为什么说是几乎所有对象实例都存在于堆中呢? 这是因为 HotSpot 虚拟机引入了 JIT 优化之后,会对对象进行逃逸分析,如果发现某一个对象并没有逃逸到方法外部,那么就可能通过标量替换来实现栈上分配,而避免堆上分配内存

⚠️ 注意:基本数据类型存放在栈中是一个常见的误区! 基本数据类型的存储位置取决于它们的作用域和声明方式。如果它们是局部变量,那么它们会存放在栈中;如果它们是成员变量,那么它们会存放在堆/方法区/元空间中。

缓存机制

Java 基本数据类型的包装类型的大部分都用到了缓存机制来提升性能。

Byte,Short,Integer,Long 这 4 种包装类默认创建了数值 [-128,127] 的相应类型的缓存数据,Character 创建了数值在 [0,127] 范围的缓存数据,Boolean 直接返回 True or False

public static Integer valueOf(int i) {
    if (i >= IntegerCache.low && i <= IntegerCache.high)
        return IntegerCache.cache[i + (-IntegerCache.low)];
    return new Integer(i);
}
private static class IntegerCache {
    static final int low = -128;
    static final int high;
    static {
        // high value may be configured by property
        int h = 127;
    }
}

如果超出对应范围仍然会去创建新的对象,缓存的范围区间的大小只是在性能和资源之间的权衡。

两种浮点数类型的包装类 Float,Double 并没有实现缓存机制。

Integer i1 = 33;
Integer i2 = 33;
System.out.println(i1 == i2);// 输出 true

Integer i1 = 128;
Integer i2 = 128;
System.out.println(i1 == i2);// 输出 false

Float i11 = 333f;
Float i22 = 333f;
System.out.println(i11 == i22);// 输出 false

Double i3 = 1.2;
Double i4 = 1.2;
System.out.println(i3 == i4);// 输出 false

Integer i1 = 40;
Integer i2 = new Integer(40);
System.out.println(i1==i2);// 输出 false

Integer i1=40 这一行代码会发生装箱,也就是说这行代码等价于 Integer i1=Integer.valueOf(40) 。因此,i1 直接使用的是缓存中的对象。而Integer i2 = new Integer(40) 会直接创建新的对象。

自动装箱拆箱

  • Integer i = 10 等价于 Integer i = Integer.valueOf(10)
  • int n = i 等价于 int n = i.intValue();

注意:如果频繁拆装箱的话,也会严重影响系统的性能。我们应该尽量避免不必要的拆装箱操作。


  目录