一、概念

int 是我们常说的整形数字,是 Java 的 8 个原始数据类型(boolean、byte 、short、char、int、float、double、long)之一。
Integer 是 int 对应的包装类,它有一个 int 类型的字段存储数据,并且提供了基本操作,比如数学运算、int 和字符串之间转换等。在 Java 5 中,引入了自动装箱和自动拆箱功能(boxing/unboxing),Java 可以根据上下文,自动进行转换,极大地简化了相关编程。同时也新增了静态工厂方法 valueOf,在调用它的时候会利用一个缓存机制,默认为-128 到 127 之间,带来了明显的性能改进。

二、Integer 分析

Integer作为一个对象,最常用的是通过 new 方法构建Integer。但是,基于大部分数据操作都是集中在有限的、较小的数值范围,所以新增了静态工厂方法 valueOf。

其背后实现是将 int 值为 -128 到 127 之间的 Integer 对象进行缓存,在调用时候直接从缓存中获取,进而提升构建对象的性能。也就是说使用该方法后,如果两个对象的 int 值相同且落在缓存值范围内,那么这个两个对象就是同一个对象。

public static Integer valueOf(int i) {
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
    }

当值较小且频繁使用时,推荐优先使用整型池方法。

三、自动装箱、拆箱

原始数据类型和 Java 泛型并不能配合使用,于是 JAVA 就设计了这个 auto-boxing/unboxing 机制,实际上就是 primitive value 与 object 之间的隐式转换机制。否则要是没有这个机制,开发过程得多么麻烦。

自动装箱算是 Java 少见的语法糖,保证生成的字节码是一致的,javac 替我们自动把装箱转换为  Integer.valueOf(),把拆箱替换为 Integer.intValue()。同理其他的原始数据类型类似。

Integer.valueOf(xxx),其内部时 IntegerCache 的实现,底层是个 cache 常量数组,在静态初始化块中创建并缓存对象。 

private static class IntegerCache {
        static final int low = -128;
        static final int high;
        static final Integer cache[];

        static {
            // high value may be configured by property
            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);
                    // Maximum array size is Integer.MAX_VALUE
                    h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
                } catch( NumberFormatException nfe) {
                    // If the property cannot be parsed into an int, ignore it.
                }
            }
            high = h;

            cache = new Integer[(high - low) + 1];
            int j = low;
            for(int k = 0; k < cache.length; k++)
                cache[k] = new Integer(j++);

            // range [-128, 127] must be interned (JLS7 5.1.7)
            assert IntegerCache.high >= 127;
        }

        private IntegerCache() {}
    }

四、缓存

对于原始数据类型同样存在缓存机制。

Boolean,缓存了 true / false 对应实例,确切说,只会返回两个常量实例 Boolean.TRUE/FALSE。

Short,同样是缓存了 -128 到 127 之间的数值。

Byte,数值有限,所以全部都被缓存。

Character,缓存范围’\u0000’ 到 ‘\u007F’。

五、越界

基本类型均具有取值范围,所以经常会出现越界的情况。例:long result = 1234567890 * 24 * 365;结果值一定不会是你所期望的那个值,因为 1234567890 * 24 已经超过了 int 的范围,如果修改为:long result= 1234567890L * 24 * 365;就正常了。

尤其是在处理货币存储时,如采用 double 常会带来差距,常采用 BigDecimal、整型(如果要精确表示分,可将值扩大100倍转化为整型)解决该问题。

六、线程

如果有线程安全的计算需要,建议考虑使用类型 AtomicInteger、AtomicLong 这样的线程安全类。特别的是,部分比较宽的数据类型,比如 float、double,甚至不能保证更新操作的原子性,可能出现程序读取到只更新了一半数据位的数值!