关于封装类的父类Number
已知所有基本类型的封装类的父类都有一个Number类(这是一个抽象类)里面定义了四个抽象方法分别对应了四个基本数据类型
abstract double doubleValue()
//Returns the value of the specified number as a double.
abstract float floatValue()
//Returns the value of the specified number as a float.
abstract int intValue()
//Returns the value of the specified number as an int.
abstract long longValue()
//Returns the value of the specified number as a long.
还有两个非抽象方法
byte byteValue()
//Returns the value of the specified number as a byte.
short shortValue()
//Returns the value of the specified number as a short.
tips:个人认为没有将这两个数据声明为抽象函数的原因是这两个类对应的基本数据类型所占的字节数太小,导致其他大部分数据类型进行拆箱操作时会导致严重的数据丢失,所以并没有强制让其他封装类继承
以上的方法都是拆箱操作,当我们使用“=”进行赋值时,例如:
Integer i1 = 100;
int i2 = i1;
虚拟机将调用Integer.valueof()这个类加载方法进行装箱操作,调用intValue()进行拆箱操作,此操作我们可以通过javap -v反编译class文件很清晰的看到。
在#7这个操作中,调用了Integer.valueof()进行装箱操作。(这是一个类加载函数)
在#13这个操作中,调用了intValue()这个方法进行拆箱操作。
Byte类的装拆箱与String类装拆箱的不同与相同
在Byte中Byte.valueof(int a) 中他将调用
public static Byte valueOf(byte b) {
final int offset = 128;
return ByteCache.cache[(int)b + offset];
}
点进cache中可以看到以下代码
private static class ByteCache {
private ByteCache() {}
static final Byte[] cache;
static Byte[] archivedCache;
static {
final int size = -(-128) + 127 + 1;
// Load and use the archived cache if it exists
CDS.initializeFromArchive(ByteCache.class);
if (archivedCache == null || archivedCache.length != size) {
Byte[] c = new Byte[size];
byte value = (byte)-128;
for(int i = 0; i < size; i++) {
c[i] = new Byte(value++);
}
archivedCache = c;
}
cache = archivedCache;
}
}
这是一个类加载的内部类,在其内部,提前创建了一个value为[-128,127]的一个Byte类数组,当我们封装一个该范围内的byte类型数时,我们将直接返回那个Byte数组中的对应值,此方法提高了[-128,127]的封装速度而这个直接传递缓存地址的操作导致了一个这样的结果
byte i = 100;
Byte i1 = Byte.valueof(i);
Byte i2 = Byte.valueof(i);
System.out.println(i1 == i2); //true
System.out.println(i1.equals(i2)); //true
然而String类却不可能具有这样的性质,例如:
public static String valueOf(int i) {
return Integer.toString(i);
}
我们点入toString可以看到
public static String toString(int i) {
int size = stringSize(i);
if (COMPACT_STRINGS) {
byte[] buf = new byte[size];
getChars(i, size, buf);
return new String(buf, LATIN1);
} else {
byte[] buf = new byte[size * 2];
StringUTF16.getChars(i, size, buf);
return new String(buf, UTF16);
}
}
我们可以看到,最终new了一个String,所以最终的结果
byte i = 100;
String i1 = String .valueof(i);
String i2 = String .valueof(i);
System.out.println(i1 == i2); //false
System.out.println(i1.equals(i2)); //true
导致false的原因是地址不同
关于Byte到整数包装类的拓展
实际上,不止Byte类拥有这个性质,所有的整数包装类都拥有这个性质(Byte,Short,Integer,Long,BigInteger)包括Char也同样拥有这个性质,相关源代码除了Character类,都与Byte类类似。
相关Character类的Character.valueof()方法源代码如下:
public static Character valueOf(char c) {
if (c <= 127) { // must cache
return CharacterCache.cache[(int)c];
}
return new Character(c);
}
@Deprecated(since="9", forRemoval = true)
public Character(char value) {
this.value = value;
}
private static class CharacterCache {
private CharacterCache(){}
static final Character[] cache;
static Character[] archivedCache;
static {
int size = 127 + 1;
// Load and use the archived cache if it exists
CDS.initializeFromArchive(CharacterCache.class);
if (archivedCache == null || archivedCache.length != size) {
Character[] c = new Character[size];
for (int i = 0; i < size; i++) {
c[i] = new Character((char) i);
}
archivedCache = c;
}
cache = archivedCache;
}
}
需要注意的是BigInteger的缓存范围是[-16,16]
其余整数包装类如果读者感兴趣可以自行查看,本文不再赘述。