1、概念
通常情况下基本数据类型的变量不是对象(基本数据类型的数据一般是在栈中的),为了满足万物皆对象的理念就需要对基本数据类型的变量进行打包封装处理变成对象,而负责将这些变量声明为成员变量进行对象化处理的相关类,叫做包装类。
包装类所做的实际上就是一种封装。
public class Integer {
// 封装
private int num;
······
}
2、Java 中的包装类与对应的数据类型
包装类与其对应的基本数据类型
3、java.lang.Integer
java.lang.Integer类内部包装了一个int类型的变量作为成员变量,主要用于实现对int类型的包装并提供int类型到String类之间的转换等方法。
public final class Integer extends Number implements Comparable {
······
private final int value;
······
}
java.lang.Integer中常用的常量与方法:
Integer 类
“装箱”(Boxing)与“拆箱”(Unboxing):
将基本数据类型转换为包装类的过程称为“装箱”;将包装类转换为基本数据类型称为“拆箱”。
在 Java 5 之前,“装箱”与“拆箱”是必须要借助方法完成的,例如说:int转Integer需要借助java.lang.Integer的构造方法或者是valueOf方法,Integer转int必须借助intValue方法。
在 Java 5 之后,支持“自动装箱”、“自动拆箱”。
public class IntegerTest {
public static void main(String[] args) {
// 自动“装箱”
Integer x = 100;
// 自动“拆箱”
int y = x + 200;
}
}
包装类的==和equals,==仍是比较引用所指向的对象(内存空间)是否是同一个,equals被重写后来比较值是否相等。对于java.lang.Integer来说,还是有些特殊的地方需要注意的。
Integer 类==特殊性
public class IntegerTest {
public static void main(String[] args) {
Integer x = 1000;
Integer y = 1000;
System.out.println(x == y); // false 比较引用指向对象是否是同一个,即比较地址
System.out.println(x.equals(y)); // true 比较值是否相等
}
}
public class IntegerTest {
public static void main(String[] args) {
Integer x = 127;
Integer y = 127;
System.out.println(x == y); // true 从缓存中直接取,两个引用指向的都是同一个对象
System.out.println(x.equals(y)); // true 比较值是否相等
}
}
Integer 类使用建议
// Integer -128~127 “自动装箱池” 源码
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 =
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() {}
}
补充说明:这个范围(-128~127)是可以调整的,将会涉及到 JVM 优化。
java.lang.Number是一个抽象类,所有数值的包装类都继承了该抽象类。
继承 Number 类的包装类
4、java.lang.Boolean
java.lang.Boolean类型内部包装了一个boolean类型的变量作为成员变量,主要用于实现对boolean类型的包装并提供boolean类型到String类之间的转换等方法。
java.lang.Boolean中常用的常量与方法:
Boolean 类
java.lang.Boolean源码需要注意的:
// parseBoolean 源码
public static boolean parseBoolean(String s) {
return "true".equalsIgnoreCase(s);
}
// equals 源码
public boolean equals(Object obj) {
if (obj instanceof Boolean) {
return value == ((Boolean)obj).booleanValue();
}
return false;
}
// valueOf 源码
public static Boolean valueOf(boolean b) {
return (b ? TRUE : FALSE);
}
从java.lang.Boolean的源码可以看出,java.lang.Boolean的设计理念有些类似于枚举,其中TRUE和FALSE就相当于枚举量。无论我们有多少个Boolean的引用,在堆上也就只有两个Boolean对象,一个对象是包装了true,另一个对象是包装了false。
Boolean 类的设计理念
通过上面的图,也就能解释下面代码的输出了:
public class BooleanTest {
public static void main(String[] args) {
Boolean b1 = true;
Boolean b2 = true;
System.out.println(b1 == b2); // true
System.out.println(b1.equals(b2)); // true
}
}
5、java.lang.Character
java.lang.Character类型内部包装了一个char类型的变量作为成员变量,主要用于实现对char类型的包装并提供字符类别的判断和转换等方法。
java.lang.Character中常用的常量与方法:
Character 类
在java.lang.Character中同样存在类似于java.lang.Integer中-128~127范围,java.lang.Character的范围是0~127,也就是 ASCII 码表中的字符都是在自动装箱池(也叫“缓存”)中的。
public class CharacterTest {
public static void main(String[] args) {
Character c1 = 'a';
Character c2 = 'a';
System.out.println(c1 == c2); // true
System.out.println(c1.equals(c2)); // true
}
}
6、总结
包装类
7、勘误与补充
在本篇文章中所叙述的包装类的缓存机制在涉及到具体实现以及底层分析的时候在语言表述上会有一定的偏差,但是也是勉强可以接受的。
虽然包装类中的构造方法被标注过时,但是,仍有些情况要稍微关注一下:
public class IntegerTest {
public static void main(String[] args) {
Integer x = new Integer(16);
Integer y = new Integer(16);
System.out.println(x == y); // false 也就是说如果使用的是 new 来创建包装类对象,是不会考虑缓存机制的。自动装箱或者 valueOf 方法是考虑缓存机制的
}
}