Ⅰ. 包装类
在 Java 中,由于基本类型不是继承自 Object,为了在泛型代码中可以支持基本类型,Java 给每个基本类型都对应了一个包装类型,如下图所示:

装箱与拆箱
所谓的装箱其实就是将基本数据类型转化为包装类,而拆箱就是将包装类转化为基本数据类型。
为了减少开发者的负担,java 提供了自动机制,即不需要强制类型转换 !
public static void main(String[] args) {
Integer i = 10;
int j = i; *// 自动装箱*
* *System.*out*.println(i); *// 输出 10*
* *System.*out*.println(j); *// 输出 10*
* *int a = 20;
Integer b = a; *// 自动拆箱*
* *System.*out*.println(a); *// 输出 20*
* *System.*out*.println(b); *// 输出 20*
}🌟面试题
public static void main(String[] args) {
Integer a = 127;
Integer b = 127;
System.*out*.println(a == b); *// true*
* *Integer c = 128;
Integer d = 128;
System.*out*.println(c == d); *// false*
* *Integer e = -128;
Integer f = -128;
System.*out*.println(e == f); *// true*
* *Integer g = -129;
Integer h = -129;
System.*out*.println(g == h); *// false*
}Java 对 Integer 类型在** [-128, 127] 之间的值进行了缓存 ,目的是优化内存和性能。
-
如果值在**
[-128, 127]之间,会直接从缓存中返回同一个Integer对象(所以==比较结果为true) -
如果值超出这个范围,每次会创建新的
Integer对象(所以==比较结果为false)
这就有点像字符串常量池的原理!当然这个值是多少,是由设计者说了算的,我们只需要知道有这个机制在即可 !
Java 基本数据类型的包装类型的大部分都用到了缓存机制来提升性能。
Byte、Short、Integer、Long这 4 种包装类默认创建了数值 [-128,127] 的相应类型的缓存数据
Character创建了数值在 [0,127] 范围的缓存数据
Boolean直接返回TRUEorFALSE
Ⅱ. 泛型
泛型是一种程序设计中的重要特性 ,主要用来编写与类型无关的代码 。
它的核心思想就是:编写一次代码,适用于多种类型 ,提高代码的复用性、可读性和类型安全性 。
一、泛型类
class MyClass<T> {
T a;
T[] b = (T[])new Object[10]; *// 数组的初始化仍然需要Object来构造,并且进行强制类型转换*
}
public class demo3 {
public static void main(String[] args) {
*// 泛型类的实例化*
* *MyClass<String> a = new MyClass<>(); // 后面尖括号可以忽略类型
MyClass<Integer> b = new MyClass<Integer>();
}
}🤖注意:泛型只能接受类,所有的基本数据类型必须使用包装类 !
二、泛型方法
public class demo {
// 在返回值之前声明一下<T>表示一个泛型T,然后在返回值和参数列表以及方法体中就能使用了!
public <T> void swap(T[] arr, int i, int j) {
T tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
public static void main(String[] args) {
Integer[] arr = {1, 2, 3, 4, 5};
System.*out*.println("交换前:" + arr[1] + " " + arr[3]);
demo d = new demo();
d.swap(arr, 1, 3);
System.*out*.println("交换后:" + arr[1] + " " + arr[3]);
}
}三、通配符与泛型的上下界
有时候我们想写更灵活的泛型 ,就会用到通配符 ?,表示匹配任何一种类型 。
? extends T(上界)
表示类型是 T 或 T 的子类。
List<? extends Number> nums = new ArrayList<Integer>();
? super T(下界)
表示类型是 T 或 T 的父类。
List<? super Integer> ints = new ArrayList<Number>();
四、类型擦除问题
在模拟实现哈希表的时候,内部需要有一个节点数组,并且采用泛型,此时按下面格式定义会报错:
public Node<K, V>[] arr = new Node<K, V>[10]; // ❌编译错误这是因为 Java 的泛型是编译时生效的 ,但在运行时会被 "擦除" ,即泛型信息不会保留在字节码中 。也就是说,运行时 JVM 看到的其实是 Node[],而不是 Node<K, V>[]。
所以只能退而求其次,创建一个原始类型的数组,再手动强转成泛型数组:
// 正确做法:
new Node[10] // 创建的是原始类型的数组
(Node<K, V>[]) ... // 强制转换为泛型数组
// 合并起来就是这样子:
public Node<K, V>[] arr = (Node<K, V>)new Node[];并且为了消除警告,可以加上 @SuppressWarnings("unchecked") 注解来表示你知道这个操作不安全,但你已经检查过了:
@SuppressWarnings("unchecked")
public Node<K, V>[] arr = (Node<K, V>[]) new Node[10];当然,现代的做法是使用集合类,而避免使用泛型数组 ,如下所示:
List<Node<K, V>> list = new ArrayList<>(); // 编译通过!