读书笔记-《ON JAVA 中文版》-摘要23[第二十章 泛型-2]

文章目录

  • 第二十章 泛型
    • 5. 泛型擦除
      • 5.1 泛型擦除
      • 5.2 迁移兼容性
      • 5.3 擦除的问题
      • 5.4 边界处的动作
    • 6. 补偿擦除
    • 7. 边界
    • 8. 通配符
      • 8.1 通配符
      • 8.2 逆变
    • 9. 问题
    • 10. 动态类型安全
    • 11. 泛型异常

第二十章 泛型

普通的类和方法只能使用特定的类型:基本数据类型或类类型。如果编写的代码需要应用于多种类型,这种严苛的限制对代码的束缚就会很大。

多态是一种面向对象思想的泛化机制。你可以将方法的参数类型设为基类,这样的方法就可以接受任何派生类作为参数,包括暂时还不存在的类。

拘泥于单一的继承体系太过局限,如果方法以接口而不是类作为参数,限制就宽松多了,只要实现了接口就可以。

即便是接口也还是有诸多限制。一旦指定了接口,它就要求你的代码必须使用特定的接口。而我们希望编写更通用的代码,能够适用“非特定的类型”,而不是一个具体的接口或类。

这就是泛型的概念,是 Java 5 的重大变化之一。在很多情况下,它可以使代码更直接更优雅。

5. 泛型擦除

5.1 泛型擦除

package generics;import java.util.ArrayList;public class ErasedTypeEquivalence {public static void main(String[] args) {Class c1 = new ArrayList<String>().getClass();Class c2 = new ArrayList<Integer>().getClass();System.out.println(c1 == c2);}
}

输出:

true

ArrayList 和 ArrayList 应该是不同的类型。不同的类型会有不同的行为。例如,如果尝试向 ArrayList 中放入一个 Integer ,所得到的行为(失败)和向ArrayList 中放入一个 Integer 所得到的行为(成功)完全不同。然而上面的程序认为它们是相同的类型。

下面的例子是对该谜题的补充:

package generics;import java.util.*;class Frob {
}class Fnorkle {
}class Quark<Q> {
}class Particle<POSITION, MOMENTUM> {
}public class LostInformation {public static void main(String[] args) {List<Frob> list = new ArrayList<>();Map<Frob, Fnorkle> map = new HashMap<>();Quark<Fnorkle> quark = new Quark<>();Particle<Long, Double> p = new Particle<>();// Class.getTypeParameters() “返回一个 TypeVariable 对象数组,// 表示泛型声明中声明的类型参数...”System.out.println(Arrays.toString(list.getClass().getTypeParameters()));System.out.println(Arrays.toString(map.getClass().getTypeParameters()));System.out.println(Arrays.toString(quark.getClass().getTypeParameters()));System.out.println(Arrays.toString(p.getClass().getTypeParameters()));}
}

输出:

[E]
[K, V]
[Q]
[POSITION, MOMENTUM]

残酷的现实是:

在泛型代码内部,无法获取任何有关泛型参数类型的信息

因此,你可以知道如类型参数标识符和泛型边界这些信息,但无法得知实际的类型参数从而用来创建特定的实例。

Java 泛型是使用擦除实现的。这意味着当你在使用泛型时,任何具体的类型信息都被擦除了,你唯一知道的就是你在使用一个对象。因此, List 和 List 在运行时实际上是相同的类型。它们都被擦除成原生类型 List 。

package generics;public class HasF {public void f() {System.out.println("HasF.f()");}
}
package generics;
class Manipulator<T> {private T obj;public Manipulator(T obj) {this.obj = obj;}public void manipulate() {obj.f(); // 报错,没有 f()}
}
public class Manipulation {public static void main(String[] args) {HasF hf = new HasF();Manipulator<HasF> manipulator = new Manipulator<>(hf);manipulator.manipulate();}
}

因为擦除,Java 编译器无法将 manipulate() 方法中调用 obj 的 f() 方法这一需求映射到HasF 的 f() 方法。为了调用 f() ,我们必须协助泛型类,给定泛型类一个边界,以此告诉编译器只能接受遵循这个边界的类型。这里重用了 extends 关键字。由于有了边界,下面的代码就能通过编译:

package generics;public class Manipulator2<T extends HasF> {private T obj;Manipulator2(T x) {obj = x;}public void manipulate() {obj.f();}
}

边界 声明 T 必须是 HasF 类型或其子类。

你可能认为泛型在 Manipulator2.java 中没有贡献任何事。你可以很轻松地自己去执行擦除,生成没有泛型的类:

package generics;public class Manipulator3 {private HasF obj;Manipulator3(HasF x) {obj = x;}public void manipulate() {obj.f();}
}

泛型只有在类型参数比某个具体类型(以及其子类)更加“泛化”——代码能跨多个类工作时才有用

如果某个类有一个返回 T 的方法,那么泛型就有所帮助,因为它们之后将返回确切的类型:

package generics;public class ReturnGenericType<T extends HasF> {private T obj;public ReturnGenericType(T x) {this.obj = x;}public T get(){return obj;}
}

5.2 迁移兼容性

擦除不是一个语言特性,它是 Java 实现泛型的一种妥协,因为泛型不是 Java 语言出现时就有的。泛型在 Java 中仍然是有用的,只是不如它们本来设想的那么有用,而原因就是擦除。

擦除的核心动机是你可以在泛化的客户端上使用非泛型的类库,反之亦然。这经常被称为“迁移兼容性”。在理想情况下,所有事物将在指定的某天被泛化。在现实中,即使程序员只编写泛型代码,他们也必须处理 Java 5 之前编写的非泛型类库。这些类库的作者可能从没想过要泛化他们的代码,或许他们可能刚刚开始接触泛型。

因此 Java 泛型不仅必须支持向后兼容性——现有的代码和类文件仍然合法,继续保持之前的含义——而且还必须支持迁移兼容性,使得类库能按照它们自己的步调变为泛型,当某个类库变为泛型时,不会破坏依赖于它的代码和应用。在确定了这个目标后,Java 设计者们和从事此问题相关工作的各个团队决策认为擦除是唯一可行的解决方案。擦除使得这种向泛型的迁移成为可能,允许非泛型的代码和泛型代码共存。

—PS:擦除虽然有弊端,但它是解决泛型向后兼容性的唯一可行方案

5.3 擦除的问题

因此,擦除主要的正当理由是从非泛化代码到泛化代码的转变过程,以及在不破坏现有类库的情况下将泛型融入到语言中。

泛型不能用于显式地引用运行时类型的操作中,例如转型、instanceof 操作和 new 表达式。因为所有关于参数的类型信息都丢失了,当你在编写泛型代码时,必须时刻提醒自己,你只是看起来拥有有关参数的类型信息而已。

public class Foo<T> {T var;
}

看上去当你创建一个 Foo 实例时:

Foo<Cat> f = new Foo<>();

class Foo 中的代码应该知道现在工作于 Cat 之上。泛型语法也在强烈暗示整个类中所有 T 出现的地方都被替换,就像在 C++ 中一样。但是事实并非如此,当你在编写这个类的代码时,必须提醒自己:“不,这只是一个 Object“。

另外,擦除和迁移兼容性意味着,使用泛型并不是强制的:

package generics;class GenericBase<T> {private T element;public void set(T arg) {element = arg;}public T get() {return element;}
}class Derived1<T> extends GenericBase<T> {
}class Derived2 extends GenericBase {
}// Derived3 产生的错误意味着编译器期望得到一个原生基类
//class Derived3 extends GenericBase<?> {}public class ErasureAndInteritance {public static void main(String[] args) {Derived2 d2 = new Derived2();Object obj = d2.get();d2.set(obj);}
}

5.4 边界处的动作

边界:即对象进入和离开方法的地点。这些正是编译器在编译期执行类型检查并插入转型代码的地点。这就告诉我们泛型的所有动作都发生在边界处——对入参的编译器检查和对返回值的转型。这有助于澄清对擦除的困惑,记住:“边界就是动作发生的地方”。

6. 补偿擦除

因为擦除,我们将失去执行泛型代码中某些操作的能力。无法在运行时知道确切类型:

package generics;public class Erased<T> {private final int SIZE = 100;public void f(Object arg) {if (arg instanceof T) {} // 报错T var = new T(); // 报错T[] array = new T[SIZE]; // 报错T[] array2 = (T[]) new Object[SIZE]; // 书上说这个地方应该有警告,但是没有,不知道为啥}
}

—PS:上文有:泛型不能用于显式地引用运行时类型的操作中,例如转型、instanceof 操作和 new 表达式。

有时,我们可以对这些问题进行编程,但是有时必须通过引入类型标签来补偿擦除。这意味着为所需的类型显式传递一个 Class 对象,以在类型表达式中使用它。

例如,由于擦除了类型信息,因此在上一个程序中尝试使用 instanceof 将会失败。类型标签可以使用动态 isInstance() :

package generics;class Building {
}class House extends Building {
}public class ClassTypeCapture<T> {Class<T> kind;public ClassTypeCapture(Class<T> kind) {this.kind = kind;}public boolean f(Object arg) {return kind.isInstance(arg);}public static void main(String[] args) {ClassTypeCapture<Building> ctt1 =new ClassTypeCapture<>(Building.class);System.out.println(ctt1.f(new Building()));System.out.println(ctt1.f(new House()));ClassTypeCapture<House> ctt2 =new ClassTypeCapture<>(House.class);System.out.println(ctt2.f(new Building()));System.out.println(ctt2.f(new House()));}
}

输出:

true
true
false
true

7. 边界

边界允许我们对泛型使用的参数类型施加约束。尽管这可以强制执行有关应用了泛型类型的规则,但潜在的更重要的效果是我们可以在绑定的类型中调用方法。

由于擦除会删除类型信息,因此唯一可用于无限制泛型参数的方法是那些 Object 可用的方法。但是,如果将该参数限制为某类型的子集,则可以调用该子集中的方法。为了应用约束,Java 泛型使用了extends 关键字

重要的是要理解,当用于限定泛型类型时, extends 的含义与通常的意义截然不同。此示例展示边界的基础应用:

package generics;import java.awt.*;interface HasColor {java.awt.Color getColor();
}class WithColor<T extends HasColor> {T item;WithColor(T item) {this.item = item;}T getItem() {return item;}java.awt.Color color() {return item.getColor();}
}class Coord {public int x, y, z;
}// 类需要放接口前面
// class WithColorCoord<T extends HasColor & Coord> {}
class WithColorCoord<T extends Coord & HasColor> {T item;WithColorCoord(T item) {this.item = item;}T getItem() {return item;}java.awt.Color color() {return item.getColor();}int getX() {return item.x;}int getY() {return item.y;}int getZ() {return item.z;}
}interface Weight {int weight();
}// 泛型只能 extends 一个类,但是可以 extends 多个接口
class Solid<T extends Coord & HasColor & Weight> {T item;Solid(T item) {this.item = item;}T getItem() {return item;}java.awt.Color color() {return item.getColor();}int getX() {return item.x;}int getY() {return item.y;}int getZ() {return item.z;}int weight() {return item.weight();}
}class Bounded extends Coord implements HasColor, Weight {@Overridepublic Color getColor() {return null;}@Overridepublic int weight() {return 0;}
}public class BasicBounds {public static void main(String[] args) {Solid<Bounded> solid = new Solid<>(new Bounded());solid.color();solid.getY();solid.weight();}
}

你可能会观察到 BasicBounds.java 中似乎包含一些冗余,它们可以通过继承来消除。在这里,每个继承级别还添加了边界约束:

package generics;class HoldItem<T> {T item;HoldItem(T item) {this.item = item;}T getItem() {return item;}
}class WithColor2<T extends HasColor> extends HoldItem<T> {WithColor2(T item) {super(item);}java.awt.Color color() {return item.getColor();}
}class WithColorCoord2<T extends Coord & HasColor> extends WithColor2<T> {WithColorCoord2(T item) {super(item);}int getX() {return item.x;}int getY() {return item.y;}int getZ() {return item.z;}
}class Solid2<T extends Coord & HasColor & Weight> extends WithColorCoord2<T> {Solid2(T item) {super(item);}int weight() {return item.weight();}
}public class InheritBounds {public static void main(String[] args) {Solid2<Bounded> solid2 = new Solid2<>(new Bounded());solid2.color();solid2.getY();solid2.color();}
}

HoldItem 拥有一个对象,因此此行为将继承到 WithColor2 中,这也需要其参数符合 HasColor

WithColorCoord2Solid2 进一步扩展了层次结构,并在每个级别添加了边界。现在,这些方法已被继承,并且在每个类中不再重复。

—PS:边界由 T extends 限定,每个类或接口中有独有的方法,在最后的继承类中都能引用

8. 通配符

8.1 通配符

起始示例要展示数组的一种特殊行为:将派生类的数组赋值给基类的引用:

package generics;class Fruit {
}class Apple extends Fruit {
}class Jonathan extends Apple {
}class Orange extends Fruit {
}public class CovariantArrays {public static void main(String[] args) {Fruit[] fruit = new Apple[10];fruit[0] = new Apple();fruit[1] = new Jonathan();try {// 编译不报错fruit[0] = new Fruit(); // 运行报错-ArrayStoreException} catch (Exception e) {System.out.println(e);}try {// 编译不报错fruit[0] = new Orange(); // 运行报错-ArrayStoreException} catch (Exception e) {System.out.println(e);}}
}

输出:

java.lang.ArrayStoreException: generics.Fruit
java.lang.ArrayStoreException: generics.Orange

看起来就像数组对它们持有的对象是有意识的,因此在编译期检查和运行时检查之间,你不能滥用它们。

数组的这种赋值并不是那么可怕,因为在运行时你可以发现插入了错误的类型。但是泛型的主要目标之一是将这种错误检测移到编译期。所以当我们试图使用泛型集合代替数组时,会发生什么呢?

package generics;import java.util.ArrayList;
import java.util.List;public class NonCovariantGenerics {public static void main(String[] args) {List<Fruit> fruit = new ArrayList<Apple>();}
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uNaoGevq-1693296802004)(img/201.png)]

阅读这段代码时会认为“不能将一个 Apple 集合赋值给一个 Fruit 集合”。记住,泛型不仅仅是关于集合,它真正要表达的是“不能把一个涉及 Apple 的泛型赋值给一个涉及 Fruit 的泛型”。

与数组不同,泛型没有内建的协变类型。这是因为数组是完全在语言中定义的,因此可以具有编译期和运行时的内建检查,但是在使用泛型时,编译器和运行时系统不知道你想用类型做什么,以及应该采用什么规则。

但是,有时你想在两个类型间建立某种向上转型关系。通配符可以产生这种关系。

package generics;import java.util.ArrayList;
import java.util.List;public class GenericsAndCovariance {public static void main(String[] args) {List<? extends Fruit> flist = new ArrayList<>();
//        flist.add(new Apple());
//        flist.add(new Fruit());
//        flist.add(new Object());flist.add(null);Fruit f = flist.get(0);}
}

展示一个简单的 Holder 类:

package generics;import java.util.Objects;public class Holder<T> {private T value;public Holder() {}public Holder(T value) {this.value = value;}public T get() {return value;}public void set(T value) {this.value = value;}@Overridepublic boolean equals(Object o) {return o instanceof Holder && Objects.equals(value, ((Holder) o).value);}@Overridepublic int hashCode() {return Objects.hashCode(value);}public static void main(String[] args) {Holder<Apple> apple = new Holder<>(new Apple());Apple d = apple.get();apple.set(d);
//        Holder<Fruit> fruit = apple; // 不能向上转型Holder<? extends Fruit> fruit = apple;Fruit p = fruit.get();d = (Apple) fruit.get();try {Orange c = (Orange) fruit.get(); // No warning} catch (Exception e) {System.out.println(e);}
//        fruit.set(new Apple());
//        fruit.set(new Fruit());System.out.println(fruit.equals(d));}
}

输出:

java.lang.ClassCastException: generics.Apple cannot be cast to generics.Orange
false

以上得到的信息:

1、创建了一个 Holder ,就不能将其向上转型为 Holder ,但是可以向上转型为 Holder<? extends Fruit> 。

2、如果调用 get() ,只能返回一个 Fruit——这就是在给定“任何;额扩展自 Fruit 的对象”这一边界后,它所能知道的一切了。如果你知道更多的信息,就可以将其转型到某种具体的 Fruit 而不会导致任何警告,但是存在得到ClassCastException 的风险。

3、set() 方法不能工作在 AppleFruit 上,因为 set() 的参数也是"?

extends Fruit",意味着它可以是任何事物,编译器无法验证“任何事物”的类型安全性。

8.2 逆变

还可以走另外一条路,即使用超类型通配符。这里,可以声明通配符是由某个特定类的任何基类来界定的,方法是指定 <?super MyClass> ,或者甚至使用类型参数: <?super T> 。

package generics;import java.util.List;public class SuperTypeWildcards {static void writeTo(List<? super Apple> apples) {apples.add(new Apple());apples.add(new Jonathan());
//        apples.add(new Fruit()); // 报错}
}

参数 applesApple 的某种基类型的 List,这样你就知道向其中添加 AppleApple 的子类型是安全的。但是因为 Apple 是下界,所以你知道向这样的 List 中添加 Fruit 是不安全的,因为这将使这个List 敞开口子,从而可以向其中添加非 Apple 类型的对象,而这是违反静态类型安全的。 下面的示例复习了一下逆变和通配符的的使用:

package generics;import java.util.Arrays;
import java.util.List;public class GenericReading {static List<Apple> apples = Arrays.asList(new Apple());static List<Fruit> fruits = Arrays.asList(new Fruit());static <T> T readExact(List<T> list) {return list.get(0);}static void f1() {Apple a = readExact(apples);Fruit f = readExact(fruits);f = readExact(apples); // 向上转型}// 内部类static class Reader<T> {T readExact(List<T> list) {return list.get(0);}}static void f2() {Reader<Fruit> fruitReader = new Reader<>();Fruit f = fruitReader.readExact(fruits);
//        Fruit a = fruitReader.readExact(apples);}static class CovariantReader<T> {T readCovariant(List<? extends T> list) {return list.get(0);}}static void f3() {CovariantReader<Fruit> fruitReader = new CovariantReader<>();Fruit f = fruitReader.readCovariant(fruits);Fruit a = fruitReader.readCovariant(apples);}public static void main(String[] args) {f1();f2();f3();}
}

看到这里有点费劲,还积攒了几个问题,没关系,带着疑惑找答案才能有收获,推荐大佬文章:Java 中的泛型(两万字超全详解)

看完大佬文章,下面的别看了,我是随便摘了几个。

9. 问题

任何基本类型都不能作为类型参数

Java 泛型的限制之一是不能将基本类型用作类型参数。因此,不能创建ArrayList 之类的东西。

实现参数化接口

一个类不能实现同一个泛型接口的两种变体,由于擦除的原因,这两个变体会成为相同的接口。下面是产生这种冲突的情况:

package generics;interface Payable<T> {}class Employee implements Payable<Employee> {}// Hourly 不能编译,因为擦除会将 Payable<Employe> 和 Payable<Hourly> 简化为相同的类
// Payable,这样,上面的代码就意味着在重复两次地实现相同的接口。
class Hourly extends Employee implements Payable<Hourly> {}public class MultipleInterfaceVariants {
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KJ8zhGL7-1693296802005)(img/202.png)]

转型和警告

使用带有泛型类型参数的转型或 instanceof 不会有任何效果。

重载

package generics;public class UseList {void f(List<T> v) {}void f(List<W> v) {}
}

因为擦除,所以重载方法产生了相同的类型签名。

基类劫持接口

package generics;public class ComparablePet implements Comparable<ComparablePet>{@Overridepublic int compareTo(ComparablePet o) {return 0;}
}class Cat extends ComparablePet implements Comparable<Cat> {}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hSzZy8zr-1693296802006)(img/203.png)]

—PS:就是上面提到的 擦除 引起的

自限定的类型

class SelfBounded<T extends SelfBounded<T>> { // ...

这就像两面镜子彼此照向对方所引起的目眩效果一样,是一种无限反射。SelfBounded 类接受泛型参数T,而 T 由一个边界类限定,这个边界就是拥有 T 作为其参数的 SelfBounded

当你首次看到它时,很难去解析它,它强调的是当 extends 关键字用于边界与用来创建子类明显是不同的。

10. 动态类型安全

因为可以向 Java 5 之前的代码传递泛型集合,所以旧式代码仍旧有可能会破坏你的集合。Java 5 的java.util.Collections 中有一组便利工具,可以解决在这种情况下的类型检查问题,它们是:静态方法 checkedCollection() 、 checkedList() 、 checkedMap() 、 checkedSet()、 checkedSortedMap() 和 checkedSortedSet() 。这些方法每一个都会将你希望动态检查的集合当作第一个参数接受,并将你希望强制要求的类型作为第二个参数接受。

11. 泛型异常

由于擦除的原因,catch 语句不能捕获泛型类型的异常,因为在编译期和运行时都必须知道异常的确切类型。泛型类也不能直接或间接继承自 Throwable(这将进一步阻止你去定义不能捕获的泛型异常)。 但是,类型参数可能会在一个方法的 throws 子句中用到。
在这里插入图片描述
(图网,侵删)

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

WPF实战项目十二(API篇):配置AutoMapper

1、新建类库WPFProjectShared&#xff0c;在类库下新建文件夹Dtos&#xff0c;新建BaseDto.cs&#xff0c;继承INotifyPropertyChanged&#xff0c;实现通知更新。 public class BaseDto : INotifyPropertyChanged{public int Id { get; set; }public event PropertyChangedEv…

Go的基础运行方式和打包

目录 基础运行方式导入路径 打包技巧相关知识点 基础运行方式 // 文件名可以不是main&#xff0c;但包名和入口函数比如是main // main.go package main // 导入包的时候可以直接导入&#xff0c;也可以导入后指定包名&#xff0c; import ("fmt"godemo "githu…

nlp系列(7)三元组识别(Bert+CRF)pytorch

模型介绍 在实体识别中&#xff1a;使用了Bert模型&#xff0c;CRF模型 在关系识别中&#xff1a;使用了Bert模型的输出与实体掩码&#xff0c;进行一系列变化&#xff0c;得到关系 Bert模型介绍可以查看这篇文章&#xff1a;nlp系列&#xff08;2&#xff09;文本分类&…

linux c编程之“hello world”一

文章目录 hello world开始学习汇编文件 hello.s第1行第2行第3行第4行第5行第6行第7行第8行第9行第10行第11行第12行第13行 X [注]&#xff1a;环境说明&#xff1a; OS&#xff1a;CentOS 7 GCC&#xff1a; 4.8.5 其他环境下的结果可能不尽相同。 声明&#xff1a;本文是我的一…

Unity中的Unistorm3.0天气系统笔记

Unistorm是Unity中的一个天气系统&#xff0c;它功能强大&#xff0c;效果优美。本文所述UniStorm为3.0版本&#xff0c;仅用于学习之用。 一、如何设置【白天】、【黑夜】和【天气类型】&#xff1f; 在Running模式下&#xff0c;按下Esc按键&#xff0c;会【弹出】或者【隐…

Rancher上的应用服务报错:413 Request Entity Too Large

UI->rancher的ingress->UI前端(在nginx里面)->zuul->server 也就是说没经过一次http servlet 都要设置一下大小 1.rancher的ingress 当出现Request Entity Too Large时&#xff0c;是由于传输流超过1M。 1、需要在rancher的ingress中设置参数解决。 配置注释&a…

JavaScript—面向对象、作用域

C#&#xff1a;从类继承 js&#xff1a;从对象继承 什么叫继承&#xff1f; 模板&#xff08;类&#xff09; 原型继承&#xff08;实体&#xff09; 有一个对象存在&#xff0c;构造函数设置原型为这个对象 创建出来的对象就继承与这个对象&#xff08;从对象那里继承&am…

Linux线程概念

目录 一、页表详解 1.地址的属性 2.页框 3.页表录和页表项 二、认识线程 1.线程的概念 2.轻量级进程 三、线程的简单控制 1.线程的创建 2.PID和LWP 3.线程异常 4.线程的资源 &#xff08;1&#xff09;共享的资源 &#xff08;2&#xff09;独有的资源 5.线程的…

mysql 存储引擎系列 (一) 基础知识

当前支持存储引擎 show engines&#xff1b; 显示默认存储引擎 select default_storage_engine; show variables like ‘%storage%’; 修改默认引擎 set default_storage_enginexxx 或 set default_storage_enginexxx; my.ini 或者 my.cnf ,需要重启 服务才能生效 systemctl …

cortex-A7核LED灯实验--STM32MP157

实验目的&#xff1a;实现LED1 / LED2 / LED3三盏灯工作 一&#xff0c;分析电路图 1&#xff0c;思路 分析电路图可知&#xff1a; 网络编号 引脚编号 LED1 PE10 LED2 > PF10 LED3 > PE8 2&#xff0c;工作原理&#xff1a; 写1&#xff1a;LED灯亮&#xf…

防雷检测综合应用方案

防雷检测是指对建筑物的防雷装置进行定期或不定期的检测&#xff0c;以评估其性能和安全状况&#xff0c;发现并消除隐患&#xff0c;保障人身和财产安全的一项重要工作。防雷检测的内容包括对接闪器、避雷带、引下线、接地装置、等电位联结、避雷器等部件的形式、位置、连接、…

RecyclerView面试问答

RecycleView 和 ListView对比: 使用方法上 ListView:继承重写 BaseAdapter,自定义 ViewHolder 与 converView优化。 RecyclerView: 继承重写 RecyclerView.Adapter 与 RecyclerView.ViewHolder。设置 LayoutManager 来展示不同的布局样式 ViewHolder的编写规范化,ListVie…

Spring security报栈溢出几种可能的情况

今天在运行spring security的时候&#xff0c;发现出现了栈溢出的情况&#xff0c;总结可能性如下&#xff1a; 1.UserDetailsService的实现类没有加上Service注入到容器中&#xff0c;导致容器循环寻找UserDetailsService的实现类&#xff0c;最终发生栈溢出的现象。 解决方法…

Redis 7 第三讲 数据类型 进阶篇

⑥ *位图 bitmap 1. 理论 由0和1 状态表现的二进制位的bit 数组。 说明:用String 类型作为底层数据结构实现的一种统计二值状态的数据类型 位图本质是数组,它是基于String 数据类型的按位操作。该数组由多个二进制位组成,每个二进制位都对应一个偏…

Java进阶(6)——抢购问题中的数据不安全(非原子性问题) Java中的synchronize和ReentrantLock锁使用 死锁及其产生的条件

目录 引出场景&#xff1a;大量请求拥挤抢购事务的基本特征ACID线程安全的基本特征 加锁(java)synchronized锁ReentrantLock锁什么是可重入锁&#xff1f;如何保证可重入 滥用锁的代价&#xff1f;&#xff08;死锁&#xff09;死锁的四个必要条件死锁的案例 总结 引出 1.大量请…

基于SpringBoot的员工(人事)管理系统

基于SpringBoot的员工&#xff08;人事&#xff09;管理系统 一、系统介绍二、功能展示三.其他系统实现五.获取源码 一、系统介绍 项目名称&#xff1a;基于SPringBoot的员工管理系统 项目架构&#xff1a;B/S架构 开发语言&#xff1a;Java语言 前端技术&#xff1a;BootS…

Go 第三方库引起的线上问题、如何在线线上环境进行调试定位问题以及golang开发中各种问题精华整理总结

Go 第三方库引起的线上问题、如何在线线上环境进行调试定位问题以及golang开发中各种问题精华整理总结。 01 前言 在使用 Go 语言进行 Web 开发时&#xff0c;我们往往会选择一些优秀的库来简化 HTTP 请求的处理。其中&#xff0c;go-resty 是一个被广泛使用的 HTTP 客户端。…

运用亚马逊云科技Amazon Kendra,快速部署企业智能搜索应用

亚马逊云科技Amazon Kendra是一项由机器学习&#xff08;ML&#xff09;提供支持的企业搜索服务。Kendra内置数据源连接器&#xff0c;支持快速访问Amazon S3、AmazonRDS、AmazonFSX以及其他外部数据源&#xff0c;帮助用户自动提取文档并建立索引。Kendra支持超过30多种多国语…

【真题解析】系统集成项目管理工程师 2022 年下半年真题卷(案例分析)

本文为系统集成项目管理工程师考试(软考) 2022 年下半年真题&#xff08;全国卷&#xff09;&#xff0c;包含答案与详细解析。考试共分为两科&#xff0c;成绩均 ≥45 即可通过考试&#xff1a; 综合知识&#xff08;选择题 75 道&#xff0c;75分&#xff09;案例分析&#x…

无涯教程-分类算法 - 逻辑回归

逻辑回归是一种监督学习分类算法&#xff0c;用于预测目标变量的概率&#xff0c;目标或因变量的性质是二分法&#xff0c;这意味着将只有两种可能的类。 简而言之&#xff0c;因变量本质上是二进制的&#xff0c;其数据编码为1(代表成功/是)或0(代表失败/否)。 在数学上&…