一.泛型
1.1 泛型概述
什么是泛型:泛型是 JDK 5 中引入的一个新特性。通过使用泛型,我们可以把不同的数据类型传递给一个方法或者一个类。泛型可以提高代码的复用性、类型安全性和可读性。泛型的使用方式如下:
泛型概念
-
ArrayList<E> 中的 E 称为类型参数变量
-
ArrayList<Integer> 中的 Integer 称为实际类型参数
-
整个称为 ArrayList<E> 泛型类型
-
整个 ArrayList<Integer> 称为参数化的类型:ParameterizedType
1.2 泛型类
定义类的时候同时定义了泛型的类就是泛型类。泛型类的作用是在编译阶段约定操作的数据的类型。泛型类的格式如下所示:
修饰符 class 类名<泛型变量>{
}
泛型类的原理:把出现泛型变量的地方全部替换成传输的真实数据类型。
1.3 泛型方法
泛型方法和泛型类以及泛型接口都有所不同,泛型方法可以是完全独立的,没有要求泛型方法一定要声明在泛型类或泛型接口中,泛型方法可以声明在普通类和普通接口中。定义方法时同时定义了泛型的方法就是泛型方法,是在调用方法的时候指明泛型的具体类型。泛型方法的作用是让方法可以使用泛型接收一切类型的参数,方法更具备通用性。泛型方法的格式如下:
泛型方法的原理
把出现泛型变量的地方全部替换成传输的真实数据类型。
1.4 泛型接口
使用了泛型定义的接口就是泛型接口。泛型接口的作用是让泛型接口可以让实现类选择当前功能需要操作的数据类型。泛型接口的格式如下:
泛型接口的原理
泛型接口可以约束实现类,实现类可以在实现接口的时候传入自己想要操作的数据类型,这样重写的方法都是针对于该类型的操作
1.5 泛型通配符
泛型通配符的符号为 ?,可以在"使用泛型"的时候代表一切类型。
泛型的上界和下界
泛型通配符和类型变量都有上界和下界,下面列举的是泛型通配符的例子。
-
上界 ? extends Car,? 必须是 Car 或者其子类。
-
下界 ? super Car,? 必须是 Car 或者其父类。
可以使用泛型通配符的地方
可以在方法中使用通配符作为参数类型,以接受不同类型的实参。例如:
public void process(List<? extends Animal> list) {
// 可以读取 list 中的元素,因为它们都是 Animal 或其子类
for (Animal animal: list) {
// 处理每个 animal 对象
}
// 不能添加元素到 list 中,因为编译器无法确定具体类型
// list.add(new Animal()); // 编译错误
}
可以在方法中使用通配符作为返回类型,以灵活返回不同类型的结果。例如:
public List<? extends Animal> getAnimals() {
List<Cat> animals = new ArrayList<>();
// 添加一些 Animal 类型的对象到 cats 中
return animals; // 可以返回包含 Animal 或其子类对象的列表
}
泛型类的实例化:可以在创建泛型类的实例时使用通配符,以表示不确定的类型参数。例如:
public class Container<T> {
private T item;
public void setItem(T item) {
this.item = item;
}
// 其他方法
}
Container<? extends Animal> container = new Container<>(); // 使用通配符来创建泛型对象
类的成员变量:可以在类的成员变量处使用泛型通配符,在为成员变量赋值时,泛型通配符会转为对应的类型。此外,自定义的泛型类也可以在成员变量处使用泛型通配符作为泛型类型。
public class ScrollResult {
private List<?> list;
private Long minTime;
private Integer offset;
}
public class Test{
public static void main(String[] args) {
ScrollResult scrollResult = new ScrollResult();
scrollResult.setList(blogs);
scrollResult.setMinTime(minTime);
scrollResult.setOffset(os);
return Result.ok(scrollResult);
}
}
泛型参数 ?和类型参数变量 T 的区别
? extends/super T这个语法其实也很好的证明了类型参数和通配符的不同之处,以 ? super T 为例:其中这个 T 就意味着你将来会传进来一个具体的参数,例如你传进来一个Apple,那么这个式子就变成了 ? super Apple,而通配符的作用就是扩大了 Apple 的范围,把它变成了 Apple 或者其父类。
如果我们在代码中创建对象的语句是:new Box<>("Hello"); 那么<>中的泛型会由编译器自己来推断。在这个例子中,构造函数里提供了一个字符串参数 "Hello",编译器会根据该参数的类型推断出应该使用 String 作为 Box 的类型参数。
二.集合类的体系结构
集合类体系结构分为二类,一类是Collection单列集合体系,一类是Map双列集合体系。集合中只能存储引用数据类型,不支持基本数据类型。集合中存储的是对象的地址。
2.1 Collection体系
集合中的每个元素只包含一个值。
2.2 Map集合体系
集合中的每个元素包含二个值。
三.Collection集合
3.1 什么是Collection集合
Collection 集合是单列集合的祖宗接口,它的功能是全部单列集合都能继承使用的。
3.2 Collection的常用API
3.3 Collecion集合的遍历方式
3.3.1 迭代器
迭代器在Java中的代表是Iterator,迭代器是集合的专用遍历方式。
1. 首先调用集合对象的iterator方法获得集合的迭代器对象。
例如: Iterator<String> iterator = collection.iterator();
2. 通过iterator中的方法遍历集合
例如:
while (iterator.hasNext()){
String ele = iterator.next();
System.out.println(ele);
}
3.3.2 增强for循环
增强for循环既可以遍历集合,也可以遍历数组。
3.3.3 Lambda表达式
通过调用集合对象的foreach方法,并以Consumer的匿名内部类对象为参数即可遍历集合。
例如:
四.List系列集合
4.1 List系列集合特点
-
有序:存储和取出的元素顺序一致。
-
可重复:存储的元素可以重复。
-
支持索引:可以通过索引操作元素。
4.2 List集合常用方法
List集合因为支持索引,所以多了很多索引操作的API。当然,Collection的功能List也都继承了。
//在此集合中的指定位置插入指定的元素,此位置原先的元素及之后的元素都会后移一位
void add(int index, E element);
//删除指定索引处的元素,返回被删除的元素
E remove(int index)
//删除指定的元素,返回删除是否成功
boolean remove(Object o)
//修改指定索引处的元素,返回被删除的元素
E set(int index, E element)
//返回指定索引处的元素
E get(int index)
//返回集合中的元素个数
int size()
4.3 List集合的遍历方式
前三种继承自Collection,后一种是因为List系列有索引。
-
迭代器
-
增强for循环
-
Lamdba表达式
-
for循环
4.4 ArrayList集合
ArrayList底层是基于数组实现的,根据索引查询元素快,增删要做元素的移位操作所以增删慢。
4.5 LinkedList集合
linkedList的底层原理是双链表,查询慢,首尾操作的速度是极快的。因为LinkedList的底层原理是双链表,所以多了很多首尾操作的特有API。LinkedList特有API如下: