Java-day12

引入

在 Java 进阶学习中,泛型和包装类是两大核心高级特性。泛型让代码具备“类型通用性”,实现一套逻辑适配多种数据类型;包装类则搭建了“基本数据类型”与“对象”之间的桥梁,让基本类型也能享受面向对象的特性。本文将围绕这两大知识点,从底层原理到实战应用,全面解析其核心逻辑,帮助读者掌握 Java 类型系统的进阶玩法。

一、泛型:类型的“占位符”

1.1 泛型的定义与核心价值

泛型是 Java 中“**参数化类型**”的机制,允许我们在定义类、接口、方法时,将**类型作为“占位符”**(如 `<V>`),在使用时再指定具体类型(如 `Stack<Integer>`)。

**核心价值**:

- **类型安全**:编译期强制类型检查,避免运行时 `ClassCastException`;

- **代码复用**:一套逻辑适配多种类型(如泛型集合可存储 `Integer`、`String` 等任意引用类型);

- **可读性提升**:明确代码的类型意图,减少注释说明。

1.2 泛型类的定义与使用

以“泛型栈”为例,演示泛型类的完整定义与使用流程:

```java

package com.qcby.hhh;



/**

 * 泛型栈类,<V> 为泛型参数,代表“任意引用类型”

 */

public class Stack<V> {

    private V[] arr;   // 泛型数组,存储栈元素

    private int top;   // 栈顶指针,初始为 -1(表示栈空)



    public Stack(int size) {

        // 泛型数组不能直接 new V[size],需通过 Object 数组强转

        arr = (V[]) new Object[size];

        top = -1;

    }



    /**

     * 入栈操作:先移动栈顶指针,再赋值

     */

    public void push(V value) {

        if (top == arr.length - 1) {

            System.out.println("栈已满");

            return;

        }

        arr[++top] = value;

    }



    /**

     * 出栈操作:先取值,再移动栈顶指针

     */

    public void pop() {

        if (top == -1) {

            System.out.println("栈已空");

            return;

        }

        System.out.println(arr[top--]);

    }

}
// 测试类

public class Test {

    public static void main(String[] args) {

        // 泛型参数指定为 Integer,栈仅能存储整数

        Stack<Integer> intStack = new Stack<>(10);

        intStack.push(1);

        intStack.push(2);

        intStack.pop();  // 输出:2



        // 泛型参数指定为 String,栈仅能存储字符串

        Stack<String> strStack = new Stack<>(10);

        strStack.push("Java");

        strStack.push("泛型");

        strStack.pop();  // 输出:泛型



        // 泛型参数指定为 Double,栈仅能存储小数

        Stack<Double> doubleStack = new Stack<>(10);

        doubleStack.push(3.14);

        doubleStack.pop();  // 输出:3.14

    }

}

```

1.3 泛型的本质:编译期“类型擦除”

Java 泛型是**编译期特性**,运行时泛型信息会被“擦除”——所有泛型都会被替换为 `Object`。例如,`Stack<V>` 运行时实际是 `Stack<Object>`,泛型的类型约束仅在编译期生效。

1.4 泛型方法的定义(拓展)

除了泛型类,还可以定义**泛型方法**,让方法具备类型通用性:

```java

public class GenericMethodDemo {

    /**

     * 泛型方法:交换数组中两个元素的位置

     * @param arr 任意类型的数组

     * @param i 索引1

     * @param j 索引2

     * @param <T> 泛型参数,代表数组元素类型

     */

    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) {

        Integer[] intArr = {1, 2, 3};

        swap(intArr, 0, 2);  // 交换整数数组元素

        for (int num : intArr) {

            System.out.print(num + " ");  // 输出:3 2 1

        }



        String[] strArr = {"A", "B", "C"};

        swap(strArr, 0, 2);  // 交换字符串数组元素

        for (String str : strArr) {

            System.out.print(str + " ");  // 输出:C B A

        }

    }

}

```

二、包装类:基本类型与对象的桥梁

2.1 包装类的定义与存在意义

Java 为每一种**基本数据类型**都提供了对应的**包装类**,用于将基本类型转换为对象,从而支持“对象特性”(如方法调用、泛型兼容)。

基本数据类型包装类
byteByte
shortShort
intInteger
longLong
floatFloat
doubleDouble
charCharacter
booleanBoolean

**存在意义**:

- 让基本类型具备“对象行为”(如调用方法、存储到泛型集合中);

- 实现“基本类型与字符串”的相互转换(如 `Integer.parseInt("123")`)。

2.2 自动装箱与自动拆箱

Java 提供了“**自动装箱**”和“**自动拆箱**”机制,实现基本类型与包装类的无缝转换:

- **自动装箱**:基本数据类型 → 包装类对象(如 `int → Integer`);

- **自动拆箱**:包装类对象 → 基本数据类型(如 `Integer → int`)。

**代码示例**:

```java

public class BoxDemo {

    public static void main(String[] args) {

        // 自动装箱:int → Integer

        Integer a = 10;  // 等价于 Integer a = Integer.valueOf(10);



        // 自动拆箱:Integer → int

        int b = a;  // 等价于 int b = a.intValue();



        // 包装类参与运算(自动拆箱后计算)

        Integer c = 20;

        int sum = a + c;  // a 和 c 自动拆箱为 int,再相加

        System.out.println(sum);  // 输出:30

    }

}

```

2.3 包装类的“128 陷阱”(以 Integer 为例)

`Integer` 类为了优化性能,对 **`-128 ~ 127`** 范围内的整数采用“**缓存复用**”策略——该范围内的 `Integer` 对象会被缓存,超出范围则创建新对象。

**代码示例与分析**:

```java

public class IntegerCacheDemo {

    public static void main(String[] args) {

        Integer i = 100;

        Integer j = 100;

        System.out.println(i == j);  // 输出:true(缓存复用,地址相同)



        Integer m = 200;

        Integer n = 200;

        System.out.println(m == n);  // 输出:false(超出缓存范围,创建新对象,地址不同)



        // 显式创建对象,始终生成新地址

        Integer p = new Integer(100);

        Integer q = new Integer(100);

        System.out.println(p == q);  // 输出:false

    }

}

```

**原理**:`Integer.valueOf(int i)` 方法会判断 `i` 是否在 `-128 ~ 127` 之间,若在则返回**缓存数组**中的对象,否则创建新对象。

2.4 包装类的常用方法(以 Integer 为例)

包装类提供了丰富的工具方法,以 `Integer` 为例:

```java

public class IntegerMethodDemo {

    public static void main(String[] args) {

        // 字符串转整数

        int num = Integer.parseInt("123");

        System.out.println(num);  // 输出:123



        // 整数转字符串

        String str = Integer.toString(456);

        System.out.println(str);  // 输出:"456"



        // 进制转换(十进制转二进制)

        String binary = Integer.toBinaryString(5);

        System.out.println(binary);  // 输出:"101"

    }

}

```

三、泛型与包装类的协同应用

泛型和包装类常常协同工作,例如在 Java 集合框架中,泛型约束集合元素类型,而包装类让基本类型可以存入集合:

```java

import java.util.ArrayList;

import java.util.List;



public class GenericAndBoxDemo {

    public static void main(String[] args) {

        // 泛型指定为 Integer(包装类),集合仅能存储整数

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

        list.add(1);    // 自动装箱:int → Integer

        list.add(2);

        list.add(3);



        // 遍历集合,自动拆箱:Integer → int

        for (int num : list) {

            System.out.print(num + " ");  // 输出:1 2 3

        }

    }

}

```

四、总结:泛型与包装类的核心要点

1. 泛型:

   - 本质是“类型参数化”,编译期检查类型,运行时类型擦除;

   - 用于创建通用类、通用方法,提升代码复用性与类型安全性;

   - 泛型类、泛型方法是 Java 集合框架的核心基础。

2. 包装类:

   - 为基本数据类型提供对象封装,支持自动装箱/拆箱;

   - 需注意“128 陷阱”(如 `Integer` 缓存机制);

   - 提供丰富的类型转换、进制转换等工具方法。

通过本文的学习,相信你已对 Java 泛型和包装类的底层逻辑与实战应用有了清晰认知。泛型让代码更“通用”,包装类让基本类型更“面向对象”,二者结合是 Java 类型系统的进阶核心。建议在日常开发中多关注泛型的类型约束和包装类的自动装箱/拆箱细节,避免因类型问题引发隐藏 Bug。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐