目录

一、包装类基础

1.1 什么是包装类

二、自动装箱与拆箱原理

2.1 核心结论

2.2 代码示例

2.3 空指针风险

三、缓存机制源码解析

3.1 享元模式应用

3.2 各包装类缓存范围

四、包装类使用场景

五、面试陷阱与最佳实践

5.1 四大陷阱

5.2 三目运算符陷阱详解

六、泛型基础与优势

6.1 什么是泛型

6.2 有无泛型对比

集合框架对比

6.3 泛型核心优势

6.4 泛型方法示例

七、泛型声明位置详解

7.1 泛型声明位置总览

7.2 泛型类 vs 泛型方法

7.3 静态方法泛型规则

7.4 记忆口诀

八、总结

8.1 包装类核心要点

8.2 泛型核心要点

8.3 一句话总结


一、包装类基础

1.1 什么是包装类

Java的包装类(Wrapper Classes)是将8种基本数据类型封装成对象的类。由于Java是"一切皆对象"的语言,但基本类型不是对象,因此需要包装类来填补这一鸿沟。

基本类型

包装类

父类

byte

Byte

Number

short

Short

Number

int

Integer

Number

long

Long

Number

float

Float

Number

double

Double

Number

char

Character

Object

boolean

Boolean

Object

核心特性:

  • 包装类是不可变的(一旦创建,值不能修改)

  • 都是 final 类(不能被继承)

二、自动装箱与拆箱原理

2.1 核心结论

结论

说明

结论1

自动装箱是编译器自动调用 valueOf() 方法将基本类型转为包装类

结论2

自动拆箱是编译器自动调用 xxxValue() 方法将包装类转为基本类型

结论3

== 运算符在包装类比较中存在陷阱(缓存范围内可能相等,超出后必须用 equals

2.2 代码示例

public class WrapperDemo {
    public static void main(String[] args) {
        // 自动装箱:编译器转为 Integer.valueOf(100)
        Integer a = 100;
        
        // 自动拆箱:编译器转为 a.intValue()
        int b = a;
        
        // 缓存陷阱:-128~127内==返回true,超出返回false
        Integer x = 127;
        Integer y = 127;
        System.out.println(x == y); // true(缓存内同一对象)
        
        Integer m = 128;
        Integer n = 128;
        System.out.println(m == n); // false(不同对象)
        System.out.println(m.equals(n)); // true(值相等)
    }
}

2.3 空指针风险

Integer nullInt = null;
// 安全:只是声明引用为null
System.out.println(nullInt); // 输出"null"

// 危险:拆箱时触发NPE
// int c = nullInt; // NullPointerException!
// int result = nullInt + 10; // 同样NPE!

记忆口诀:包装类本身可以为 null(安全),但一旦试图把它当作基本类型使用(赋值、运算、比较),就会触发自动拆箱,此时如果为 null 就会 NPE。

三、缓存机制源码解析

3.1 享元模式应用

Integer.valueOf() 使用享元模式(Flyweight Pattern),通过缓存常用对象减少内存开销。

// Integer.valueOf源码逻辑(Java 8)
public static Integer valueOf(int i) {
    // i在-128到127之间直接返回缓存数组中的对象
    if (i >= IntegerCache.low && i <= IntegerCache.high)
        return IntegerCache.cache[i + (-IntegerCache.low)];
    // 超出范围则新建对象
    return new Integer(i);
}

// 缓存类(静态内部类)
private static class IntegerCache {
    static final int low = -128;
    static final int high = 127; // 可通过JVM参数调整
    static final Integer cache[] = new Integer[(-low) + high + 1];
    static {
        // 类加载时预填充缓存数组
        for(int k = 0; k < cache.length; k++)
            cache[k] = new Integer(k - 128);
    }
}

3.2 各包装类缓存范围

包装类

缓存范围

Integer

-128 ~ 127(可通过 -XX:AutoBoxCacheMax=<size> 调整)

Byte、Short、Long

-128 ~ 127

Character

0 ~ 127

Boolean

TRUE 和 FALSE 两个静态常量

四、包装类使用场景

场景

为什么需要包装类

代码示例

集合存储

集合只能存储对象,不能存基本类型

List<Integer> list = new ArrayList<>();

泛型约束

泛型参数必须是对象类型

Map<String, Integer> map;

可空值

基本类型不能为null,包装类可以

Integer age = null;

类型转换

提供字符串与数字互转的方法

Integer.parseInt("123")

反射操作

反射机制操作的是对象

Field.set(Object obj, Object value)

public class WrapperUsage {
    public static void main(String[] args) {
        // 场景1:集合必须使用包装类
        List<Integer> scores = new ArrayList<>();
        scores.add(95); // 自动装箱
        
        // 场景2:泛型必须使用对象类型
        Map<String, Integer> ageMap = new HashMap<>();
        
        // 场景3:允许null
        Integer dbAge = null; // 数据库查询结果可能为空
        
        // 场景4:字符串转换
        int num = Integer.parseInt("100");
        
        // 场景5:进制转换
        String binary = Integer.toBinaryString(10); // "1010"
    }
}

五、面试陷阱与最佳实践

5.1 四大陷阱

陷阱

说明

示例

== vs equals

-128~127范围内 == 可能为true,超出为false

Integer a = 128, b = 128; a == b // false

性能损耗

频繁装箱拆箱产生大量临时对象

循环中使用 Integer 做累加

空指针风险

拆箱时对象为null会抛出NPE

int c = nullInt;

三目运算符陷阱

混合使用基本类型和包装类时自动拆箱

int c = true ? nullInt : 0;

5.2 三目运算符陷阱详解

Integer a = null;
Integer b = true ? a : 0; // 不会报错,结果还是Integer
int c = true ? a : 0;     // 抛出NullPointerException!
// 原因:a被拆箱为int,null.intValue()导致NPE

六、泛型基础与优势

6.1 什么是泛型

Java泛型(Generics)是类型参数化的机制,让代码可以适用于多种类型,同时保持编译期类型检查。

6.2 有无泛型对比

集合框架对比

无泛型(JDK 1.4风格):

List list = new ArrayList();
list.add("Hello");
list.add(100); // 编译通过,隐患埋下

// 取值时必须强转
String s = (String) list.get(0);
String s2 = (String) list.get(1); // 运行时ClassCastException!

有泛型:

List<String> list = new ArrayList<>();
list.add("Hello");
// list.add(100); // 编译错误!直接阻止

String s = list.get(0); // 无需强转

6.3 泛型核心优势

维度

无泛型时代

有泛型时代

重要性

类型安全

运行时ClassCastException

编译期类型检查

提前发现错误

代码复用

为每种类型复制粘贴代码

一套泛型代码适配所有类型

减少重复代码90%

可读性

满屏(Type)强制转换

类型声明即约束

降低维护成本

性能

装箱拆箱频繁

编译期擦除后效率同原始类型

零运行时开销

API设计

返回Object

返回T

增强封装性

6.4 泛型方法示例

public class GenericUtil {
    // 泛型方法:类型参数化,一次编写,到处使用
    public static <T> void swap(T[] arr, int i, int j) {
        T temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }
    
    public static void main(String[] args) {
        String[] strs = {"A", "B"};
        swap(strs, 0, 1); // T推断为String
        
        Integer[] ints = {1, 2};
        swap(ints, 0, 1); // T推断为Integer
    }
}

七、泛型声明位置详解

7.1 泛型声明位置总览

声明目标

语法位置

示例

泛型类

类名后

class MyArray<E> { ... }

泛型接口

接口名后

interface List<T> { ... }

泛型方法

返回值前

public <T> void swap(...) { ... }

泛型构造器

构造器名前

public <T> MyClass(T value) { ... }

7.2 泛型类 vs 泛型方法

泛型类:

class Box<T> {  // <T>在类名后,整个类都认识T
    private T value;
    public void set(T val) {  // 直接用T,无需重复声明
        this.value = val;
    }
    public T get() {
        return value;
    }
    // 静态方法不能直接用类参数T!
    // static void foo(T val) {} // 编译错误!
    static <E> void foo(E val) {} // 必须自己声明
}

泛型方法:

class Normal {
    // <T>在返回值前,作用域仅限本方法
    public <T> T method(T val) {
        return val;
    }
    
    // void返回值,<T>在void前
    public static <T> void print(T obj) {
        System.out.println(obj);
    }
}

7.3 静态方法泛型规则

class MyClass<T> {
    // ❌ 错误:静态方法不能直接访问类参数T
    // static void bad(T val) {}
    
    // ✅ 正确:静态方法必须自己声明类型参数
    static <E> void good(E val) {}
    
    // ⚠️ 合法但不推荐:用相同字母T会造成遮蔽
    static <T> void confusing(T val) {}
}

最佳实践:静态方法必须自立门户自己声明 <>,但不要用和类相同的字母。

7.4 记忆口诀

口诀

解释

"类在后,方法在前"

类声明<T>在类名后;方法声明<T>在返回值前

"没有返回值,T在void前"

即使是void,<T>也要放在void前面

"谁用谁声明"

类用泛型→类声明;方法用泛型(类没声明或静态方法)→方法自己声明

八、总结

8.1 包装类核心要点

  1. 8种包装类:Byte、Short、Integer、Long、Float、Double、Character、Boolean

  2. 自动装箱/拆箱:编译器自动调用 valueOf()xxxValue()

  3. 缓存机制:-128~127范围内使用缓存对象,超出范围新建对象

  4. == vs equals:比较值必须用 equals== 比较的是对象地址

  5. NPE风险:拆箱时对象为null会抛出NullPointerException

8.2 泛型核心要点

  1. 类型参数化:让代码适用于多种类型,保持编译期类型检查

  2. 类型安全:编译期发现类型错误,避免运行时ClassCastException

  3. 代码复用:一套泛型代码适配所有类型,减少重复代码

  4. 零成本抽象:类型擦除机制,运行时不牺牲性能

  5. 声明位置:类在后(class Box<T>),方法在前(<T> void method()

8.3 一句话总结

  • 包装类:连接基本类型与对象世界的桥梁,掌握自动装箱拆箱和缓存机制是避免生产环境bug的基础。

  • 泛型:让Java代码在保持灵活性的同时获得严格的类型约束,是连接"动态语言的便捷"与"静态语言的安全"的桥梁。

本文整理自Java包装类与泛型技术文档,去除重复内容,提炼核心知识点,适合作为面试复习和日常开发参考。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐