1. 包装类的基本概念与作用
包装类(Wrapper Class)是Java中一种特殊的类,它们将基本数据类型封装成对象。Java为每种基本数据类型都提供了对应的包装类:
- byte → Byte
- short → Short
- int → Integer
- long → Long
- float → Float
- double → Double
- char → Character
- boolean → Boolean
包装类的主要作用体现在三个方面:
- 实现基本数据类型的对象化操作:在需要对象而非基本类型的场景下(如集合类中),包装类提供了解决方案
- 提供丰富的操作方法:包装类包含各种实用的静态方法,如字符串转换、进制转换等
- 实现null值表示:基本类型不能为null,而包装类可以表示"无值"状态
注意:从Java 5开始引入的自动装箱(AutoBoxing)和拆箱(Unboxing)机制,使得基本类型和包装类之间的转换更加便捷,但过度使用可能导致性能问题。
2. 包装类的核心方法解析
2.1 数值型包装类的共性方法
所有数值型包装类(Byte、Short、Integer、Long、Float、Double)都继承自Number类,提供以下关键方法:
java复制// 类型转换方法
byte byteValue()
short shortValue()
int intValue()
long longValue()
float floatValue()
double doubleValue()
// 字符串解析方法(静态)
static Integer parseInt(String s)
static Integer parseInt(String s, int radix)
// 其他实用方法
static String toBinaryString(int i) // 转换为二进制字符串
static String toHexString(int i) // 转换为十六进制字符串
static String toOctalString(int i) // 转换为八进制字符串
2.2 Character类的特殊方法
Character类针对字符处理提供了丰富的方法:
java复制static boolean isDigit(char ch) // 判断是否为数字
static boolean isLetter(char ch) // 判断是否为字母
static boolean isWhitespace(char ch) // 判断是否为空白字符
static char toUpperCase(char ch) // 转换为大写
static char toLowerCase(char ch) // 转换为小写
2.3 Boolean类的特殊处理
Boolean类有两个常量字段特别值得注意:
java复制public static final Boolean TRUE = new Boolean(true);
public static final Boolean FALSE = new Boolean(false);
Boolean.valueOf()方法会返回这两个常量之一,而不是每次都创建新对象,这是享元模式的应用。
3. 自动装箱与拆箱机制
Java 5引入的自动装箱/拆箱极大简化了编码,但背后隐藏着一些需要注意的细节:
3.1 自动装箱的实现原理
java复制Integer i = 10; // 实际编译为:Integer i = Integer.valueOf(10);
编译器会自动将基本类型转换为对应的包装类对象。
3.2 拆箱操作及潜在风险
java复制int num = i; // 实际编译为:int num = i.intValue();
拆箱操作可能引发NullPointerException:
java复制Integer nullInt = null;
int num = nullInt; // 运行时抛出NullPointerException
3.3 缓存机制与对象复用
Java对部分包装类实现了缓存优化:
java复制Integer a = 127;
Integer b = 127;
System.out.println(a == b); // true,因为-128~127被缓存
Integer c = 128;
Integer d = 128;
System.out.println(c == d); // false,超出缓存范围
这种缓存机制仅适用于Byte、Short、Integer、Long的范围-128~127,以及Character的0~127。
4. 泛型的基本概念与应用
4.1 为什么需要泛型
在没有泛型之前,集合类需要这样使用:
java复制List list = new ArrayList();
list.add("hello");
String s = (String) list.get(0); // 需要强制类型转换
这种方式存在两个问题:
- 需要繁琐的类型转换
- 运行时可能发生ClassCastException
泛型提供了编译时类型安全检查:
java复制List<String> list = new ArrayList<>();
list.add("hello");
String s = list.get(0); // 自动类型转换
4.2 泛型类定义
自定义泛型类的语法:
java复制public class Box<T> {
private T content;
public void setContent(T content) {
this.content = content;
}
public T getContent() {
return content;
}
}
使用示例:
java复制Box<String> stringBox = new Box<>();
stringBox.setContent("Generic Box");
String content = stringBox.getContent();
4.3 泛型方法
泛型方法可以在非泛型类中定义:
java复制public class ArrayUtils {
public static <T> T getMiddle(T... a) {
return a[a.length / 2];
}
}
调用时编译器可以推断类型参数:
java复制String middle = ArrayUtils.getMiddle("John", "Q.", "Public");
4.4 类型通配符
泛型中使用通配符增加灵活性:
java复制public static void printList(List<?> list) {
for (Object elem : list) {
System.out.println(elem);
}
}
通配符边界限制:
java复制// 上界通配符
public static double sumOfList(List<? extends Number> list) {
double sum = 0.0;
for (Number num : list) {
sum += num.doubleValue();
}
return sum;
}
// 下界通配符
public static void addNumbers(List<? super Integer> list) {
for (int i = 1; i <= 10; i++) {
list.add(i);
}
}
5. 包装类与泛型的实际应用场景
5.1 集合框架中的使用
Java集合框架大量使用泛型,而集合只能存储对象,因此基本类型需要包装类:
java复制List<Integer> intList = new ArrayList<>();
intList.add(42); // 自动装箱
int value = intList.get(0); // 自动拆箱
Map<String, Integer> wordCount = new HashMap<>();
wordCount.put("hello", 3);
int count = wordCount.get("hello");
5.2 反射API中的类型安全
泛型使反射API更安全:
java复制Class<Integer> intClass = Integer.class;
Method[] methods = intClass.getDeclaredMethods();
5.3 函数式接口与Lambda表达式
Java 8的函数式接口大量使用泛型:
java复制Function<String, Integer> stringToInt = Integer::parseInt;
int num = stringToInt.apply("123");
Predicate<Integer> isEven = n -> n % 2 == 0;
boolean result = isEven.test(4);
6. 性能考量与最佳实践
6.1 包装类与基本类型的性能对比
包装类是对象,相比基本类型有额外开销:
- 内存占用:Integer对象占用16字节,而int仅4字节
- 访问速度:包装类需要间接访问,性能略低
- 算术运算:包装类运算需要拆箱/装箱
性能敏感场景应优先使用基本类型。
6.2 避免不必要的装箱/拆箱
不良实践:
java复制Integer sum = 0;
for (int i = 0; i < 1000000; i++) {
sum += i; // 反复拆箱和装箱
}
优化方案:
java复制int sum = 0;
for (int i = 0; i < 1000000; i++) {
sum += i;
}
6.3 泛型数组的限制与解决方案
Java不允许直接创建泛型数组:
java复制// 编译错误
T[] array = new T[10];
替代方案:
- 使用Object数组并强制转换:
java复制T[] array = (T[]) new Object[10];
- 通过Array.newInstance()创建:
java复制T[] array = (T[]) Array.newInstance(componentType, length);
7. 常见问题与解决方案
7.1 包装类的相等性比较
错误做法:
java复制Integer a = 1000;
Integer b = 1000;
System.out.println(a == b); // false
正确做法:
java复制System.out.println(a.equals(b)); // true
7.2 泛型类型擦除带来的限制
由于类型擦除,以下代码无法编译:
java复制public class GenericClass<T> {
public void doSomething(Object item) {
if (item instanceof T) { // 编译错误
// ...
}
}
}
解决方案:
java复制public class GenericClass<T> {
private final Class<T> type;
public GenericClass(Class<T> type) {
this.type = type;
}
public void doSomething(Object item) {
if (type.isInstance(item)) {
// ...
}
}
}
7.3 泛型与可变参数的潜在问题
可变参数与泛型结合可能导致堆污染警告:
java复制@SafeVarargs
public static <T> List<T> asList(T... elements) {
List<T> list = new ArrayList<>();
for (T element : elements) {
list.add(element);
}
return list;
}
使用@SafeVarargs注解的前提是确保方法内部不会将泛型数组暴露给外部。
