当前位置: 首页 > article >正文

java教程笔记(十一)-泛型

Java 泛型(Generics)是 Java 5 引入的重要特性之一,它允许在定义类、接口和方法时使用类型参数。泛型的核心思想是将类型由具体的数据类型推迟到使用时再确定,从而提升代码的复用性和类型安全性。

1.泛型的基本概念

1. 什么是泛型?

泛型的本质是参数化类型。参数化类型是指带有类型参数的类或接口在定义类或方法时不指定具体的类型,而是在实例化时传入具体的类型。

  • 泛型中不能写基本数据类型
  • 指定具体的泛型类型后,传递数据时,可以传递该类类型及其子类类型
  • 如果不写泛型,类型默认为Object
/*
List<E> 是 java.util 包下的接口。
E(Element)是类型参数,表示集合中元素的类型。
在代码中你看到的 List<T> 实际上是程序员习惯使用的泛型变量名,与 List<E> 等价。
*/public interface List<E> extends Collection<E> { ... }/*
List<E> 是一个泛型接口(类型参数为 E)
List<String> 是一个参数化类型(String 是类型实参)
*/List<String> list = new ArrayList<>(); 
list.add("hello"); 
String str = list.get(0); // 无需强制转换

2.为什么需要泛型 

在没有泛型的情况下,集合类默认存储的是 Object 类型,这意味着你可以往集合中添加任何类型的对象,但取出来时需要手动强制转换,容易引发 ClassCastException

示例:非泛型带来的问题

List list = new ArrayList();
list.add("hello");
list.add(100); // 编译通过String str = (String) list.get(1); // 运行时报错:ClassCastException

 使用泛型后

泛型确保了编译期的类型检查,避免运行时类型转换错误。

List<String> list = new ArrayList<>();
list.add("hello");
// list.add(100); // 编译错误,不能添加 Integer 类型
String str = list.get(0); // 不需要强制转换

泛型允许我们编写通用的类、接口和方法,而无需为每种数据类型重复实现相同逻辑。

示例:一个通用的容器类

public class Box<T> {private T item;public void setItem(T item) {this.item = item;}public T getItem() {return item;}
}

你可以这样使用:

 一份代码支持多种类型,提高复用性和可维护性。

Box<String> stringBox = new Box<>();
stringBox.setItem("Hello");
String s = stringBox.getItem(); // 直接获取String类型Box<Integer> intBox = new Box<>();
intBox.setItem(123);
Integer i = intBox.getItem();

避免强制类型转换(Avoid Casting)

在没有泛型时,从集合中取出元素必须进行强制类型转换,这不仅繁琐,还可能出错。

非泛型写法

List list = new ArrayList(); 
list.add("hello"); 
String str = (String) list.get(0); // 强制转换

泛型写法

 泛型让代码更简洁、清晰,减少出错机会。

List<String> list = new ArrayList<>(); 
list.add("hello");String str = list.get(0); // 自动类型匹配

3. 泛型的优点

特性描述
类型安全避免运行时 ClassCastException
自动类型转换不需要手动强转
代码复用使用泛型编写通用逻辑

2.泛型的使用方式

1. 泛型类

通过在类名后加上 <T> 来声明一个泛型类,T 是类型参数(Type Parameter)。可以表示属性类型、方法的返回值类型、参数类型

创建该对象时,该标识确定类型

静态方法不能使用类级别的泛型参数(如class MyClass<T>中的T),但可以定义自己的泛型参数。

/*
<>括号中的标识是任意设置的,用来表示类型参数,指代任何数据类型T :代表一般的任何类。E :代表 Element 元素的意思,或者 Exception 异常的意思。K :代表 Key 的意思。V :代表 Value 的意思,通常与 K 一起配合使用。S :代表 Subtype 的意思,文章后面部分会讲解示意。
*/
public class Box<T> {private T item;public void setItem(T item) { 
this.item = item; 
} 
public T getItem() { 
return item; 
} 
} 
// 使用 
Box<String> stringBox = new Box<>(); 
stringBox.setItem("Hello"); 
Box<Integer> integerBox = new Box<>(); 
integerBox.setItem(123);
//多个泛型参数
public class Pair<K, V> {private K key;private V value;public Pair(K key, V value) {this.key = key;this.value = value;}public K getKey() { return key; }public V getValue() { return value; }
}

泛型类的继承

子类也可以保留父类的泛型特性 

public class NumberBox<T extends Number> extends Box<T> {public double getDoubleValue() {return getValue().doubleValue();}
}

2. 泛型接口

  • T 表示实体类型(如 User、Product)。
  • ID 表示主键类型(如 Long、String)。
public interface Repository<T, ID> {T findById(ID id);void save(T entity);void deleteById(ID id);
}
public interface Repository<T, ID> { 
T findById(ID id); 
void save(T t); 
} public class UserRepository implements Repository<User, Long> { 
@Override 
public User findById(Long id) { 
return null; 
}@Override 
public void save(User user) {}}

实现泛型接口并保留泛型

如果希望实现类也保持泛型特性,可以这样做:

public class GenericRepository<T, ID> implements Repository<T, ID> { 
@Override public T findById(ID id) { 
// 泛型实现逻辑 
return null;} 
@Override public void save(T entity) { 
// 泛型保存逻辑 
} 
@Override public void deleteById(ID id) { 
// 泛型删除逻辑}}

调用示例:

GenericRepository<User, Long> userRepository = new GenericRepository<>(); 
User user = userRepository.findById(1L); 
userRepository.save(user);

3. 泛型方法

泛型方法的定义需要在返回类型前使用 <T> 来声明一个类型参数,其中 T 是一个占位符,表示任意类型。

方法中参数类型不确定时,泛型方案选择:

1.使用类名后面定义的泛型,所有方法都能用

2.在方法申明上定义自己的泛型,只有本方法能用

/*
<T>:声明一个类型参数。
T value:接收任何类型的参数。
*/
public <T> void printValue(T value) {System.out.println(value);
}
printValue(10);       // 整数
printValue("Hello");  // 字符串
printValue(3.14);     // 浮点数
public class Utils { 
public static <T> void printArray(T[] array) { 
for (T item : array) { 
System.out.println(item);} 
} 
} 
// 调用 
Utils.<String>printArray(new String[]{"A", "B"}); 
Utils.printArray(new Integer[]{1, 2, 3}); // 类型推断
//泛型方法也可以返回泛型类型的数据
public <T> T getValue(T defaultValue) {return defaultValue;
}String result = getValue("Default");
Integer number = getValue(100);

 4.泛型类与泛型接口的区别

特性泛型类泛型接口
定义方式class ClassName<T>interface InterfaceName<T>
主要用途封装通用的数据结构或行为定义通用的行为规范
实现方式直接实例化使用需要被类实现后再使用
类型约束可以通过 extends 限制类型同样支持 extends 约束
多类型参数支持多个泛型参数同样支持多个泛型参数

3.泛型通配符

在 Java 泛型中,通配符(Wildcard) 是一种特殊的类型参数,用于表示未知的类型。它增强了泛型的灵活性,特别是在集合类的操作中非常有用。

1.为什么需要泛型通配符?

1. 泛型不具备多态性

在 Java 中,即使 Dog 是 Animal 的子类,List<Dog> 并不是 List<Animal> 的子类型。也就是说,泛型是不变的)即泛型类型之间不继承其参数类型的多态关系。

List<Dog> dogs = new ArrayList<>();
// List<Animal> animals = dogs; // 编译错误!不能这样赋值

2.为什么泛型不具备多态性?

1. 为了保证类型安全

如果允许 List<Dog> 赋值给 List<Animal>,就会带来潜在的类型不安全风险。

假设允许这种赋值:
List<Dog> dogs = new ArrayList<>(); 
List<Animal> animals = dogs; 
// 如果允许 animals.add(new Cat()); // 合法吗?理论上可以,因为 Cat 是 Animal 子类Dog dog = dogs.get(0); // 错误 ClassCastException!

这会导致运行时异常,破坏了类型安全性。

因此,Java 在编译期就禁止了这种行为。

3.对比数组的协变性

Java 中的数组是协变的(covariant),即:

Dog[] dogs = new Dog[3]; Animal[] animals = dogs; //  合法

但这其实也存在安全隐患,例如:

animals[0] = new Cat(); // 运行时报错:ArrayStoreException

所以,Java 数组的协变性是在运行时进行类型检查的,而泛型为了避免这种风险,在编译期就禁止了这种操作。

如果你写一个方法用于打印列表中的元素,你可能希望它能接受任何类型的 List,比如 List<String>List<Integer> 等。 那么就需要泛型具有多态性,由此就出现了通配符

4.通配符的类型 

1.上界通配符(只能进行只读操作

在 Java 泛型中,上界通配符 <? extends T> 是一种特殊的泛型表达方式,用于表示某个类型是 T 或其子类型。它提供了一种灵活的方式来处理具有继承关系的泛型集合。

List<? extends T> list;
  • ?:表示未知类型。
  • extends T:表示该未知类型是 T 或其子类。
List<? extends Number> numbers = new ArrayList<Integer>();

 合法赋值包括:

  • List<Integer>
  • List<Double>
  • List<AtomicInteger>
  • 等等 Number 的子类列表
1.为什么使用上界通配符?
1. 允许读取为父类型

你可以安全地将集合中的元素当作 T 类型来读取。

public void printNumbers(List<? extends Number> numbers) {for (Number number : numbers) {System.out.println(number);}
}

安全地读取为 Number,无论实际是 Integer 还是 Double。 

List<Integer> ints = List.of(1, 2);
List<Double> doubles = List.of(3.5, 4.5);printNumbers(ints);   // ✅ 合法
printNumbers(doubles); // ✅ 合法
2. 避免类型不安全的写入

虽然可以读取,但不能向 <? extends T> 集合中添加除 null 外的任何对象。

List<? extends Number> list = new ArrayList<Integer>(); // 
list.add(10); //  编译错误! 
list.add(null); // 合法(但几乎无意义)
 2.为什么上界通配符只能进行只读操作
List<Integer> integers = new ArrayList<>();
List<? extends Number> list = integers;// list.add(10);       //  编译错误!
// list.add(new Integer(5)); //  同样不允许

虽然我们知道 list 实际上是一个 List<Integer>,并且可以添加 Integer 类型的值,但编译器无法确定 ? extends Number 到底是 IntegerDouble 还是其他子类,所以为了保证类型安全,直接禁止写入操作

假设允许写入会发生什么?

List<Integer> intList = new ArrayList<>();List<? extends Number> list = intList; 
list.add(3.14); // 如果允许,会怎样?Integer i = intList.get(0); //  ClassCastException!
  • 3.14 是 Double 类型。
  • 虽然它是 Number 的子类,但 intList 只能存储 Integer
  • 此时如果允许写入,就会破坏 intList 的类型一致性。

因此,Java 在编译期就阻止了这种风险

为什么可以添加 null?

list.add(null); //  合法

  • null 是所有引用类型的合法值。
  • 它不违反任何类型约束,因为 null 可以被当作任何类型来处理。

2.下界通配符(只写不可读)

在 Java 泛型中,下界通配符 <? super T> 表示一个未知类型,它是 T 或其任意父类。这种通配符用于增强泛型的灵活性,特别是在需要向集合中写入数据时。

List<? super Integer> list;
  • ? super Integer:表示该列表可以是 IntegerNumber 或 Object 类型的列表。
  • 合法赋值包括:
    List<Integer> integers = new ArrayList<>(); 
    List<Number> numbers = new ArrayList<>();List<Object> objects = new ArrayList<>(); 
    List<? super Integer> list1 = integers; //  允许List<? super Integer> list2 = numbers; // 允许 
    List<? super Integer> list3 = objects; //  允许
    1.为什么下界通配符只能写入,不能读?

你可以安全地向 <? super T> 集合中添加 T 类型或其子类型的对象。

public void addIntegers(List<? super Integer> list) { 
list.add(10); //  合法 
list.add(new Integer(5)); //  合法}

原因:

  • 编译器知道 ? super Integer 是 Integer 的父类之一(如 Number 或 Object)。
  • 所以你传入一个 Integer,它一定能被接受(因为它是所有可能类型的子类)。

不能读取为具体类型的原因:

虽然你可以写入 Integer,但你无法确定从集合中读出的元素是什么类型。

List<? super Integer> list = new ArrayList<Number>();Object obj = list.get(0); //  只能读作 Object // Integer i = list.get(0); //  编译错误
原因:
  • list 实际上可能是 List<Number> 或 List<Object>
  • 所以编译器不能保证返回的对象一定是 Integer,只能保证是 Object 类型。

3.无限定通配符 

在 Java 泛型中,无限定通配符 <?> 是一种特殊的泛型表达方式,表示“某种未知类型”。它用于定义一个可以接受任何泛型类型的集合或对象。

List<?> list;
  • ?:表示一个未知的类型。
  • 可以赋值为任何泛型类型的集合:
    List<String> stringList = new ArrayList<>();List<Integer> intList = new ArrayList<>(); 
    List<?> list1 = stringList; //  合法 
    List<?> list2 = intList; // 合法
    1.为什么使用无限定通配符?
1. 适用于只读操作(但只能读作 Object)

你可以遍历集合并读取元素,但只能当作 Object 类型处理:

public void printList(List<?> list) { 
for (Object obj : list) { 
System.out.println(obj); 
} 
}

调用示例:

printList(stringList); //  输出字符串printList(intList); // 输出整数
 注意:

你不能向 List<?> 中添加任何非 null 元素:

list.add("test"); //  编译错误
list.add(10); //  编译错误list.add(null); //  合法,但几乎无意义

因为编译器不知道 ? 到底是什么类型,为了保证类型安全,禁止写入。

4.通配符的对比

特性无限定通配符 <?>上界通配符 <? extends T>下界通配符 <? super T>
表示类型任意未知类型T 或其子类T 或其父类
读取能力✅ 只能读作 Object✅ 可读为 T✅ 可读为 Object
写入能力❌ 不允许(除 null❌ 不允许(除 null✅ 可写入 T 类型
使用场景通用只读操作生产者(只读)消费者(只写)

4.PECS 原则详解

PECS(Producer Extends, Consumer Super) 是 Java 泛型中一个非常重要的设计原则,用于指导在使用泛型通配符时如何选择合适的通配符类型,以确保类型安全和代码灵活性。

1.PECS 的含义

角色描述使用的通配符
Producer(生产者)只从集合中读取数据<? extends T>
Consumer(消费者)只向集合中写入数据<? super T>

✅ 简单记忆:读用 extends,写用 super

2.详细解释

1. Producer Extends

当你只需要从集合中读取数据,并且希望集合可以接受 T 或其子类的任意一种类型时,使用 <? extends T>

public void process(List<? extends Number> numbers) {for (Number number : numbers) { 
System.out.println(number.doubleValue());} 
}

调用示例:

List<Integer> ints = List.of(1, 2); 
List<Double> doubles = List.of(3.5, 4.5); 
process(ints); //  合法 
process(doubles); //  合法
  •  优点:可以安全地读取为 Number
  •  缺点:不能写入任何非 null 元素
2. Consumer Super

当你只需要向集合中写入数据,并希望集合能接受 T 类型及其父类的集合时,使用 <? super T>

public void addNumbers(List<? super Integer> list) { list.add(10); list.add(20); }

调用示例:

List<Number> numbers = new ArrayList<>(); addNumbers(numbers); // ✅ 合法

  •  优点:可以安全地写入 Integer 或其子类对象
  •  缺点:只能读作 Object,无法还原为具体类型

3.为什么需要 PECS?

Java 泛型不具备多态性(Invariance),即即使 Dog 是 Animal 的子类,List<Dog> 也不是 List<Animal> 的子类。这导致我们在处理集合时面临类型兼容性的挑战。

通过使用通配符并遵循 PECS 原则,我们可以在保持类型安全的前提下,写出更加通用、灵活的代码。

4.典型应用示例

1. 生产者 + 消费者组合使用

public static <T> void copy(List<? super T> dest, List<? extends T> src) { for (T item : src) { dest.add(item); } }

  • src 是生产者 → 使用 <? extends T>
  • dest 是消费者 → 使用 <? super T>

调用示例:

List<Integer> source = List.of(1, 2, 3); List<Number> target = new ArrayList<>(); copy(target, source); // ✅ 合法

5.总结

内容描述
PECS 原则Producer Extends, Consumer Super
核心思想根据集合是“读”还是“写”来选择合适的通配符
优势提高代码复用性、增强类型安全性
限制不能同时作为生产者和消费者
最佳实践在泛型集合操作中优先考虑通配符,避免直接使用具体泛型类型

理解并掌握 PECS 原则,是编写高质量 Java 泛型代码的关键所在。它帮助你在保持类型安全的同时,实现更通用、更灵活的设计。

 5.类型擦除(Type Erasure)

Java 的泛型类型擦除(Type Erasure)是 Java 泛型实现的核心机制之一,它指的是在编译期间,泛型类型信息会被移除(擦除),以兼容非泛型的旧代码(即 Java 5 之前的版本)。这意味着泛型只存在于编译阶段,在运行时并不存在具体的泛型类型

示例:

尽管 List<String> 和 List<Integer> 在源码中指定了不同的泛型类型,但在运行时它们的类型都是 ArrayList泛型信息被擦除了

当把集合定义为string类型的时候,当数据添加在集合当中的时候,仅仅在门口检查了一下数据是否符合String类型,  如果是String类型,就添加成功,当添加成功以后,集合还是会把这些数据当做Object类型处理,当往外获取的时候,集合在把他强转String类型

List<String> list1 = new ArrayList<>(); 
List<Integer> list2 = new ArrayList<>(); 
System.out.println(list1.getClass() == list2.getClass()); // true

1.类型擦除的过程

1. 替换所有类型参数为原始类型

  • 类型参数如 <T> 被替换为其上界(upper bound)
  • 如果没有指定上界,默认使用 Object

例如:

public class Box<T> {private T value; 
public void setValue(T value) { 
this.value = value; 
} 
public T getValue() {return value;} 
}

编译后相当于:

public class Box { 
private Object value; p
ublic void setValue(Object value) { 
this.value = value; 
} 
public Object getValue() {return value;} 
}

2. 插入类型转换代码

编译器会在适当的位置自动插入强制类型转换,确保类型安全。

例如:

Box<String> box = new Box<>(); 
box.setValue("Hello"); 
String s = box.getValue(); // 编译器自动插入 (String)box.getValue()

2.类型擦除的影响

影响说明
无法获取泛型类型信息运行时无法通过反射获取 List<String> 中的 String 类型
不能实例化泛型类型new T() 是非法的,因为运行时不知道 T 是什么
不能创建泛型数组T[] array = new T[10]; 是非法的
重载方法冲突方法签名在擦除后可能重复,导致编译错误

示例:

public void process(List<String> list) {} 
public void process(List<Integer> list) {} // 编译错误:方法签名冲突

3.如何绕过类型擦除(获取泛型信息)

虽然 Java 擦除了泛型信息,但在某些情况下可以通过反射获取泛型类型信息,前提是该泛型类型是在声明时明确指定的(不是变量类型)。

示例:获取父类的泛型类型

abstract class Base<T> { 
abstract T get(); 
} 
class StringSub extends Base<String> {@Override String get() { return null; } } // 获取泛型类型 
Type type = StringSub.class.getGenericSuperclass(); 
if (type instanceof ParameterizedType pt) { 
Type actualType = pt.getActualTypeArguments()[0]; 
System.out.println(actualType); // 输出: class java.lang.String 
}

5.泛型与继承

  • 子类可以继承父类并指定泛型类型。
  • 子类也可以继续保留泛型参数。
class Animal {} 
class Dog extends Animal {} 
class Cage<T> { 
private T animal;
public void set(T animal) { 
this.animal = animal;} 
public T get() { 
return animal; 
} 
} 
class DogCage extends Cage<Dog> { // 此处 T 已经固定为 Dog }

6.泛型常见错误

错误示例原因
List<int>泛型不能使用基本类型,应使用 List<Integer>
new T()编译器不知道 T 是什么类型
new List<String>[]泛型数组不可创建
if (obj instanceof List<String>)泛型被擦除,无法判断

7.Java 明确禁止创建具体泛型参数类型的数组

 在 Java 中,泛型数组(如 List<Dog>[])与单个泛型对象(如 List<Integer>)的创建规则完全不同,核心区别在于 数组的协变性(Covariance) 和 泛型的不变性(Invariance)。以下是详细解释:

 类型擦除

Java 的泛型是通过编译时的类型检查实现的,但在运行时,泛型信息会被擦除。例如:

List<Dog> list1 = new ArrayList<>(); 
List<Cat> list2 = new ArrayList<>(); 
// 运行时 list1 和 list2 都是 List 类型

所以当你尝试创建一个 ArrayList<Dog>[10] 时,运行时实际上只能看到 ArrayList[],而无法区分里面存储的是 List<Dog> 还是 List<Cat>

数组协变性

Java 数组是协变的(covariant),即:

String[] strings = new String[10]; Object[] objects = strings; // 合法

1. 为什么 List<Dog>[] listArray = new ArrayList<Dog>[10]; 不可以?

1.1 核心原因:泛型数组的类型安全问题

Java 的泛型是通过 类型擦除(Type Erasure) 实现的,即泛型信息在运行时被移除。而 数组在运行时保留类型信息,并且支持协变性(Dog[] 是 Animal[] 的子类型),这会导致类型不安全。

示例:泛型数组的潜在风险

// 假设允许创建泛型数组 
List<Dog>[] listArray = new ArrayList<Dog>[10];// 插入错误类型的 List(Dog 是 Animal 的子类)List<Animal> animalList = new ArrayList<>();listArray[0] = animalList; // 编译通过,但实际类型不匹配! 
// 后续访问时可能出现 ClassCastException 
Dog dog = listArray[0].get(0); // 如果 animalList 中有 Cat,此处抛出异常
  • 问题List<Dog> 和 List<Animal> 没有继承关系(泛型是不变的),但数组的协变性允许将 List<Animal> 赋值给 List<Dog>[],导致运行时类型不一致。

1.2 Java 的设计限制

Java 禁止直接创建泛型数组,因为:

  1. 运行时无法验证数组元素的类型(类型擦除导致)。
  2. 数组的协变性与泛型的不变性冲突,可能引发类型不安全。

2. 为什么 List<Integer> list01 = new ArrayList<>(); 可以?

2.1 钻石操作符(Diamond Operator)

  • Java 7 引入:允许在实例化泛型类时省略类型参数,编译器根据上下文自动推断类型。
  • 示例
    List<Integer> list01 = new ArrayList<>(); //  合法
    • 编译器推断 ArrayList<> 为 ArrayList<Integer>
    • 等价于显式声明:List<Integer> list01 = new ArrayList<Integer>();

2.2 为什么这是安全的?

  • 单个对象的类型是固定的ArrayList<Integer> 仅存储 Integer 类型元素,不会出现多态赋值问题。
  • 泛型的不变性不影响单个对象:无需考虑数组的协变性问题。

3. 关键区别总结

复制

特性泛型数组(如 List<Dog>[]单个泛型对象(如 List<Integer>
类型检查时机运行时检查(数组保留类型信息)编译时检查(泛型通过类型擦除实现)
协变性支持协变性(Dog[] 是 Animal[] 的子类型)不支持协变性(List<Dog> 与 List<Animal> 无继承关系)
类型安全无法保证(可能插入错误类型)完全保证(编译器强制类型匹配)
Java 允许性 不允许(编译警告或错误)允许(钻石操作符合法)

4. 替代方案:如何安全创建泛型数组?

如果需要存储泛型集合的数组,推荐以下方式:

4.1 使用通配符数组

List<?>[] listArray = new ArrayList<?>[10]; //  安全创建
listArray[0] = new ArrayList<Dog>(); 
listArray[1] = new ArrayList<Animal>(); // 合法,但只能读取(不能添加元素)
  • 限制:不能向 List<?> 中添加元素(除了 null)。

4.2 使用嵌套集合

List<List<Dog>> listList = new ArrayList<>();listList.add(new ArrayList<>()); // 安全
  • 优点:完全利用泛型的类型安全性,避免数组的协变性问题。

5. 总结

  • 泛型数组不可创建:由于类型擦除和数组的协变性,Java 禁止直接创建泛型数组(如 List<Dog>[]),以避免运行时类型不安全。
  • 单个泛型对象可创建:使用钻石操作符 <>(Java 7+)可安全创建单个泛型对象(如 List<Integer>),因为类型推断在编译期完成,且不涉及多态赋值。

通过理解数组和泛型的设计差异,可以更安全地编写 Java 代码,避免潜在的类型错误。

相关文章:

java教程笔记(十一)-泛型

Java 泛型&#xff08;Generics&#xff09;是 Java 5 引入的重要特性之一&#xff0c;它允许在定义类、接口和方法时使用类型参数。泛型的核心思想是将类型由具体的数据类型推迟到使用时再确定&#xff0c;从而提升代码的复用性和类型安全性。 1.泛型的基本概念 1. 什么是泛…...

JUnit​​ 和 ​​Mockito​​ 的详细说明及示例,涵盖核心概念、常用注解、测试场景和实战案例。

一、JUnit 详解 1. JUnit 核心概念 ​​测试类​​&#xff1a;以 Test 结尾的类&#xff08;或通过 Test 注解标记的方法&#xff09;。​​断言&#xff08;Assertions&#xff09;​​&#xff1a;验证预期结果与实际结果是否一致&#xff08;如 assertEquals()&#xff0…...

【Go语言基础【7】】条件语句

文章目录 零、概述一、if 条件语句1. 单条件模型2. 多条件模型&#xff08;else if&#xff09;3. 条件嵌套与优化 二、switch 条件判断1. 基本用法2. fallthrough 穿透执行3. break 终止执行 零、概述 语句类型适用场景核心特点if-else单条件或简单多条件判断逻辑清晰&#x…...

【Python 算法零基础 4.排序 ⑪ 十大排序算法总结】

目录 一、选择排序回顾 二、冒泡排序回顾 三、插入排序回顾 四、计数排序回顾 五、归并排序回顾 六、快速排序回顾 七、桶排序回顾 八、基数排序 九、堆排序 十、希尔排序 十一、十大排序算法对比 十二、各算法详解与应用场景 1. 选择排序&#xff08;Selection Sort&#xff…...

解决神经网络输出尺寸过小的实战方案

训练CIFAR10分类模型时出现报错&#xff1a;RuntimeError: Given input size: (256x1x1). Calculated output size: (256x0x0). Output size is too small。该问题由网络结构设计缺陷导致图像尺寸过度缩小引发。 核心原因分析 网络结构缺陷 原始模型采用六层卷积层&#xff0c…...

Python备忘

1. 自定义多线程程序&#xff1a; import concurrent.futures import threadingclass CustomThreadPool:def __init__(self, max_workers):self.max_workers max_workersself.pool concurrent.futures.ThreadPoolExecutor(max_workers)self.running_num 0self.semaphore t…...

如何在 Windows 11 中永久更改默认浏览器:阻止 Edge 占据主导地位

在 Windows 11 中更改默认浏览器对于新手或技术不太熟练的用户来说可能会令人沮丧。 为什么要在 Windows 11 中更改默认浏览器? 这是一个重要的问题:你为什么要从 Microsoft Edge 切换过来? 生态系统集成:如果你已经在广泛使用 Google 服务,Chrome 可以提供无缝集成。同…...

量子比特实现方式

经典计算机是通过电子电路运转起来的。使用硅制半导体制成的名为晶体管的小元件发挥了开关的作用&#xff0c;将其与金属布线组合起来即可实现逻辑门&#xff0c;再将逻辑门集成起来就能制造出经典计算机。量子计算机的制造过程则要复杂许多&#xff0c;因为量子计算机既需要量…...

智慧水务发展迅猛:从物联网架构到AIoT系统的跨越式升级

AI大模型引领智慧水务迈入新纪元 2025年5月25日&#xff0c;水利部自主研发的“水利标准AI大模型”正式发布&#xff0c;它标志着水务行业智能化进程的重大突破。该模型集成1800余项水利标准、500余项法规及海量科研数据&#xff0c;支持立项、编制、审查等全流程智能管理&…...

1、cpp实现Python的print函数

实现一 #include <iostream> #include <list> #include <string>using namespace std;// 定义一个空的print函数&#xff0c;作为递归终止条件 void print(){// };// 可变参数模板函数&#xff0c;用于递归输出传入的参数 template <typename T, typenam…...

【Linux基础知识系列】第十四篇-系统监控与性能优化

一、简介 随着信息技术的飞速发展&#xff0c;Linux系统在服务器领域占据着重要地位。无论是web服务器、数据库服务器还是文件服务器&#xff0c;都需要高效的运行以满足业务需求。系统监控与性能优化是确保Linux系统稳定、高效运行的关键任务。通过实时监测系统资源的使用情况…...

云原生思维重塑数字化基座:从理念到实践的深度剖析

&#x1f4dd;个人主页&#x1f339;&#xff1a;慌ZHANG-CSDN博客 &#x1f339;&#x1f339;期待您的关注 &#x1f339;&#x1f339; 一、引言&#xff1a;云原生为何成为数字化的“基础设施语言”&#xff1f; 随着5G、人工智能、物联网等技术逐步进入规模化落地阶段&am…...

Animate On Scroll 用于在用户滚动页面时实现元素的动画效果

AOS (Animate On Scroll) 详细介绍 什么是AOS&#xff1f; AOS&#xff08;Animate On Scroll&#xff09;是一个轻量级的JavaScript库&#xff0c;用于在用户滚动页面时实现元素的动画效果。它允许网页元素在进入或离开视口&#xff08;viewport&#xff09;时触发各种CSS动…...

Java高级 | 【实验五】Spring boot+mybatis操作数据库

隶书文章&#xff1a;Java高级 | &#xff08;二十二&#xff09;Java常用类库-CSDN博客 系列文章&#xff1a;Java高级 | 【实验一】Springboot安装及测试 |最新-CSDN博客 Java高级 | 【实验二】Springboot 控制器类相关注解知识-CSDN博客 Java高级 | 【实验三】Springboot 静…...

[蓝桥杯]搭积木

搭积木 题目描述 小明对搭积木非常感兴趣。他的积木都是同样大小的正立方体。 在搭积木时&#xff0c;小明选取 mm 块积木作为地基&#xff0c;将他们在桌子上一字排开&#xff0c;中间不留空隙&#xff0c;并称其为第 0 层。 随后&#xff0c;小明可以在上面摆放第 1 层&a…...

在MATLAB中使用自定义的ROS2消息

简明结论&#xff1a; 无论ROS2节点和MATLAB运行在哪&#xff0c;MATLAB本机都必须拥有自定义消息源码并本地用ros2genmsg生成&#xff0c;才能在Simulink里订阅这些消息。只要你想让MATLAB或Simulink能识别自定义消息&#xff0c;必须把消息包源码(.msg等)拷到本机指定目录&a…...

使用C/C++和OpenCV实现图像拼接

使用 C 和 OpenCV 实现图像拼接 本文将详细介绍如何利用 OpenCV 库&#xff0c;在 C 环境中实现图像拼接。图像拼接技术可以将多张具有重叠区域的图像合成为一张高分辨率的全景图。OpenCV 提供了一个功能强大的 Stitcher 类&#xff0c;它封装了从特征点检测、匹配到图像融合的…...

神经网络-Day46

目录 一、 什么是注意力二、 特征图的提取2.1 简单CNN的训练2.2 特征图可视化 三、通道注意力3.1 通道注意力的定义3.2 模型的重新定义&#xff08;通道注意力的插入&#xff09; 一、 什么是注意力 注意力机制&#xff0c;本质从onehot-elmo-selfattention-encoder-bert这就是…...

Ubuntu中常用的网络命令指南

Ubuntu中常用的网络命令指南 在Ubuntu系统中&#xff0c;网络管理是日常运维和故障排查的核心技能。 &#x1f6e0;️ 基础网络诊断 ping - 测试网络连通性 ping google.com # 持续测试 ping -c 4 google.com # 发送4个包后停止traceroute / tracepath - 追踪数据包路径 …...

JVM——如何打造一个类加载器?

引入 在Java应用程序的生命周期中&#xff0c;类加载器扮演着至关重要的角色。它是Java运行时环境的核心组件之一&#xff0c;负责在需要时动态加载类文件到JVM中。理解类加载器的工作原理以及如何自定义类加载器&#xff0c;不仅可以帮助我们更好地管理应用程序的类加载过程&…...

【MATLAB去噪算法】基于ICEEMDAN联合小波阈值去噪算法

ICEEMDAN联合小波阈值去噪算法相关文献 &#xff08;注&#xff1a;目前相关论文较少&#xff0c;应用该套代码可发直接一些水刊&#xff09; 一、CEEMDAN的局限性 模式残留噪声问题&#xff1a;原始CEEMDAN在计算每个IMF时直接对噪声扰动的信号进行模态分解并平均。 后果&a…...

c++ Base58编码解码

Base58 字符集 Base58 使用 58 个字符进行编码&#xff0c;字符集为&#xff1a;123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz。注意&#xff1a;0&#xff08;零&#xff09;、O&#xff08;大写字母O&#xff09;、I&#xff08;大写字母I&#xff09;和 l&a…...

证券交易柜台系统解析与LinkCounter解决方案开发实践

第一章 证券交易柜台系统基础解析 1.1 定义与行业定位 证券交易柜台系统&#xff08;Trading Counter System&#xff09;是券商经纪业务的核心支撑平台&#xff0c;承担投资者指令传输、风险控制、清算结算等职能。根据中国证监会《证券期货业网络信息安全管理办法》要求&am…...

XXTEA,XTEA与TEA

TEA、XTEA和XXTEA都是分组加密算法&#xff0c;它们在设计、安全性、性能等方面存在显著区别。以下是它们的主要区别&#xff1a; 密钥长度 TEA&#xff1a;使用128位密钥。 XTEA&#xff1a;通常使用128位或256位密钥。 XXTEA&#xff1a;密钥长度更灵活&#xff0c;可以使用任…...

机器人玩转之---嵌入式开发板基础知识到实战选型指南(包含ORIN、RDK X5、Raspberry pi、RK系列等)

1. 基础知识讲解 1.1 什么是嵌入式开发板&#xff1f; 嵌入式开发板是一种专门设计用于嵌入式系统开发的硬件平台&#xff0c;它集成了微处理器、内存、存储、输入输出接口等核心组件于单块印刷电路板上。与传统的PC不同&#xff0c;嵌入式开发板具有体积小、功耗低、成本适中…...

腾讯云国际版和国内版账户通用吗?一样吗?为什么?

在当今全球化的数字化时代&#xff0c;云计算服务成为众多企业和个人拓展业务、存储数据的重要选择。腾讯云作为国内领先的云服务提供商&#xff0c;其国际版和国内版备受关注。那么&#xff0c;腾讯云国际版和国内版账户是否通用&#xff1f;它们究竟一样吗&#xff1f;背后又…...

OrCAD X Capture CIS设计小诀窍系列第二季--03.如何在Capture中输出带有目录和元器件信息的PDF

背景介绍&#xff1a;我们在进行原理图设计时&#xff0c;经常需要输出PDF来查看或评审&#xff0c;但通过”Print”功能导出的PDF较为简单&#xff0c;只能查看设计视图&#xff1b;而通过使用Ghostscript软件可以输出带有目录和元器件信息的PDF&#xff0c;让设计师可以直接在…...

汽车的安全性能测试:试验台铁地板的重要性

汽车的安全性能测试是非常重要的&#xff0c;其中试验台铁地板的设计和材料选择起着至关重要的作用。试验台铁地板是指在进行汽车碰撞、侧翻等试验时&#xff0c;用于支撑汽车底部和提供稳定支撑的重要部件。 在进行汽车碰撞试验时&#xff0c;试验台铁地板的设计和材料需要具…...

Lua和JS的垃圾回收机制

Lua 和 JavaScript 都采用了 自动垃圾回收机制&#xff08;GC&#xff09; 来管理内存&#xff0c;开发者无需手动释放内存&#xff0c;但它们的 实现机制和行为策略不同。下面我们从原理、策略、优缺点等方面来详细对比&#xff1a; &#x1f536; 1. 基本原理对比 特性LuaJa…...

实践指南:从零开始搭建RAG驱动的智能问答系统

LLM 赋能的最强大的应用之一是复杂的问答 (Q&A) 聊天机器人。这些是可以回答关于特定来源信息问题的应用程序。这些应用程序使用一种称为检索增强生成的技术&#xff0c;或 RAG。本文将展示如何基于 LangChain 构建一个简单的基于非结构化数据文本数据源的问答应用程序。 温…...