包装类
基本类型 | 大小 | 包装器类型 |
---|---|---|
byte | 8bit | Byte |
short | 16bit | Short |
int | 32bit | Integer |
long | 64bit | Long |
float | 32bit | Float |
double | 64bit | Double |
boolean | / | Boolean |
char | 16bit | Character |
void | / | Void |
其中,前 6 个类派生于公共的超类 Number。对象包装器类是不可变的,即一旦构造了包装器,就不允许更改包装在其中的值。同时,对象包装器类还是 final,因此不能定义它们的子类
Java 中的包装器类有两个主要的目的:
提供一种机制,将基本值“包装”到对象中,从而使基本值能够包含在为对象而保留的操作中,比如添加到 Collections 中,或者从带对象返回值的方法中返回。注意,Java 5 增加了自动装箱和拆箱,程序员过去需手工执行的许多包装操作,现在可以由 Java 自动处理了
为基本值提供分类功能。这些功能大多数于各种转换有关:在基本值和 String 对象间相互转换,在基本值和 String 对象之间按不同基数转换,如二进制、八进制和十六进制
类型互转
装箱和拆箱
装箱就是自动将基本数据类型转换为包装器类型;拆箱就是自动将包装器类型转换为基本数据类型:
public static void main(String[] args){
Integer i = 10; //装箱
int index = i; //拆箱
}
反编译 class 文件之后可以看到:
编译器在生成类的字节码时,插入了必要的方法调用,而虚拟机只是执行这些字节码。说明装箱和拆箱是编译器认可的,而不是虚拟机。
装箱过程是通过调用包装器的 valueOf 方法实现的,而拆箱过程是通过调用包装器的 xxxValue 方法实现的。(xxx 代表对应的基本数据类型)
注意,如果在一个表达式中混合使用 Integer 和 Double 类型,Integer 值就会拆箱,提升为 double,再装箱为 Double:
Integer n = 1;
Double x = 2.0;
System.out.println(true ? n : x); // 1.0
相关问题
- 下面这段代码的输出结果是什么?
public class Main {
public static void main(String[] args) {
Integer i1 = 100;
Integer i2 = 100;
Integer i3 = 200;
Integer i4 = 200;
System.out.println(i1 == i2);
System.out.println(i3 == i4);
}
}
结果:
true
false
这里注意 “== ”和 “equal” 的区别:
基本类型 | == | equals |
---|---|---|
字符串变量 | 对象在内存中的首地址 | 字符串内容 |
非字符串变量 | 对象在内存中的首地址 | 对象在内存中的首地址 |
基本类型 | 值 | 不可用 |
包装类 | 地址 | 内容 |
输出结果表明 i1 和 i2 指向的是同一个对象,而 i3 和 i4 指向的是不同的对象。此时只需一看源码便知究竟,下面这段代码是 Integer 的 valueOf 方法的具体实现:
public static Integer valueOf(int i) {
if(i >= -128 && i <= IntegerCache.high)
return IntegerCache.cache[i + 128];
else
return new Integer(i);
}
可以看出,在通过 valueOf 方法创建 Integer 对象的时候,如果数值在 [-128,127] 之间,便返回指向 IntegerCache.cache 中已经存在的对象的引用;否则创建一个新的 Integer 对象
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 是一个工具类,初始化了 cache,并将构造方法声明为 private,禁止外部创建
上面的代码中 i1 和 i2 的数值为 100,因此会直接从 cache 中取已经存在的对象,所以 i1 和 i2 指向的是同一个对象,而 i3 和 i4 则是分别指向不同的对象
- 下面这段代码的输出结果是什么?
public class Main {
public static void main(String[] args) {
Double i1 = 100.0;
Double i2 = 100.0;
Double i3 = 200.0;
Double i4 = 200.0;
System.out.println(i1 == i2);
System.out.println(i3 == i4);
}
}
结果:
false
false
在某个范围内的整型数值的个数是有限的,而浮点数却不是。所以 Double 类型没有缓存
注意,Integer、Short、Byte、Character、Long 这几个类的 valueOf 方法的实现是类似的,Double、Float 的 valueOf 方法的实现是类似的
- 下面这段代码的输出结果是什么?
public class Main {
public static void main(String[] args) {
Boolean i1 = false;
Boolean i2 = false;
Boolean i3 = true;
Boolean i4 = true;
System.out.println(i1==i2);
System.out.println(i3==i4);
}
}
结果为:
true
true
至于为什么是这个结果,看了 Boolean 类的源码也会一目了然:
public static Boolean valueOf(boolean b) {
return (b ? TRUE : FALSE);
}
public static final Boolean TRUE = new Boolean(true);
/**
* The {@code Boolean} object corresponding to the primitive
* value {@code false}.
*/
public static final Boolean FALSE = new Boolean(false);
- 下面这段代码的输出结果是什么?
public class Main {
public static void main(String[] args) {
Integer a = 1;
Integer b = 2;
Integer c = 3;
Integer d = 3;
Integer e = 321;
Integer f = 321;
Long g = 3L;
Long h = 2L;
System.out.println(c == (a + b));
System.out.println(c.equals(a + b));
System.out.println(g == (a + b));
System.out.println(g.equals(a + b));
System.out.println(g.equals(a + h));
}
}
注意:当 “==” 运算符的两个操作数都是包装器类型的引用,则是比较指向的是否是同一个对象,而如果其中有一个操作数是表达式(即包含算术运算)则比较的是数值(即会触发自动拆箱的过程)。另外,对于包装器类型,equals 方法并不会进行类型转换。下面是 Integer 的 equals 方法:
public boolean equals(Object obj) {
if (obj instanceof Integer) {
return value == ((Integer)obj).intValue();
}
return false;
}
结果:
true
true
true
false
true
第一句由于 a+b 包含了算术运算,因此会触发自动拆箱过程(会调用 intValue 方法),因此它们比较的是数值是否相等。
而对于 c.equals(a+b) 会先触发自动拆箱过程,再触发自动装箱过程,也就是说 a+b,会先各自调用intValue方法,得到了加法运算后的数值之后,便调用 Integer.valueOf 方法,再进行 equals 比较。
同理对于后面的也是这样,不过要注意倒数第二个和最后一个输出的结果(如果数值是 int 类型的,装箱过程调用的是 Integer.valueOf;如果是 long 类型的,装箱调用的 Long.valueOf 方法)