Int和Integer有什么区别

Java语言虽然号称一切都是对象,但是原始数据类型除外。Java有8个原始数据类型(primitive type),那就是boolean,int,short,long,char,float,double,byte。Integer是int的包装类,内部有一个private final int value,并且内部提供了基本操作,比如数学运算,int和字符串之间的转换。更重要的是从Java 5中引用了自动装箱/自动拆箱(boxing/unboxing)的操作,Java可以根据上下文,自动的进行转换,极大的简化了相关编程。关于Integer值的缓存,这涉及到Java 5另外一个改进。构建Integer对象的传统方式是直接new一个对象,但是要注意的是new一个对象并没有用到Integer值的缓存。根据实践,我们发现大部分的数据操作都是集中在一个有限的,较小的数值范围内。所以Java 5中新增了静态工厂方法valueOf()方法,在调用这个静态工厂方法的时候,会利用一个缓存机制,带来了明显的性能改进。根据Java Doc,这个缓存的范围在-128~127。

既然这里说到Integer的缓存机制,就展开一下。下面是Integer的源码中关于缓存的实现代码

//从这里可以看出调用valueOf的时候,首先会先判断要转化的i是否在缓存的范围内,也就是是否
//在low和high之间,如果在,并不用直接创建对象,而是从cache这个数组中取值
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 final Integer[] cache;
        static Integer[] archivedCache;

        static {
            // high value may be configured by property
            int h = 127;
            //从JVM参数中获取参数high
            String integerCacheHighPropValue =
                VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
            if (integerCacheHighPropValue != null) {
                try {
                    //比较获取的参数值和127比较,确保最后设置的值是最大的
                    h = Math.max(parseInt(integerCacheHighPropValue), 127);
                    // Maximum array size is Integer.MAX_VALUE
                    //public static final int   MAX_VALUE = 0x7fffffff;
                    //我在Eclipse中打印出2147483647,也就是说h最大是0x7ffffffff+127
                    h = Math.min(h, Integer.MAX_VALUE - (-low) -1);
                } catch( NumberFormatException nfe) {
                    // If the property cannot be parsed into an int, ignore it.
                }
            }
            high = h;

            // Load IntegerCache.archivedCache from archive, if possible
            VM.initializeFromArchive(IntegerCache.class);
            int size = (high - low) + 1;

            // Use the archived cache if it exists and is large enough
            if (archivedCache == null || size > archivedCache.length) {
                Integer[] c = new Integer[size];
                int j = low;
                for(int i = 0; i < c.length; i++) {
                    c[i] = new Integer(j++);
                }
                archivedCache = c;
            }
            cache = archivedCache;
            // range [-128, 127] must be interned (JLS7 5.1.7)
            assert IntegerCache.high >= 127;
        }

        private IntegerCache() {}
    }

接下来我们理解一下Java的自动装箱和自动拆箱行为:

自动装箱实际上算是一种语法糖,可以简单理解为Java平台为我们自动进行了一些转换,保证不同的写法在运行时的结果是一致的,也就是说装箱发生在编译阶段,生成的字节码文件是一样的。Javac替我们把自动装箱替换为Integer.valueOf(),自动拆箱替换为integer.intValue()。这中缓存机制并不是Integer特有的,而是包装类都有这种机制。比如:

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

Short同样是缓存了-128~127的数值

Byte因为数值有限,所以全都被缓存

Character缓存范围是'\u0000'到'\u007F'

自动装箱和自动拆箱看起来很牛逼的样子,在编程实践中,我们有什么要注意的吗?

原则上应该避免无意中的自动装箱/自动拆箱的行为,尤其是在性能敏感的场合,创建10万个java对象和10万个int类型花费的开销并不是一个数量级的,不管是内存使用还是处理速度,光是对象头的内存空间就已经是数量级的差距了。

这里要扩展一下对象的内存结构:

对象在内存的存储结构分为三部分:对象头(object head),实例数据(instance data),填充数据(padding)。

更加详细的内容,请参考这篇博客           https://www.jianshu.com/p/0a63a4d22765

最后思考一下原始数据类型是线程安全的吗?

字符串是不可变的,Integer类的属性value被定义成final类型,同样是不可变的,而是还是private属性,这样就保证了基本的信息安全和并发编程中的线程安全。但是对于原始数据类型,显然我们要使用并发相关手段才能保证线程安全。如果有线程安全的计算需要,建议使用类似的AtomicInteger, AtomicLong这样的线程安全类。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值