001 Java 泛型与类型知识总结

约 6 分钟 · 1483 字评论
请注意,本文编写于 1018 天前,最后修改于 145 天前,其中某些信息可能已经过时。

Java 泛型与类型知识总结

本篇总结涵盖了 Java 中基本数据类型与引用数据类型的区别、泛型为何不能使用基本数据类型声明、泛型上下限的使用以及 Java 原生 API 中泛型的经典应用场景。每个知识点都配有详细的解释和代码示例,便于理解和复习。

一、Java 中基本数据类型和引用数据类型的区别

1. 存储位置

  • 基本数据类型:存储在 栈内存 中,变量直接包含实际的值。
  • 引用数据类型:存储在 堆内存 中,变量保存对象的引用(内存地址)。

2. 类型分类

  • 基本数据类型(共 8 种):
    • 整数类型:byteshortintlong
    • 浮点类型:floatdouble
    • 字符类型:char
    • 布尔类型:boolean
  • 引用数据类型
    • 类(Class)
    • 接口(Interface)
    • 数组(Array)
    • 枚举(Enum)
    • 注解(Annotation)

3. 内存大小

  • 基本数据类型:有固定的内存大小,例如:
    • int:4 字节
    • double:8 字节
  • 引用数据类型:大小不固定,取决于对象的具体内容。

4. 默认值

  • 基本数据类型:有默认值,例如:
    • int0
    • booleanfalse
  • 引用数据类型:默认值为 null,表示不引用任何对象。

5. 传值方式

  • 基本数据类型按值传递,方法调用时会复制一份值,不影响原值。
  • 引用数据类型按引用传递,传递对象的引用,可能影响原对象。

6. 操作方式

  • 基本数据类型:可直接进行算术运算。
  • 引用数据类型:需要通过方法调用操作对象。

7. 可空性

  • 基本数据类型:不能为 null
  • 引用数据类型:可以为 null

二、泛型为何不可用基本数据类型声明

1. 类型擦除机制

Java 的泛型是通过 类型擦除(Type Erasure)实现的,编译时会将泛型参数擦除为其上限类型(默认为 Object)。由于基本数据类型不是对象类型,不能被替换为 Object,因此泛型无法直接使用基本数据类型。

java
List<Integer> list = new ArrayList<>(); // 编译后类型擦除为: List list = new ArrayList<>();

2. 基本数据类型与对象类型的区别

基本数据类型不是对象,不能作为泛型的类型参数。泛型要求类型参数是一个对象类型(引用类型)。

3. 自动装箱与拆箱机制

Java 提供了 自动装箱(Autoboxing)和 自动拆箱(Unboxing)机制,将基本数据类型与其对应的包装类互相转换,使得基本数据类型可以间接用于泛型。

  • intInteger
  • doubleDouble
  • charCharacter
  • booleanBoolean 示例:
plaintext
java复制代码List<Integer> intList = new ArrayList<>(); intList.add(10); // 自动装箱,将 int 类型的 10 转换为 Integer 对象 int num = intList.get(0); // 自动拆箱,将 Integer 转换为 int

三、泛型上下限的使用

泛型上下限通过通配符 ? extends T(上限)和 ? super T(下限)来限定类型参数的范围,提高代码的灵活性和类型安全性。

1. 上限通配符 ? extends T

? extends T 表示类型是 TT 的子类,主要用于 读取,不能 写入(除了 null)。 示例:

java
import java.util.List; import java.util.ArrayList; public class ExtendsExample { public static void main(String[] args) { List<? extends Number> list = new ArrayList<Integer>(); // list.add(1); // 编译错误,不能添加元素 Number num = list.get(0); // 可以读取,类型为 Number } }

解释:

  • List<? extends Number> 表示列表中的元素类型是 Number 或其子类(如 IntegerDouble)。
  • 由于无法确定列表的具体子类型,不能安全地添加元素。
  • 可以安全地读取元素,读取的类型为上限 Number

2. 下限通配符 ? super T

? super T 表示类型是 TT 的父类,允许写入 T 及其子类的对象,适合 写入示例:

java
import java.util.List; import java.util.ArrayList; public class SuperExample { public static void main(String[] args) { List<? super Integer> list = new ArrayList<Number>(); list.add(1); // 可以添加 Integer list.add(new Integer(2)); // 可以添加 Integer // list.add(new Object()); // 编译错误,不能添加非 Integer 子类 Object obj = list.get(0); // 读取时只能作为 Object 类型 } }

解释:

  • List<? super Integer> 表示列表中的元素类型是 Integer 或其父类(如 NumberObject)。
  • 可以安全地添加 Integer 及其子类对象,因为它们都可以向上转型为 Integer 的父类。
  • 读取时只能作为 Object 类型,因为无法确定具体类型。 多态性解释:
  • Java 中的多态性允许子类对象赋值给父类引用。
  • 因此,Integer 类型的对象可以存储到 NumberObject 类型的列表中。

3. 无界通配符 ?

? 表示任意类型,由于类型未知,既不能安全地进行 读取,也不能 写入(除了 null)。 示例:

java
java复制代码import java.util.List; import java.util.ArrayList; public class UnboundedExample { public static void main(String[] args) { List<?> list = new ArrayList<String>(); // list.add("hello"); // 编译错误,不能添加元素 Object obj = list.get(0); // 可以读取,类型为 Object } }

解释:

  • List<?> 表示列表中的元素类型未知,可以是任何类型。
  • 由于无法确定类型,不能安全地添加元素,也不能确定读取元素的具体类型。

四、Java 原生 API 中泛型的经典应用

Java 的泛型在标准库中广泛应用,以下是一些经典的使用泛型的地方。

1. Java 集合框架(Java Collections Framework)

a. List 接口

List 接口及其实现类(如 ArrayListLinkedList)使用泛型,确保列表中的元素类型一致。

java
java复制代码List<String> stringList = new ArrayList<>(); stringList.add("Hello"); // stringList.add(1); // 编译错误,不能添加非 String 类型

b. Map 接口

Map<K, V> 是键值对集合,使用泛型参数 KV 表示键和值的类型。

java
java复制代码Map<Integer, String> map = new HashMap<>(); map.put(1, "One"); // map.put("Two", 2); // 编译错误,键必须是 Integer,值必须是 String

c. Set 接口

Set 接口及其实现类(如 HashSetTreeSet)使用泛型,确保集合中的元素类型一致。

plaintext
java复制代码Set<Double> set = new HashSet<>(); set.add(3.14); // set.add("Pi"); // 编译错误,不能添加非 Double 类型

d. Queue 接口

Queue 接口及其实现类(如 LinkedListPriorityQueue)使用泛型,指定队列中元素的类型。

plaintext
java复制代码Queue<Integer> queue = new LinkedList<>(); queue.add(1); // queue.add("String"); // 编译错误,不能添加非 Integer 类型

2. Comparator<T> 接口

Comparator 接口用于比较对象,泛型参数 T 确保类型一致。

plaintext
java复制代码Comparator<String> comparator = new Comparator<String>() { @Override public int compare(String s1, String s2) { return s1.length() - s2.length(); } }; List<String> strings = Arrays.asList("apple", "banana", "cherry"); Collections.sort(strings, comparator);

3. Optional<T>

Optional 是一个容器对象,用于表示可能存在或不存在的值,避免 NullPointerException

plaintext
java复制代码Optional<String> optionalString = Optional.of("Hello"); optionalString.ifPresent(System.out::println); // 输出 "Hello" Optional<Integer> optionalInteger = Optional.ofNullable(null); Integer value = optionalInteger.orElse(0); // 如果为空,返回默认值 0

4. Stream<T> API

Stream API 使用泛型,支持对集合的函数式操作。

plaintext
java复制代码List<String> names = Arrays.asList("Alice", "Bob", "Charlie"); List<String> filteredNames = names.stream() .filter(name -> name.startsWith("A")) .collect(Collectors.toList());

5. Future<T>CompletableFuture<T>

在并发编程中,FutureCompletableFuture 使用泛型表示异步计算的结果类型。

plaintext
java复制代码ExecutorService executorService = Executors.newFixedThreadPool(2); Future<Integer> future = executorService.submit(() -> { // 执行耗时任务 return 42; }); CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> "Hello"); completableFuture.thenAccept(result -> System.out.println(result)); // 输出 "Hello"

6. Class<T>

Class<T> 使用泛型表示类的类型信息,常用于反射机制。

plaintext
java复制代码Class<String> stringClass = String.class; String str = stringClass.newInstance(); // 创建新的 String 实例

7. 函数式接口

Java 8 引入的函数式接口广泛使用泛型。

a. Supplier<T>

提供一个类型 T 的值。

plaintext
java复制代码Supplier<String> supplier = () -> "Hello"; String result = supplier.get(); // 返回 "Hello"

b. Consumer<T>

消费一个类型 T 的值。

plaintext
java复制代码Consumer<String> consumer = s -> System.out.println(s); consumer.accept("Hello"); // 输出 "Hello"

c. Function<T, R>

将类型 T 的值映射为类型 R 的值。

plaintext
java复制代码Function<String, Integer> function = s -> s.length(); Integer length = function.apply("Hello"); // 返回 5

五、总结

Java 中基本数据类型和引用数据类型在存储方式、默认值、操作方式等方面存在显著区别。由于泛型的类型擦除机制,泛型无法直接使用基本数据类型,但可以通过包装类和自动装箱/拆箱机制间接地在泛型中使用基本数据类型。 泛型上下限的使用可以提高代码的灵活性和类型安全性。在 Java 原生 API 中,泛型被广泛应用于集合框架、并发编程、函数式编程等方面,提高了代码的可读性和可维护性。

版权声明

本文作者:hedeoer

本文链接:/post/__gh__58

许可说明:本博客所有文章除特别声明外,均采用 许可协议。转载请注明出处!

001 Java 泛型与类型知识总结

0

匿名访客仅支持文本评论。

暂无评论