7、int 和 Integer有什么区别?

1、数据类型

iint是基本数据类型,保存在栈中;Integer是引用类型,在堆中申请内存存放数据,在栈里申明引用指向堆中的内存地址。

基本数据类型在栈中怎么存储?

int a = 10;

int b = 10;

JVM有一个局部变量表用来保存引用,如上,建立引用a和引用b,再申请两块内存来存放数据,由引用a和引用b指向它们的内存地址。在Java中, "=="符号是用于比较引用的值,这话没毛病。

那引用类型在堆中怎么存储?

Java普通对象在HotSpot虚拟机Java堆中的内存布局大致可分为3个区域:对象头(Header)、实例数据(Instance)和对齐填充(Padding)。

对象头分为两部分:

Mark Word:用于存储对象自身的运行时数据,长度为32bit或者64bit,同时Mark Word被设计成一个有着动态定义的数据结构,以便在极小的空间内存储尽量多的数据,会根据对象的状态复用自己的存储空间 。

类型指针:记录指向它的类型元数据的指针,如果是数组对象,还需要记录它的数组长度。

实例数据:

对象存储有效信息的位置

对其填充:

HotSpot虚拟机要求对象大小必须是8字节的整数倍; So,我们需要把它补齐。

2、Integer和int的关系

Integer是int的包装类,java有8个基本数据类型:byte、int、long、float、double、boolean、char,同时也会有8个包装类。

我们将int转Integer就是装箱,从Integer转int就是拆箱

装箱/拆箱又分为自动和手动,下面是例子:

public class MainTest {
public static void main(String[] args) {
// 手动装箱
Integer c = new Integer(10);
// 自动装箱
Integer d = 10;
// 手动拆箱
int e = c.intValue();
// 自动拆箱
int f = c;
}
}

我们将上述的源代码进行编译后,使用jd-gui工具进行反编译,得到如下结果:

public class MainTest
{
public static void main(String[] paramArrayOfString)
{
Integer localInteger1 = new Integer(10);
Integer localInteger2 = Integer.valueOf(10);
int i = localInteger1.intValue();
int j = localInteger1.intValue();
}
}

我们会得到如下结论:

自动装箱时编译器会调用valueOf(XX)的方法来量基本数据类型转化为对象;

自动拆箱时编译器会调用intValue、doubleValue等方法将对象装换到基本数据类型。

3、缓存池

valueOf(XX)内部是由玄机的,它有一个缓存池,我们先来看下IntegerCache内部类:

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() {}
}

IntegerCache内部的组成不多,我们来看下:

定义了缓存的下限;

static final int low = -128;

定义缓存的上限引用,没有赋值;

static final int high;

定义缓存存放的数据,我们可以看到,缓存就是一个普通的Integer数组;

static final Integer cache[];

我们能看到静态初始语句由如下功能:

先给缓存的上限(high)赋值初始值,但这个值可以被后续读取到配置属性覆盖

从JVM配置中获取缓存上限的值(integerCacheHighPropValue)

选择上限值,从integerCacheHighPropValue,high,Integer.MAX_VALUE - (-low) -1三者选择

h+(-low)+1不能超过Integer.MAX_VALUE

初始化Integer数组

给Integer数组赋初始值

static {
// 1. 先给缓存的上限(high)赋值初始值,但这个值可以被后续读取到配置属性覆盖
int h = 127;
// 2. 从JVM配置中获取缓存上限的值
String integerCacheHighPropValue =
sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
// 3. 选择上限值,从integerCacheHighPropValue,h,Integer.MAX_VALUE - (-low) -1三者选择
if (integerCacheHighPropValue != null) {
try {
int i = parseInt(integerCacheHighPropValue);
i = Math.max(i, 127);
// h+(-low)+1不能超过Integer.MAX_VALUE
h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
} catch( NumberFormatException nfe) {
}
}
high = h;
// 4. 初始化Integer数组
cache = new Integer[(high - low) + 1];
int j = low;
// 5. 给Integer数组赋初始值
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);
// 6. 断言判断缓存的上限
assert IntegerCache.high >= 127;
}

我们再来看下valueOf(XX)方法,我们就明白了;

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

总结来说: Integer类型的缓存就是一个Integer数组,下限的值为-128(写死),上限的值默认为127,不过我们可以通过配置JVM参数来修改它,只有调用valueOf(XX)方法才能会经过缓存池,也就是手动调用valueOf(XX)或者自动装箱。

缓存上限值是根据需要调整的,JVM 提供了如下参数:

-XX:AutoBoxCacheMax=N