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

Java 泛型详细解析

泛型的定义

泛型类的定义

下面定义了一个泛型类 Pair,它有一个泛型参数 T

public class Pair<T> {private T start;private T end;
}

实际使用的时候就可以给这个 T 指定任何实际的类型,比如下面所示,就指定了实际类型为 LocalDate,泛型给了我们一个错觉就是通过个这个模板类 Pair<T>,我们可以在实际使用的时候动态的派生出各种实际的类型,比如这里的 Pair<LocalDate> 类。

Pair<LocalDate> period = new Pair<>();

泛型类的继承

子类是一个泛型类的定义方法如下:

public class Interval<T> extend Pair<T> {}

这里的 Interval<T> 类是一个泛型类,也可以像上面使用 Pair<T> 类一样给它指定实际的类型。

子类是一个具体类的定义方法如下:

public class DateInterval extends Pair<LocalDate> {}

这里的 DateInterval 类就是一个具体的类,而不再是一个泛型类了。这里的语义是 DateInteral 类继承了 Pair<LocalDate> 类,这里的 Pair<LocalDate> 类也是一个具体类。但是由于 Java 的泛型实现机制,这里会带来多态上的一个问题,见下面的分析。

而像下面的这种定义具体类的写法是错误的:

public class DateInterval<LocalDate> extends Pair<LocalDate> {}

泛型方法的定义

泛型方法定义时,类型变量放在修饰符的后面,返回值的前面。泛型方法既可以泛型类中定义,在普通类中定义。

public static <T> T genericMethod(T a) {}

这里顺便记录一下,因为是使用擦除来实现的泛型,因此字节码中的方法的签名是不会包含泛型信息的。对于泛型方法会多生成一个 Signature 的属性,用于记录方法带泛型信息的签名,反编译器也可以根据这个信息将泛型方法还原回来。
image.png
请添加图片描述

构造函数泛型

下面的代码定义了一个泛型类 ConstructorGeneric,它的泛型参数是 T,这个类的构造函数也是泛型的,它有一个泛型参数 X

class ConstructorGeneric<T> {public <X> ConstructorGeneric(X a) {}
}

创建该对象的代码如下:

ConstructorGeneric<Number> t = new <String>ConstructorGeneric<Number>("123");

这里 new 后面的 String 是传给构造器的泛型 X 的,即 X 的实际类型为 String;类的范型参数是由 Number 传递的,即 T 的实际类型是 Number。这里两个都是省略,写在这里是为了显示区分出两个参数传递的位置。

类型变量的限定

带单个上界限定
下面的代码定义了一个 NatualNumber 类,它的泛型参数 T 限制为 Integer 或者 Integer 的子类。

public class NaturalNumber<T extends Integer> {private T n;public NaturalNumber(T n)  { this.n = n; }public boolean isEven() {return n.intValue() % 2 == 0;}
}

调用代码如下:

// 正常
NaturalNumber<Integer> natural1 = new NaturalNumber<>(1);  // 无法编译,因为这里和泛型类定义的上界不符合
NaturalNumber<Double> natualral2 = new NaturalNumber<>(1.0);

带多个上界的限定
多个上界之间使用 & 符号进行分隔,如果多个限定中有类,则类需要排在接口后面(因为 Java 不支持多继承,所以不存在有多个限定的类的情况)。使用时需要满足所有的限定条件才能执行,这个校验应该是在编译时期做的,因为擦除之后,只会保留第一个限定界。

class A {}
interface B {}
class C extends A implements B {}
public static <T extends A & B> void test(T a) {}public static void main(String[] args) {// 编译错误,A 只能满足一个上界test(new A());// 正常test(new C());
}

通配符

在泛型中使用 ? 表示通配符,它的语义是表示未知的类型。通配符可以用作方法的形参、字段的定义、局部变量的定义,以及有的时候作为函数的返回值。通配符不能作为实参调用泛型方法,不能创建对象,或者派生子类型。

上界通配符

当你想定义一个普通方法,这个普通方法可以处理某一类的 List 中的元素时,比如像:List<Number>List<Integer>List<Double> 时,这个时候如果你把方法的入参定义为 List<Number> 是不行的,因为在 Java 中 List<Integer> 不是 List<Number> 的子类。

public static void process(List<Number> numbers) {}// 编译错误
List<Number> numbers = new ArrayList<>();
proess(numbers);

假设 List 是 List 的子类,则可以实现如下的代码:

List<Integer> integers = new ArrayList<>();// 假设下面是成立的
List<Number> numbers = integers;// 下面这句也应该是合法的,但是这违背了 intergers 只能存放 Integer 的语义
numbers.add(new Double());

从上面的例子可以看出,如果允许 List<Integer>List<Number> 的子类型,则会破坏泛型的语义,因此这在 Java 中是不允许的。

但是又实际存在上面描述的这种需求,因此 Java 提供了上界通配符的语法,则方法定义可以定义为如下:

public static void process(List<? extends Number> numbers) {for (Number num : numbers) {// do something}
}// 下面的调用都是能够正常编译通过的
List<Number> numbers = new ArrayList<>();
process(numbers);List<Integer> integers = new ArrayList<>();
process(integers);List<Double> doubles = new ArrayList<>();
process(doubles);

这里的 ? extends Number 的语义就是可以匹配 Number 或者 Number 子类的 List,需要注意的是在 Java 中的继承(extends)和实现(implements)在这里都用关键字 extends 来表示。

从这里也可以看出,List<? extends Number> 的返回值是可以赋值给 Number 类型的。这里可以想象一下 Listget() 方法的泛型参数 E 就变成了 ? extends Number 这个实际类型,而它表达的语义是 Number 以及 Number 的子类,因此赋值给一个 Number 类型的变量是合法的。

但是下面的代码是不合法的:

public static void process(List<? extends Number> numbers) {numbers.add(new Integer());
}

这里同样可以想象一下 Listadd() 方法的入参的泛型参数 E 就变成了 ? extends Number 这个实际类型,它表达的语义是 Number 以及 Number 的子类,但是具体是哪个子类是无法确定的。上面的例子也解释了它可能是 NumberIntegerDouble 等,假设它是 Double 类型,这里放一个 Integer 类型,又违背了泛型只能放 Double 的语义,因此这里的赋值是不合法的。

无界通配符

下面的代码就是定义了一个 List<?> 形参的方法,这里的 List<?> 语义是一个未知类型的 List

public static void printList(List<?> list) {}

无界通配符定义的 List 里面的元素只能赋值给 Object 类型。这里可以想象一下 Listget() 方法的泛型参数 E 就变成了 ? 这个实际类型,它的语义是一个未知的类型,既然是一个未知的类型那么我只能赋值给 Object 类型的变量了。

public static void printList(List<?> list) {for (Object obj : list) {// do something}
}

无界通配符定义的 List 里面只能添加 null,不能添加其它的任何类型的元素,即使是 Object 也不行,因为添加了之后就会违背泛型的语义了。

无界通配符的主要使用场景是:

  • 需要使用 Object 类中的方法
  • 使用了泛型类中不用关心泛型的方法,比如 List 中的 size()clear() 方法

下界通配符

在使用上面的上界通配时,发现了一个问题,如果一个 List 类型形参声明为了上界通配符,是没有办法往这个 List 里面添加元素的,为了解决这个问题,可以使用下界通配符,可以定义如下的方法:

public static void addNumbers(List<? super Number> list) {list.add(new Integer());list.add(new Double());
}

这里可以想象一下这个时候 Listadd() 方法的入参的泛型参数 E 就变成了 ? super Integer 类型,它的语义是匹配 Number 以及 Number 类型的超类。根据 Java 多态的原理,这里实际可以传递的类型为 Integer 以及 Integer 的子类型,因为形参声明的是超类,实际传递子类的引用当然是合法的。

泛型继承关系

泛型的继承关系如下图所示:

image.png

通配符捕获

假设定义了一个无界限通配符的方法如下,这个方法会编译错误,因为按照之前分析的 List<?> 中不能添加任何类型的对象,而这里 list.get(0) 返回的是 Object 类型的对象,肯定是无法放入进去的。代码如下:

public void foo(List<?> list) {list.set(0, list.get(0)); // 编译报错
}

为了解决这个问题这个时候就可以通过新建一个私有的泛型方法来帮助捕获通配符的类型,这个私有的泛型方法名称通常是原有方法加上Helper后缀,这种技巧称为通配符捕获。代码如下:

pulic void foo(List<?> list) {// 调用这个方法的语义是告诉编译器我不知道具体类型是什么,// 但是取出来和放进去的元素类型是相同的fooHelper(list);
}private <T> void fooHelper(List<T> list) {// 合法T temp = list.get(0);// 合法list.set(0, temp);
}

对于泛型方法,因为 add() 方法的入参,get() 方法返回值的泛型参数都是 T,当传入一个 List 进来,虽然这个 List 里面的对象实际类型不知道,但是通过泛型参数可以判断 get() 方法返回类型和 add() 方法的入参类型都是一样的,都是 T 捕获到的一个实际类型 X

对于带通配符参数的方法,因为方法的声明没有一个泛型参数,不能捕获到实际的参数类型 X。那么对于每次方法的调用编译器都会认为是一个不同的类型。比如编译器编译的时候 list.set(0, xxx),这里的入参的类型就会是 CAP#1list.get(0) 返回的类型就是 CAP#2,因为没有一个泛型参数来告诉编译器说 CAP#1CAP#2 是一样的类型,因此编译器就会认为这两个是不同的类型,从而拒绝编译。下图是编译器实际的提示信息:
image.png

image.png
从上面的图也可以看出,第二次调用方法时,类型又变成 CAP#3CAP#4 了,这也证明了每次编译器都会认为是一个新的类型。

实际上这里也可以将这个私有的 Helper 方法定义为公共的,然后去掉通配符的方法。这两种定义实际上是达到了相同的效果,但是 Java 语言规范 5.1.10 章节中更推荐采用通配符的方式定义,但它上面阐述的原因没太看懂,但是在另外一篇博客里面看到一个观点感觉有点道理。
image.png

它说如果定义成一个泛型方法,那么老的遗留的没有用泛型的代码调用这个方法就会产生一个警告,但是如果是使用通配符则不会有警告产生。

public static void foo1(List<?>) {}public static <T> void foo2(List<T>) {}// 假设老的代码没有用泛型
List rawList = Arrays.asList("1", "2");
// 不会产生告警
foo1(rawList);
// 会产生告警,提示未经检查的转换
foo2(rawList);

然而实际上 JDK 中真正的实现并没有采用这种方式,而是直接用注解忽略了异常,直接用的原生类型来实现的。Collections 中的 reverse() 方法内部实现逻辑如下:

@SuppressWarnings({"rawtypes", "unchecked"})  
public static void reverse(List<?> list) {  int size = list.size();  if (size < REVERSE_THRESHOLD || list instanceof RandomAccess) {  for (int i=0, mid=size>>1, j=size-1; i<mid; i++, j--)  swap(list, i, j);  } else {  // instead of using a raw type here, it's possible to capture // the wildcard but it will require a call to a supplementary         // private method ListIterator fwd = list.listIterator();  ListIterator rev = list.listIterator(size);  for (int i=0, mid=list.size()>>1; i<mid; i++) {  Object tmp = fwd.next();  fwd.set(rev.previous());  rev.set(tmp);  }  }  
}

桥接方法

假设定义了如下代码:

public class Node<T> {public T data;public Node(T data) { this.data = data; }public void setData(T data) {System.out.println("Node.setData");this.data = data;}
}public class MyNode extends Node<Integer> {public MyNode(Integer data) { super(data); }public void setData(Integer data) {System.out.println("MyNode.setData");super.setData(data);}
}

泛型擦除后的实际代码如下,注意看 MyNode 里面的 setData() 方法并没有重写 Node 里面的 setData() 方法了,因为方法签名不一样。这就违背了 Java 多态的语义。
Java 编译器在编译的时候会自动给 MyNode 生成一个桥接方法,这个方法的签名和 Node 类里面的一样,然后在这个方法里面去调用真正的 setData() 方法。
通过查看 MyNode.class 文件可以看到真的有两个 setData() 方法存在。
image.png

方法的形参类型是 Object 类型,和 Node 类中泛型擦除后的类型相同,说明这个方法才是真正重载了 Node 类中的方法。
image.png

方法实现中调用了 MyNode 类中形参为 Integer 类型的 setData() 方法。
image.png

同时在 MyNode 类中不允许自己定义形参为 Object 类型的 setData() 方法了,如果定义了则无法编译:
image.png

经过编译器编译后的代码等效为如下的代码:

public class Node {public Object data;public Node(Object data) { this.data = data; }public void setData(Object data) {System.out.println("Node.setData");this.data = data;}
}public class MyNode extends Node<Integer> {public MyNode(Integer data) { super(data); }public void setData(Integer data) {System.out.println("MyNode.setData");super.setData(data);}// 由编译器生成的桥接方法// 如果手动定义了这个方法编译器就会报错了public void setData(Object data) {setData((Integer) data);}
}

泛型的局限性

泛型不能用于基本类型

泛型是通过擦除实现的,擦除之后 ArrayList 内部是 Object[] 类型的数组,是不能存放基本类型的,因为基本类型不是 Object 类型的子类。

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

不能创建泛型类型的实例

泛型是通过擦除来实现的,所以擦除之后都会变成 new Object() (没有指定上界的情况),而实际上我们是要创建 T 类型的实例的。

public static <T> void test(List<T> list) {E ele = new E();list.add(ele);
}// 可以通过如下方式
public static <T> void test(List<T> list, Class<T> clazz) {E ele = clazz.newInstance();list.add(ele);
}
// 调用
List<String> list = new ArrayList<>();
test(list, String.class);

不能声明静态的泛型变量

泛型相当于是类的工厂,可以创建不同类型的实例。而静态变量是所有实例共享的,如果允许声明静态的泛型变量,那么不同类型的实例之间就会存在矛盾。

public class MobileDevice<T> {private static T os;
}// 这两个实例的静态变量就会存在矛盾
MobileDevice<Smartphone> phone = new MobileDevice<>();
MobileDevice<TabletPC> pc = new MobileDevice<>();

不能使用 instanceof 判断泛型类型

泛型是通过擦除实现的,因此 List<T>.class 在内存中是不存在的,只有 List.class,这个类型也被称为原生类型。

// 错误
if (list instanceof List<String>) {
}// 正确
if (list instanceof List) {
}

不能创建泛型数组

泛型是通过擦除实现的,如果允许声明泛型数组,则无法实现数组在存放时会校验数组的元素类型这个语义。

// 假设允许创建,这个数组的每个元素只允许存放 List<String> 类型的元素
Object[] stringLists = new List<String>[2]; 
// 正确执行
stringLists[0] = new ArrayList<String>(); 
// 这行应该抛出 ArrayStoreException 异常,
// 但是由于擦除,实际上和上面是一样的,这里违背了数组的语义
stringLists[1] = new ArrayList<Integer>();

不能创建、捕获、抛出带泛型的异常

// 编译报错
class MathException<T> extends Exception {}    // 编译报错
class QueueFullException<T> extends Throwable {}// 编译报错
public static <T extends Exception, J> void execute(List<J> jobs) {try {for (J job : jobs)} catch (T e) {  // 编译报错}
}class Parser<T extends Exception> {// 这样是允许的// 我觉得允许的原因是声明了抛出父类,而实际抛出子类也是合法的public void parse(File file) throws T {  }
}

不能使用擦除后原生类型相同的泛型参数方法来重载

public class Example {// 这两个方法擦除后的参数是一样的,所以不能算重载public void print(Set<String> strSet) { }public void print(Set<Integer> intSet) { }
}

堆污染

当定义变长的泛型参数时,如果尝试把一个原生类型赋值给变成泛型参数就有可能发生堆污染。堆污染的本质原因就是可以通过语法糖变长参数列表来创建泛型的的数组导致的。例如下面的代码:

public class ArrayBuilder {public static <T> void addToList (List<T> listArg, T... elements) {for (T x : elements) {listArg.add(x);}}public static void faultyMethod(List<String>... l) {// 这里编译应该会有告警,如果忽略这个告警,则有可能带来堆污染Object[] objectArray = l;   objectArray[0] = Arrays.asList(42);String s = l[0].get(0);     }
}

编译告警中就会提示有堆污染,如下图所示:
image.png

当编译器遇到一个变长参数方法时,它会把它转换为一个数组。对于 T... elements 这种参数声明就会转为 T[] elements,因为泛型的擦除,最终会被转换为 Object[] elements,这里编译器就会认为有可能发生堆污染。

可以通过以下三种方式抑制这种警告:

  • @SuppressWarnings({"unchecked", "varargs"})
    这种方式只能抑制方法声明时候的告警,方法调用处还是会产生告警;
    image.png
  • @SafeVarargs
    不会产生任何警告
    image.png
  • 增加 -Xlint:varags 编译选项
    不会产生任何警告
    image.png

JVM 控制参数

显示所有告警信息

给编译器增加 -Xlint:unchecked ,在 Idea 中可以参考如下图配置:
image.png

显示更详细的诊断信息

给编译增加 -Xdiags: verbose 选项
image.png

显示所有告警信息为英文

增加如下环境变量:
image.png
Idea 中可以将配置放在 vmproperties 文件中,如下图所示:
image.png

参考

Java Generic Tutorial
Java核心技术·卷 I(原书第10版)
深入理解Java虚拟机(第3版)
When to use generic methods and when to use wild-card?
Why use a wild card capture helper method?
Capture Conv: rev/reverse - what’s the point?
Difference between <? super T> and <? extends T> in Java
What is PECS (Producer Extends Consumer Super)?
Differences between copy(List<? super T> dest, List<? extends T> src) and copy(List dest, List<? extends T> src)

相关文章:

Java 泛型详细解析

泛型的定义 泛型类的定义 下面定义了一个泛型类 Pair&#xff0c;它有一个泛型参数 T。 public class Pair<T> {private T start;private T end; }实际使用的时候就可以给这个 T 指定任何实际的类型&#xff0c;比如下面所示&#xff0c;就指定了实际类型为 LocalDate…...

题解:CF332B Maximum Absurdity

CF332B CF332B 暴力思路 题目要我们找两个不重叠的区间&#xff0c;并使区间的值最大。那我们可以考虑使用双重循环搭配前缀和暴力求最大值。代码如下。 for(int i1;i<n;i) {ll lsum[ik-1]-sum[i-1],maxx;for(int jik;j<n;j){maxxlsum[jk-1]-sum[j-1];if(maxx>ans.…...

Vue 集成和使用 SQLite 的完整指东

1. 引言 SQLite 是一种轻量级的关系型数据库管理系统&#xff0c;以其简单易用、无需服务器等特点广泛应用于嵌入式系统、移动应用和小型应用程序中。在 Web 开发中&#xff0c;尤其是前端应用开发中&#xff0c;SQLite 可以作为客户端本地存储的一种选择&#xff0c;为用户提…...

【JVM什么时候触发YoungGC和FullGC】

YoungGC 年轻代Eden区满&#xff0c;就会触发YoungGC FullGC 老年代空间不足 经过多次GC后的大年龄对象会被放进老年代&#xff0c;或创建的大对象会直接在老年代分配&#xff0c;此时若老年代空间不足&#xff0c;就会触发FullGC。空间分配担保失败 触发YoungGC的时候会进行…...

ubuntu配置网络

1&#xff0c;设置桥接模式 1-1&#xff1a; 确定。 1-2&#xff1a; 编辑--->虚拟网络编辑器 刚安装ubuntu的时候&#xff0c;可能没有任何VMnet. 更改设置的目的&#xff1a; 添加VMnet0&#xff0c;并且设置VMnet为桥接模式--自动桥接。 如果没有VMnet0,选择添加网络…...

第十一课 Unity编辑器创建的资源优化_预制体和材质篇(Prefabs和Materials)详解

预制体(Prefabs) Unity中的预制体是用来存储游戏对象、子对象及其所需组件的可重用资源&#xff0c;一般来说预制体资源可充当资源模版&#xff0c;在此模版基础上可以在场景中创建新的预制体实例。 使用预制体的好处 由于预制体系统可以自动保持所有实例副本同步&#xff0c…...

2024.11.29(单链表)

思维导图 声明文件 #ifndef __LINKLIST_H__ #define __LINKLIST_H__#include <myhead.h>typedef char datatype; //数据元素类型 //定义节点类型 typedef struct Node {union{int len; //头节点数据域datatype data; //普通节点数据域};struct Node *next; //指针域…...

基于深度学习和卷积神经网络的乳腺癌影像自动化诊断系统(PyQt5界面+数据集+训练代码)

乳腺癌是全球女性中最常见的恶性肿瘤之一&#xff0c;早期准确诊断对于提高生存率具有至关重要的意义。传统的乳腺癌诊断方法依赖于放射科医生的经验&#xff0c;然而&#xff0c;由于影像分析的复杂性和人类判断的局限性&#xff0c;准确率和一致性仍存在挑战。近年来&#xf…...

opengl 三角形

最后效果&#xff1a; OpenGL version: 4.1 Metal 不知道为啥必须使用VAO 才行。 #include <glad/glad.h> #include <GLFW/glfw3.h>#include <iostream> #include <vector>void framebuffer_size_callback(GLFWwindow *window, int width, int heigh…...

23种设计模式-抽象工厂(Abstract Factory)设计模式

文章目录 一.什么是抽象工厂设计模式&#xff1f;二.抽象工厂模式的特点三.抽象工厂模式的结构四.抽象工厂模式的优缺点五.抽象工厂模式的 C 实现六.抽象工厂模式的 Java 实现七.代码解析八.总结 类图&#xff1a; 抽象工厂设计模式类图 一.什么是抽象工厂设计模式&#xff1f…...

手机上怎么拍证件照,操作简单且尺寸颜色标准的方法

在数字化时代&#xff0c;手机已成为我们日常生活中不可或缺的一部分。它不仅是通讯工具&#xff0c;更是我们拍摄证件照的便捷利器。然而&#xff0c;目前证件照制作工具鱼龙混杂&#xff0c;很多打着免费名号的拍照软件背后却存在着泄漏用户信息、照片制作不规范导致无法使用…...

IDEA报错: java: JPS incremental annotation processing is disabled 解决

起因 换了个电脑打开了之前某个老项目IDEA启动springcloud其中某个服务直接报错&#xff0c;信息如下 java: JPS incremental annotation processing is disabled. Compilation results on partial recompilation may be inaccurate. Use build process “jps.track.ap.depen…...

OCR实现微信截图改名

pip install paddlepaddle -i https://pypi.tuna.tsinghua.edu.cn/simple/ ──(Sat,Nov30)─┘ pip install shapely -i https://pypi.tuna.tsinghua.edu.cn/simple/ pip install paddleo…...

第一届“吾杯”网络安全技能大赛 Writeup

战队信息 战队名称&#xff1a;在你眼中我是誰&#xff0c;你想我代替誰&#xff1f; 战队排名&#xff1a;13 Misc Sign Hex 转 Str&#xff0c;即可得到flag。 原神启动&#xff01; 不好评价&#xff0c;stegsolve 秒了&#xff1a; WuCup{7c16e21c-31c2-439e-a814-b…...

再谈Java中的String类型是否相同的判断方法

目录 第一部分 代码展示 画图展示 第二部分 代码展示 画图展示 第一部分 代码展示 画图展示 第二部分 代码展示 画图展示...

<一>51单片机环境

目录 1,51单片机开发语言是C,环境keil 1.1,工程创建 1.2用什么把代码放进单片机里面 2,初识代码 1,51单片机开发语言是C,环境keil 1.1,工程创建 1. 创建项目工程文件夹&#xff0c;可以当作模板Template 2. 创建文件&#xff0c;取名main.c 3,编译&#xff0c;选择输出文…...

【0x0001】HCI_Set_Event_Mask详解

目录 一、命令概述 二、命令格式 三、命令参数说明 四、返回参数说明 五、命令执行流程 5.1. 主机准备阶段 5.2. 命令发送阶段 5.3. 控制器接收与处理阶段 5.4. 事件过滤与反馈阶段 5.5. 主机处理&#xff08;主机端&#xff09; 5.6. 示例代码 六、命令应用场景 …...

第三方Express 路由和路由中间件

文章目录 1、Express 应用使用回调函数的参数&#xff1a; request 和 response 对象来处理请求和响应的数据。2、Express路由1.路由方法2.路由路径3.路由处理程序 3. 模块化路由4. Express中间件1.中间件简介2.中间件分类3.自定义中间件 1、Express 应用使用回调函数的参数&am…...

七、Python —— 元组、集合和字典

文章目录 一、元组1.1、元组的初始化1.2、元组的解包1.3、元组的比较运算1.4、元组的其他操作 二、集合 set2.1、集合的初始化2.2、集合的常用操作2.3、使用 for 循环遍历集合 三、字典 map3.1、字典的初始化3.2、字典的常用操作3.3、使用 for 循环遍历字典 四、补充 一、元组 …...

Aes加解密

加解密概念 加密AES加密填充模式加密模式示例 加密 通过一系列计算将明文转换成一个密文。 加密和解密的对象通常是字节数组&#xff08;有的语言动态数组类比切片&#xff09; 加密后的数据&#xff0c;可能有很多是不可读字符。通常会将其转换为可见的字符串。 直接将字节…...

【时时三省】Tessy 故障入侵 使用教程

目录 1,故障入侵 介绍 故障入侵适用场景: 打故障入侵的方法和选项介绍: 2,打单个函数的故障入侵 3,打整体用例的故障入侵 4,一个函数打多个故障入侵 山不在高,有仙则名。水不在深,有龙则灵。 ----CSDN 时时三省 1,故障入侵 介绍 故障入侵适用场景: 故障入侵 …...

.NET 9 AOT的突破 - 支持老旧Win7与XP环境

引言 随着技术的不断进步&#xff0c;微软的.NET 框架在每次迭代中都带来了令人惊喜的新特性。在.NET 9 版本中&#xff0c;一个特别引人注目的亮点是 AOT&#xff08; Ahead-of-Time&#xff09;支持&#xff0c;它允许开发人员将应用程序在编译阶段就优化为能够在老旧的 Win…...

CondaValueError: Malformed version string ‘~‘: invalid character(s).

问题描述&#xff1a;在window下使用conda安装任何包都会报错。报错信息是CondaValueError: Malformed version string ~: invalid character(s). 解决办法&#xff1a;把.condarc文件的源地址删除&#xff08;八成是源地址访问不了了&#xff09;&#xff0c;只保存默认的&am…...

01-Ubuntu24.04LTS上安装PGSQL

目录 一、准备工作 1.1、系统要求 1.2 、更新 Ubuntu 系统 1.3 、安装依赖 1.4 、添加 PostgreSQL 16 软件源 二、安装 PostgreSQL 16 数据库 三、管理 PostgreSQL 服务 四、PostgreSQL 管理操作 4.1 、访问 Postgres 超级用户账户 4.2 、创建数据库并设置管理权限 4…...

Esp32使用micropython基于espnow实现语音对讲机

ESP-NOW协议介绍 ESP-NOW 是乐鑫自主研发的无连接通信协议,具有短数据包传输功能。该协议使多个设备能够以简单的方式相互通信。ESP-NOW 支持以下功能: 加密和未加密的单播通信; 混合加密和未加密的对等设备; 最多可携带 250 字节 的有效载荷; 发送回调功能,可以设置用于…...

Docker 容器隔离关键技术:SELinux

Docker 容器隔离关键技术&#xff1a;SELinux SELinux&#xff08;Security-Enhanced Linux&#xff09; 是 Linux 内核中的一项安全机制&#xff0c;用于实现强制访问控制&#xff08;MAC&#xff09;。Docker 利用了 SELinux 来增强容器的隔离性&#xff0c;通过对文件、进程…...

Java并发07之ThreadLocal

文章目录 1 ThreadLocal原理2 内部结构3 内存泄露问题4 entry的key为什么被设计为弱引用 1 ThreadLocal原理 ThreadLocal类用来提供线程内部的局部变量。这种变量在多线程环境下访问时能保证各个线程的变量相对独立于其他线程内的变量。ThreadLocal实例通常来说都是private st…...

【单细胞数据库】癌症单细胞数据库CancerSEA

数据库地址&#xff1a;home (hrbmu.edu.cn) Cite Huating Yuan, Min Yan, Guanxiong Zhang, Wei Liu, Chunyu Deng, Gaoming Liao, Liwen Xu, Tao Luo, Haoteng Yan, Zhilin Long, Aiai Shi, Tingting Zhao, Yun Xiao, Xia Li, CancerSEA: a cancer single-cell state atlas…...

Rsa加解密 + 签名验签

Rsa加解密 概述聚合算法名称&#xff08;用于创建加密器&#xff09;基本概念填充方式分块加密 基本使用生成密钥加解密创建加密器设置模式&#xff08;加密&#xff09;、公钥对明文加密&#xff0c;并对结果进行Base64编码对以上结果&#xff0c;进行解密 设置模式&#xff0…...

bugku-web-留言板1

大小写绕过也不行 <ScRipt>ALeRt(“XSS”);</sCRipT> 双写绕过可以 <scscriptript>alert(z)</scscriptript> 改变大小写 在测试过程中&#xff0c;我们可以改变测试语句的大小写来绕过XSS规则&#xff1a; 比如&#xff1a;<script>alert(“xs…...

深圳大公司/百度热搜关键词排名优化

倒数60秒价格走势 可是然而但是&#xff0c;别人家都是怎么拍中的&#xff1f;答&#xff1a;不按套路出牌这次你就中了。 那么即使是这样&#xff0c;还有没有“套路”可循&#xff1f;答&#xff1a;肯定有的。 相信很多人都读过国拍官网的《网上投标拍卖操作流程》&#xff…...

一个公司能备案多个网站吗/搜索引擎实训心得体会

int main(int argc , char *argv[])//测试调用函数 {const char *env_init[] {"USERunkown","PATHmypath",NULL};pid_t pid;if( (pid fork()) < 0 )err_sys("fork error");else if(pid 0)//这个很牛逼的样子哦,还可以这样玩,在子函数里面调…...

新版lnmp安装wordpress/seo搜索引擎优化怎么做

拓扑&#xff1a; 加密点和通信点 加密点10.1.1.1---10.1.1.2&#xff0c;通信点&#xff0c;1.1.1.1-2.2.2.2 说明&#xff1a;看这篇文章的前提是 &#xff0c;你已经知道了怎么样申请证书。已经初步了解了证书的基本原理和一些域名的知识。这些细节&#xff0c;需要的可以百…...

家装公司网站建设网站/百度识图在线入口

学习适配器有一段时间了,现在对BaseAdapter的优化和要是项目中用到的适配其很多的话,完全可以建立一个SimpleBaseAdapter(继承自BaseAdapter),方便扩展. 主布局代码: 1 <LinearLayout xmlns:android"http://schemas.android.com/apk/res/android"2 xmlns:tool…...

跟随网站滚动的悬浮框怎么做/电商卖货平台有哪些

先上一个简单的流程示意图然后再从客户端输入用户名和密码开始说起&#xff1a;1、客户端输入用户名和密码登录渠道流程&#xff1a;1 客户端输入渠道账号user_name和密码password&#xff1b;2 登录&#xff0c;登录SDK成功之后&#xff0c;会返回一个token令牌&#xff1b;3 …...

南京有哪些做网站的公司/360网站排名优化

问题 MediaConvert进行转码任务的时候&#xff0c;需要及时了解MediaConvert转码任务的状态。因为AWS设计成MediaConvert转码任务只能给AWS的服务监控平台CloudWatch发事件&#xff0c;这次就来说说怎么在CloudWatch上面配置对MediaConvert转码任务的监听。 步骤 MediaConve…...