一、包装类
关注点1:基本类型与包装类
(1)一切皆对象,但是基本类型却不是对象。
(2)声明方式不同,引用数据类型需要用new关键字来创建,基本数据类型不需要。
(3)存储位置不同,基本数据类型直接存储保存在堆栈中,能高效存取,引用数据类型需要引用指向实例,具体的实列保存在堆中。
(4)引用数据类型的初始值为null
,基本数据类型的初始值依其数据类型而定。
关注点2:包装类存在的意义
基本数据类型不是对象,不是Object
的子类,不能参与面向对象的开发。在设计到面向对象开发时,就会需要包装类。比较典型的场景,例如hashCode
()获取哈希值,或者getClass
()获取类等。包装类型可以解决基本数据类型解决不了的事情:泛型类型参数、序列化、类型转换、高频区间数据缓存等。
List<Integer> list = new ArrayList<>();
list.add(1);
---------------------------------------
List list = new ArrayList();
list.add(Integer.valueOf(1));
二、Integer源码分析与高速区间数据缓存
1. 类定义
public final class Integer extends Number implements Comparable<Integer>
(1)由final
修饰,不能被继承。
(2)实现Comparable
接口,可比较,代码中实现compareTo
(i)方法。
(3)继承Number
类,代码中实现6种数值类型转换方法。Number
类实现Serializable
接口,表示Integer
类可序列化。
2. 属性和常量
常量
@Native public static final int MIN_VALUE = 0x80000000; // 最小值
@Native public static final int MAX_VALUE = 0x7fffffff; // 最大值
@Native public static final int SIZE = 32; // int二进制位数
public static final int BYTES = SIZE / Byte.SIZE; // 二进制int字节数,JDK8新增
public static final Class<Integer> TYPE = (Class<Integer>) Class.getPrimitiveClass("int"); // 标记int包装类
属性
private final int value;
value是Integer中唯一的属性,由final修饰,赋值后不能修改。
3. 常用方法
Integer
中方法很多是类型转换方法,例如实现Number
类的抽象方法(intValue
()等),实现compareTo
方法。Integer
的hashCode
值是int
值。
// 返回保存指定值的Integer对象,默认十进制
public static Integer valueOf(String s)
// 返回Integer对象对应int值
public int intValue()
// 以二进制无符号整数形式返回一个整数的字符串表示。
public static String toBinaryString(int i)
// 获取系统属性中指定名称nm对应值,val为默认值。
public static Integer getInteger(String nm, int val)
JDK8新增了几个聚合函数
public static int sum(int a, int b)
public static int max(int a, int b)
public static int min(int a, int b)
4. Integer高速区间缓存
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
返回int数值的Integer
实例。如果不需要创建新的Integer
实例,valueOf
()相比于new Integer(i)
方法有着更好的空间和时间性能。性能提升是通过缓存常用值实现的。IntegerCache.cache
数组中至少缓存[-128,127]
范围的Integer
实例。
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
是私有的静态内部类,外部类不能引用该类。类中包含三个final
属性,分别是缓存实例对应int值范围low
和high
,一个Integer
数组cache
用来存放实例。IntegerCache
缓存在首次使用时初始化。
在8中基本数据类型对应的包装类中,除了浮点类型Float
和Double
类型,其余6个包装类都设置了高速区间数据缓存,缓存范围可见《基本数据类型小节》的总结图。比较特殊的是,Integer
是唯一可以修改缓存范围的包装类。IntegerCache
中high
默认值是127
,可以设置JVM
参数-XX:AutoBoxCacheMax=<size>
。缓存数组cache
的最大范围是Integer.MAX_VALUE
,size值小于等于Integer.MAX_VALUE - 128 - 1
,并且必然大于等于127
。
对于Integer var = 42
,我们知道自动装箱是Java语法糖,实际上底层实现代码时Integer var = Integer.valueOf(42)
。使用了valueOf
()方法,就用到了IntegerCache
缓存。因此,在高速区间数据缓存范围内的值可以通过 ==
判断。区间内的值会复用已有对象,区间外的数据会在堆上生成,并不会复用已有的对象。因此,对于包装类的比较,采用equals
方法比较好,保持一致性。
Integer a = 42;
Integer b = 42;
System.out.println(a == b); // true
System.out.println(a.equals(b)); // true
网友评论