我在Vscode学Java泛型(泛型设计、擦除、通配符)

Java泛型

  • 一、泛型 Generics的意义
    • 1.1 在没有泛型的时候,集合如何存储数据
    • 1.2 引入泛型的好处
    • 1.3 注意事项
      • 1.3.1 泛型不支持基本数据类型
      • 1.3.2 当泛型指定类型,传递数据时可传入该类及其子类类型
      • 1.3.3 如果不写泛型,类型默认是Object
  • 二、泛型程序设计
    • 2.1 类型参数的含义
    • 2.2 泛型类(普通类的工厂)
      • 2.2.1 泛型类的格式
      • 2.2.2 注意事项
        • 泛型类与静态成员
          • 错误示例
          • 正确示例
      • 2.2.3 泛型类使用的例子
      • 2.2.4 泛型类的继承
        • (1)子类保持父类的泛型性
        • 2. 子类为父类的泛型类型指定具体类型
      • 3.泛型方法的继承
    • 2.3 泛型方法
      • 2.3.1 格式
      • 2.3.2 可变参数(Varargs)
      • 2.3.3 泛型方法的使用例子
        • 方式1:使用类名后面定义的泛型
        • 方式2:单独定义泛型
        • 区别总结
    • 2.4 泛型接口
      • 2.4.1 格式:
      • 2.4.2 示例
  • 三、类型擦除(Type Erasure)
    • 3.1 为什么使用类型擦除
  • 四、泛型通配符
    • 4.1 ? extends T (上界通配符)
    • 4.2 ? super T(下界通配符)
    • 4.3 无界通配符 `?`
    • 4.4 详细解释
        • `? extends T`
        • `? super T`
        • 无界通配符 `?`
    • 4.5 PECS 原则

一、泛型 Generics的意义

在泛型这个概念出现之前,程序员必须使用Object编写适用于多种类型的代码。

1.1 在没有泛型的时候,集合如何存储数据

在 Java 中,如果我们没有给集合指定类型,默认情况下,集合中的所有数据类型都会被认为是 Object 类型。这意味着我们可以向集合中添加任何类型的数据,例如 IntegerStringDouble 等。

List list = new ArrayList();
list.add(1);       // 添加 Integer 类型
list.add("hello"); // 添加 String 类型
list.add(3.14);    // 添加 Double 类型

虽然这样使用集合很灵活,但也带来了一些问题:

  1. 类型安全问题
    由于集合中可以存储任意类型的数据,我们在取出数据时无法确定其实际类型。这可能会导致类型转换错误(ClassCastException)。

    Object obj = list.get(0); // 获取集合中的第一个元素,类型是 Object
    Integer num = (Integer) obj; // 需要强制类型转换
    
  2. 丧失类型特有行为
    由于所有元素都被视为 Object 类型,我们无法直接调用其特有的方法。

    由于 Object 类型不包含特定类型的方法或行为,所以无法直接调用这些对象的特有方法。需要进行类型转换(强制类型转换)来使用具体类型的方法,这样不仅麻烦,还可能导致运行时错误(如 ClassCastException)。

    // 运行时错误示例:如果类型转换不正确,会抛出 ClassCastException
    // 尝试将 Integer 转换为 String
    String str2 = (String) list.get(1); // 运行时异常:ClassCastException
    

    例如,如果集合中存储的是 String 类型,我们不能直接调用 String 的方法,而必须先进行类型转换。

    Object obj = list.get(1);
    String str = (String) obj;
    System.out.println(str.length()); // 调用 String 的方法
    

1.2 引入泛型的好处

核心意义在于 类属性或方法的参数在定义数据类型时,可以直接使用一个标记进行占位 ,在具体使用时才设置其对应的实际数据类型,这样当设置的数据类型出现错误后,就可以在程序编译时检测 来。

为了克服上述问题,Java 5 引入了泛型。通过使用泛型,我们可以在创建集合时指定其存储的数据类型,从而在编译时就能进行类型检查,确保类型安全。

List<String> stringList = new ArrayList<>();
stringList.add("hello");
// stringList.add(1); // 编译时会报错String str = stringList.get(0); // 不需要强制类型转换
System.out.println(str.length());

使用泛型的好处如下:

  1. 类型安全
    在添加元素时,编译器会检查类型是否匹配,不匹配的类型会在编译时报错,避免了运行时的类型转换错误。

  2. 减少强制类型转换
    在获取集合中的元素时,不需要进行强制类型转换,代码更加简洁和安全。

    (把运行时期的问题提前到了编译期间,避免了强制类型转换可能出现的异常)

  3. 提高可读性
    通过指定集合中元素的类型,代码的可读性和维护性得到提高,因为开发者可以明确知道集合中应该存储什么类型的数据。


总结:

  • 在没有泛型之前,集合可以存储任意类型的对象,但这带来了类型安全和使用上的不便。

  • 泛型允许在编译时指定集合中的元素类型,从而提高了类型安全性和代码的可读性、可维护性。

1.3 注意事项

1.3.1 泛型不支持基本数据类型

泛型的类型参数只能使用引用类型的,其对于基本数据类型(intchardouble)等是不支持直接转化为Object的。

解决方案:

可以使用基本数据类型对应的包装类(如 IntegerCharacter 等)来替代。

1.3.2 当泛型指定类型,传递数据时可传入该类及其子类类型

**泛型的本质是将类型的参数化。**通过将类型作为参数引入,泛型允许在编写代码时不必指定具体的数据类型,而是在使用时才确定具体的类型。

class Animal {}
class Dog extends Animal {}
class Cat extends Animal {}List<Animal> animalList = new ArrayList<>();
animalList.add(new Dog()); // 正确:Dog 是 Animal 的子类
animalList.add(new Cat()); // 正确:Cat 是 Animal 的子类

在这里插入图片描述

1.3.3 如果不写泛型,类型默认是Object

在这里插入图片描述

二、泛型程序设计

泛型不具备继承性,但数据具备

泛型程序设计可以被分为三种:泛型类、泛型接口、泛型方法

2.1 类型参数的含义

在编程中使用字母如 T、E、K、V 等来表示变量通常是为了提高代码的可读性和通用性。

  • T (Type): 用来表示一个类型,可以是任意类型。通常用于泛型编程中。
  • E (Element): 用来表示一个元素类型,通常用于集合类的数据结构(如列表、集合等)。
  • K (Key): 用来表示键的类型,通常用于映射类型的数据结构(如字典、映射等)。
  • V (Value): 用来表示值的类型,通常与 K 一起使用,表示字典或映射中的值。

2.2 泛型类(普通类的工厂)

泛型类是一个允许使用或者多个类型参数(类型变量)的类。

例如:一个简单的泛型类可以表示一个容器类,用于存储和检索不同类型的对象。

2.2.1 泛型类的格式

这个类可以使得我们可以只关注泛型,而可以不再为数据存储的细节而分心

修饰符 class 类名<泛型> {}// 例如
public class ClassName<T1, T2, ..., Tn> {// 类体
}

在类名后面定义泛型,在创建该类对象时确定类型。

public class Box<T> {private T t;public void set(T t) {this.t = t;}public T get() {return t;}
}Box<Integer> integerBox = new Box<>();
integerBox.set(10);
Integer value = integerBox.get();

2.2.2 注意事项

泛型类与静态成员

在Java中,静态成员是指用static关键字修饰的类成员。静态成员包括静态变量(或静态字段)、静态方法、静态块和静态内部类。

  • 静态成员属于类本身,而非类的实例

    由于静态成员在类加载时就存在,而此时泛型参数还未被实例化为具体类型,不与类的实例关联的。

    • 静态成员在类加载时就已经初始化,而此时泛型参数尚未被具体化,静态成员无法知道或引用泛型参数的具体类型。

      因此,静态成员(如静态变量、静态常量、静态方法等)不能直接访问或使用类的泛型参数

    • 静态方法可以有自己的泛型参数,静态内部类也可以定义自己的泛型参数,这些参数独立于外部类的泛型参数。这允许在静态上下文中使用泛型,只要这些泛型参数是在调用静态方法或创建静态内部类的实例时具体化的。

  • 静态的成员不能使用类的泛型

    • 静态成员在类加载时就存在,而此时泛型参数尚未被具体化(因为没有创建实例),因此静态成员无法知道泛型参数的具体类型。
    • 如果静态成员能够使用泛型参数,那么在类加载时就必须确定泛型参数的类型,但这与泛型参数的设计目标相冲突,即在实例化时才确定类型。
错误示例
public class GenericClass<T> {// 静态变量// private static int staticVar; // 这个不是关于泛型的错误// 静态方法public static void staticMethod() {// T temp; // 编译错误:无法从静态上下文访问泛型类型T}// ...// 静态内部类static class StaticNestedClass {// 静态内部类不能使用外部类的泛型类型参数T// T temp; // 编译错误:无法从静态上下文访问泛型类型Tpublic void print() {// 这里会报编译错误// T temp = null; // 编译错误:无法从静态上下文访问泛型类型TSystem.out.println(static-class);}}// 静态代码块static{// T temp; // 编译错误:无法从静态上下文访问泛型类型T}
}
正确示例
public class GenericClass<T> {// 实例变量private T instanceVar;// 静态方法public static <U> U staticMethod(U u) { // 使用独立的泛型参数Ureturn u;}// 可以进行正常的构造方法和setter和getter方法// 静态内部类static class StaticNestedClass<U> { // 使用独立的泛型参数Uprivate U temp;public StaticNestedClass(U temp) {this.temp = temp;}// ...}// 实例方法public void instanceMethod(){T temp = instanceVar;System.out.println(temp);}// 静态代码块static{// 使用原始类型或其他方式初始化静态成员List list = new ArrayList(); // 假设list是静态成员}
}
  • 静态方法:尝试使用类的泛型参数T,这是不允许的。修改后,静态方法使用了一个独立的泛型参数U,这样就避免了依赖于类的泛型参数。

  • 静态内部类:尝试使用外部类的泛型参数T,这也是不允许的。修改后,静态内部类使用了自己的泛型参数U

  • 静态代码块:尝试使用泛型参数T,这是不允许的。修改后,静态代码块中使用了原始类型或其他方式来初始化静态成员,避免了直接使用泛型参数。

2.2.3 泛型类使用的例子

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;}
}

传入什么样的类型就会转变为该类型输出

在这里插入图片描述

2.2.4 泛型类的继承

(1)子类保持父类的泛型性
// 泛型父类  
class Animal<T> {  private T food;  public Animal(T food) {  this.food = food;  }  public T getFood() {  return food;  }  public void setFood(T food) {  this.food = food;  }  
}  // 泛型子类,保持父类的泛型性  
class Person<T> extends Animal<T> {  public Person(T food) {  super(food);  }  
}  // 使用  
Person<String> person = new Person<>("apple");  
System.out.println(person.getFood()); // 输出: apple
2. 子类为父类的泛型类型指定具体类型
// 泛型父类  
class Animal<T> {  // ...(与上面相同)  
}  // 非泛型子类,为父类的泛型类型指定具体类型  
class Dog extends Animal<String> {  public Dog(String food) {  super(food);  }  
}  // 使用  
Dog dog = new Dog("bone");  
System.out.println(dog.getFood()); // 输出: bone

3.泛型方法的继承

// 父类  
class Parent {  // 泛型方法  public <T> void print(T item) {  System.out.println(item);  }  
}  // 子类  
class Child extends Parent {  // 子类可以调用父类的泛型方法  public void test() {  print("Hello, World!"); // 调用继承自Parent的泛型方法  }  // 如果子类需要定义与父类相同签名的泛型方法,则实际上是覆盖父类的方法  // 但在这个例子中,我们没有这样做  
}  // 使用  
Child child = new Child();  
child.test(); // 输出: Hello, World!

2.3 泛型方法

泛型方法在Java中是用于处理多种数据类型的灵活工具。泛型方法允许在方法定义中使用类型参数
通过使用泛型,可以在方法中处理不同类型的数据,而不需要重载多个方法。

2.3.1 格式

`修饰符 <泛型> 返回值类型 方法名(形参列表){ }`public <T> void show(T t) {}

在修饰符后面定义泛型,在调用该方法时确定类型。

public class GenericMethodExample {public <T> void printArray(T[] array) {for (T element : array) {System.out.println(element);}}
}GenericMethodExample example = new GenericMethodExample();
Integer[] intArray = {1, 2, 3, 4, 5};
example.<Integer>printArray(intArray); // 可以省略 <Integer>

2.3.2 可变参数(Varargs)

可变参数:方法参数个数不固定,用…表示,其底层实现是通过数组来实现的

形参列表中可变参数只能有一个X
可变参数必须放在形参列表的最后面

  • 泛型方法 addAll 来动态地向 ArrayList 中添加不同类型的元素
public class CC {private CC() {}//    可变参数public  static <E> void addAll(ArrayList<E> list, E... e) {for (E e1 : e) {list.add(e1);}}
}public static void main(String[] args) {ArrayList<String> list = new ArrayList<>();CC.addAll(list, "a", "b", "c", "d");System.out.println(list);
}
  • 多个参数加法
public class Test {public static void main(String[] args) {int x=test(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);System.out.println(x);}public static int test(int... args) {int sum=0;//    可变参数for (int arg : args) {sum += arg;System.out.println(arg);}return sum;}
}

2.3.3 泛型方法的使用例子

一个简单的泛型方法可以用来交换两个对象的值。

public class Utils {public static <T> void swap(T[] array, int i, int j) {T temp = array[i];array[i] = array[j];array[j] = temp;}public static void main(String[] args) {Integer[] intArray = {1, 2, 3, 4};swap(intArray, 0, 3);for (int i : intArray) {System.out.print(i + " ");}String[] strArray = {"a", "b", "c", "d"};swap(strArray, 1, 2);for (String s : strArray) {System.out.print(s + " ");}}
}

在这个例子中,swap方法是一个泛型方法,类型参数T在方法定义中声明,可以在调用时指定具体的类型,如IntegerString

泛型方法是Java中用于处理多种数据类型的灵活工具。通过使用泛型,可以在方法中处理不同类型的数据,而不需要重载多个方法。泛型方法有两种定义方式:类名后定义泛型和方法上单独定义泛型。

方式1:使用类名后面定义的泛型

在这种方式中,泛型类型在类定义时声明,并在类的所有方法中可用。适用于需要在类的多个方法中使用相同泛型类型的情况。

// 使用类名后面定义的泛型
public class GenericClass<E> {// 泛型类型E在整个类中可用public void show(E e) {System.out.println(e);}public E getValue(E e) {return e;}public static void main(String[] args) {GenericClass<String> genericClass = new GenericClass<>();genericClass.show("Hello");System.out.println(genericClass.getValue("World"));}
}
方式2:单独定义泛型

这种方式在方法定义时单独声明泛型类型,适用于仅在某个特定方法中需要使用泛型类型的情况。

// 单独定义泛型的方法
public class GenericMethodExample {// 在方法中单独定义泛型类型Tpublic <T> void show(T t) {System.out.println(t);}public <T> T getValue(T t) {return t;}public static void main(String[] args) {GenericMethodExample example = new GenericMethodExample();example.show("Hello");System.out.println(example.getValue("World"));example.show(123);System.out.println(example.getValue(456));}
}
区别总结
  • 作用域不同:类名后定义的泛型类型在整个类中可见和可用,而单独定义的泛型类型仅在当前方法中可见和可用。
  • 适用范围不同:类名后定义的泛型适用于需要在多个方法中使用相同泛型类型的情况,而单独定义的泛型适用于仅在特定方法中需要使用泛型类型的情况。

2.4 泛型接口

可以使接口能够处理多种不同的数据类型,而无需指定具体的数据类型。泛型接口在定义时包含一个或多个类型参数,这些类型参数在接口的实现类中可以具体化为特定的类型。

2.4.1 格式:

修饰符 interface 接口名<泛型> { }// 定义一个泛型接口
public interface GenericInterface<T> {void doSomething(T t);
}

在接口名后面定义泛型,实现类确定类型或实现类延续泛型。

public interface Container<T> {void add(T item);T get(int index);
}public class StringContainer implements Container<String> {private List<String> items = new ArrayList<>();@Overridepublic void add(String item) {items.add(item);}@Overridepublic String get(int index) {return items.get(index);}
}

2.4.2 示例

// 实现泛型接口的类
public class GenericClass implements GenericInterface<String> {@Overridepublic void doSomething(String t) {System.out.println("Doing something with: " + t);}
}public class Main {public static void main(String[] args) {GenericClass gc = new GenericClass();gc.doSomething("Hello, World!");}
}

也可以创建一个泛型类来实现泛型接口

// 定义一个泛型类来实现泛型接口
public class GenericClass<T> implements GenericInterface<T> {@Overridepublic void doSomething(T t) {System.out.println("Doing something with: " + t);}
}public class Main {public static void main(String[] args) {GenericClass<String> gcString = new GenericClass<>();gcString.doSomething("Hello, World!");GenericClass<Integer> gcInteger = new GenericClass<>();gcInteger.doSomething(123);}
}

三、类型擦除(Type Erasure)

它指的是在编译期间,编译器会删除(或擦除)所有泛型类型信息,使得在运行时,程序只能看到原始的类型(即泛型参数被擦除)。

类型擦除:在编译时,Java会将泛型类型信息擦除,将泛型类型替换为其上限类型(默认是0bject)。

List在编译后会被转换为List,这意味着在运行时无法获取到String类型的信息。
在这里插入图片描述

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

在编译后,泛型类型 T 会被擦除为 Object

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

3.1 为什么使用类型擦除

  1. 兼容性:类型擦除使得泛型代码能够与 Java 1.4 及以前的版本兼容,因为在这些版本中并没有泛型支持。
  2. 简化:通过类型擦除,可以减少对运行时类型信息的需求,从而降低运行时开销。

四、泛型通配符

泛型通配符(wildcards)是泛型编程中的一个重要概念,主要用于在不确定泛型类型时,提供灵活的类型约束。

4.1 ? extends T (上界通配符)

  • 主要用途:用于返回类型限定。

  • 适用场景:主要用于从集合中读取数据。(不能往里存,只能往外取)

  • 限制:不能用于参数类型限定,因为编译器无法确定具体类型,只能接受 null

  • 在这里插入图片描述

  • 定义与使用

    • <? extends T>` 表示通配符类型的上界,即表示参数化类型可以是 `T` 类型或 `T` 的任何子类。
    • 可以安全地从这样的通配符类型中读取数据,因为可以确保获取的元素至少是 T 类型的实例或其子类

  • 不能往里存的原因:与多态的概念的类似

    • 编译器无法确定具体的子类类型,因此不能安全地向这样的列表中添加任何具体的子类实例,只能添加 null

示例

import java.util.ArrayList;
import java.util.List;public class Test {public static void main(String[] args) {List<Dog> dogs = new ArrayList<>();dogs.add(new Dog());dogs.add(new Dog());List<? extends Animal> animalList = dogs;for (Animal animal : animalList) {System.out.println(animal.getClass().getSimpleName()); // 读取时安全}// animalList.add(new Dog()); // 编译错误,不能添加元素}
}class Animal {}
class Dog extends Animal {}
class Cat extends Animal {}

4.2 ? super T(下界通配符)

  • 主要用途:用于参数类型限定。

  • 适用场景:主要用于向集合中写入数据。(不能往外取,只能往外取)

  • 限制:不能用于返回类型限定,因为返回的类型只能用 Object 接收。

  • 在这里插入图片描述

  • 定义与使用

    • <? super T> 表示通配符类型的下界,即表示参数化类型是 T 类型或 T 的任何超类(父类),直至 Object
    • 可以安全地向这样的通配符类型中添加 T 类型及其子类的实例。
  • 往里存的原因

    • 允许添加 Father 类型及其子类的实例,因为可以确保这样的列表至少可以接受 Father 类型的实例。

示例

import java.util.ArrayList;
import java.util.List;public class Test {public static void main(String[] args) {List<Animal> animals = new ArrayList<>();List<? super Animal> animalList = animals;animalList.add(new Dog()); // 可以添加 Dog 类型animalList.add(new Cat()); // 可以添加 Cat 类型animalList.add(new Animal()); // 可以添加 Animal 类型for (Object obj : animalList) {System.out.println(obj.getClass().getSimpleName()); // 读取时只能确保是 Object 类型}}
}class Animal {}
class Dog extends Animal {}
class Cat extends Animal {}

4.3 无界通配符 ?

  • 主要用途:表示未知类型。

  • 适用场景:一般用于参数和返回类型都不重要的情况。

  • 限制:不能用于方法参数传入,也不能用于方法返回。

    • 读操作:可以安全地读取列表中的元素,但因为没有具体类型信息,所以只能读取为 Object 类型。这意味着不能对这些元素进行具体的操作,只能执行泛用的 Object 方法。

    • 写操作:不能向列表中添加元素,除非添加 null。这是因为你不知道列表的具体类型,添加具体类型的元素可能会破坏列表的类型安全性。

示例

import java.util.ArrayList;
import java.util.List;public class Test {public static void main(String[] args) {List<String> strings = new ArrayList<>();strings.add("Hello");strings.add("World");List<?> unknownList = strings;for (Object obj : unknownList) {System.out.println(obj); // 读取时只能确保是 Object 类型}// unknownList.add("hello"); // 编译错误:无法添加元素unknownList.add(null); // 可以添加 null}
}

4.4 详细解释

? extends T

? extends T:用于返回类型限定,适合读取操作,不能用于参数类型限定。

在使用 ? extends T 时,必须确保列表在访问前已经被填充了数据。在读取元素时,编译器能够确保读取到的元素类型为 T 或其子类,但无法确定具体类型,因此不能添加元素。

? super T

? super T:用于参数类型限定,适合写入操作,不能用于返回类型限定。

在使用 ? super T 时,可以安全地向列表中添加 T 类型或其子类的对象,因为编译器知道列表中至少可以容纳 T 类型的对象。但是在读取时,只能确保读取到的元素是 Object 类型,因此需要进行类型转换。

无界通配符 ?

?:表示未知类型,通常用于对类型没有特别要求的场景,不能用于方法参数传入和返回类型。

无界通配符 ? 表示未知类型,可以用于任何类型,但在读取时只能确保类型为 Object,在添加时只能添加 null

4.5 PECS 原则

  1. Producer Extends:如果你有一个生产者方法,它返回泛型类型T的实例,那么你应该使用<? extends T>。这是因为生产者返回的实例可以是T的子类型,因此使用extends通配符可以确保你能处理这些子类型。
  2. Consumer Super:如果你有一个消费者方法,它接收泛型类型T的实例,那么你应该使用<? super T>。这是因为消费者方法接收的实例可以是T的父类型,因此使用super通配符可以确保你可以传递T及其子类型的实例。

在这里插入图片描述

Producer Extends Consumer Super 原则

  • 当你需要从集合中获取【生产、输出】元素时(Producer),使用 <? extends T>
  • 当你需要向集合中添加【消费、输入】元素时(Consumer),使用 <? super T>

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

Java语言程序设计——篇九(1)

&#x1f33f;&#x1f33f;&#x1f33f;跟随博主脚步&#xff0c;从这里开始→博主主页&#x1f33f;&#x1f33f;&#x1f33f; 内部类 概述内部类—内部类的分类成员内部类实战演练 局部内部类实战演练 匿名内部类实战演练 静态内部类实战演练 概述 内部类或嵌套类&#…

【计算机网络】RIP路由协议实验

一&#xff1a;实验目的 1&#xff1a;掌握在路由器上配置RIPv2。 二&#xff1a;实验仪器设备及软件 硬件&#xff1a;RCMS交换机、网线、内网网卡接口、Windows 2019操作系统的计算机等。具体为&#xff1a;三层交换机1台、路由器2台。 软件&#xff1a;wireshark软件、记…

virtualbox ubuntu扩充磁盘大小

首先在虚拟存储管理里面修改磁盘大小 然后安装gparted sudo gparted 打开管理工具 选中要调整的区域右键选择调整区域大小 拖动上述位置就可以实现扩容。完成后点击应用 然后重启虚拟机即可。

WSL快速入门

1. WSL介绍 WSL文档地址&#xff1a;https://learn.microsoft.com/zh-cn/windows/wsl WSL&#xff1a;全称 Windows Subsystem for Linux&#xff0c;即windows上的Linux子系统&#xff08;虚拟机工具&#xff09;。是Win10推出的全新特性&#xff0c;可以更轻量地在Windows系统…

vue3中element tabs标签页 tab-click事件无法拿到最新值

element tabs标签页有2个常用的事件方法&#xff0c;tab-click 和 tab-change tab-click事件 tab-click事件&#xff1a;当用户点击Tab标签时触发&#xff0c;有2个返回参数&#xff0c; (pane: TabsPaneContext, ev: Event) pane.props.name 中可以获取到最新的tab页签绑定值 …

定时器+外部中断实现NEC红外线协议解码

一、前言 1.1 功能介绍 随着科技的进步和人们生活水平的提高&#xff0c;红外遥控器已经成为了日常生活中不可或缺的电子设备之一&#xff0c;广泛应用于电视、空调、音响等多种家电产品中。 传统的红外遥控器通常只能实现预设的有限功能&#xff0c;无法满足用户对设备更加智…

WHAT - 屏幕 dpr 设备像素比率

目录 一、设备像素比率 (DPR)二、DPR 与每英寸点数 (DPI)三、dpr2 和 dpr1 有什么区别设备像素比率为 1 (DPR 1)设备像素比率为 2 (DPR 2)实际区别示例代码例子 一、设备像素比率 (DPR) DPR&#xff08;设备像素比率&#xff09;常常在讨论屏幕分辨率和显示质量时使用。 设…

RockyLinux 9 PXE Server bios+uefi 自动化部署 RockLinux 8 9

pxe server 前言 PXE&#xff08;Preboot eXecution Environment&#xff0c;预启动执行环境&#xff09;是一种网络启动协议&#xff0c;允许计算机通过网络启动而不是使用本地硬盘。PXE服务器是实现这一功能的服务器&#xff0c;它提供了启动镜像和引导加载程序&#xff0c;…

1.2 单链表定义及操作实现(链式结构)

1.单链表定义 链式存储&#xff1a;用一组任意的存储单元存储线性表中的数据元素。用这种方法存储的线性 表简称线性链表。 为了正确表示结点间的逻辑关系&#xff0c;在存储每个结点值的同时&#xff0c;还必须存储指示其直接 后继结点的地址&#xff08;或位置&#xff09;…

故障诊断 | 基于Transformer故障诊断分类预测(Matlab)

文章目录 预测效果文章概述程序设计参考资料预测效果 文章概述 Transformer故障诊断/分类预测 | 基于Transformer故障诊断分类预测(Matlab) Transformer 模型本质上都是预训练语言模型,大都采用自监督学习 (Self-supervised learning) 的方式在大量生语料上进行训练,也就是…

Java解析epub电子书文件实战demo

如何使用 Java、Spring Boot 和 Epublib 库解析存储在阿里云对象存储服务&#xff08;OSS&#xff09;上的 EPUB 文件。这里将指导您完成设置必要依赖项、配置 OSS 客户端以及编写服务以读取和提取 EPUB 文件章节的全过程。 步骤1&#xff1a;添加依赖项 首先&#xff0c;将 E…

微信小程序消息订阅处理实践

微信小程序提供订阅消息功能&#xff0c;分为一次性订阅消息、长期订阅消息。长期订阅消息目前只针对民生、金融、教育等有线下服务场景的类目开放。这些只是大类&#xff0c;并不表示所包含的所有二级类目都能申请长期订阅消息&#xff0c;具体看官方文档。 另一个长期订阅消…

RNN(一)——循环神经网络的实现

文章目录 一、循环神经网络RNN1.RNN是什么2.RNN的语言模型3.RNN的结构形式 二、完整代码三、代码解读1.参数return_sequences2.调参过程 一、循环神经网络RNN 1.RNN是什么 循环神经网络RNN主要体现在上下文对理解的重要性&#xff0c;他比传统的神经网络&#xff08;传统的神…

04 卷积神经网络

目录 1. 基本概念 1.1 卷积神经网络 1.2 卷积 1.3 汇聚&#xff08;池化&#xff09; 2. CNN网络架构及参数学习 2.1 网络架构 2.2 参数学习 3. 典型的卷积神经网络 3.1 LeNet-5 3.2 AlexNet 3.3 Inception网络 3.4 残差网络 4. 其他卷积方式 1. 基本概念 1.1 …

ReentrantReadWriteLock详解

目录 ReentrantReadWriteLock详解1、ReentrantReadWriteLock简介2、ReentrantReadWriteLock类继承结构和类属性3、ReentrantReadWriteLock的读写锁原理分析4、ReentrantReadWriteLock.WriteLock类的核心方法详解非公平写锁的获取非公平写锁的释放公平写锁的获取公平写锁的释放 …

全网最最实用--模型高效推理:量化基础

文章目录 一、量化基础--计算机中数的表示1. 原码&#xff08;Sign-Magnitude&#xff09;2. 反码&#xff08;Ones Complement&#xff09;3. 补码&#xff08;Twos Complement&#xff09;4. 浮点数&#xff08;Floating Point&#xff09;a.常用的浮点数标准--IEEE 754(FP32…

ElasticSearch核心之DSL查询语句实战

什么是DSL&#xff1f; Elasticsearch提供丰富且灵活的查询语言叫做DSL查询(Query DSL),它允许你构建更加复杂、强大的查询。 DSL(Domain Specific Language特定领域语言)以JSON请求体的形式出现。目前常用的框架查询方法什么的底层都是构建DSL语句实现的&#xff0c;所以你必…

跨境电商独立站:Shopify/Wordpress/店匠选哪个?

在面对不断增加的平台运营压力时&#xff0c;不少跨境电商的商家逐渐将注意力转向建立自己的独立站。据《中国跨境出口电商发展报告&#xff08;2022&#xff09;》所示&#xff0c;中国拥有的独立站数量在2022年已接近20万个&#xff0c;这表明独立站已成为卖家拓展海外市场的…

IndentationError:unindent does not match any outer indentation level

IndentationError:unindent does not match any outer indentation level 目录 IndentationError:unindent does not match any outer indentation level 【常见模块错误】 【解决方案】 原因分析&#xff1a; 解决方法&#xff1a; 示例&#xff1a; 欢迎来到我的主页&am…

正则采集器——前端搭建

前端使用有名的饿了么管理后台&#xff0c;vue3版本vue3-element-admin&#xff0c;首先从gitee中克隆一个vue3-element-admin模板代码vue3-element-admin: Vue3 Element Admin开箱即用的中后台管理系统前端解决方案&#xff0c;然后在此基础上进行开发。 1、修改vite.config.…