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

[Java]泛型(二)泛型方法

1.定义

在 Java 中,泛型方法是指在方法声明中使用泛型类型参数的一种方法。它使得方法能够处理不同类型的对象,而不需要为每种类型写多个方法,从而提高代码的重用性。

泛型方法与泛型类不同,泛型方法的类型参数仅仅存在于方法的范围内,而不是整个类的范围内。使用泛型方法时,可以在方法调用时指定实际的类型参数。

2.基本语法

泛型方法的语法结构与普通方法类似,只不过在方法返回类型前加上一个泛型参数列表(用尖括号<>表示)。泛型参数列表是类型参数(例如 <T>),可以是一个或多个。

public <T> 返回类型 方法名(参数列表) {// 方法体
}

例如: 

public <T> void methodName(T param) {// 方法实现
}

3.泛型方法的示例

3.1 示例:

3.1.1 示例1:打印任意类型的值

public class GenericMethodExample {// 泛型方法:打印任意类型的值public <T> void printValue(T value) {System.out.println(value);}public static void main(String[] args) {GenericMethodExample example = new GenericMethodExample();example.printValue("Hello, World!");  // 输出:Hello, World!example.printValue(100);              // 输出:100example.printValue(3.14);             // 输出:3.14}
}
  • 这里的 <T> 是类型参数的声明,表示该方法可以接受任何类型的参数。
  • printValue 方法中,T 代表传递给方法的实际类型,在调用时根据实际传入的参数类型自动推导。

3.1.2 示例2:交换两个元素的位置

public class GenericMethodExample {// 泛型方法:交换两个元素的位置public <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) {GenericMethodExample example = new GenericMethodExample();// 测试交换整数数组中的元素Integer[] intArray = {1, 2, 3, 4};example.swap(intArray, 0, 2);System.out.println(java.util.Arrays.toString(intArray));  // 输出:[3, 2, 1, 4]// 测试交换字符串数组中的元素String[] strArray = {"apple", "banana", "cherry"};example.swap(strArray, 0, 1);System.out.println(java.util.Arrays.toString(strArray));  // 输出:[banana, apple, cherry]}
}
  • 在这个例子中,swap 方法使用了泛型 T[] 数组和泛型类型 T,允许我们交换任何类型数组中的元素。
  • T[] 表示该方法可以接受任何类型的数组,并且 T 会在运行时根据传递的实际数组类型确定。

4.泛型方法的限制和注意事项

4.1 类型擦除

4.1.1 定义:

类型擦除(Type Erasure)是 Java 泛型的一项关键特性,它在编译时通过将泛型类型转换为原始类型(通常是 Object 或指定的边界类型)来实现泛型的类型安全,但在运行时丢失了对类型参数的具体信息。

4.1.2 为什么需要类型擦除?

Java 的泛型是 编译时的类型安全检查,而不是运行时的类型参数。因此,泛型类型的具体信息只存在于编译阶段,编译器会根据类型擦除机制将泛型转换成原始类型(通常是 Object)。这种做法可以在保证类型安全的同时,避免运行时因类型信息的传递导致的性能问题。

泛型的出现是为了增强代码的灵活性,同时 保持类型安全。但是,Java 的泛型并不支持在运行时保留类型信息,这是由于 Java的设计选择性能优化考虑。

4.2 类型擦除如何工作?

在 Java 编译器将泛型代码转换为字节码时,所有的泛型类型参数都会被替换为它们的 原始类型。对于不带类型边界的泛型,默认使用 Object 来替代类型参数。而对于有类型边界的泛型,编译器会将类型参数替换为边界类型。

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

编译后,Box<T>会变成:

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

所以 类型擦除的结果 是:

  • 泛型参数 T 被替换为 Object
  • 编译器会删除 T 的所有信息,只保留 Object 或者它的边界类型(如 Number)。
  • 你在使用泛型时,实际上操作的是 Object,即使在代码中看起来是 T

4.3  类型擦除的原因

类型擦除是 Java 设计的一种机制,其目的是确保 Java 的兼容性,同时增强泛型的灵活性:

  1. 向后兼容性

    • 在 Java 5 引入泛型之前,Java 已经有大量的代码和类库。为了让新版本的 Java 仍然能够兼容这些旧代码,泛型类型的具体信息必须在编译时被擦除。
    • 这样,即使老代码不支持泛型,新旧代码依然能够共存。
  2. 性能优化

    • 在 Java 中,泛型是 编译时的 类型检查,而不是 运行时的 类型参数。泛型的引入是为了提高代码的 类型安全,但同时为了避免在运行时进行类型检查和反射等耗费性能的操作,所有的类型信息会在编译后被擦除。
    • 运行时只有原始类型(如 Object),不再有泛型类型的负担,从而提高程序的性能。
  3. 简化实现

    • 由于在运行时不需要额外的类型信息,Java 只需要维护单一的原始类型(通常是 Object)的字节码结构。这简化了 Java 虚拟机(JVM)的实现,不需要考虑复杂的泛型类型。

4.4 类型擦除的具体实现细节

4.4.1 泛型的类型擦除

  • 编译器会用 Object 替换没有指定类型边界的泛型类型参数(如 T)。
  • 如果指定了类型边界(例如 T extends Number),编译器会用边界类型替代泛型类型参数。
示例:
public class Box<T> {public T value;public void setValue(T value) {this.value = value;}
}

类型擦除后的字节码

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

4.4.2 具有边界的泛型

如果泛型带有边界,擦除时会使用边界类型。

public class Box<T extends Number> {public T value;public void setValue(T value) {this.value = value;}
}

类型擦除后的字节码

public class Box {public Number value;public void setValue(Number value) {this.value = value;}
}

4.4.3 为什么不能在运行时获取泛型类型信息?

  • 类型擦除后,泛型类型的具体信息被丢弃,JVM 在运行时只知道原始类型。这意味着你无法在运行时直接获取一个泛型类型参数的信息,例如通过 getClass()instanceof 等方法。这是因为在运行时,泛型参数已经被擦除为 Object 或指定的边界类型。

例如:

public <T> void printType(T value) {System.out.println(value.getClass().getName());  // 获取类型信息
}

调用 printType(new Integer(10)),输出 java.lang.Integer。但是,如果你使用 T 类型的 getClass(),它并不会返回 T 的类型,而是 Object,因为在运行时 T 已经被擦除。

4.5 泛型数组与类型擦除的冲突

在 Java 中创建泛型数组会导致问题,因为 类型擦除后的 T 被替换成了 Object,这使得编译器无法知道实际数组的类型。例如:

public <T> void example() {T[] array = new T[10];  // 编译错误:不能创建泛型数组
}

原因

  • 由于 T 在编译时被擦除为 Object,因此编译器无法确定 T[] 代表的实际类型。Java 无法知道该数组是 Integer[]String[] 还是其他类型的数组。
  • 数组的类型必须在运行时确定,因此无法通过泛型类型直接创建数组。

解决方法:使用反射或者使用 Object[] 来替代泛型数组。

4.6 类型擦除对集合类的影响

类型擦除影响最明显的地方是在集合类中,例如 List<T>,我们不能通过 List<T> 来确定元素的具体类型,因为泛型已经被擦除。

List<Integer> list = new ArrayList<>();

虽然我们创建了 List<Integer> 类型的集合,但在运行时,它其实是一个 List 类型的集合,元素类型已经变为 Object

4.7 如何解决类型擦除带来的问题

4.7.1 解决办法

为了绕开类型擦除的限制,可以使用以下几种方法:

  1. 反射(Reflection): 使用反射可以动态获取类型信息,比如 Array.newInstance() 可以在运行时创建泛型类型的数组。

  2. 传递 Class 对象: 通过传递 Class<T> 类型参数,我们可以在泛型方法中通过反射创建具体类型的数组。

  3. 使用 List 或其他集合类: 尽量避免使用数组,在需要泛型集合时,可以使用如 ArrayList<T>HashMap<K, V> 等通用集合类,它们对泛型有更好的支持,并且能够处理不同类型的数据。

4.7.2 注意事项

1.不能直接使用泛型数组

如之前所述,泛型数组在 Java 中是不被允许的,无法直接创建 T[] 类型的数组。需要使用 Object[] 或者通过反射等方式来实现。

2.类型参数的作用范围

泛型类型仅在方法体内有效。方法外部和其他类的泛型类型不会受到影响。

5.泛型方法的类型推导

5.1 定义

类型推导是指 Java 编译器根据实际传递给泛型方法的参数类型,自动推断出泛型类型参数的具体类型。这意味着你可以省略显式声明类型参数,编译器会在调用方法时根据传入的实参类型推导出泛型类型。

5.2 泛型方法类型推导的关键点

  • 编译器根据传入的实际类型来推导出泛型参数。
  • 你不需要显式指定泛型类型,编译器会根据方法调用的上下文进行推断。

5.3 类型推导的基本规则

在 Java 中,泛型方法的类型推导遵循以下基本规则:

  • 规则 1:根据方法调用时传递的参数类型自动推导泛型类型

    Java 编译器会根据传递给方法的实参的类型自动推导出泛型的实际类型。

  • 规则 2:类型推导基于方法调用时的参数类型

    传递给方法的参数类型将用于推导出泛型类型参数的类型。

  • 规则 3:如果方法调用的上下文不能唯一确定泛型类型,编译器会报错

    如果无法根据传入的参数明确推导出泛型类型,编译器会抛出错误。

5.4 泛型方法的类型推导示例:

5.4.1 示例1:简单的类型推导

public class GenericMethodExample {// 泛型方法,自动推导类型 Tpublic <T> void printArray(T[] array) {for (T element : array) {System.out.println(element);}}public static void main(String[] args) {GenericMethodExample example = new GenericMethodExample();// 传入 Integer 数组,编译器推导出 T 为 IntegerInteger[] intArray = {1, 2, 3, 4};example.printArray(intArray);// 传入 String 数组,编译器推导出 T 为 StringString[] strArray = {"apple", "banana", "cherry"};example.printArray(strArray);}
}
/*
输出:
1
2
3
4
apple
banana
cherry
*/

解释

  • printArray 方法中,类型参数 T 被推导为 Integer(在传递 Integer[] 时)和 String(在传递 String[] 时)。你无需显式指定类型参数,编译器会根据参数类型自动推导。

5.4.2 示例2:类型推导与多个参数

public class GenericMethodExample {// 泛型方法,接受多个参数public <T, U> void printPair(T first, U second) {System.out.println("First: " + first);System.out.println("Second: " + second);}public static void main(String[] args) {GenericMethodExample example = new GenericMethodExample();// 传递 Integer 和 String,编译器推导出 T 为 Integer,U 为 Stringexample.printPair(1, "apple");// 传递 Double 和 Character,编译器推导出 T 为 Double,U 为 Characterexample.printPair(3.14, 'A');}
}
/*
输出:
First: 1
Second: apple
First: 3.14
Second: A
*/

解释

  • 在这个例子中,printPair 方法接受两个参数 firstsecond,分别是不同的类型。编译器根据传入的参数类型推导出泛型类型 TU 的具体类型。

5.5 类型推导的局限性与注意事项

虽然类型推导非常方便,但它也有一些局限性和需要注意的地方:

5.5.1 方法无法推导泛型类型时会报错

当 Java 编译器无法根据传递给泛型方法的参数推导出唯一的类型时,它会报错。例如,如果你传递了一个 null 值或其他无法确定类型的值,编译器就无法推导出类型。

public class GenericMethodExample {public <T> void printElement(T element) {System.out.println(element);}public static void main(String[] args) {GenericMethodExample example = new GenericMethodExample();// 编译器无法推导出类型,因为传入的是 nullexample.printElement(null);  // 编译错误:null 值无法推导出具体类型}
}

解释

  • null 作为一个类型不确定的值,不能直接用于推导类型。在这种情况下,编译器无法确定泛型参数 T 的具体类型,因而报错。
5.5.2 类型推导失败时的明确类型声明

在无法自动推导出类型时,你可以显式地指定泛型类型参数。例如,使用 printElement(Integer) 来明确指定类型,而不是依赖推导。

public class GenericMethodExample {public <T> void printElement(T element) {System.out.println(element);}public static void main(String[] args) {GenericMethodExample example = new GenericMethodExample();// 显式指定类型为 Integerexample.<Integer>printElement(10);}
}
 5.5.3 类型擦除与泛型类型推导(不能在泛型方法中直接创建数组)

即使你在方法中使用了泛型,Java 编译器在运行时会执行类型擦除,所有的泛型类型会在运行时被擦除为原始类型(通常是 Object)。因此,你无法在运行时直接通过泛型类型获得具体类型信息。

关于泛型方法中不能直接创建数组,因为类型擦除会把数组元素类型擦除,导致数组创建的时候u程序不知道要创建什么类型的数组,无法为数组分配空间,但是,有同学可能会有疑问:不是还有泛型类型推导吗,在使用泛型方法的时候不是将数组的类型传递进来了吗?为什么不能通过类型推导传递数组的数据类型,从而在泛型方法中创建数组呢?

这个问题涉及到 Java 泛型的 类型擦除机制,以及 数组的创建限制。尽管泛型类型可以通过方法的参数传递给方法,但是 数组的创建类型擦除 之间的关系使得无法直接推断出新建的泛型数组的类型。

5.5.3.1 为什么不能直接推断数组的类型?

类型擦除 是 Java 泛型的一大特性,它在编译时会将所有泛型类型擦除为 Object(或者在某些情况下是指定的边界类型),因此 在运行时并没有保留泛型类型的信息。具体到数组的创建,Java 不允许直接通过泛型类型来创建数组,因为在运行时,泛型类型 T 已经被擦除为 Object,而数组在运行时需要明确的类型。

详细解释:

  1. 泛型擦除与数组的创建

    • 当你在 Java 中定义了一个泛型方法:
      public <T> void example(T[] array) {T[] newArray = new T[10];  // 编译错误
      }
      

      你可能认为 T 是通过参数传递的,因此可以推断出 T 的类型并用它来创建一个新的数组。但问题是,在编译后,Java 编译器会将所有的泛型 T 替换为 Object(即发生了类型擦除),所以在运行时,T[] 就变成了 Object[]。这就导致了问题,因为 Java 不允许你直接通过 T 创建一个数组,因为在运行时 T 已经被擦除了。
  2. 泛型类型和数组的创建

    • 数组的创建与普通对象的创建不同。数组是一个 固定类型的数据结构,而且 Java 需要知道数组的元素类型,以便分配内存和进行类型安全检查。由于泛型类型在运行时没有保留,所以你无法通过泛型类型直接创建数组。例如,new T[10] 这样的写法会在编译时产生错误,原因是编译器无法确定 T 的实际类型。
    • 而且 Java 在运行时是通过 反射具体类型信息 来创建数组的,因此无法直接通过 T 来创建数组。

5.3.2 为什么传递类型参数后不能直接推断?

传递 T[] 类型的参数时,编译器已经能够根据方法调用来推断 T 的具体类型(如 IntegerString),但是在 方法内部创建数组时,编译器无法推断出 T[] 的具体类型,因为此时 Java 泛型类型已被擦除,运行时并没有存储类型信息。所以,即使你通过方法参数传递了类型,Java 仍然不知道如何通过泛型 T 来创建具体的数组。

举个例子:

假设你有一个泛型方法,并且你希望在方法内部创建一个泛型数组:

public <T> void example(T[] array) {T[] newArray = new T[10];  // 编译错误
}

这里,T[] 是一个泛型数组,你期望通过 T 创建一个数组。然而,由于 类型擦除T 会在编译时被擦除为 Object,所以 Java 不能确定在运行时应该创建什么类型的数组。换句话说,在运行时,T 就变成了 Object,所以无法创建 Object[] 类型的数组

5.5.4 如果一定要在泛型方法中创建一个数组,要怎么办?

1. 使用 Class 对象和反射来创建数组:

通过传递 Class<T> 类型参数,结合反射机制,可以动态创建泛型类型的数组。

import java.lang.reflect.Array;public class GenericMethodExample {public <T> void example(Class<T> clazz) {T[] newArray = (T[]) Array.newInstance(clazz, 10);  // 通过反射创建数组System.out.println(newArray.length);  // 输出:10}public static void main(String[] args) {GenericMethodExample example = new GenericMethodExample();example.example(Integer.class);  // 创建 Integer 类型的数组example.example(String.class);   // 创建 String 类型的数组}
}
2. 使用 Object[] 数组:

一种简单的方式是,直接使用 Object[] 类型来存储泛型类型的元素,因为所有类型都会被转换为 Object 类型。

public <T> void example() {Object[] newArray = new Object[10];  // 使用 Object[]
}
3. 使用集合类(如 ArrayList)替代数组:

Java 泛型的使用主要是为了类型安全和灵活性。如果不需要严格使用数组,可以使用 ArrayList<T>,它是更通用的集合类型,能够处理不同类型的数据,并且不需要关注数组的大小。

import java.util.ArrayList;public <T> void example() {ArrayList<T> list = new ArrayList<>();  // 使用 ArrayListlist.add(someElement);  // 添加元素
}

5.6 泛型方法的多个类型参数

泛型方法不仅可以有一个类型参数,还可以有多个类型参数。这种情况下,你需要在方法声明中使用多个类型参数,并且在方法内部使用这些类型。

public class GenericMethodExample {// 泛型方法:接受多个类型参数public <T, U> void printPair(T first, U second) {System.out.println("First: " + first + ", Second: " + second);}public static void main(String[] args) {GenericMethodExample example = new GenericMethodExample();// 使用不同类型的参数example.printPair("Hello", 100);  // 输出:First: Hello, Second: 100example.printPair(3.14, true);    // 输出:First: 3.14, Second: true}
}

在这个例子中,printPair 方法使用了两个泛型类型参数 TU,分别代表方法的两个不同参数类型。这样的方法可以灵活地处理不同类型的参数。 

6.泛型方法实际应用场景

1.最简单的泛型方法示例

public class GenericMethodExample {// 定义一个泛型方法,打印传入的参数public static <T> void print(T value) {System.out.println(value);}public static void main(String[] args) {// 调用泛型方法,传入不同类型的参数print("Hello, world!");   // 输出:Hello, world!print(123);               // 输出:123print(45.67);             // 输出:45.67}
}

在这个例子中,print 方法是一个泛型方法,<T> 表示方法可以接受任何类型的参数。我们调用 print 方法时,不需要指定类型,编译器会根据传入的参数类型推断出 T 的类型。

2.泛型方法的多重类型参数

public class GenericMethodExample {// 定义一个泛型方法,接受两个类型的参数public static <T, U> void printPair(T first, U second) {System.out.println("First: " + first);System.out.println("Second: " + second);}public static void main(String[] args) {printPair("Hello", 123);  // 输出 First: Hello  Second: 123printPair(45.67, true);    // 输出 First: 45.67  Second: true}
}

在这个例子中,<T, U> 表示泛型方法接受两个不同类型的参数。方法可以处理不同类型的传入数据。

3.泛型方法的规则

3.1 泛型方法与泛型类不同:泛型方法的类型参数只在方法内有效,而泛型类的类型参数在整个类内都有效。

3.2 类型推断:在调用泛型方法时,Java 编译器会自动推断类型参数。如果调用时没有显式指定类型,编译器会根据方法的参数自动推断。

3.3 多个类型参数:你可以为泛型方法定义多个类型参数(如 <T, U>)。

3.4 泛型方法可以是静态的:即使是静态方法,依然可以定义泛型类型参数。

3.5 类型安全:避免了类型强制转换,减少了运行时错误。

3.6 提高代码复用性:你可以在不同的情况下使用同一个方法,只需提供不同的类型参数。

3.7 简洁性:通过泛型方法,你不需要为每种类型写一个独立的方法,代码更加简洁。

3.8 通用的交换方法

public class GenericMethodExample {// 泛型方法,用于交换两个元素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) {String[] names = {"Alice", "Bob", "Charlie"};swap(names, 0, 2);  // 交换 Alice 和 CharlieSystem.out.println(names[0]);  // 输出 CharlieSystem.out.println(names[2]);  // 输出 Alice}
}

泛型方法在集合中的应用

import java.util.List;public class GenericMethodExample {// 泛型方法,打印列表中的所有元素public static <T> void printList(List<T> list) {for (T item : list) {System.out.println(item);}}public static void main(String[] args) {List<String> list = List.of("Apple", "Banana", "Cherry");printList(list);}
}

相关文章:

[Java]泛型(二)泛型方法

1.定义 在 Java 中&#xff0c;泛型方法是指在方法声明中使用泛型类型参数的一种方法。它使得方法能够处理不同类型的对象&#xff0c;而不需要为每种类型写多个方法&#xff0c;从而提高代码的重用性。 泛型方法与泛型类不同&#xff0c;泛型方法的类型参数仅仅存在于方法的…...

ProfibusDP主机与从机交互

ProfibusDP 主机SD2索要数据下发&#xff1a;68 08 F7 68 01 02 03 21 05 06 07 08 1C 1668&#xff1a;SD2 08&#xff1a;LE F7&#xff1a;LEr 68&#xff1a;SD2 01:目的地址 02&#xff1a;源地址 03:FC_CYCLIC_DATA_EXCHANGE功能码 21&#xff1a;数据地址 05,06,07,08&a…...

jQuery小游戏(二)

jQuery小游戏&#xff08;二&#xff09; 今天是新年的第二天&#xff0c;本人在这里祝大家&#xff0c;新年快乐&#xff0c;万事胜意&#x1f495; 紧接jQuery小游戏&#xff08;一&#xff09;的内容&#xff0c;我们开始继续往下咯&#x1f61c; 游戏中使用到的方法 key…...

【MQ】如何保证消息队列的高可用?

RocketMQ NameServer集群部署 Broker做了集群部署 主从模式 类型&#xff1a;同步复制、异步复制 主节点返回消息给客户端的时候是否需要同步从节点 Dledger&#xff1a;要求至少消息复制到半数以上的节点之后&#xff0c;才给客户端返回写入成功 slave定时从master同步数据…...

简易计算器(c++ 实现)

前言 本文将用 c 实现一个终端计算器&#xff1a; 能进行加减乘除、取余乘方运算读取命令行输入&#xff0c;输出计算结果当输入表达式存在语法错误时&#xff0c;报告错误&#xff0c;但程序应能继续运行当输出 ‘q’ 时&#xff0c;退出计算器 【简单演示】 【源码位置】…...

AI大模型开发原理篇-4:神经概率语言模型NPLM

神经概率语言模型&#xff08;NPLM&#xff09;概述 神经概率语言模型&#xff08;Neural Probabilistic Language Model, NPLM&#xff09; 是一种基于神经网络的语言建模方法&#xff0c;它将传统的语言模型和神经网络结合在一起&#xff0c;能够更好地捕捉语言中的复杂规律…...

SpringBoot 基础特性

SpringBoot 基础特性 SpringApplication 相关特性 自定义 banner 在主配置文件写 banner.txt 的地址 #也可以不写默认路径就是 banner.txt #从类路径下找 banner #类路径就是 编译的target 目录 还有导入的第三方类路径。 spring.banner.locationclasspath:banner.txt#控制…...

网站快速收录:提高页面加载速度的重要性

本文转自&#xff1a;百万收录网 原文链接&#xff1a;https://www.baiwanshoulu.com/32.html 网站快速收录中&#xff0c;提高页面加载速度具有极其重要的意义。以下从多个方面详细阐述其重要性&#xff1a; 一、提升用户体验 减少用户等待时间&#xff1a;页面加载速度直接…...

如何使用formlinker,重构微软表单创建的数字生产力法则?

仅需三步&#xff1a;上传文件-下载文件-导入文件到微软表单 凌晨两点的格式炼狱&#xff1a;被浪费的300万小时人类创造力 剑桥大学的实验室曾捕捉到一组震撼数据&#xff1a;全球教育工作者每年花在调整试题格式上的时间&#xff0c;足够建造3座迪拜哈利法塔。当北京某高校的…...

从零搭建一个Vue3 + Typescript的脚手架——day3

3.项目拓展配置 (1).配置Pinia Pinia简介 Pinia 是 Vue.js 3 的状态管理库&#xff0c;它是一个轻量级、灵活、易于使用的状态管理库。Pinia 是 Vue.js 3 的官方状态管理库&#xff0c;它可以帮助开发者更好地管理应用的状态。Pinia 是一个开源项目&#xff0c;它有丰富的文档…...

Three.js实战项目02:vue3+three.js实现汽车展厅项目

文章目录 实战项目02项目预览项目创建初始化项目模型加载与展厅灯光加载汽车模型设置灯光材质设置完整项目下载实战项目02 项目预览 完整项目效果: 项目创建 创建项目: pnpm create vue安装包: pnpm add three@0.153.0 pnpm add gsap初始化项目 修改App.js代码&#x…...

Linux——网络(tcp)

文章目录 目录 文章目录 前言 一、TCP逻辑 1. 面向连接 三次握手&#xff08;建立连接&#xff09; 四次挥手&#xff08;关闭连接&#xff09; 2. 可靠性 3. 流量控制 4. 拥塞控制 5. 基于字节流 6. 全双工通信 7. 状态机 8. TCP头部结构 9. TCP的应用场景 二、编写tcp代码函数…...

Ubuntu Server 安装 XFCE4桌面

Ubuntu Server没有桌面环境&#xff0c;一些软件有桌面环境使用起来才更加方便&#xff0c;所以我尝试安装桌面环境。常用的桌面环境有&#xff1a;GNOME、KDE Plasma、XFCE4等。这里我选择安装XFCE4桌面环境&#xff0c;主要因为它是一个极轻量级的桌面环境&#xff0c;适合内…...

分享|通过Self-Instruct框架将语言模型与自生成指令对齐

结论 在大型 “指令调整” 语言模型依赖的人类编写指令数据存在数量、多样性和创造性局限&#xff0c; 从而阻碍模型通用性的背景下&#xff0c; Self - Instruct 框架&#xff0c; 通过 自动生成 并 筛选指令数据 微调预训练语言模型&#xff0c; 有效提升了其指令遵循能…...

指针空值——nullptr(C++11)——提升指针安全性的利器

C11引入的nullptr是对指针空值的正式支持&#xff0c;它提供了比传统NULL指针更加安全和明确的指针空值表示方式。在C语言中&#xff0c;指针操作是非常基础且常见的&#xff0c;而如何安全地处理指针空值&#xff0c;一直是开发者关注的重要问题。本文将详细讲解nullptr的引入…...

C++游戏开发

C 是游戏开发中广泛使用的编程语言&#xff0c;因其高性能、灵活性和对硬件的直接控制能力而备受青睐。以下是 C 游戏开发的一些关键点&#xff1a; 1. 游戏引擎 Unreal Engine&#xff1a;使用 C 作为主要编程语言&#xff0c;适合开发高质量 3D 游戏。Unity&#xff1a;虽然…...

【Docker】ubuntu中 Docker的使用

之前记录了 docker的安装 【环境配置】ubuntu中 Docker的安装&#xff1b; 本篇博客记录Dockerfile的示例&#xff0c;docker 的使用&#xff0c;包括镜像的构建、容器的启动、docker compose的使用等。   当安装好后&#xff0c;可查看docker的基本信息 docker info ## 查…...

Linux C openssl aes-128-cbc demo

openssl 各版本下载 https://openssl-library.org/source/old/index.html#include <stdio.h> #include <string.h> #include <openssl/aes.h> #include <openssl/rand.h> #include <openssl/evp.h>#define AES_KEY_BITS 128 #define GCM_IV_SIZ…...

【卫星通信】链路预算方法

本文介绍卫星通信中的链路预算方法&#xff0c;应该也适用于地面通信场景。 更多内容请关注gzh【通信Online】 文章目录 下行链路预算卫星侧参数信道参数用户侧参数 上行链路预算链路预算计算示例 下行链路预算 卫星侧参数 令卫星侧天线数为 M t M_t Mt​&#xff0c;每根天线…...

【Elasticsearch】 索引模板 ignore_missing_component_templates

解释 ignore_missing_component_templates 配置 在Elasticsearch中&#xff0c;ignore_missing_component_templates 是一个配置选项&#xff0c;用于处理索引模板中引用的组件模板可能不存在的情况。当您创建一个索引模板时&#xff0c;可以指定一个或多个组件模板&#xff0…...

Github 2025-01-30 Go开源项目日报 Top10

根据Github Trendings的统计,今日(2025-01-30统计)共有10个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量Go项目10Ollama: 本地大型语言模型设置与运行 创建周期:248 天开发语言:Go协议类型:MIT LicenseStar数量:42421 个Fork数量:2724 次关注人…...

Linux 下注册分析(4)

系列文章目录 Linux 设备树 Linux 下注册分析&#xff08;1&#xff09; Linux 下注册分析&#xff08;2&#xff09; Linux 下注册分析&#xff08;3&#xff09; Linux 下注册分析&#xff08;4&#xff09; 文章目录 系列文章目录1、device_create简介device_createdevice_c…...

PhotoShop中JSX编辑器安装

1.使用ExtendScript Tookit CC编辑 1.安装 打开CEP Resource链接&#xff1a; CEP-Resources/ExtendScript-Toolkit at master Adobe-CEP/CEP-Resources (github.com) 将文件clone到本地或者下载到本地 点击AdobeExtendScriptToolKit_4_Ls22.exe安装&#xff0c;根据弹出的…...

目前市场主流的AI PC对于大模型本地部署的支持情况分析-Deepseek

以下是目前市场主流AI PC对**大模型本地部署支持情况**的综合分析&#xff0c;结合硬件能力、软件生态及厂商动态进行总结&#xff1a; --- ### **一、硬件配置与算力支持** 1. **核心处理器架构** - **异构计算方案&#xff08;CPUGPUNPU&#xff09;**&#xff1a;主流…...

51单片机开发:独立键盘实验

实验目的&#xff1a;按下键盘1时&#xff0c;点亮LED灯1。 键盘原理图如下图所示&#xff0c;可见&#xff0c;由于接GND&#xff0c;当键盘按下时&#xff0c;P3相应的端口为低电平。 键盘按下时会出现抖动&#xff0c;时间通常为5-10ms&#xff0c;代码中通过延时函数delay…...

微服务网关鉴权之sa-token

目录 前言 项目描述 使用技术 项目结构 要点 实现 前期准备 依赖准备 统一依赖版本 模块依赖 配置文件准备 登录准备 网关配置token解析拦截器 网关集成sa-token 配置sa-token接口鉴权 配置satoken权限、角色获取 通用模块配置用户拦截器 api模块配置feign…...

STM32 TIM输入捕获 测量频率

输入捕获简介&#xff1a; IC&#xff08;Input Capture&#xff09;输入捕获 输入捕获模式下&#xff0c;当通道输入引脚出现指定电平跳变时&#xff0c;当前CNT的值将被锁存到CCR中&#xff0c;可用于测量PWM波形的频率、占空比、脉冲间隔、电平持续时间等参数 每个高级定时器…...

python | OpenCV小记(一):cv2.imread(f) 读取图像操作(待更新)

python | OpenCV小记&#xff08;一&#xff09;&#xff1a;cv2.imread&#xff08;f&#xff09;读取图像操作 1. 为什么 [:, :, 0] 提取的是第一个通道&#xff08;B 通道&#xff09;&#xff1f;OpenCV 的通道存储格式索引操作 [:, :, 0] 的解释常见误解 1. 为什么 [:, :,…...

解析静态链接

文章目录 静态链接空间与地址分配相似段合并虚拟地址分配符号地址确定符号解析与重定位链接器优化重复代码消除函数链接级别静态库静态链接优缺点静态链接 一组目标文件经过链接器链接后形成的文件即可执行文件,如果没有动态库的加入,那么这个可执行文件被加载后无需再进行重…...

Ollama 运行从 ModelScope 下载的 GGUF 格式的模型

本文系统环境 Windows 10 Ollama 0.5.7 Ollama 是什么&#xff1f; Ollama 可以让你快速集成和部署本地 AI 模型。它支持各种不同的 AI 模型&#xff0c;并允许用户通过简单的 API 进行调用 Ollama 的安装 Ollama 官网 有其下载及安装方法&#xff0c;非常简便 但如果希…...

【2025年最新版】Java JDK安装、环境配置教程 (图文非常详细)

文章目录 【2025年最新版】Java JDK安装、环境配置教程 &#xff08;图文非常详细&#xff09;1. JDK介绍2. 下载 JDK3. 安装 JDK4. 配置环境变量5. 验证安装6. 创建并测试简单的 Java 程序6.1 创建 Java 程序&#xff1a;6.2 编译和运行程序&#xff1a;6.3 在显示或更改文件的…...

探索性测试与自动化测试的结合

随着软件开发周期的不断缩短和质量要求的不断提高&#xff0c;测试行业正在经历一场深刻的变革。自动化测试因其高效性和可重复性成为测试团队必不可少的工具&#xff0c;而探索性测试&#xff08;Exploratory Testing, ET&#xff09;则因其灵活性和创意性在面对复杂、动态变化…...

我是如何写作的?

以前是如何写作的 从小学三年级开始学写作文&#xff0c;看的作文书&#xff0c;老师布置作文题目&#xff0c;内容我都是自己写的。那时会积累一些好词&#xff0c;听到什么好词就记住了。并没有去观察什么&#xff0c;也没有好好花心思在写作上。总觉得我写的作文与真正好的…...

智慧园区管理系统为企业提供高效运作与风险控制的智能化解决方案

内容概要 快鲸智慧园区管理系统&#xff0c;作为一款备受欢迎的智能化管理解决方案&#xff0c;致力于为企业提供高效的运作效率与风险控制优化。具体来说&#xff0c;这套系统非常适用于工业园、产业园、物流园、写字楼及公寓等多种园区和商办场所。它通过数字化与智能化的手…...

INCOSE需求编写指南-附录 B: 首字母缩略词和缩写

附录 Appendix B: 首字母缩略词和缩写ACRONYMS AND ABBREVIATIONS AD 难易程度的进阶 Advancement Degree of Difficulty AI 人工智能 Artificial Intelligence CM 配置管理 Configuration Management ConOps 运作理念 Concept of Operations COTS 商业现货 Comme…...

VS2008 - debug版 - 由于应用程序配置不正确,应用程序未能启动。重新安装应用程序可能会纠正这个问题。

文章目录 VS2008 - debug版 - 由于应用程序配置不正确&#xff0c;应用程序未能启动。重新安装应用程序可能会纠正这个问题。概述笔记VS2008安装环境VS2008测试程序设置默认报错的情况措施1措施2备注 - exe清单文件的问题是否使用静态库?_BIND_TO_CURRENT_VCLIBS_VERSION的出处…...

Docker容器数据恢复

Docker容器数据恢复 1 创建mongo数据库时未挂载数据到宿主机2 查找数据卷位置3 将容器在宿主机上的数据复制到指定目录下4 修改docker-compose并挂载数据&#xff08;注意端口&#xff09;5 重新运行新容器 以mongodb8.0.3为例。 1 创建mongo数据库时未挂载数据到宿主机 versi…...

翼星求生服务器搭建【Icarus Dedicated Server For Linux】

一、前言 本次搭建的服务器为Steam平台一款名为Icarus的沙盒、生存、建造游戏,由于官方只提供了Windows版本服务器导致很多热爱Linux的小伙伴无法释怀,众所周知Linux才是专业服务器的唯一准则。虽然Github上已经有大佬制作了容器版本但是容终究不够完美,毕竟容器无法与原生L…...

如何在data.table中处理缺失值

&#x1f4ca;&#x1f4bb;【R语言进阶】轻松搞定缺失值&#xff0c;让数据清洗更高效&#xff01; &#x1f44b; 大家好呀&#xff01;今天我要和大家分享一个超实用的R语言技巧——如何在data.table中处理缺失值&#xff0c;并且提供了一个自定义函数calculate_missing_va…...

react中如何获取dom元素

实现代码 const inputRef useRef(null) inputRef.current.focus()...

引入@Inject的依赖包

maven引入Inject的依赖包 在 Maven 项目中引入 Inject 注解所需的依赖包同样取决于你打算使用的依赖注入框架。以下是一些常见框架及其 Maven 依赖配置的示例&#xff1a; 1. Google Guice 如果你打算使用 Google Guice&#xff0c;你需要在 pom.xml 文件中添加 Guice 的依赖…...

Deep Seek R1本地化部署

目录 说明 一、下载ollama 二、在ollama官网下载模型 三、使用 后记 说明 操作系统&#xff1a;win10 使用工具&#xff1a;ollama 一、下载ollama 从官网下载ollama&#xff1a; ollama默认安装在C盘&#xff0c;具体位置为C:\Users\用户名\AppData\Local\Programs\O…...

RDMA 工作原理 | 支持 RDMA 的网络协议

注&#xff1a;本文为 “RDMA” 相关文章合辑。 英文引文机翻未校。 图片清晰度受引文所限。 Introduction to Remote Direct Memory Access (RDMA) Written by: Dotan Barak on March 31, 2014.on February 13, 2015. What is RDMA? 什么是 RDMA&#xff1f; Direct me…...

再见了流氓软件~~

聊一聊 最近一直在测试软件&#xff0c;需要装各种软件和工具配合测试&#xff0c;导致现在电脑都快装满了&#xff0c;需要把不用的软件卸载。电脑自带的卸载只能一个一个卸载&#xff0c;不但麻烦还卸载不干净。 相信很多人也有这方面的需要&#xff0c;电脑装了很多软件&a…...

165. 比较版本号

两个注意的点&#xff1a; 分割字符串的时候&#xff0c;要用split("\\.")而不能用split(".")&#xff0c;因为前者表示“对.使用斜杠转义&#xff0c;\\表示一个斜杠”&#xff0c;而后者表示匹配任意单个字符&#xff0c;例如version2 "1.2.3&quo…...

一文大白话讲清楚webpack进阶——9——ModuleFederation实战

文章目录 一文大白话讲清楚webpack进阶——9——ModuleFederation实战1. 啥是ModuleFederation2. 创建容器应用3. 创建远程应用4. 启动远程应用5. 使用远程应用的组件 一文大白话讲清楚webpack进阶——9——ModuleFederation实战 1. 啥是ModuleFederation 先看这篇文章&#…...

【llm对话系统】LLM 大模型Prompt 怎么写?

如果说 LLM 是一个强大的工具&#xff0c;那么 Prompt 就是使用这个工具的“说明书”。一份好的 Prompt 可以引导 LLM 生成更准确、更相关、更符合你期望的输出。 今天&#xff0c;我们就来聊聊 LLM Prompt 的编写技巧&#xff0c;掌握这把解锁 LLM 潜能的钥匙&#xff01; 一…...

INCOSE需求编写指南-附录 C: 需求模式

附录 Appendix C: 需求模式 Requirement Patterns C.1 需求模式简介 Introduction to Requirement Patterns 需求模式&#xff08;样板或模板&#xff09;的概念最初于 1998 年在英国的未来水面战斗人员 (FSC) 国防项目中应用&#xff08;Dick 和 Llorens&#xff0c;2012 年…...

WGCLOUD使用介绍 - 如何监控ActiveMQ和RabbitMQ

根据WGCLOUD官网的信息&#xff0c;目前没有针对ActiveMQ和RabbitMQ这两个组件专门做适配 不过可以使用WGCLOUD已经具备的通用监测模块&#xff1a;进程监测、端口监测或者日志监测、接口监测 来对这两个组件进行监控...

【VASP】AIMD计算总结

【VASP】AIMD计算总结 vasp 计算文件INCAR 参数介绍后处理 二维材料与异质结的构造除了筛选优势还应该判断是否稳定&#xff0c;所以我在这分享一篇基于vasp6.2计算的AIMD 示例&#xff1a; https://www.vasp.at/wiki/index.php/Liquid_Si_-_Standard_MD vasp 计算文件 POSCA…...