主要区别
int 是 Java的8个原始数据类型(Primitive Types,boolean、byte 、short、char、int、foat、double、long)之一。Java语言虽然号称一切都是对象,
但原始数据类型是例外。
Integer是int对应的包装类,它有一个int类型的字段存储数据,并且提供了基本操作,比如数学运算、int和字符串之间转换等。在Java 5中,引入了自动装箱和自动拆箱功能
(boxing/unboxing),Java可以根据上下文,自动进行转换,极大地简化了相关编程。
关于Integer的值缓存,这涉及Java 5中另一个改进。构建Integer对象的传统方式是直接调用构造器,直接new一个对象。但是根据实践,我们发现大部分数据操作都是集中在有
限的、较小的数值范围,因而,在Java 5中新增了静态工厂方法valueOf,在调用它的时候会利用一个缓存机制,带来了明显的性能改进。按照Javadoc,这个值默认缓存
是-128到127之间。
扩展
自动拆、装箱
语法糖
以简单理解为Java平台为我们自动进行了一些转换,保证不同的写法在运行时等价,它们发生在编译阶段,也就是生成的字节码
是一致的。
Integer.valueOf()、Integer.intValue()
javac 在装、拆箱子的过程,会自动调用这两个函数,valueOf() 对应装箱。这样自然能够得到缓存的好处。
并且这样的缓存机制并不是 Integer 独有,其他包装类也有,比如:
- Boolean,缓存了true/false对应实例,确切说,只会返回两个常量实例Boolean.TRUE/FALSE。
- Short,同样是缓存了-128到127之间的数值。
- Byte,数值有限,所以全部都被缓存。
- Character,缓存范围’\u0000’ 到 ‘\u007F’。
建议
尽量避免无意的拆、装箱,创建 10 万个 java 对象和 10 个整数的开销不是一个数量级。毕竟对象中的对象头的空间就占用很大了。
使用原始数据类型、数组甚至本地代码实现等,在性能极度敏感的场景往往具有比较大的优势,用其替换掉包装类、动态数组(如ArrayList)等可
以作为性能优化的备选项。一些追求极致性能的产品或者类库,会极力避免创建过多对象。
源码
Integer它主要包括各种基础的常量,比如最大值、最小值、位数等;静态工厂方法valueOf();获取环境变量数值的方法;各种转换方法(parseInt()),比如
转换为不同进制的字符串,如8进制,或者反过来的解析方法等。
缓存部分
Integer的缓存范围虽然默认是-128到127,但是是可以调节的,jvm 参数如下:
-xx:AutoBoxCacheMax=N
我们看看 valueOf() 是怎么获取缓存的。
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
可以看出,它主要是通过 IntegerCache 来获取,我们在看看这个 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");
...
...
...
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 的静态初始化代码块里。
构造方法处
典型的构造方式如下:
private final int value;
/**
* Constructs a newly allocated {@code Integer} ... {@code Integer} object.
*/
public Integer(int value) {
this.value = value;
}
我们知道 String 是不可变的。保证了信息安全和并发线程安全。其实其他包装类,它们存储数值的 value 自动,也是声明为 “private final” 不可变性。这样在对于某些可能会轻易更改数据的场景就有了保障。
线程安全问题
部分比较宽的数据类型,比如foat、double,甚至不能保证更新操作的原子性,可能出现程序读取到只更新了一半数据位的数值!
对比引用类型的局限性
原始数据类型不能和 java 泛型配合使用
Java 的泛型某种程度上可以算作伪泛型,它完全是一种编译期的技巧,Java编译期会自动将类型转换为对应的特定类型,这就决定了使用泛型,必须保证相应类型可以转换为 Object,而原始数据类型就要使用装箱操作。
无法高效的表达数据,也不能表达复制的数据结构
Java的对象都是引用类型,一个原始数据类型数组,它在内存里是一段连续的内存,而对象数组存储的是引用,对象往往是分散地存储在堆的不同位置。这种设计虽然带来了极大灵活性,结果导致了数据操作的低效,CPU缓存机制无法充分利用。
补充
对象的内存布局
对象头
标记字段
-
存储 Java 虚拟机有关对象的运行数据
- 哈希码
- GC
- 锁信息
- 等
-
64 位虚拟机
- 占 64 位
类型指针
-
指向该对象的类,虚拟机通过这个指针来确定这个对象是哪个类的实例
-
64 位虚拟机
- 占 64位
- 占 32 位
实例数据
实例数据部分是对象真正存储的有效信息,也是在程序代码中所定义的各种类型的字段内容。无论是从父类继承下来的,还是在子类中定义的,都需要记录起来。
对齐填充
并不是必然存在的,也没有特别的含义,它仅仅起着占位符的作用。由于HotSpot VM的自动内存管理系统要求对象起始地址必须是8字节的整数倍,换句话说,就是对象的大
小必须是8字节的整数倍。