原始数据类型及其包装类

原始数据类型

boolean、byte、short、int、long、float、double和char是Java的8个原始数据类型(Primitive Type)。

包装类

Java的8个原始数据类型对应的包装类是:Boolean、Byte、Short、Int、Long、Float、Double和Character。

包装类是一个不可变类

  • class用final来声明。

  • 用一个final成员变量来保存对应的原始数据类型的值。

public final class Integer {
    private final int value;

    public Integer(int value) {
        this.value = value;
    }
}
1
2
3
4
5
6
7

包装类的缓存池

从Java5起,每个包装类的内部都有一个缓存池,它会预先构建出常用数据范围的包装类对象。然后,包装类对外提供静态方法valueOf(),调用者可调用此方法来获取对应值的包装类对象,而无需自己直接调用包装类的构造器来new对象。

包装类的缓存实现得益于以下几点:

  • 包装类对象是不可变的,可以很轻松地实现复用的。

  • 根据实践发现,大部分数据操作都是集中在有限的、较小的数值范围。

Integer包装类的缓存池IntegerCache为例,展示包装类的缓存池。

public final class Integer {
    private static class IntegerCache {
        static final int low = -128;
        static final int high;
        static final Integer cache[];
    
        static {
            // high值默认为127,可通过JVM配置来设置,但其最终值范围为[127, Integer.MAX_VALUE - 129]
            int h = 127;
            String integerCacheHighPropValue =
                sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
            if (integerCacheHighPropValue != null) {
                try {
                    int i = parseInt(integerCacheHighPropValue);
                    i = Math.max(i, 127);
                    h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
                } catch( NumberFormatException nfe) {
                }
            }
            high = h;

            cache = new Integer[(high - low) + 1];
            int j = low;
            for(int k = 0; k < cache.length; k++)
                cache[k] = new Integer(j++);
            
            assert IntegerCache.high >= 127;
        }
    
        private IntegerCache() {}
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

各包装类的缓存池默认的缓存情况如下:

类型 缓存范围
Boolean Boolean.TRUE和Boolean.FALSE
Byte -127 ~ 128
这也是Byte的全部取值范围
Short -127 ~ 128
Int -127 ~ 128
可通过JVM配置来修改最大值,如-XX:AutoBoxCacheMax=256
Long -127 ~ 128
Float 无缓存
Double 无缓存
Character '\u0000 ~ '\u007F'

自动装箱/拆箱

Java5引入了自动装箱与拆箱功能,Java编译器可根据上下文实现原始数据类型与对应包装类对象之间的自动转换。

  • 装箱是将原始数据类型值装换成对应的包装类对象。

  • 拆箱是将包装类对象转换成对应的原始数据类型值。

Integer integer = 1;
int i = integer;
1
2
Integer integer = Integer.valueOf(1);
int i = integer.intValue;
1
2

避免无意中的装箱和拆箱行为,尤其是在性能敏感的场合,如创建10万个Java对象和10万个整数的开销不是一个数量级,不管是内存使用还是处理速度,仅仅是对象头的空间占用就已经是数量级的差距。