Java虽然号称是面向对象的语言,但是原始数据类型仍然是重要的组成元素,所以在面试中,经常考察原始数据类型和包装类等Java语言特性
int和Integer有什么区别?谈谈Integer的值缓存范围
常规回答:
int是整形数字,是Java的8个原始数据类型(Primitive Types, boolean、 byte 、 short、 char、 int、 foat、 double、 long)之一。 Java语言虽然号称一切都是对象,但原始数据类型是例外。
关于Integer的值缓存,在Java 5之前。构建Integer对象的传统方式是直接调用构造器,直接new一个对象。但是实践发现,我们发现大部分数据操作都是集中在有限的、较小的数值范围;
因而,在Java 5中新增了静态工厂方法valueOf,在调用它的时候会利用一个缓存机制,带来了明显的性能改进。按照Javadoc, 这个值默认缓存是-128到127之间。
知识扩展
1.理解自动装箱、拆箱
自动装箱实际上算是一种语法糖。什么是语法糖?简单理解为Java平台为我们自动进行了一些转换,保证不同的写法在运行时等价,它们发生在编译阶段,也就是生成的字节码是一致的。
javac替我们自动把装箱转换为Integer.valueOf(),把拆箱替换为Integer.intValue() ,这似乎这也顺道回答了另一个问题,既然调用的是Integer.valueOf,
自然能够得到缓存的好处啊。
如何程序化的验证上面的结论呢?
我们写一段简单的程序包含下面两句代码,然后反编译一下
Integer integer = 1;
int unboxing = integer ++;
反编译输出:
1: invokestatic #2
// Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
8: invokevirtual #3 // Method java/lang/Integer.intValue:()I
这种缓存机制并不是只有Integer才有,同样存在于其他的一些包装类,比如:
Boolean,缓存了true/false对应实例,确切说,只会返回两个常量实例Boolean.TRUE/FALSE。
Short,同样是缓存了-128到127之间的数值。
Byte,数值有限,所以全部都被缓存。
Character,缓存范围'\u0000' 到 '\u007F'。
在编程实践中,有什么需要注意的吗?
建议避免无意中的装箱、拆箱行为,尤其是在性能敏感的场合,创建10万个Java对象和10万个整数的开销可不是一个数量级的,不管是内存使用还是处理速度,光是对象头的空间占用就已经是数量级的差距了。
Integer的缓存范围虽然默认是-128到127,但是在特别的应用场景,比如我们明确知道应用会频繁使用更大的数值,这时候应该怎么办呢?
缓存上限值实际是可以根据需要调整的, JVM提供了参数设置:
-XX:AutoBoxCacheMax=N
分析字符串的设计实现时,提到过字符串是不可变的,保证了基本的信息安全和并发编程中的线程安全。如果你去看包装类里存储数值的成员变量“value”,你会发现,
不管是Integer还Boolean等,都被声明为“private fnal”,所以,它们同样是不可变类型!
原始类型线程安全
前面提到了线程安全设计,你有没有想过,原始数据类型操作是不是线程安全的呢?
这里可能存在着不同层面的问题:
原始数据类型的变量,显然要使用并发相关手段,才能保证线程安全,这些我会在专栏后面的并发主题详细介绍。如果有线程安全的计算需要,建议考虑使用类
似AtomicInteger、 AtomicLong这样的线程安全类。
特别的是,部分比较宽的数据类型,比如foat、 double,甚至不能保证更新操作的原子性,可能出现程序读取到只更新了一半数据位的数值!