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 代码,避免潜在的类型错误。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.pswp.cn/diannao/85948.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

力扣刷题(第四十九天)

灵感来源 - 保持更新&#xff0c;努力学习 - python脚本学习 反转链表 解题思路 迭代法&#xff1a;通过遍历链表&#xff0c;逐个改变节点的指针方向。具体步骤如下&#xff1a; 使用三个指针&#xff1a;prev&#xff08;初始为None&#xff09;、curr&#xff08;初始为…

设置应用程序图标

(1)找一张图片 (2)然后转ico图片 在线生成透明ICO图标——ICO图标制作 验证16x16就可以 降低exe大小 (3) 在xxx.pro修改 添加 &#xff08;4&#xff09; 删除 build 和 xxxpro_user文件 (5)编译project 和运行xx.exe (6)右键 设置快捷方式

免费wordpress模板下载

西瓜红色的免费wordpress模板&#xff0c;简洁实用又容易上手&#xff0c;适合新手使用。 下载 https://www.waimaoyes.com/moban/2231.html

【React】React 18 并发特性

React 18 引入了 并发特性&#xff08;Concurrent Features&#xff09;&#xff0c;这是一次对 React 渲染机制的重大升级&#xff0c;让 React 更加智能、响应更流畅、资源更节省。 我们来详细讲解一下它的原理、特性、API 以及实际应用。 &#x1f9e0; 一、什么是并发特性…

FFMPEG 提取视频中指定起始时间及结束时间的视频,给出ffmpeg 命令

以下是提取视频中指定起始时间及结束时间的 ffmpeg 命令示例: bash 复制 ffmpeg -i input.mp4 -ss 00:01:30.00 -to 00:05:00.00 -c copy output.mp4 其中,-i input.mp4 是指定要处理的输入视频文件为 “input.mp4”。 -ss 00:01:30.00 表示指定视频的起始时间为 1 分 30 …

mybatis的if判断==‘1‘不生效,改成‘1‘.toString()才生效的原因

mybatis的xml文件中的if判断‘1’不生效&#xff0c;改成’1’.toString()才生效 Mapper接口传入的参数 List<Table> queryList(Param("state") String state);xml内容 <where><if test"state ! null and state 1">AND EXISTS(select…

AI 模型分类全解:特性与选择指南

人工智能&#xff08;AI&#xff09;技术正以前所未有的速度改变着我们的生活和工作方式。AI 模型作为实现人工智能的核心组件&#xff0c;种类繁多&#xff0c;功能各异。从简单的线性回归模型到复杂的深度学习网络&#xff0c;从文本生成到图像识别&#xff0c;AI 模型的应用…

01-python爬虫-第一个爬虫程序

开始学习 python 爬虫 第一个获取使用最多的网站-百度 源代码 并将源代码保存到文件中 from urllib.request import urlopenurl https://www.baidu.com resp urlopen(url)with open(baidu.html, w, encodingutf-8) as f:f.write(resp.read().decode(utf-8))知识点&#xf…

四六级监考《培训学习》+《培训考试》

1 线上注册 &#xff08;网址&#xff1a; https://passport.neea.edu.cn 2 登录培训平台参加线上必修课程学习和考核 &#xff08;平台网址&#xff1a; https://kwstudy.neea.edu.cn 注意选择学员入口&#xff09; 3 考试要求&#xff1a;考试成绩须达应到80分以上&#xf…

回顾Java与数据库的30年历程

当 Java 1.0 于 1996 年推出时&#xff0c;语言和互联网都与今天大不相同。当时&#xff0c;网络主要是静态的&#xff0c;而 Java 承诺通过注入交互式游戏和动画来为网络注入活力&#xff0c;这一承诺极具前景。根据 1995 年写给《连线》杂志的 David Banks 的说法&#xff0c…

simulink有无现成模块可以实现将三个分开的输入合并为一个[1*3]的行向量输出?

提问 simulink有无现成模块可以实现将三个分开的输入合并为一个[1*3]的行向量输出&#xff1f; 回答 Simulink 本身没有一个单独的模块能够直接将三个分开的输入合并成一个 [13] 行向量输出&#xff0c;但是可以通过 组合模块实现你要的效果。 ✅ 推荐方式&#xff1a;Mux …

代码训练LeetCode(24)数组乘积

代码训练(24)LeetCode之数组乘积 Author: Once Day Date: 2025年6月5日 漫漫长路&#xff0c;才刚刚开始… 全系列文章可参考专栏: 十年代码训练_Once-Day的博客-CSDN博客 参考文章: 238. 除自身以外数组的乘积 - 力扣&#xff08;LeetCode&#xff09;力扣 (LeetCode) 全…

NLP学习路线图(十七):主题模型(LDA)

在浩瀚的文本海洋中航行&#xff0c;人类大脑天然具备发现主题的能力——翻阅几份报纸&#xff0c;我们迅速辨别出"政治"、"体育"、"科技"等板块&#xff1b;浏览社交媒体&#xff0c;我们下意识区分出美食分享、旅行见闻或科技测评。但机器如何…

vue对axios的封装和使用

在 Vue 项目中&#xff0c;使用 axios 进行 HTTP 请求是非常常见的做法。为了提高代码的可维护性、统一错误处理和请求拦截/响应拦截逻辑&#xff0c;对axios进行封装使用。 一、基础封装&#xff08;适用于 Vue 2 / Vue 3&#xff09; 1. 安装 axios npm install axios2. 创…

HTML实现端午节主题网站:龙舟争渡,凭吊祭江诵君赋。

名人说:龙舟争渡,助威呐喊,凭吊祭江诵君赋。——苏轼《六幺令天中节》 创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊) 目录 一、项目概览:传统与现代的技术碰撞1. 核心特性一览2. 网站结构设计二、技术亮点深度解析1. 响应式布局的精妙设计2. CSS动画系统的…

【Redis】笔记|第9节|Redis Stack扩展功能

Redis Stack 扩展功能笔记&#xff08;基于 Redis 7&#xff09; 一、Redis Stack 概述 定位&#xff1a;Redis OSS 扩展模块&#xff08;JSON、搜索、布隆过滤器等&#xff09;&#xff0c;提供高级数据处理能力。核心模块&#xff1a; RedisJSON&#xff1a;原生 JSON 支持…

如何选择专业数据可视化开发工具?为您拆解捷码全功能和落地指南!

分享大纲&#xff1a; 1、捷码核心功能&#xff1a;4维能力支撑大屏开发 2、3步上手&#xff1a;可视化大屏开发操作路径 3、适配场景&#xff1a;8大行业已验证方案 在各行各业要求数字化转型时代&#xff0c;数据可视化大屏已成为众多企业数据驱动的核心工具。面对市场上繁杂…

测试W5500的第11步_使用ARP解析IP地址对应的MAC地址

本文介绍了基于W5500芯片的ARP协议实现方法&#xff0c;详细阐述了ARP请求与回复的工作机制。ARP协议通过广播请求和单播回复实现IP地址与MAC地址的映射&#xff0c;确保局域网设备间的可靠通信。文章提供了完整的STM32F10x开发环境下的代码实现&#xff0c;包括网络初始化、SP…

在树莓派上添加音频输入设备的几种方法

在树莓派上添加音频输入设备可以通过以下步骤完成&#xff0c;具体方法取决于设备类型&#xff08;如USB麦克风、3.5mm接口麦克风或HDMI音频输入&#xff09;。以下是详细指南&#xff1a; 1. 连接音频输入设备 USB麦克风/声卡&#xff1a;直接插入树莓派的USB接口。3.5mm麦克…

IDEA 打开文件乱码

问题&#xff1a;文件乱码 底部编码无法切换 解决方案&#xff1a; 第一步 使用Nodepad 查询文件编码 本项目设置为 转为 UTF-8 无 BOM 第二步&#xff1a;在 IntelliJ IDEA 中&#xff1a;右键点击文件 → File Encoding → 选择目标编码&#xff08;如 UTF-8&#xff09; 最…