Java泛型详解
文章目录
- 1. 引言
- 1.1 什么是泛型
- 1.2 为什么需要泛型
- 1.3 泛型的优势
- 2. 泛型基础
- 2.1 泛型类
- 多个类型参数
- 2.2 泛型方法
- 2.3 泛型接口
- 2.4 类型参数命名约定
- 3. 类型擦除
- 3.1 什么是类型擦除
- 3.2 类型擦除的影响
- 1. 无法获取泛型类型参数的实际类型
- 2. 无法创建泛型类型的数组
- 3. 无法使用`instanceof`检查泛型类型
- 4. 泛型类的静态上下文中不能使用类型参数
- 5. 异常类不能是泛型的
- 3.3 桥接方法
- 4. 泛型通配符
- 4.1 无界通配符(?)
- 4.2 上界通配符(? extends T)
- 4.3 下界通配符(? super T)
- 4.4 PECS原则
- 5. 泛型约束
- 5.1 类型边界
- 5.2 多重边界
- 5.3 泛型与继承
- 泛型类的继承
- 协变与逆变
- 泛型数组创建
- 1. 使用通配符数组
- 2. 使用反射创建泛型数组
- 3. 使用ArrayList作为替代
- 4. 泛型数组的安全实现
- 6. 泛型的高级用法
- 6.1 递归类型界定
- 自比较类型
- 6.2 泛型与数组
- 1. 使用通配符数组
- 2. 使用反射创建泛型数组
- 3. 使用ArrayList作为替代
- 4. 泛型数组的安全实现
- 6.3 类型推断
- 基本类型推断
- 菱形操作符(Diamond Operator)
- 目标类型推断
- Lambda表达式中的类型推断
- 方法引用中的类型推断
- 更高级的类型推断(Java 8+)
- 7. 实际应用场景
- 7.1 集合框架
- 类型安全的集合
- 集合的流式处理
- 自定义集合的泛型实现
- 7.2 自定义数据结构
- 泛型二叉树
- 泛型优先队列
- 7.3 泛型与设计模式
- 工厂模式
- 单例模式
- 观察者模式
- 8. 常见问题与解决方案
- 8.1 类型擦除导致的问题
- 1. 无法获取泛型类型参数
- 2. 泛型类无法作为真实的数组元素类型
- 3. 泛型类不能直接继承Throwable
- 8.2 泛型与反射
- 使用反射创建泛型实例
- 获取泛型类型信息
- 使用反射构建泛型安全的API
- 8.3 使用通配符时的问题
- 多层嵌套通配符
- PECS原则应用不当
- 9. 泛型最佳实践
- 9.1 API设计指南
- 1. 尽可能使用泛型
- 2. 优先使用泛型集合而非原始类型集合
- 3. 通配符使用指南
- 4. 使类和方法尽可能通用
- 5. 泛型方法优于泛型类
- 9.2 性能考虑
- 1. 避免过度使用泛型
- 2. 了解装箱和拆箱带来的性能问题
- 3. 考虑使用专门的集合库
- 9.3 可读性和代码风格
- 1. 有意义的类型参数名
- 2. 适当的文档
- 3. 一致的风格
- 4. 避免类型参数隐藏
- 10. 总结
- 10.1 泛型的核心价值
- 10.2 泛型的关键概念
- 10.3 实际应用价值
- 10.4 学习建议
1. 引言
1.1 什么是泛型
泛型(Generics)是Java 5引入的一个重要特性,它允许类、接口和方法操作未知类型的对象。通过使用泛型,我们可以编写更加通用、类型安全的代码,同时保持代码的简洁性和可读性。
泛型本质上是一种"代码模板",它使用类型参数来表示类型,这些类型参数可以在实际使用时被实际的类型替换。这样,一份代码可以适用于多种不同的数据类型,而不需要为每种数据类型编写单独的实现。
简单来说,泛型就是允许我们在定义类、接口和方法时使用类型参数(type parameters),这些类型参数稍后会被用来指定具体的类型(实际类型参数)。
// 没有泛型的情况下,我们需要处理Object类型
List listWithoutGenerics = new ArrayList();
listWithoutGenerics.add("Hello");
listWithoutGenerics.add(123); // 可以添加任何类型的对象
// 使用时需要强制类型转换,且容易出错
String s = (String) listWithoutGenerics.get(0); // 正确
String s2 = (String) listWithoutGenerics.get(1); // 运行时错误:ClassCastException// 使用泛型
List<String> listWithGenerics = new ArrayList<>();
listWithGenerics.add("Hello");
// listWithGenerics.add(123); // 编译错误,只能添加String类型
String s3 = listWithGenerics.get(0); // 不需要强制类型转换
1.2 为什么需要泛型
在Java 5之前,Java集合框架(如List、Set、Map等)只能存储Object类型的对象。这带来了两个主要问题:
- 类型安全问题:可以将任何类型的对象添加到集合中,容易引入类型不匹配的错误。
- 类型转换的繁琐:从集合中获取元素时需要进行显式的类型转换,既麻烦又容易出错。
下面通过一个简单的例子来说明:
// Java 5之前的代码
List names = new ArrayList();
names.add("张三");
names.add("李四");
names.add(100); // 可以添加任何类型,编译器不会检查// 获取元素时需要类型转换
String name = (String) names.get(0);
// 如果忘记元素的实际类型,可能引发运行时错误
String anotherName = (String) names.get(2); // 运行时抛出ClassCastException
上面的代码在运行时会抛出ClassCastException
,因为我们试图将一个Integer对象转换为String类型。而且,这种错误只能在运行时才能被发现,在编译时无法检测。
泛型的引入解决了这些问题:
// 使用泛型的代码
List<String> names = new ArrayList<>();
names.add("张三");
names.add("李四");
// names.add(100); // 编译错误,类型安全// 不需要类型转换
String name = names.get(0);
String anotherName = names.get(1);
使用泛型后,编译器会确保只有String类型的对象才能被添加到names列表中,这样就避免了类型不匹配的运行时错误。同时,从列表中获取元素时不再需要显式的类型转换,代码更加简洁。
1.3 泛型的优势
泛型的引入带来了诸多优势:
- 类型安全:编译器可以在编译时检查类型约束,防止类型错误。
List<Integer> numbers = new ArrayList<>();
numbers.add(1);
numbers.add(2);
// numbers.add("三"); // 编译错误:不兼容的类型
- 消除类型转换:从泛型集合中获取元素时不需要进行显式的类型转换。
List<Integer> numbers = new ArrayList<>();
numbers.add(100);
Integer number = numbers.get(0); // 不需要类型转换
- 代码重用:通过使用类型参数,同一段代码可以操作不同类型的对象。
public class Box<T> {private T content;public void set(T content) {this.content = content;}public T get() {return content;}
}// 可以用于各种类型
Box<Integer> intBox = new Box<>();
Box<String> stringBox = new Box<>();
Box<Double> doubleBox = new Box<>();
- 提高代码可读性:通过在编译时指定类型,使代码更易于理解和维护。
// 没有泛型时,需要注释或文档说明类型
Map customerOrders = new HashMap();
// 使用泛型,类型信息一目了然
Map<Customer, List<Order>> customerOrders = new HashMap<>();
- 支持泛型算法:可以编写适用于不同类型的通用算法,而无需为每种类型重新实现。
public static <T extends Comparable<T>> T findMax(List<T> list) {if (list.isEmpty()) {return null;}T max = list.get(0);for (T item : list) {if (item.compareTo(max) > 0) {max = item;}}return max;
}// 可用于不同类型
List<Integer> numbers = Arrays.asList(1, 5, 3, 9, 7);
Integer maxNumber = findMax(numbers); // 返回9List<String> words = Arrays.asList("apple", "orange", "banana");
String maxWord = findMax(words); // 返回"orange"(按字典序)
总之,泛型使Java代码更加类型安全、简洁和灵活,同时提高了代码的可读性和可维护性。这些优势使泛型成为Java语言的一个重要特性,广泛应用于Java库和应用程序开发中。
2. 泛型基础
2.1 泛型类
泛型类是指在类声明中使用一个或多个类型参数的类。这些类型参数在类使用时可以被替换成具体的类型。泛型类的定义格式如下:
public class ClassName<T> {// T是类型参数,可以在类的定义中使用private T field;public void setField(T field) {this.field = field;}public T getField() {return field;}
}
泛型类的典型例子是Java的集合类,如ArrayList<E>
、HashMap<K, V>
等,其中E
、K
、V
都是类型参数。
下面是一个简单的泛型类示例:
// 定义一个泛型类Box,可以存储任何类型的单个对象
public class Box<T> {private T content;public Box() {}public Box(T content) {this.content = content;}public void setContent(T content) {this.content = content;}public T getContent() {return content;}public boolean hasContent() {return content != null;}
}// 使用泛型类
public class BoxDemo {public static void main(String[] args) {// 创建一个存储Integer的BoxBox<Integer> intBox = new Box<>();intBox.setContent(100);Integer intValue = intBox.getContent(); // 不需要类型转换System.out.println("整数值:" + intValue);// 创建一个存储String的BoxBox<String> stringBox = new Box<>("Hello Generics");String strValue = stringBox.getContent();System.out.println("字符串值:" + strValue);// 创建一个存储自定义类型的BoxBox<Person> personBox = new Box<>();personBox.setContent(new Person("张三", 30));Person person = personBox.getContent();System.out.println("姓名:" + person.getName() + ",年龄:" + person.getAge());}
}class Person {private String name;private int age;public Person(String name, int age) {this.name = name;this.age = age;}public String getName() { return name; }public int getAge() { return age; }
}
可以看到,使用相同的Box类,我们可以处理不同类型的数据,而不需要为每种类型创建单独的类。这体现了泛型的代码重用能力。
多个类型参数
泛型类可以有多个类型参数,各个类型参数之间用逗号分隔:
// 定义一个具有两个类型参数的键值对类
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 void setKey(K key) { this.key = key; }public void setValue(V value) { this.value = value; }@Overridepublic String toString() {return "(" + key + ", " + value + ")";}
}// 使用多类型参数的泛型类
public class PairDemo {public static void main(String[] args) {// 创建一个String-Integer对Pair<String, Integer> score = new Pair<>("张三", 95);System.out.println(score.getKey() + "的分数是:" + score.getValue());// 创建一个String-Double对Pair<String, Double> price = new Pair<>("苹果", 5.99);System.out.println(price.getKey() + "的价格是:$" + price.getValue());}
}
2.2 泛型方法
泛型方法是在方法声明中使用类型参数的方法。泛型方法可以定义在普通类中,也可以定义在泛型类中。泛型方法的类型参数独立于所在类的类型参数。
泛型方法的定义格式如下:
public <T> returnType methodName(T param) {// 方法体
}
泛型方法的类型参数列表(如上面的<T>
)位于方法返回类型之前。
下面是一些泛型方法的例子:
public class GenericMethodExample {// 泛型方法,打印任何类型的数组public static <E> void printArray(E[] array) {for (E element : array) {System.out.print(element + " ");}System.out.println();}// 返回两个值中较大的一个(需要参数类型实现Comparable接口)public static <T extends Comparable<T>> T findMax(T first, T second) {int result = first.compareTo(second);return result >= 0 ? first : second;}// 在列表中查找特定元素,返回其索引,不存在则返回-1public static <T> int findElement(List<T> list, T element) {for (int i = 0; i < list.size(); i++) {if (list.get(i).equals(element)) {return i;}}return -1;}public static void main(String[] args) {// 使用printArray方法Integer[] intArray = {1, 2, 3, 4, 5};String[] strArray = {"Hello", "World", "Generics"};System.out.println("整数数组:");printArray(intArray);System.out.println("字符串数组:");printArray(strArray);// 使用findMax方法System.out.println("较大的数:" + findMax(10, 20));System.out.println("字典序较大的字符串:" + findMax("apple", "orange"));// 使用findElement方法List<String> fruits = Arrays.asList("apple", "banana", "orange", "grape");System.out.println("'orange'的索引:" + findElement(fruits, "orange"));System.out.println("'watermelon'的索引:" + findElement(fruits, "watermelon"));}
}
当调用泛型方法时,通常不需要显式指定类型参数,因为Java编译器可以通过类型推断确定类型参数。但在某些情况下,可能需要显式指定类型参数:
List<String> list = new ArrayList<>();
// 显式指定类型参数
GenericMethodExample.<String>findElement(list, "apple");
2.3 泛型接口
泛型接口是在接口声明中使用类型参数的接口。实现泛型接口的类需要指定接口的类型参数。
泛型接口的定义格式如下:
public interface InterfaceName<T> {// 接口方法void method(T t);T getResult();
}
下面是一个泛型接口的例子:
// 定义一个泛型接口
public interface Processor<T> {T process(T input);boolean isValid(T input);
}// 实现泛型接口,指定类型参数为String
public class StringProcessor implements Processor<String> {@Overridepublic String process(String input) {return input.toUpperCase();}@Overridepublic boolean isValid(String input) {return input != null && !input.isEmpty();}
}// 实现泛型接口,指定类型参数为Integer
public class NumberProcessor implements Processor<Integer> {@Overridepublic Integer process(Integer input) {return input * 2;}@Overridepublic boolean isValid(Integer input) {return input != null && input >= 0;}
}// 演示泛型接口的使用
public class ProcessorDemo {public static void main(String[] args) {Processor<String> stringProc = new StringProcessor();String input = "hello";if (stringProc.isValid(input)) {System.out.println("处理结果:" + stringProc.process(input));}Processor<Integer> numberProc = new NumberProcessor();Integer num = 10;if (numberProc.isValid(num)) {System.out.println("处理结果:" + numberProc.process(num));}}
}
也可以定义一个泛型类来实现泛型接口,这样可以在创建类实例时指定接口的类型参数:
// 泛型类实现泛型接口
public class GenericProcessor<T> implements Processor<T> {private Function<T, T> processFunction;private Predicate<T> validationFunction;public GenericProcessor(Function<T, T> processFunction, Predicate<T> validationFunction) {this.processFunction = processFunction;this.validationFunction = validationFunction;}@Overridepublic T process(T input) {return processFunction.apply(input);}@Overridepublic boolean isValid(T input) {return validationFunction.test(input);}
}// 使用泛型类实现的泛型接口
public class FlexibleProcessorDemo {public static void main(String[] args) {// 创建一个处理String的处理器Processor<String> stringProc = new GenericProcessor<>(s -> s.toUpperCase(),s -> s != null && !s.isEmpty());// 创建一个处理Integer的处理器Processor<Integer> intProc = new GenericProcessor<>(i -> i * i,i -> i != null && i >= 0);System.out.println("字符串处理:" + stringProc.process("hello"));System.out.println("数字处理:" + intProc.process(5));}
}
2.4 类型参数命名约定
在Java中,泛型类型参数的命名有一些常见的约定。这些约定不是强制性的,但遵循这些约定可以使代码更易读和理解:
- 单个大写字母:通常使用单个大写字母来表示类型参数,这是最常见的约定。
常见的类型参数名称有:
T
- Type(类型),最常用的类型参数名E
- Element(元素),常用于集合类,如List<E>
K
- Key(键),常用于映射中的键V
- Value(值),常用于映射中的值N
- Number(数字),表示数字类型S
,U
,V
等 - 用于表示多个类型参数时的第2, 3, 4个类型参数
- 描述性名称:在某些情况下,尤其是当类型参数有特定含义时,可以使用更有描述性的名称。
public class CustomMap<KeyType, ValueType> {// 使用有描述性的名称
}public class DataProcessor<InputType, OutputType> {// 使用有描述性的名称
}
- 类型参数的使用约定:
// 定义泛型接口
public interface Repository<T> {T findById(long id);List<T> findAll();void save(T entity);
}// 定义泛型类
public class Box<T> {private T content;public T getContent() {return content;}
}// 定义泛型方法
public <T> T firstOrDefault(List<T> list, T defaultValue) {return list.isEmpty() ? defaultValue : list.get(0);
}
遵循这些命名约定可以使代码更加一致和易于理解,尤其是当其他开发人员阅读你的代码时。
3. 类型擦除
3.1 什么是类型擦除
类型擦除是Java泛型实现的关键机制。简单来说,Java的泛型是在编译时由编译器实现的,在运行时,所有的泛型信息都会被"擦除",这就是类型擦除(Type Erasure)。
类型擦除的基本原则是:
- 将所有的泛型类型参数替换为它们的边界或者
Object
(如果没有指定边界)。 - 必要时插入类型转换以保证类型安全。
- 生成桥接方法(bridge methods)以保持多态性。
下面通过一个简单的例子说明类型擦除:
// 原始泛型代码
public class Box<T> {private T content;public void setContent(T content) {this.content = content;}public T getContent() {return content;}
}// 经过类型擦除后的等效代码
public class Box {private Object content;public void setContent(Object content) {this.content = content;}public Object getContent() {return content;}
}
如上所示,编译器会将泛型类型参数T
替换为Object
(因为T
没有指定边界),并在必要的地方添加类型转换。这样,Box<String>
和Box<Integer>
在经过类型擦除后都会变成相同的类。
如果类型参数有边界,则会被替换为边界类型:
// 带有边界的泛型代码
public class Box<T extends Number> {private T content;public void setContent(T content) {this.content = content;}public T getContent() {return content;}
}// 经过类型擦除后的等效代码
public class Box {private Number content;public void setContent(Number content) {this.content = content;}public Number getContent() {return content;}
}
3.2 类型擦除的影响
类型擦除虽然简化了Java泛型的实现,但也带来了一些影响和限制:
1. 无法获取泛型类型参数的实际类型
由于类型擦除,在运行时无法获知泛型类型参数的实际类型:
public class TypeErasureExample {public static void main(String[] args) {Box<String> stringBox = new Box<>();Box<Integer> intBox = new Box<>();// 两者的类是相同的System.out.println(stringBox.getClass() == intBox.getClass()); // 输出:true// 无法获取类型参数的实际类型System.out.println(stringBox.getClass().getName()); // 输出:Box}
}
2. 无法创建泛型类型的数组
由于类型擦除,无法直接创建泛型类型的数组:
// 这行代码会导致编译错误
Box<Integer>[] boxArray = new Box<Integer>[10]; // 编译错误:不能创建泛型数组// 可以通过通配符来创建
Box<?>[] wildcardBoxArray = new Box<?>[10]; // 这是允许的
3. 无法使用instanceof
检查泛型类型
无法使用instanceof
运算符检查对象是否为特定泛型类型的实例:
Box<Integer> intBox = new Box<>();
// 这行代码会导致编译错误
if (intBox instanceof Box<Integer>) { // 编译错误:不能使用参数化类型的instanceof// ...
}// 只能检查原始类型
if (intBox instanceof Box) { // 这是允许的// ...
}
4. 泛型类的静态上下文中不能使用类型参数
public class StaticContext<T> {// 编译错误:无法在静态上下文中使用类型参数Tprivate static T staticField;// 编译错误:无法在静态方法中使用类型参数Tpublic static T getStaticValue() {return null;}// 编译错误:无法在静态块中使用类型参数Tstatic {T temp = null;}// 这是允许的:泛型方法可以是静态的,因为它有自己的类型参数public static <E> E getStaticValue(E value) {return value;}
}
5. 异常类不能是泛型的
// 编译错误:泛型类不能扩展Throwable
public class GenericException<T> extends Exception { // 编译错误private T data;public GenericException(T data) {this.data = data;}public T getData() {return data;}
}
3.3 桥接方法
桥接方法(Bridge Methods)是Java编译器在类型擦除过程中生成的特殊方法,用于保持泛型类型的多态性。
考虑以下情况:
public class Node<T> {private T data;public void setData(T data) {this.data = data;}public T getData() {return data;}
}public class
相关文章:
Java泛型详解
文章目录 1. 引言1.1 什么是泛型1.2 为什么需要泛型1.3 泛型的优势2. 泛型基础2.1 泛型类多个类型参数2.2 泛型方法2.3 泛型接口2.4 类型参数命名约定3. 类型擦除3.1 什么是类型擦除3.2 类型擦除的影响1. 无法获取泛型类型参数的实际类型2. 无法创建泛型类型的数组3. 无法使用`…...
机器学习,深度学习,神经网络,深度神经网络之间有何区别?
先说个人观点:机器学习>神经网络>深度学习≈深度神经网络。深度学习是基于深度神经网络的,深度神经网络和浅层神经网络都是神经网络,而机器学习是包括神经网络在内的算法。 一、机器学习 先说涵盖范围最广的机器学习。机器学习&#…...
AtomicInteger
AtomicInteger 是 Java 并发包 (java.util.concurrent.atomic) 中的一个原子类,用于在多线程环境下对整数进行原子操作。 核心特性 原子性 提供线程安全的原子操作(如自增、加法、比较并交换等),确保在多线程环境中操作不会被中…...
威布尔比例风险模型(Weibull Proportional Hazards Model, WPHM)详解:原理、应用与实施
威布尔比例风险模型(Weibull Proportional Hazards Model, WPHM)详解:原理、应用与实施 一、核心原理:从威布尔分布到比例风险模型 1. 威布尔分布的数学本质 威布尔分布通过两个关键参数(形状参数 (k) 和尺度参数 (\…...
Dubbo:Docker部署Zookeeper、Dubbo Admin的详细教程和SpringBoot整合Dubbo的实战与演练
🪁🍁 希望本文能给您带来帮助,如果有任何问题,欢迎批评指正!🐅🐾🍁🐥 文章目录 一、背景二、Dubbo概述三、Dubbo与SpringCloud的关系四、Dubbo技术架构五、Docker安装Zoo…...
Windows 上安装下载并配置 Apache Maven
1. 下载 Maven 访问官网: 打开 Apache Maven 下载页面。 选择版本: 下载最新的 Binary zip archive(例如 apache-maven-3.9.9-bin.zip)。 注意:不要下载 -src 版本(那是源码包)。 2. 解压 Mave…...
Unbuntu 命令
Ubuntu 命令速查表 分类命令功能描述示例/常用选项文件与目录ls列出目录内容ls -a(显示隐藏文件); ls -lh(详细列表易读大小) cd切换目录cd ~(主目录); cd ..(上级…...
机器学习-人与机器生数据的区分模型测试-数据处理1
附件为训练数据,总体的流程可以作为参考。 导入依赖 import pandas as pd import os import numpy as np from sklearn.model_selection import train_test_split,GridSearchCV from sklearn.ensemble import RandomForestClassifier,VotingClassifier from skle…...
【Linux】进程间通信(一):认识管道
📝前言: 这篇文章我们来讲讲进程间通信——认识管道 🎬个人简介:努力学习ing 📋个人专栏:Linux 🎀CSDN主页 愚润求学 🌄其他专栏:C学习笔记,C语言入门基础&a…...
AMD Vivado™ 设计套件生成加密比特流和加密密钥
概括 重要提示:有关使用AMD Vivado™ Design Suite 2016.4 及更早版本进行 eFUSE 编程的重要更新,请参阅AMD设计咨询 68832 。 本应用说明介绍了使用AMD Vivado™ 设计套件生成加密比特流和加密密钥(高级加密标准伽罗瓦/计数器模式 (AES-GCM)…...
第三十四节:特征检测与描述-SIFT/SURF 特征 (专利算法)
一、特征检测:计算机视觉的基石 在计算机视觉领域中,特征检测与描述是实现图像理解的核心技术。就像人类通过识别物体边缘、角点等特征来认知世界,算法通过检测图像中的关键特征点来实现: 图像匹配与拼接 物体识别与跟踪 三维重建 运动分析 其中,SIFT(Scale-Invariant F…...
【AI】SpringAI 第二弹:基于多模型实现流式输出
目录 一、基于多模型实现流式输出 1.1 什么是流式输出 1.2 多模型引入 1.3 代码实现 1.3.1 流式输出的API介绍 1.3.2 Flux 源码分析 二、了解 Reactor 模型 三、SSE 协议 一、基于多模型实现流式输出 1.1 什么是流式输出 流式输出(Streaming Output)是指数据在生成过程…...
SQL语句执行问题
执行顺序 select [all|distinct] <目标列的表达式1> AS [别名], <目标列的表达式2> AS [别名]... from <表名1或视图名1> [别名],<表名2或视图名2> [别名]... [where <条件表达式>] [group by <列名>] [having <条件表达式>] [ord…...
模型量化AWQ和GPTQ哪种效果好?
环境: AWQ GPTQ 问题描述: 模型量化AWQ和GPTQ哪种效果好? 解决方案: 关于AWQ(Adaptive Weight Quantization)和GPTQ(Generative Pre-trained Transformer Quantization)这两种量化方法的…...
Github 2025-05-17 Rust开源项目日报 Top10
根据Github Trendings的统计,今日(2025-05-17统计)共有10个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量Rust项目10Dart项目1RustDesk: 用Rust编写的开源远程桌面软件 创建周期:1218 天开发语言:Rust, Dart协议类型:GNU Affero General Public Li…...
借助 CodeBuddy 打造我的图标预览平台 —— IconWiz 开发实录
我正在参加CodeBuddy「首席试玩官」内容创作大赛,本文所使用的 CodeBuddy 免费下载链接:腾讯云代码助手 CodeBuddy - AI 时代的智能编程伙伴 想做一款自己的图标预览平台 这段时间我在做前端 UI 设计时,常常需要到处找图标素材,复…...
KL散度 (Kullback-Leibler Divergence)
KL散度,也称为相对熵 (Relative Entropy),是信息论中一个核心概念,用于衡量两个概率分布之间的差异。给定两个概率分布 P ( x ) P(x) P(x) 和 Q ( x ) Q(x) Q(x)(对于离散随机变量)或 p ( x ) p(x) p(x) 和 q ( x …...
【Linux网络】NAT和代理服务
NAT 之前我们讨论了,IPv4协议中,IP地址数量不充足的问题。 原始报文途径路由器WAN口时,对报文中的源IP进行替换的过程,叫做NAT。 NAT技术当前解决IP地址不够用的主要手段,是路由器的一个重要功能: NAT能…...
DeepSeek赋能电商,智能客服机器人破解大型活动人力困境
1. DeepSeek 与电商客服结合的背景 1.1 电商行业客服需求特点 电商行业具有独特的客服需求特点,这些特点决定了智能客服机器人在该行业的必要性和重要性。 高并发性:电商平台的用户数量庞大,尤其是在促销活动期间,用户咨询量会…...
Unity序列化字段、单例模式(Singleton Pattern)
一、序列化字段 在Unity中,序列化字段是一个非常重要的概念,主要用于在Unity编辑器中显示和编辑类的成员变量,或者在运行时将对象的状态保存到文件或网络中。 1.Unity序列化字段的作用 在编辑器中显示和编辑字段:默认情况下&…...
一个可拖拉实现列表排序的WPF开源控件
从零学习构建一个完整的系统 推荐一个可通过拖拉,来实现列表元素的排序的WPF控件。 项目简介 gong-wpf-dragdrop是一个开源的.NET项目,用于在WPF应用程序中实现拖放功能,可以让开发人员快速、简单的实现拖放的操作功能。 可以在同一控件内…...
hadoop.proxyuser.代理用户.授信域 用来干什么的
在Hadoop的core-site.xml文件中存在三个可选配置,如下 <property><name>hadoop.proxyuser.root.hosts</name><value>*</value> </property> <property><name>hadoop.proxyuser.root.groups</name><value…...
python 自动化教程
文章目录 前言整数变量字符串变量列表变量算术操作比较操作逻辑操作if语句for循环遍历列表while循环定义函数调用函数导入模块使用模块中的函数启动Chrome浏览器打开网页定位元素并输入内容提交表单关闭浏览器发送GET请求获取网页内容使…...
C++学习:六个月从基础到就业——C++11/14:列表初始化
C学习:六个月从基础到就业——C11/14:列表初始化 本文是我C学习之旅系列的第四十三篇技术文章,也是第三阶段"现代C特性"的第五篇,主要介绍C11/14中的列表初始化特性。查看完整系列目录了解更多内容。 引言 在C11之前&a…...
城市静音革命:当垃圾桶遇上缓冲器
缓冲垃圾桶的核心原理是通过机械或液压装置实现垃圾桶盖的缓慢闭合,包含以下技术要点:能量吸收机制液压式:通过活塞挤压油液产生阻尼力,将动能转化为热能耗散弹簧式:利用弹性变形储存和释放能量,配合摩…...
数据库的规范化设计方法---3种范式
第一范式(1NF):确保表中的每个字段都是不可分割的基本数据项。 第二范式(2NF):在满足1NF的基础上,确保非主属性完全依赖于主键。 第三范式(3NF):在满足2NF的基…...
p024基于Django的网上购物系统的设计与实现
开发语言:Python框架:djangoPython版本:python3.7.7数据库:mysql 5.7数据库工具:Navicat11开发软件:PyCharm 系统展示 管理员登录 管理员功能界面 用户管理 商品类型管理 商品信息管理 系统管理 订单管理…...
C++跨平台开发:挑战与应对策略
C跨平台开发:挑战与应对策略 在如今设备多样、操作系统碎片化的开发环境中,跨平台能力已成为衡量软件生命力与团队工程效率的重要指标。C 作为高性能系统级语言,在游戏引擎、嵌入式系统、实时渲染等领域依旧坚挺。然而,实现“一次…...
Kotlin 作用域函数(let、run、with、apply、also)对比
Kotlin 的 作用域函数(Scope Functions) 是简化代码逻辑的重要工具,它们通过临时作用域为对象提供更简洁的操作方式。以下是 let、run、with、apply、also 的对比分析: 一、核心区别对比表 函数上下文对象引用返回值是否扩展函数…...
JavaScript性能优化实战(11):前沿技术在性能优化中的应用
引言 随着Web应用复杂度和性能需求不断提高,传统的JavaScript优化技术已经无法满足某些高性能计算场景的需求。本文将深入探讨前沿Web技术如何突破JavaScript的性能瓶颈,为Web应用提供接近原生应用的性能体验。从底层计算到图形渲染,从并发处理到动画优化,我们将通过实际案…...
数据结构【AVL树】
AVL树 1.AVL树1.AVL的概念2.平衡因子 2.AVl树的实现2.1AVL树的结构2.2AVL树的插入2.3 旋转2.3.1 旋转的原则 1.AVL树 1.AVL的概念 AVL树可以是一个空树。 它的左右子树都是AVL树,且左右子树的高度差的绝对值不超过1。AVL树是一颗高度平衡搜索二叉树,通…...
电动调节V型球阀:行业应用与材质选择全解析
电动调节V型球阀:行业应用与材质选择全解析 作为工业自动化控制中的关键设备,电动调节V型球阀凭借其独特的结构设计与高性能调节能力,在石油、化工、造纸等高要求领域广泛应用。本文将从核心功能、行业应用场景、材质选择要点等方面深入解析…...
页面上如何显示特殊字符、Unicode字符?
在前端开发中,显示特殊字符通常涉及到HTML实体(HTML Entities)或 Unicode 字符的使用。以下是一些常见的方法来处理特殊字符的显示: 1、HTML实体: HTML为一些常见的特殊字符提供了预定义的实体。例如,要显…...
桌面端进程通信
以下是关于 Electron 桌面端进程通信的基本知识点总结: 一、Electron 进程模型基础 1. 进程类型与职责 进程类型职责权限主进程(Main)创建窗口、系统级操作、IPC中枢完全Node.js访问权限渲染进程(Renderer)展示Web内容、UI交互默认受限(可配置开启Node.js)预加载脚本(Prelo…...
vue2 切换主题色以及单页面好使方法
今天要新增一个页面要根据不同公司切换不同页面主题色,一点一点来,怎么快速更改 el-pagination 分页组件主题色。 <el-pagination :page-size"pageSize" :pager-count"pageCount"layout"sizes, prev, pager, next, jumper,…...
三层固定实体架构:高效实现图上的检索增强生成(RAG)
知识图谱正在成为跨各个领域组织和检索信息的强大工具。它们越来越多地与机器学习和自然语言处理技术相结合,以增强信息检索和推理能力。在本文中,我介绍了一种用于构建知识图谱的三层架构,结合了固定本体实体、文档片段和提取的命名实体。通过利用嵌入和余弦相似度,这种方…...
pnpm 与 npm 的核心区别
以下是 pnpm 与 npm 的核心区别总结,涵盖依赖管理、性能、安全性等关键维度: 1. 依赖存储机制 • npm: 每个项目的依赖独立存储于 node_modules,即使多个项目使用相同版本的包,也会重复下载和存储。例如,1…...
NVMe简介6之PCIe事务层
PCIe的事务层连接了PCIe设备核心与PCIe链路,这里主要基于PCIe事务层进行分析。事务层采用TLP传输事务,完整的TLP由TLPPrefix、TLP头、Payload和TLP Digest组成。TLP头是TLP中最关键的部分,一般由三个或四个双字的长度,其格式定义如…...
【C++详解】string各种接口如何使用保姆级攻略
文章目录 一、string介绍二、string使用构造函数析构函数赋值运算符重载string的遍历修改方法1、下标[]2、迭代器3、范围for 迭代器使用详解const迭代器反向迭代器(reverse) Capacity(容量相关)size/lengthmax_sizecapacityclear/emptyshrink_to_fit(缩容)reserve(扩…...
深入理解 requestIdleCallback:浏览器空闲时段的性能优化利器
requestIdleCallback 核心作用 requestIdleCallback 是浏览器提供的 API,用于将非关键任务延迟到浏览器空闲时段执行,避免阻塞用户交互、动画等关键任务,从而提升页面性能体验。 基本语法 const handle window.requestIdleCallback(callb…...
QML鼠标事件和按键事件
1 鼠标事件 1.1 MouseArea组件 在QML中,鼠标事件主要通过MouseArea元素处理,它是用于检测和响应鼠标交互的核心组件。常用属性 cursorShape: 光标形状acceptedButtons: 设置响应鼠标的哪些按键事件,默认为鼠标左键 Qt.LeftButton࿱…...
Animaster:一次由 CodeBuddy 主导的 CSS 动画编辑器诞生记
我正在参加CodeBuddy「首席试玩官」内容创作大赛,本文所使用的 CodeBuddy 免费下载链接:腾讯云代码助手 CodeBuddy - AI 时代的智能编程伙伴 起心动念:我想要一个动画编辑器 那天我突然想到,如果能有一个简单好用的 CSS 动画编辑…...
Git 版本控制系统入门指南
Git 版本控制系统入门指南 一、Git 基础概念 1. 什么是 Git? Git 是一个分布式版本控制系统,它可以: 跟踪文件变化协调多人协作管理代码版本支持离线工作保证数据完整性 2. Git 的特点 分布式架构快速分支操作完整历史记录数据完整性保…...
GitHub 趋势日报 (2025年05月16日)
本日报由 TrendForge 系统生成 https://trendforge.devlive.org/ 🌐 本日报中的项目描述已自动翻译为中文 📈 今日整体趋势 Top 10 排名项目名称项目描述今日获星总星数语言1TapXWorld/ChinaTextbookPDF教材。⭐ 4792⭐ 19814Roff2xming521/WeClone&…...
C/C++之内存管理
1. 内存分布 我们定义的变量对于电脑来说也叫数据,同时电脑也会把这些数据分为不同的类型,分别是局部数据,静态数据,全局数据,常量数据和动态申请数据。 在 C 中,各类数据存储位置如下: • 局…...
GitHub文档加载器设计与实现
文章结构: 目录 GitHub文档加载器设计与实现 引言 架构设计 主要组件 核心功能 文档加载流程 加载单个文件 加载目录内容 错误处理与健壮性 分支回退策略 文件类型和大小限制 安全性考虑 SSL证书验证 使用示例 基本使用 测试环境配置 最佳实践 结…...
历史数据分析——中证白酒
简介 中证白酒指数选取涉及白酒生产业务相关上市公司证券作为指数样本,为投资者提供更多样化的投资标的。 估值 中证白酒总体的PB是5.26,在过去十年间位于23.76%,属于较低的水平。 中证白酒总体的PE是20.13,在过去十年间,位于14.24%,属于较低的水平。 从估值的角度似…...
PHP8.0版本导出excel失败
环境:fastadmin框架,不是原版接手的项目。PHP8.0,mysql5.7. code // 创建一个新的 Spreadsheet 对象 $spreadsheet new Spreadsheet(); $worksheet $spreadsheet->getActiveSheet();// 设置表头 $worksheet->setCellValue(A1, ID); $worksheet…...
Seata源码—5.全局事务的创建与返回处理二
大纲 1.Seata开启分布式事务的流程总结 2.Seata生成全局事务ID的雪花算法源码 3.生成xid以及对全局事务会话进行持久化的源码 4.全局事务会话数据持久化的实现源码 5.Seata Server创建全局事务与返回xid的源码 6.Client获取Server的响应与处理的源码 7.Seata与Dubbo整合…...
mac-M系列芯片安装软件报错:***已损坏,无法打开。推出磁盘问题
因为你安装的软件在Intel 或arm芯片的mac上没有签名导致。 首先打开任何来源操作 在系统设置中配置,如下图: 2. 然后打开终端,输入: sudo spctl --master-disable然后输入电脑锁屏密码 打开了任何来源,还遇到已损坏…...