Java泛型筆記

1 為什么需要泛型

Java5之前,是沒有泛型的。通過兩段代碼我們就可以知道為何我們需要泛型

public int addInt(int a, int b) {return a + b;
}public double addDouble(double a, double b) {return a + b;
}

實際開發中,經常有數值類型求和的需求,例如實現int類型的加法,?有時候還需要實現long類型的求和,?如果還需要Float類型的求和,需要重新在重載一個輸入是Float類型的add方法。

再舉個例子:

public class NoGenericsTest {public static void main(String[] args) {List list = new ArrayList();list.add("Hello");list.add(123);list.add(45.67);for (int i = 0; i < list.size(); i++) {String item = (String) list.get(i); //1System.out.println(item);}}
}

上面的代碼執行,將會出現如下的錯誤:

定義了一個List類型的集合,先向其中加入了兩個字符串類型的值,隨后加入一個Integer類型的值。這是完全允許的,因為此時list默認的類型為Object類型。在之后的循環中,由于忘記了之前在list中也加入了Integer類型的值或其他編碼原因,很容易出現類似于//1中的錯誤。因為編譯階段正常,而運行時會出現“java.lang.ClassCastException”異常。因此,導致此類錯誤編碼過程中不易發現。

在如上的編碼過程中,我們發現主要存在兩個問題:

1.當我們將一個對象放入集合中,集合不會記住此對象的類型,當再次從集合中取出此對象時,改對象的編譯類型變成了Object類型,但其運行時類型任然為其本身類型。

2.因此,//1處取出集合元素時需要人為的強制類型轉化到具體的目標類型,且很容易出現“java.lang.ClassCastException”異常。

所以泛型的好處就是:

  1. 適用于多種數據類型執行相同的代碼
  2. 泛型中的類型在使用時指定,不需要強制類型轉換

2 泛型類和泛型接口

泛型,即“參數化類型”。一提到參數,最熟悉的就是定義方法時有形參,然后調用此方法時傳遞實參。那么參數化類型怎么理解呢?

顧名思義,就是將類型由原來的具體的類型參數化,類似于方法中的變量參數,此時類型也定義成參數形式(可以稱之為類型形參),然后在使用/調用時傳入具體的類型(類型實參)。

泛型的本質是為了參數化類型(在不創建新的類型的情況下,通過泛型指定的不同類型來控制形參具體限制的類型)。也就是說在泛型使用過程中,操作的數據類型被指定為一個參數,這種參數類型可以用在類、接口和方法中,分別被稱為泛型類、泛型接口、泛型方法。

引入一個類型變量T(其他大寫字母都可以,不過常用的就是T,E,K,V等等),并且用<>括起來,并放在類名的后面。泛型類是允許有多個類型變量的.

2.1 泛型類

public class NormalGeneric<T> {private T value;public NormalGeneric(T value) {this.value = value;}public T getValue() {return value;}public void setValue(T value) {this.value = value;}@Overridepublic String toString() {return "NormalGeneric{" +"value=" + value +'}';}
}
package com.coy.generics;public class NormalGeneric2<T,K> {private T data;private K result;public NormalGeneric2(T t, K k) {this.data = t;this.result = k;}public T getData() {return data;}public void setData(T data) {this.data = data;}public K getResult() {return result;}public void setResult(K result) {this.result = result;}
}

泛型接口與泛型類的定義基本相同:

2.2 泛型接口

public interface Generator<T> {public T next();
}

而實現泛型接口的類,有兩種實現方法:

1. 未傳入泛型實參時:

public class ImplGenerator<T> implements Generator<T>{@Overridepublic T next() {return null;}
}

在new出類的實例時,需要指定具體類型:

public static void main(String[] args) {ImplGenerator<String> implGenerator = new ImplGenerator<>();
}

2. 傳入泛型實參

public class ImplGenerator2 implements Generator<String>{@Overridepublic String next() {return "OK";}
}

在new出類的實例時,和普通的類沒區別。

2.3 泛型方法

public class GenericMethod {public <T> T genericMethod(T... t) {// 這里可以對 T 類型的參數進行處理return t[t.length/ 2]; // 返回中間的元素作為示例}public void test(int x, int y) {// 這是一個普通方法,不是泛型方法System.out.println("普通方法: " + (x + y));}public static void main(String[] args) {GenericMethod gm = new GenericMethod();// 調用普通方法gm.test(12, 34);// 調用泛型方法String result = gm.genericMethod("Hello", "World", "Generics");System.out.println(result); // 輸出: WorldInteger intResult = gm.genericMethod(1, 2, 3, 4, 5);System.out.println(intResult); // 輸出: 3}
}

泛型方法,是在調用方法的時候指明泛型的具體類型?,泛型方法可以在任何地方和任何場景中使用,包括普通類和泛型類。注意泛型類中定義的普通方法和泛型方法的區別。

普通方法:

public class GenericNormalMethod<T> {/*** 普通泛型方法* 雖然在方法中使用了泛型,但是這并不是一個泛型方法。* 這只是類中一個普通的成員方法,只不過他的返回值是在聲明泛型類已經聲明過的泛型* 所以在這個方法中才可以繼續使用 т 這個泛型。** @param t 泛型參數* @return 返回泛型參數**/public T normalMethod(T t) {return t;}
}

泛型方法:

public class GenericGenericMethod {/*** 這才是一個真正的泛型方法。* 首先在public與返回值之間的<T>必不可少,這表明這是一個泛型方法,并且聲明了一個泛型* 這個τ可以出現在這個泛型方法的任意位置,* 泛型的數量也可以為任意多個* 如:public <T, V> T show(T t, V v) {}* * @param t* @return* @param <T>*/public <T> T show(T t) {return t;}public <T, V> T show(T t, V v) {return t;}
}

3 限定類型變量

有時候,我們需要對類型變量加以約束,比如計算兩個變量的最小,最大值。

public static <T> T min(T a, T b) {if (a instanceof Comparable && b instanceof Comparable) {Comparable<T> compA = (Comparable<T>) a;Comparable<T> compB = (Comparable<T>) b;return compA.compareTo(compB) < 0 ? a : b;}throw new IllegalArgumentException("Arguments must be comparable");
}

請問,如何確保傳入的兩個變量一定有compareTo方法?那么解決這個問題的方案就是將T限制為實現了接口Comparable的類

public static <T extends Comparable> T min(T a, T b) {if (a.compareTo(b) < 0) {return a;} else {return b;}
}

T extends Comparable中

T表示應該綁定類型的子類型,Comparable表示綁定類型,子類型和綁定類型可以是類也可以是接口。

如果這個時候,我們試圖傳入一個沒有實現接口Comparable的類的實例,將會發生編譯錯誤。

public static <T extends Comparable> T min(T a, T b) {if (a.compareTo(b) < 0) {return a;} else {return b;}
}public static <T extends Comparable & Serializable> T max(T a, T b) {if (a.compareTo(b) > 0) {return a;} else {return b;}
}static class Test{}public static void main(String[] args) {GenericGenericMethod.min("abc", "xyz"); // 輸出: abcInteger minInt = GenericGenericMethod.min(3, 5);System.out.println(minInt); // 輸出: 3Integer maxInt = GenericGenericMethod.max(3, 5);System.out.println(maxInt); // 輸出: 5Test test1 = new Test();Test test2 = new Test();Test minTest = GenericGenericMethod.min(test1, test2); // 報錯System.out.println(minTest); // 輸出: com.coy.generics.GenericGenericMethod$Test@<hashcode>
}

同時extends左右都允許有多個,如 <T,V?extends Comparable & Serializable>

注意:限定的類型中(Comparable & Serializable),只允許有一個是class類型,而且如果有class類型,這個class的類必須是限定列表的第一個,因為Java中類是單繼承。這種類的限定既可以用在泛型方法上也可以用在泛型類上。

4 泛型中的約束和局限性

現在我們有泛型類

public class Restrict<T> {
}

4.1 不能用基本類型實例化類型參數

Restrict<double> restrict = new Restrict<>(); // 錯誤
Restrict<Double> restrictDouble = new Restrict<>(); // 正確

Java 的泛型只支持引用類型,不支持基本類型(如 int、double 等)。這是因為泛型的實現基于類型擦除,編譯后泛型類型參數會被替換為 Object 或限定類型,而基本類型不能作為 Object 的子類。

例如,Restrict<int> 這樣的寫法會報錯,必須用包裝類型 Restrict<Integer>。

總結:
泛型類型參數只能是引用類型,不能是基本類型。
如果需要用基本類型,使用對應的包裝類(如 Integer、Double)

4.2 運行時類型查詢只適用于原始類型

if (restrictDouble instanceof Restrict<Double>) {} // 這種不允許
if (restrictDouble instanceof Restrict) {} // 這種不允許
Restrict<String> restrictString = new Restrict<>();
System.out.println(restrictDouble.getClass() == restrictString.getClass()); // This will print false because they are different type parameters
System.out.println(restrictDouble.getClass().getSimpleName()); // This will print "Restrict"

因為 Java 的泛型在編譯后會進行類型擦除,所有的泛型類型參數(如 Restrict<String>、Restrict<Integer>)在運行時都變成了原始類型(如 Restrict)。運行時虛擬機只知道原始類型,不知道具體的泛型參數類型。

所以,instanceof Restrict 是允許的,但 instanceof Restrict<String> 是非法的,因為運行時沒有 Restrict<String> 這個類型信息。

總結:
運行時只保留原始類型信息,泛型參數信息被擦除。
因此類型查詢(如 instanceof、getClass())只能用于原始類型。

4.3 泛型類的靜態上下文中類型變量失效

// 靜態域或者方法里不能引用類型變量
private static T instance; // 錯誤, 靜態成員不能直接引用類的泛型類型參數// 靜態方法不能引用類的類型變量
// private static T getInstance2() {} // 錯誤,靜態方法不能直接引用類的泛型類型參數//靜態方法本身是泛型方法就行
private static <T> T getInstance() {} // 正確,如果需要在靜態方法中使用泛型,必須讓方法本身成為泛型方法(即在方法聲明上加<T>),這樣T是方法級別的,與類的泛型參數無關。

不能在靜態域或方法中引用類型變量。泛型類型參數(如T)是在類實例化時由外部指定的,而靜態成員屬于類本身,不依賴于任何實例。類加載時,靜態成員就已經初始化了,但這時還沒有任何泛型類型信息(因為還沒創建對象)。同樣的靜態方法在類加載時隨類一起加載到方法區(或元空間),但只有在第一次主動使用該類(如創建對象、訪問靜態成員、調用靜態方法等)時,類才會被加載和初始化。靜態方法屬于類本身,不依賴于實例。所以靜態域或靜態方法不能直接引用類的泛型類型參數。

通俗點講因為泛型是要在對象創建的時候才知道是什么類型的,而對象創建的代碼執行先后順序為先是static的部分,然后才是構造函數等等。所以在對象初始化之前static的部分已經執行了,如果你在靜態部分引用的泛型,那么毫無疑問虛擬機根本不知道是什么東西,因為這個時候類還沒有初始化。

4.4 不能創建參數化類型的數組

// 不能創建參數化類型的數組
Restrict<Double>[] restrictArray; // 可以
// private Restrict<String>[] restrictArray = new Restrict<String>[10]; // 不允許

Java 的泛型是通過類型擦除實現的,編譯后泛型類型參數會被擦除為原始類型(如 Object)。而數組在運行時需要知道其元素的具體類型,以保證類型安全。如果允許創建參數化類型的數組(如 new Restrict<String>[10]),在運行時無法檢查數組元素的實際類型,可能導致類型安全問題。這樣做會破壞 Java 的類型安全機制,因此 Java 不允許直接創建參數化類型的數組。

總結:泛型信息在運行時不可用,數組需要運行時類型信息,所以不能創建參數化類型的數組。

4.5 不能實例化類型變量

public class Restrict<T> {private T value;// 不能實例化類型變量public Restrict() {value = new T();}
}

Java 的泛型是通過類型擦除實現的,類型參數 T 在編譯后會被擦除為它的限定類型(默認是 Object)。在運行時,JVM 并不知道 T 具體是什么類型,因此無法直接執行 new T() 這樣的操作,因為沒有類型信息來創建對象。

如果需要實例化類型變量,通常有兩種做法:
1. 通過構造函數傳入 Class<T>,用反射創建對象。
2. 讓調用者傳入需要的實例。

public class Restrict<T> {private T value;public Restrict(Class<T> clazz) throws IllegalAccessException, InstantiationException {value = clazz.newInstance();}
}

4.6 不能捕獲泛型類的實例

    //泛型類不能extends Exception/Throwable// private class Problem<K> extends Exception{}// 不能捕獲泛型類對象
//    public <T extends Throwable> void doSomething(T t) {
//        // 不能捕獲泛型類對象
//        try {
//
//        } catch (T e) {
//            // 這里的e是T類型,但T是Throwable的子類
//            System.out.println("Caught: " + e);
//        }
//    }

Java 的泛型是通過類型擦除實現的,類型參數(如 T)在編譯后會被擦除為限定類型(如 Throwable 或 Object)。而 catch 語句在編譯時需要明確的異常類型,不能是類型變量。編譯器無法確定 T 具體是什么類型,因此不能寫 catch (T e)。

總結:
1. catch 語句要求捕獲的異常類型在編譯期已知。
2. 泛型類型參數在編譯期無法確定具體類型,類型擦除后只剩原始類型。
所以不能捕獲泛型類對象。

但是這樣是可以的:

public <T extends Throwable> void doSomething(T t) throws T {// 不能捕獲泛型類對象try {// 模擬一些操作throw t; // 拋出傳入的異常} catch (Throwable e) {// 這里的e是Throwable類型,但T是Throwable的子類System.out.println("Caught: " + e);throw (T) e; // 強制轉換為T類型并拋出}
}

5 泛型類型的繼承規則

現在我們有一個類和子類:

public class Employee {
}
public class Worker extends Employee{
}

有一個泛型類:

public class Pair<T> {
}

請問Pair<Employee>和Pair<Worker>是繼承關系嗎?

答案:不是。Pair<Employee> 和 Pair<Worker> 之間沒有繼承關系。雖然 Worker 繼承自 Employee,但泛型類型參數不同的泛型類是完全獨立的類型。在 Java 中,Pair<Worker> 不是 Pair<Employee> 的子類。

但是泛型類可以繼承或者擴展其他泛型類,比如List和ArrayList

/*** 但是泛型類可以繼承或者擴展其他泛型類,比如List和ArrayList* @param <T>*/
public class ExtendPair<T> extends Pair<T> {
}
Pair<Employee> pair = new ExtendPair<Employee>();

6 通配符類型

正是因為前面所述的,Pair<Employee>和Pair<Worker>沒有任何關系,

public class Fruit {
}public class Apple extends Fruit{
}public class Orange extends Fruit{
}public class HongFuShi extends Apple{
}

如果我們有一個泛型類和一個方法:

public static void print(GenericType<Fruit> generics) {System.out.println(generics);
}public class GenericType<T> {
}

則會產生這種情況:

為解決這個問題,于是提出了一個通配符類型??

有兩種使用方式:

? extends X??表示類型的上界,類型參數是X的子類

? super X??表示類型的下界,類型參數是X的超類

這兩種方式從名字上來看,特別是super,很有迷惑性,下面我們來仔細辨析這兩種方法。

6.1 ? extends X

表示傳遞給方法的參數,必須是X的子類(包括X本身)

public class Caller {public static void print2(GenericType<? extends Fruit> generics) {System.out.println(generics);}public static void use2() {GenericType<Fruit> a = new GenericType<>();print2(a);GenericType<Apple> b = new GenericType<>();print2(b);GenericType<? extends Fruit> c = b;}
}

但是對泛型類GenericType來說,如果其中提供了get和set類型參數變量的方法的話,set方法是不允許被調用的,會出現編譯錯誤

public class GenericType<T> {private T value;public T getValue() {return value;}public void setValue(T value) {this.value = value;}@Overridepublic String toString() {return "GenericType{" +"value=" + value +'}';}
}

get方法則沒問題,會返回一個Fruit類型的值。

為何?

道理很簡單,? extends X??表示類型的上界,類型參數是X的子類,那么可以肯定的說,get方法返回的一定是個X(不管是X或者X的子類)編譯器是可以確定知道的。但是set方法只知道傳入的是個X,至于具體是X的那個子類,不知道。

總結:主要用于安全地訪問數據,可以訪問X及其子類型,并且不能寫入非null的數據。

6.2?? super?X

表示傳遞給方法的參數,必須是X的超類(包括X本身)

public class Caller {public static void printSuper(GenericType<? super Apple> generics) {System.out.println(generics);}public static void useSuper() {GenericType<Fruit> fruitGenericType = new GenericType<>();printSuper(fruitGenericType);GenericType<Apple> appleGenericType = new GenericType<>();printSuper(appleGenericType);GenericType<HongFuShi> hongFuShiGenericType = new GenericType<>();printSuper(hongFuShiGenericType); // 編譯錯誤,HongFuShi不是Apple的父類GenericType<Orange> orangeGenericType = new GenericType<>();printSuper(orangeGenericType); // 編譯錯誤,Orange不是Apple的父類}
}

但是對泛型類GenericType來說,如果其中提供了get和set類型參數變量的方法的話,set方法可以被調用的,且能傳入的參數只能是X或者X的子類:

public class GenericType<T> {private T value;public T getValue() {return value;}public void setValue(T value) {this.value = value;}@Overridepublic String toString() {return "GenericType{" +"value=" + value +'}';}
}
public static void useSuper() {GenericType<? super Apple> e = new GenericType<>();Apple apple = new Apple();Fruit fruit = new Fruit();HongFuShi hongFuShi = new HongFuShi();e.setValue(fruit); // 編譯錯誤,不能將Fruit賦值給Apple的父類e.setValue(apple); // 可以將Apple賦值給Apple的父類e.setValue(hongFuShi); // 可以將HongFuShi賦值給Apple的父類
}

get方法只會返回一個Object類型的值:

GenericType<? super Apple> e = new GenericType<>();
Object fruit1 = e.getValue(); // 唯一可行的獲取值,返回類型為Object,因為e的類型是Apple的父類,所以getValue()返回的是Object類型

為什么會這樣子呢?

? super ?X??表示類型的下界,類型參數是X的超類(包括X本身),那么可以肯定的說,get方法返回的一定是個X的超類,那么到底是哪個超類?不知道,但是可以肯定的說,Object一定是它的超類,所以get方法返回Object。編譯器是可以確定知道的。對于set方法來說,編譯器不知道它需要的確切類型,但是X和X的子類可以安全的轉型為X。

總結:主要用于安全地寫入數據,可以寫入X及其子類型。

7?無限定的通配符 ?

表示對類型沒有什么限制,可以把?看成所有類型的父類,如Pair< ?>;

比如:

ArrayList<T> al=new ArrayList<T>(); 指定集合元素只能是T類型

ArrayList<?> al=new ArrayList<?>(); 集合元素可以是任意類型,這種沒有意義,一般是方法中,只是為了說明用法。

在使用上:

? getFirst() : 返回值只能賦給 Object;

void?setFirst(?) : setFirst 方法不能被調用, 甚至不能用 Object 調用;

8 虛擬機是如何實現泛型的?

泛型思想早在C++語言的模板(Template)中就開始生根發芽,在Java語言處于還沒有出現泛型的版本時,只能通過Object是所有類型的父類和類型強制轉換兩個特點的配合來實現類型泛化。由于Java語言里面所有的類型都繼承于java.lang.Object,所以Object轉型成任何對象都是有可能的。但是也因為有無限的可能性,就只有程序員和運行期的虛擬機才知道這個Object到底是個什么類型的對象。在編譯期間,編譯器無法檢查這個Object的強制轉型是否成功,如果僅僅依賴程序員去保障這項操作的正確性,許多ClassCastException的風險就會轉嫁到程序運行期之中。

泛型技術在C#和Java之中的使用方式看似相同,但實現上卻有著根本性的分歧,C#里面泛型無論在程序源碼中、編譯后的IL中(Intermediate Language,中間語言,這時候泛型是一個占位符),或是運行期的CLR中,都是切實存在的,List<int>與List<String>就是兩個不同的類型,它們在系統運行期生成,有自己的虛方法表和類型數據,這種實現稱為類型膨脹,基于這種方法實現的泛型稱為真實泛型。

Java語言中的泛型則不一樣,它只在程序源碼中存在,在編譯后的字節碼文件中,就已經替換為原來的原生類型(Raw Type,也稱為裸類型)了,并且在相應的地方插入了強制轉型代碼,因此,對于運行期的Java語言來說,ArrayList<int>與ArrayList<String>就是同一個類,所以泛型技術實際上是Java語言的一顆語法糖,Java語言中的泛型實現方法稱為類型擦除,基于這種方法實現的泛型稱為偽泛型。

將一段Java代碼編譯成Class文件,然后再用字節碼反編譯工具進行反編譯后,將會發現泛型都不見了,程序又變回了Java泛型出現之前的寫法,泛型類型都變回了原生類型

public static String method(List<String> list) {return "List<String>";
}public static Integer method(List<Integer> list) {return 0;
}

上面這段代碼是不能被編譯的,因為參數List<Integer>和List<String>編譯之后都被擦除了,變成了一樣的原生類型List<E>,擦除動作導致這兩種方法的特征簽名變得一模一樣。

由于Java泛型的引入,各種場景(虛擬機解析、反射等)下的方法調用都可能對原有的基礎產生影響和新的需求,如在泛型類中如何獲取傳入的參數化類型等。因此,JCP組織對虛擬機規范做出了相應的修改,引入了諸如Signature、LocalVariableTypeTable等新的屬性用于解決伴隨泛型而來的參數類型的識別問題,Signature是其中最重要的一項屬性,它的作用就是存儲一個方法在字節碼層面的特征簽名[3],這個屬性中保存的參數類型并不是原生類型,而是包括了參數化類型的信息。修改后的虛擬機規范要求所有能識別49.0以上版本的Class文件的虛擬機都要能正確地識別Signature參數。

另外,從Signature屬性的出現我們還可以得出結論,擦除法所謂的擦除,僅僅是對方法的Code屬性中的字節碼進行擦除,實際上元數據中還是保留了泛型信息,這也是我們能通過反射手段取得參數化類型的根本依據。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/87841.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/87841.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/87841.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

mysql 圖形化界面工具 DataGrip 安裝與配置

安裝地址&#xff1a; Download DataGrip: Cross-Platform IDE for Databases & SQLhttps://www.jetbrains.com/datagrip/download/?sectionwindows 添加數據源&#xff1a; 下載驅動文件&#xff1a;直接點擊下載即可 點擊測試連接&#xff1a;成功后點擊確定 顯示所有數…

linux下進程之間socket通信c程序例程

以下是一個基于 Linux 的 C 程序示例&#xff0c;展示了如何使用 Unix 域套接字&#xff08;Unix domain socket&#xff09;在不同進程之間互傳 JSON 消息。我們將實現一個簡單的客戶端 - 服務器模型&#xff0c;服務器監聽連接&#xff0c;客戶端連接到服務器并發送 JSON 消息…

高云GW5AT-LV60 FPGA圖像處理板|MIPI攝像頭幀率測試

高云GW5AT-LV60 FPGA圖像處理板套件中附帶了三個攝像頭模組&#xff0c;這三個模組真是各有千秋&#xff0c;接下來我通過簡單的一些測試來看看這幾個攝像頭的差異。 VS-SC130GS 、 VS-SC2210 這兩個模組是手動對焦&#xff0c;在使用時需要手動轉動鏡頭調整焦距&#xff0c;這…

機器學習在智能能源管理中的應用:需求響應與可再生能源整合

隨著全球能源需求的不斷增長和環境問題的日益突出&#xff0c;智能能源管理成為實現可持續發展的關鍵。智能能源管理系統通過整合先進的信息技術&#xff0c;如物聯網&#xff08;IoT&#xff09;、大數據和機器學習&#xff0c;能夠優化能源的分配和使用&#xff0c;提高能源效…

【網絡】Linux 內核優化實戰 - net.ipv4.tcp_timestamps

目錄 net.ipv4.tcp_timestamps 詳解1. 功能與作用2. 參數取值與含義3. 啟用/禁用的影響4. 配置方法5. 適用場景建議6. 注意事項總結 net.ipv4.tcp_timestamps 詳解 net.ipv4.tcp_timestamps 是 Linux 內核中一個與 TCP 協議相關的網絡參數&#xff0c;用于控制是否啟用 TCP 時…

第一個Flink 程序:詞頻統計 WordCount(流處理)

本文重點 本文將通過一個統計詞頻的小程序來看一下flink是如何對數據進行批處理的,需要聲明的是,一般我們使用Flink常常用于流式處理,即使是有界的數據,我們也將其看成是無界數據進行流式處理,所以批量處理并不是很常用,這里只是為了了解一下Flink是如何進行批處理的。 …

在 Kodi 中添加 AList 搭建 WebDav 的方法

文章目錄 一、問題背景二、使用方法&#xff08;一&#xff09;開啟 AList 的 WebDav&#xff08;二&#xff09;在 Kodi 添加 WebDav1. 打開設置跳轉到媒體設置添加指定類型的媒體庫2. 選擇添加媒體庫3. 添加新的網絡位置 一、問題背景 AList 是一種使用 Gin 和 Solidjs 編寫…

DAY 49

CBAM 是一種能夠集成到任何卷積神經網絡架構中的注意力模塊。它的核心目標是通過學習的方式&#xff0c;自動獲取特征圖在通道和空間維度上的重要性&#xff0c;進而對特征圖進行自適應調整&#xff0c;增強重要特征&#xff0c;抑制不重要特征&#xff0c;提升模型的特征表達能…

LLM:位置編碼詳解與實現

文章目錄 前言一、絕對位置編碼二、相對位置編碼三、旋轉位置編碼 前言 由于attetnion運算的特性&#xff0c;Transformer本身不感知順序&#xff0c;位置編碼是彌補這一缺陷的關鍵。 一、絕對位置編碼 絕對位置編碼的方式是通過將每個位置映射到一個高維空間中&#xff0c;該…

pytorch學習-10.卷積神經網絡(基礎篇)

2.線性模型 3.梯度下降算法 4.反向傳播(用pytorch算梯度) 5.用pytorch實現線性回歸 6.logistic回歸 7.處理多維特征的輸入 8.加載數據集 9.多分類問題 10.卷積神經網絡&#xff08;基礎篇&#xff09;_嗶哩嗶哩_bilibili 10.1卷積神經網絡 10.1.1 卷積神經網絡工作流程&…

ARMv8 創建1、2、3級頁表代碼與注釋

對下面的地址空間創建3級頁表 // level 1 table, 4 entries: // 0000 0000 - 3FFF FFFF, 1GB block, DDR // 4000 0000 - 7FFF FFFF, 1GB block, DDR // 8000 0000 - BFFF FFFF, 1GB block, DDR // C000 0000 - FFFF FFFF, point to level2 tabel // // level 2 table, 512 en…

DeepSeek-R1滿血版:硅基流動API或本地部署

大家好! 想在手機上部署 DeepSeek-R1 滿血版&#xff08;671B&#xff09;&#xff1f;我來手把手教你最靠譜的兩種方式&#xff01;滿血版模型參數高達 671 億&#xff0c;手機本地運行幾乎不可能&#xff0c;但通過「云服務 手機 App」的組合&#xff0c;你一樣能在手機上絲…

React 各顏色轉換方法、顏色值換算工具HEX、RGB/RGBA、HSL/HSLA、HSV、CMYK

&#x1f4d6; 簡介 基于 React Tailwind CSS 構建的專業顏色轉換工具&#xff0c;支持多種顏色格式的實時轉換。無論是設計師、開發者&#xff0c;都能在這個工具中找到所需的顏色轉換功能。 ? 核心功能 &#x1f3af; 多格式顏色轉換 HEX 格式: 支持 3 位縮寫 (#000, #…

開關電源抄板學習

一、實物 輸入220V&#xff0c;輸出12V5A 二、拍照并使用PS矯正 用卡尺測量下PCB的尺寸&#xff0c;在PS中作為畫布。 用相機拍下照片&#xff0c;導入到PS中&#xff0c;用拉伸工具對圖片進行矯正處理&#xff0c;并拉伸到和畫布一樣大小。 三、打開嘉立創EDA&#xff0c;導…

大數據在UI前端的應用探索:基于用戶行為分析的產品優化策略

hello寶子們...我們是艾斯視覺擅長ui設計、前端開發、數字孿生、大數據、三維建模、三維動畫10年經驗!希望我的分享能幫助到您!如需幫助可以評論關注私信我們一起探討!致敬感謝感恩! 一、引言&#xff1a;用戶行為分析重構產品優化的技術邏輯 在數字化產品體驗競爭日益激烈的今…

優化 WebSocket 實現單例連接用于打印【待測試 】

class PrinterWebSocket { constructor(url) { if (PrinterWebSocket.instance) { return PrinterWebSocket.instance; } this.url url; this.socket null; this.queue []; // 打印任務隊列 this.isConnecting false; this.retry…

Spring Cloud Alibaba/Spring Boot整合華為云存儲實例(REST API方式)

一個小作業&#xff0c;初次嘗試華為云存儲&#xff0c;一點分享 原項目采用Spring Cloud Alibaba微服務技術、Spring Boot框架技術、VueJS前端框架開發技術&#xff0c;nacos注冊中心&#xff0c;數據庫為mysql 下面看一下沒有運用云存儲的原項目&#xff08;可跳過&#xf…

Petalinux工程如何離線編譯

目錄 一.下載離線包 1.1 共享狀態緩存包&#xff1a;sstate-cache 1.1.1 進入官網打開Petalinux工具網頁 1.1.2 找到相應的Petalinux版本 1.1.3 根據平臺下載 1.2 下載downloads源碼包 1.3 open_components源碼包 二.解壓 2.1 sstate-cache 2.2 downloads源碼包 2.3…

w446數字化農家樂管理平臺的設計與實現

&#x1f64a;作者簡介&#xff1a;多年一線開發工作經驗&#xff0c;原創團隊&#xff0c;分享技術代碼幫助學生學習&#xff0c;獨立完成自己的網站項目。 代碼可以查看文章末尾??聯系方式獲取&#xff0c;記得注明來意哦~&#x1f339;贈送計算機畢業設計600個選題excel文…

AWS WebRTC:通過shell分析viewer端日志文件

在并發過程中,每個viewer會產生一個對應的日志文件,日志文件名為: viewer_channel_index_20250626_030943_145.logviewer端日志比master端日志文件數量多,比例大概是5:1,有1個master就會有5個viewer,每個viewer對應一個日志文件。 我要統計的是從啟動viewer到出第一幀視…