原始数据类型#
int 和 Integer 有什么区别?谈谈Integer的值缓存范围。

典型回答

int,整型数字,是Java的8个基本类型之一

Integer是int对应的包装类,它有一个int类型的字段存储数据,并且提供了基本操作,比如数学运算,int和字符串之间的转换等。在Java5中,引入了自动装箱和自动拆箱功能,Java可以根据上下文,自动进行转换,极大地简化了相关编程。

关于Integer的值缓存,在Java5中新增了静态工厂方法valueOf,在调用它的时候会利用一个缓存机制,默认缓存是在 [-128,127]

考点分析

自动装箱,自动拆箱机制,源码分析等

知识扩展

  1. 理解自动装箱,自动拆箱

什么是自动装箱和拆箱

自动装箱就是 Java 自动将原始类型值转换成对应的对象,比如将 int 的变量转换成 Integer 对象,这个过程叫做装箱,反之将 Integer 对象转换成 int 类型值,这个过程叫做拆箱。因为这里的装箱和拆箱是自动进行的非人为转换,所以就称作为自动装箱和拆箱。参考文章

自动装箱实际上算是一种 语法糖。什么是语法糖?可以简单理解为 Java 平台为我们自动进行了一些转换,保证不同的写法在运行时等价,它们发生在编译阶段,也就是生成的字节码是一致的。

像前面提到的整数,javac 替我们自动把装箱转换为 Integer.valueOf(),把拆箱替换为 Integer.intValue(),这似乎这也顺道回答了另一个问题,既然调用的是 Integer.valueOf,自然能够得到缓存的好处啊。

  1. ArrayList<Integer> intList = new ArrayList<Integer>();
  2. intList.add(1); //autoboxing - primitive to object
  3. intList.add(2); //autoboxing
  4. ThreadLocal<Integer> intLocal = new ThreadLocal<Integer>();
  5. intLocal.set(4); //autoboxing
  6. int number = intList.get(0); // unboxing
  7. int local = intLocal.get(); // unboxing in Java
  8. // 编程时,需要注意到这一点,正确地声明变量类型,避免因为自动装箱引起的性能问题。
  9. Integer sum = 0;
  10. for(int i=1000; i<5000; i++){
  11. sum+=i;
  12. }
  13. int result = sum.intValue() + i;
  14. Integer sum = new Integer(result);

缓存上限值实际是可以根据需要调整的,JVM 提供了参数设置:
-XX:AutoBoxCacheMax=N

  1. 源码分析

成员变量,不可变类型

  1. 原始类型线程安全
  • 原始数据类型的变量,显然要使用并发相关手段,才能保证线程安全,这些我会在专栏后面的并发主题详细介绍。如果有线程安全的计算需要,建议考虑使用类似 AtomicInteger、AtomicLong 这样的线程安全类。
  • 特别的是,部分比较宽的数据类型,比如 float、double,甚至不能保证更新操作的原子性,可能出现程序读取到只更新了一半数据位的数值!
  1. Java原始数据类型和引用类型局限性
  • 原始数据类型和Java泛型并不能配合使用:这是因为 Java 的泛型某种程度上可以算作伪泛型,它完全是一种编译期的技巧,Java 编译期会自动将类型转换为对应的特定类型,这就决定了使用泛型,必须保证相应类型可以转换为 Object。
  • 无法高效的表达数据,也不便于表达复杂的数据结构

课后练习

对象的内存结构是什么样的?比如对象头的结构。如何计算或者获取某个对象的大小?

评论学习

[JKID-Kyle]
节选自《深入理解JAVA虚拟机》:
在HotSpot虚拟机中,对象在内存中存储的布局可以分为3块区域:对象头(Header)、实例数据(Instance Data)和对齐填充(Padding)。

HotSpot虚拟机的对象头包括两部分信息,第一部分用于存储对象自身的运行时数据,如哈希码(HashCode)、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等,这部分数据的长度在32位和64位的虚拟机(未开启压缩指针)中分别为32bit和64bit,官方称它为”Mark Word“。

对象头的另外一部分是类型指针,即对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例。并不是所有的虚拟机实现都必须在对象数据上保留类型指针,换句话说,查找对象的元数据信息并不一定要经过对象本身,这点将在2.3.3节讨论。另外,如果对象是一个Java数组,那在对象头中还必须有一块用于记录数组长度的数据,因为虚拟机可以通过普通Java对象的元数据信息确定Java对象的大小,但是从数组的元数据中却无法确定数组的大小。

接下来的实例数据部分是对象真正存储的有效信息,也是在程序代码中所定义的各种类型的字段内容。无论是从父类继承下来的,还是在子类中定义的,都需要记录起来。

第三部分对齐填充并不是必然存在的,也没有特别的含义,它仅仅起着占位符的作用。由于HotSpot VM的自动内存管理系统要求对象起始地址必须是8字节的整数倍,换句话说,就是对象的大小必须是8字节的整数倍。