Java泛型方法的定义和使用、泛型类、泛型接口、泛型方法、通配符、泛型的上界与下界

news2024/9/29 9:32:10

文章目录

  • 一、包装类
    • 1.1、基本数据类型和对应的包装类
    • 1.2、自动装箱和自动拆箱
  • 二、基本介绍
    • 2.1、泛型引入背景
    • 2.1、什么是泛型
    • 2.2、为什么使用泛型
  • 三、常见泛型字母含义
  • 四、泛型的使用
    • 4.1、泛型类
    • 4.2、泛型接口
    • 4.3、泛型方法
  • 五、泛型的继承
    • 5.1、泛型不具备继承性
    • 5.2、何为数据具备继承性
  • 六、泛型通配符
    • 6.1 通配符之 <?>
    • 6.2 通配符之 <? extend E>、泛型的上界
    • 6.3 通配符之 <? super E>、泛型的下界
  • 七泛型的上界与下界
    • 7.1、泛型的上界
  • 八、泛型是如何编译的/泛型底层数据存取的实质
  • 九、泛型擦除

一、包装类

在了解泛型之前我们先了解什么是包装类,在Java中由于基本类型不是继承自Object,为了在泛型代码中可以支持基本类型,Java给每个基本类型都对应了一个包装类型。

Q: 为什么泛型不能传入基本数据类型,为什么泛型只支持引用数据类型?
A: 因为泛型底层存储的本质是 在存入之后,容器底层还是会把你存入的所有数据类型当作 Object 类型保存起来,当你取数据的时候,它会做一个强转,再从 Object 类型强转变成泛型对应的类型。这也就是为什么泛型只能写引用数据类型,因为泛型的底层会做一个强转,在存取时会在Object类型与泛型类型之间互相强转 ,显然,int,float,double等基本数据类型是不能强转为Object类型的,所以泛型必须为引用数据类型,如果想存入 int 类型数据,只能写 int 的包装类 Integer。

1.1、基本数据类型和对应的包装类

基本数据类型包装类
byteByte
shortShort
intInteger
longLong
floatFloat
doubleDouble
charCharacter
booleanBoolean

注意:泛型只能接受类,所有的基本数据类型必须使用包装类。若泛型中传入的是基本数据类型,则只能使用对应的包装类

public static void main(String[] args) {
    List<String> s = new ArrayList<>();
    List<Integer> i = new ArrayList<>();
    List<Long> l = new ArrayList<>();
}

在这里插入图片描述

1.2、自动装箱和自动拆箱

public class Test {
    public static void main(String[] args) {
        int a = 10;
        Integer i = a;//自动装箱 把一个基本数据类型转变为包装类型
        System.out.println(i);

        Integer j = new Integer(20);
        int b = j;//自动拆箱 把一个包装类型转变为基本数据类型
        System.out.println(b);
    }
}

在这里插入图片描述

3.手动装箱和手动拆箱

public class Test {
    public static void main(String[] args) {
        int a = 10;
        Integer i = Integer.valueOf(a);//手动装箱
        System.out.println(i);

        Integer j = new Integer(20);
        int b = j.intValue();//手动拆箱
        System.out.println(b);
    }
}

在这里插入图片描述

二、基本介绍

Java泛型是J2 SE1.5中引入的一个新特性,其本质是参数化类型,也就是说所操作的数据类型被指定为一个参数(type parameter)这种参数类型可以用在类、接口和方法的创建中,分别称为泛型类泛型接口泛型方法

2.1、泛型引入背景

集合容器类在设计阶段/声明阶段不能确定这个容器到底实际存的是什么类型的对象,所以在JDK1.5之前只能把元素类型设计为Object,JDK1.5之后使用泛型来解决。因为这个时候除了元素的类型不确定,其他的部分是确定的,例如关于这个元素如何保存,如何管理等是确定的,因此此时把元素的类型设计成一个参数,这个类型参数叫做泛型。Collection,List,ArrayList 这个就是类型参数,即泛型。

为什么要有泛型(Generic)
为什么要有泛型呢,直接Object不是也可以存储数据吗?

先来看一个例子

List<Object> arrayList = new ArrayList();
arrayList.add("aaaa");
arrayList.add(100);

for(int i = 0; i< arrayList.size();i++){
    String item = (String)arrayList.get(i);
    Log.d("泛型测试","item = " + item);
}

毫无疑问,程序的运行结果会以崩溃结束:

java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String

ArrayList可以存放任意类型,例子中添加了一个String类型,添加了一个Integer类型,使用时都以String接收,Integer被强转成String,因此报错。为了解决类似这样的类型转换问题,在编译阶段就规定只能存放某种类型的数据,泛型应运而生。

2.1、什么是泛型

泛型:是一种把明确类型的工作推迟到创建对象或者调用方法的时候才去明确的特殊的类型。也就是说在泛型使用过程中,操作的数据类型被指定为一个参数,而这种参数类型可以用在类、方法和接口中,分别被称为泛型类泛型方法泛型接口

注意:一般在创建对象时,将未知的类型确定具体的类型。当没有指定泛型时,默认类型为Object类型。

2.2、为什么使用泛型

使用泛型的好处

  • 避免了类型强转的麻烦。
    若没有指定参数类型,底层在存储时会把数据存储为Object类型,取值时转换为对应类型。有个强制转换的过程。

  • 它提供了编译期的类型安全,确保在泛型类型(通常为泛型集合)上只能使用正确类型的对象,避免了在运行时出现ClassCastException。

  • 使用泛型的类型或者返回值的方法,自动进行数据类型转换即自动拆箱。

泛型的细节注意点:
(1)泛型的数据类型只能填写引用数据类型,不可以用基本数据类型,具体原因下面会讲。
(2)指定泛型的具体类型之后,可以传入该类类型或其子类类型;
(3)如果不手动添加泛型,则默认泛型为 Object 。
(4)泛型可能有多个参数,此时应将多个参数一起放在尖括号内。比如:<E1,E2,E3>
(5)泛型如果不指定,将被擦除,泛型对应的类型均按照Object处理,但不等价于Object。

泛型如果不指定,按Object处理,但不等价于Object。即ArrayList不等价与ArrayList<Object> ,以下为示例:

class GenericTest {
    public static void main(String[] args) {
        // 1、使用时:类似于Object,不等同于Object
        ArrayList list = new ArrayList();
        // list.add(new Date());//有风险
        list.add("hello");
        test(list);// 泛型擦除,编译不会类型检查
        // ArrayList<Object> list2 = new ArrayList<Object>();
        // test(list2);//一旦指定Object,编译会类型检查,必须按照Object处理
    }
    public static void test(ArrayList<String> list) {
        String str = "";
        for (String s : list) {
        str += s + ","; }
        System.out.println("元素:" + str);
    }
}

在使用泛型时,若明确知道参数类型,则应该使用具体的引用数据类型;若不知道具体数据类型,则可使用通配符Object接收对象。

泛型示例:

public class Test {
    public static void main(String[] args) {
        //创建Integer集合
        List<Integer> list1 = new ArrayList<>();
        //创建String集合
        List<String>  list2 = new ArrayList<>();
        //当不确定集合的类型时,可以用通配符?  相当于下面的Object
        List<?>       list3 = new ArrayList<>();
        //创建Object集合
        List<Object>  list4 = new ArrayList<>();
    }
}

三、常见泛型字母含义

泛型字母没有特别的限定,即使你使用A-Z英文字母的任意一个,编译器也不会报错。之所以有不同的标记符,这是一种约定, 为了便于可阅读性。

常见的泛型字母如下:

  • E ----Element(在集合中使用,因为集合中存放的是元素)
  • T ----Type(Java类)
  • K ----Key(键),一般与V搭配使用
  • V ----Value(值)
  • N ----Number(数值类型)
  • ?----表示不确定的java类型(泛型通配符,与Object等同)

四、泛型的使用

泛型的使用主要在于泛型类泛型接口泛型方法中。

泛型参数是可以支持多个的,以泛型类为示例,如下图:
在这里插入图片描述

4.1、泛型类

泛型类型用于类的定义中,被称为泛型类。通过泛型可以完成对一组类的操作对外开放相同的接口。最典型的就是各种集合框架容器类,如:List、Set、Map。

我们平常所用的ArrayList类,就是一个泛型类,我们看如下源码
在这里插入图片描述

(1)泛型类的定义格式:

# 语法
修饰符 class 类名<代表泛型的变量> { }


示例:
//支持多个泛型,用逗号分隔
public class ClassName<T1, T2, ..., Tn> {
}
public class 泛型类名称<类型形参列表> extends 继承类/* 这里可以使用类型参数 */ {
// 这里可以使用类型参数
}
public class ClassName<T1, T2, ..., Tn> extends ParentClass<T1> {
// 可以只使用部分类型参数
}

代码示例:

/**
 * @param <T> 这里解释下<T>中的T:
 *           此处的T可以随便写为任意标识,常见的有T、E等形式的参数表示泛型
 *           泛型在定义的时候不具体,使用的时候才变得具体。
 *           在使用的时候确定泛型的具体数据类型。即在创建对象的时候确定泛型。
 */
public class GenericsClassDemo<T> {
    //t这个成员变量的类型为T,T的类型由外部指定
    private T t;
    //泛型构造方法形参t的类型也为T,T的类型由外部指定
    public GenericsClassDemo(T t) {
        this.t = t;
    }
    //泛型方法getT的返回值类型为T,T的类型由外部指定
    public T getT() {
        return t;
    }
}

泛型在定义的时候不具体,使用的时候才变得具体。在使用的时候确定泛型的具体数据类型。即:在创建对象的时候确定泛型。

使用示例:

GenericsClassDemo<User> u = new GenericsClassDemo<>(new User());
GenericsClassDemo<Dog>  d = new GenericsClassDemo<>(new Dog());
GenericsClassDemo<String> s= new GenericsClassDemo<String>("hello");

注意: 定义的泛型类,就一定要传入泛型类型实参么?
并不是这样,在使用泛型的时候如果传入泛型实参,则会根据传入的泛型实参做相应的限制,此时泛型才会起到本应起到的限制作用。如果不传入泛型类型实参的话,在泛型类中使用泛型的方法或成员变量定义的类型可以为任何的类型。即跟之前的经典案例一样,没有写ArrayList的泛型类型,容易出现类型强转的问题。

(2)注意事项与细节

  1. 普通成员可以使用泛型(属性,方法)
  2. 使用泛型的数组,不能初始化
  3. 静态方法中不能使用类的泛型
  4. 泛型类的类型,是在创建对象的时候确定的
  5. 如果在创建对象时,没有指定类型,默认为Object

示例:

//Tiger 后面的为泛型,Tiger就称为自定义泛型类
//T,R,M 为泛型的标识符,一般是单个的大写字母
//普通成员可以使用泛型(属性,方法)
class Tiger<T,R,M>{
    String name;
    T t;        //属性使用泛型
    R r;
    M[] m;//可以用数组,但不能new初始化
//    T[] t1 = new T[8];  //报错,无法确定数组类型,数组不知道开辟多大空间
    public Tiger(String name, T t, R r, M[] m) {  //方法使用泛型
        this.name = name;
        this.t = t;
        this.r = r;
        this.m = m;
    }
    //静态方法,静态属性,是在类加载的时候进行的,但是对象还没有创建,JVM就无法初始化
//    static R r2;
//    public static void m1(T t){}
}

4.2、泛型接口

泛型接口与泛型类的定义及使用基本相同。泛型接口常被用在各种类的生产器中

(1)定义格式

修饰符 interface 接口名<代表泛型的变量> { }

代码示例

/**
 * 定义一个泛型接口
 */
public interface GenericsInteface<T> {
    public abstract void add(T t); 
}
# 定义类时确定泛型的类型
public class GenericsImpl<T> implements GenericsInteface<T> {
    @Override
    public void add(T t) {
        System.out.println("没有设置类型");
    }
}

始终不确定泛型的类型,直到创建对象时,确定泛型的类型

public class GenericsTest {
    public static void main(String[] args) {
        GenericsImpl<Integer> gi = new GenericsImp<>();
        gi.add(66);
    }
}

(4)注意事项与细节

  1. 接口中,静态成员不能使用泛型
  2. 泛型接口的类型,在继承接口或者实现接口时确定
  3. 没有指定类型,默认为Object

示例:

//接口中,成员属性时默认静态的 final static
interface IUsb<U,R>{
    //普通方法,可以使用泛型接口
//    U name; //报错,成员属性时默认静态的 final static
    void run(R r1,U u1);
    //在jdk8中,可以在接口中,使用默认方法,也是可以使用泛型
    default R method(U u){
        return null;
    }
}

//继承接口时,指定类型 U->String, R->Double
interface AUsb extends IUsb<String, Double>{}

//实现接口时,指定类型 U->String, R->Double
class BUsb implements IUsb<String, Double>{
    @Override
    public void run(Double r1, String u1) { //自动填充类型
    }
}
//未指定,默认为Object
class CUsb implements IUsb{
    @Override
    public void run(Object r1, Object u1) {//默认为Object
    }
}

4.3、泛型方法

泛型方法,是在调用方法的时候指明泛型的具体类型 。通常情况下,当一个方法的形参不确定的情况下,我们会使用到泛型方法。

(1)定义格式:

修饰符 <代表泛型的变量> 返回值类型 方法名(参数){ }

测试示例:

/**
*
* @param t 传入泛型的参数
* @param <T> 泛型的类型
* @return T 返回值为T类型
* 说明:
*   1)public 与 返回值中间<T>非常重要,可以理解为声明此方法为泛型方法。
*   2)只有声明了<T>的方法才是泛型方法,泛型类中的使用了泛型的成员方法并不是泛型方法。
*   3)<T>表明该方法将使用泛型类型T,此时才可以在方法中使用泛型类型T。
*   4)与泛型类的定义一样,此处T可以随便写为任意标识,常见的如T、E等形式的参数常用于表示泛型。
*/
public <T> T genercMethod(T t){
    System.out.println(t.getClass());
    System.out.println(t);
    return t;
}

//定义一个泛型方法,不返回内容
public <T> void print(T t){
	System.out.println(t);
}

//定义一个泛型方法,传入多个泛型
public <T,F> void query(T t,List<F> f){
	System.out.println(t);
}

//传入T, F  返回T
public <T,F> T query1(T t,F f){
}


//静态的泛型方法 需要在static后用<>声明泛型类型参数
public static <E> void swap(E[] array, int i, int j) {
	E t = array[i];
	array[i] = array[j];
	array[j] = t;
}

使用示例:

String str = this.genercMethod("hello");//传入的是String类型,返回的也是String类型
Integer i = this.genercMethod(123);//传入的是Integer类型,返回的也是Integer类型

this.print("你好")//调用泛型方法,不返回值

这里我们可以看下结果:

class java.lang.String
hello

class java.lang.Integer
123

你好

这里可以看出,泛型方法随着我们的传入参数类型不同,他得到的类型也不同。泛型方法能使方法独立于类而产生变化。

(2)注意事项和使用方法

  1. 泛型方法可以定义在普通类中,也可以定义在泛型类中
  2. 当泛型方法被调用时,类型被确定
  3. public void eat(E e){},修饰符后面没有<T,R…>,这里的方法不是泛型方法,而是使用了泛型
  4. 泛型方法可以使用自己的泛型,也可以使用类声明的泛型

使用示例

public class Test{
    public static void main(String[] args) {
        Car car = new Car();
        //调用方法,传入参数,编译器,就会确定类型
        car.run("run"); //调用的时候指定泛型的类型
        new Fish<Integer>().swim1(2);
        Fish fish = new Fish<Integer>();
        fish.swim(2,new ArrayList());
    }
}

class Car{//普通类的泛型方法
    public<E> void run(E e){
        System.out.println("泛型方法类型为"+e.getClass());
    }
}

class Fish<T>{//泛型类里面的泛型方法
    public<U> void swim(U u, T t){  //泛型使用了泛型类的泛型,也可以使用自己方法里面的泛型
        System.out.println(u.getClass());
        System.out.println(t.getClass());
    }
    public  void swim1(T t){    //使用了泛型,但是不是泛型方法
        System.out.println(t.getClass());
    }
}

五、泛型的继承

泛型本身并不具备继承性,但是数据具备继承性。

5.1、泛型不具备继承性

如下图,我定义了GrandFathor类,Fathor类,Son类;Fathor类继承GrandFathor类,Son类又继承Fathor类。

我们再定义一个空方法体的 method 方法,方法需要传入一个带泛型的集合,我就写 GrandFathor;

分别创建泛型为 GrandFathor,Fathor,Son 的集合对象 list1,list2,list3;
在这里插入图片描述
我们可以得出结论,泛型是不具备继承性的,也就是说,一个方法传入的对象泛型是什么类型,我们不能把参数泛型的子类泛型对象作为参数传递给方法,该泛型是不具备继承性的,传入编译器会报错。

5.2、何为数据具备继承性

刚才我们验证了也演示了泛型不具备继承性,那么接下来我们来说一下,数据具备继承性是什么意思。

还拿刚才的代码举例,
在这里插入图片描述
我们把刚才的代码注释,然后往 list1 对象中添加对象;

添加 GrandFathor 类对象,添加成功,这也是当然的,因为该类的泛型指定的就是 GrandFathor;

添加 Fathor 类对象,发现也添加成功;

添加 Son 类对象,发现也添加成功;

执行 main 方法,如下结果,说明没有问题
在这里插入图片描述
以上我们可以得出结论

当我们为一个类指定泛并创建对象之后,对象中不仅可以加入泛型所指定的类对象,还可以加入泛型类子类的类对象,这就是数据的继承性。

注意这里说的是对象,上面不具备继承性中说的是参数,不要混为一谈。

六、泛型通配符

上面刚刚说到了使用一个类型来表示泛型类型是必须要申明的,也即 <T> ,那是不是不申明就不能使用泛型呢?当然不是,这小节介绍的 <?> 就是为了解决这个问题的。

当使用泛型类或者接口时,传递的数据中,泛型类型不确定,可以通过通配符<?>表示。但是一旦使用泛型的通配符后,只能使用Object类中的共性方法,集合中元素自身方法无法使用。

在Java中,泛型的通配符是一个 “?”, 通配符 ? 即占位符的意思,也就是在使用期间是无法确定其类型的,只有在将来实际使用时再指明类型,它有三种形式

  • <?> 无限定的通配符。支持任意泛型的类型
  • <? extends E>有上限的通配符。支持E类以及E类的子类,规定了泛型的上限
  • <? super E>有下限的通配符。支持E类以及E类的父类,不限于直接父类,规定了泛型的下限
//支持存放任意类型
List<?> list1 = new ArrayList<>();

//支持Number及Number的子类, 规定了泛型的上限
List<? extends Number> list2 = new ArrayList(Arrays.asList(new Integer[]{1, 2, 3}));

//支持Integer及Integer的父类, 规定了泛型的下限
List<? super Integer> list3 = new ArrayList(Arrays.asList(new Number[]{1.0, 2.0, 3.0}));

6.1 通配符之 <?>

<?> 表示任意类型,那 能不能用 ? 去接收一个对象呢,请看代码中的注释

在这里插入图片描述
而又因为任何类型都是 Object 的子类,所以,这里可以使用 Object 来接收,对于 ? 的具体使用会在下面两小节介绍

/**
 * ?表示未知类型,若要接收具体对象,需要用Object接收
 * @param list
 */
public void test(List<?> list){
    Object item = list.get(0);
}

另外,大家要搞明白 泛型和通配符不是一回事

6.2 通配符之 <? extend E>、泛型的上界

<? extend E> 表示有上限的通配符,能接受其类型和其子类的类型 E 指上边界,还是写个例子来说明

public class GenericExtend {
    public static void main(String[] args) {
        List<Father> listF = new ArrayList<>();
        List<Son> listS = new ArrayList<>();
        List<Daughter> listD = new ArrayList<>();
        testExtend(listF);
        testExtend(listS);
        testExtend(listD);
    }
 
    private static <T> void testExtend(List<? extends Father> list) {}
}
 
class Father {}
 
class Daughter extends Father{}
 
class Son extends Father {    
}

这个时候一切都还是很和平的,因为大家都遵守着预定,反正 List 中的泛型要么是 Father 类,要么是 Father 的子类。但是这个时候如果这样子来写(具体原因已经在截图中写明了)

在这里插入图片描述

6.3 通配符之 <? super E>、泛型的下界

<? super E> 表示有下限的通配符。也就说能接受指定类型及其父类类型,E 即泛型类型的下边界,直接上来代码然后来解释

public class GenericSuper {
 
    public static void main(String[] args) {
        List<Son> listS = new Stack<>();
        List<Father> listF = new Stack<>();
        List<GrandFather> listG = new Stack<>();
        testSuper(listS);
        testSuper(listF);
        testSuper(listG);
        
    }
    private static void testSuper(List<? super Son> list){}
}
class Son extends Father{}
class Father extends GrandFather{}
class GrandFather{}

因为 List<? super Son> list 接受的类型只能是 Son 或者是 Son 的父类,而 Father 和 GrandFather 又都是 Son 的父类,所以以上程序是没有任何问题的,但是如果再来一个类是 Son 的子类(如果不是和 Son 有关联的类那更不行了),那结果会怎么样?看下图,相关重点已经在图中详细说明
在这里插入图片描述

七泛型的上界与下界

7.1、泛型的上界

在定义泛型类时,有时需要对传入的类型变量做一定的约束,可以通过类型边界来约束

语法:

class 泛型类名称<类型形参 extends 类型边界> {
...
}

示例:

public class MyArray<E extends Number> {
...
}

只接受 Number 的子类型作为 E 的类型实参

复杂示例:

public class MyArray<E extends Comparable<E>> {
...
}

E必须是实现了Comparable接口的

八、泛型是如何编译的/泛型底层数据存取的实质

刚才我说到了泛型只能用引用数据类型,是有原因的;

当我们往写入泛型的集合中添加元素的时候,泛型其实可以理解成一个看门大爷,你在添加数据之前,它会看看你要添加的数据类型是否与标注的泛型类型相匹配,不匹配则不会让你存入,匹配的话,在存入之后,容器底层还是会把你存入的所有数据类型当作 Object 类型保存起来,当你取数据的时候,它会做一个强转,再从 Object 类型强转变成泛型对应的类型。这也就是为什么泛型只能写引用数据类型,因为泛型的底层会做一个强转,在存取时会在Object类型与泛型类型之间互相强转,显然,int,float,double等基本数据类型是不能强转为Object类型的,所以泛型必须为引用数据类型,如果想存入 int 类型数据,只能写 int 的包装类 Integer。

在编译的过程当中,将所有的T替换为Object这种机制,我们称为:擦除机制
Java的泛型机制是在编译级别实现的。编译器生成的字节码在运行期间并不包含泛型的类型信息

九、泛型擦除

先来看下泛型擦除的定义

因为泛型的信息只存在于 java 的编译阶段,编译期编译完带有 java 泛型的程序后,其生成的 class 文件中与泛型相关的信息会被擦除掉,以此来保证程序运行的效率并不会受影响,也就说泛型类型在 jvm 中和普通类是一样的。

所有泛型类型参数,若没有设置泛型上限,则编译之后统一擦除为Object类型。若设置了泛型上限,则编译之后统一擦除为相应的泛型上限

看完概念可能还是不明白什么叫泛型擦除,举个例子:

下面示例中我们泛型没有设置上限,擦除后类型为Object类型
在这里插入图片描述

当设置了泛型上限,则编译之后统一擦除为相应的泛型上限,具体看下图:
在这里插入图片描述









参考文章:
https://blog.csdn.net/bjweimengshu/article/details/117793971
https://baike.baidu.com/item/java%E6%B3%9B%E5%9E%8B/511821?fr=ge_ala

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

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

相关文章

【Python】递归

专栏文章索引&#xff1a;Python 有问题可私聊&#xff1a;QQ&#xff1a;3375119339 文章内容改自&#xff1a;bilibili博主(又懂啦) 目录 一、递归函数 二、理解递归函数 一、递归函数 一个函数在其函数体内调用函数自身&#xff0c;这样的函数就称为递归函数。递归函数的…

每日一练 2024.9.29(2)

目录 解题思路与代码实现 题目分析 一、解题策略 关键步骤&#xff1a; 二、代码实现 三、代码解析 四、复杂度分析 五、运行示例 示例1&#xff1a; 示例2&#xff1a; 六、总结 解题思路与代码实现 题目分析 这道题目要求我们找到字符串列表 strs 中的相似字符组…

Arch - 架构安全性_验证(Verification)

文章目录 OverView导图1. 引言&#xff1a;数据验证的重要性概述2. 数据验证的基本概念3. 数据验证的层次前端验证后端验证 4. 数据验证的标准做法5. 自定义校验注解6. 校验结果的处理7. 性能考虑与副作用8. 小结 OverView 即使只限定在“软件架构设计”这个语境下&#xff0c…

物理学基础精解【40】

文章目录 矢量积矢量积&#xff08;又称叉积、外积&#xff09;的几何意义一、面积表示二、垂直性三、方向性四、应用实例五、数学表达 矢量积&#xff08;叉积&#xff09;的坐标表示法矢量积的坐标表示法的几何意义矢量积的性质矢量积的应用 矢量积&#xff08;又称叉积、外积…

Linux——k8s组件

kubernetes 使用1.31.1 版本搭建集群核心组件&#xff0c;选择flannel 网络插件为整体集群的运行提供网络通信功能。 flannel 网络插件 kube-flannel kube-flannel-ds-9fgml 1/1 Running 1 (18m ago) 2d21h kube-flannel kube-flannel-ds-ghwbq …

<<迷雾>> 第 3 章 怎样才能让机器做加法 示例电路

全加器示意图 info::操作说明 鼠标单击开关切换开合状态 primary::在线交互操作链接 https://cc.xiaogd.net/?startCircuitLinkhttps://book.xiaogd.net/cyjsjdmw-examples/assets/circuit/cyjsjdmw-ch03-01-full-adder.txt 原图 由3个全加器组成的3比特加法机 info::操作说明…

Linux——pod的调度

pod的调度 控制器: rc/rs 副本数量控制器 主要保证pod的数量符合管理员要求&#xff0c;并不会对pod进行额外的管理 以下三种控制器&#xff0c;本质上是服务控制器。具备以下特性&#xff1a; 副本数量的控制服务的滚动更新&#xff08;更新pod&#xff09;支持更新失…

基于springboot vue 投票系统设计与实现

博主介绍&#xff1a;专注于Java vue .net php phython 小程序 等诸多技术领域和毕业项目实战、企业信息化系统建设&#xff0c;从业十五余年开发设计教学工作 ☆☆☆ 精彩专栏推荐订阅☆☆☆☆☆不然下次找不到哟 我的博客空间发布了1000毕设题目 方便大家学习使用 感兴趣的…

基于51单片机的2路电压采集proteus仿真

地址&#xff1a;https://pan.baidu.com/s/1oNOJJv78ecfWZkdlMyhNVQ 提取码&#xff1a;1234 仿真图&#xff1a; 芯片/模块的特点&#xff1a; AT89C52/AT89C51简介&#xff1a; AT89C52/AT89C51是一款经典的8位单片机&#xff0c;是意法半导体&#xff08;STMicroelectron…

Linux:LCD驱动开发

目录 1.不同接口的LCD硬件操作原理 应用工程师眼中看到的LCD 1.1像素的颜色怎么表示 ​编辑 1.2怎么把颜色发给LCD 驱动工程师眼中看到的LCD 统一的LCD硬件模型 8080接口 TFTRGB接口 什么是MIPI Framebuffer驱动程序框架 怎么编写Framebuffer驱动框架 硬件LCD时序分析…

OpenAI全新多模态内容审核模型上线:基于 GPT-4o,可检测文本和图像

在数字时代&#xff0c;内容安全问题愈发受到重视。9月26日&#xff0c;OpenAI 正式推出了一款全新的多模态内容审核模型&#xff0c;名为 “omni-moderation-latest”。 该模型基于最新的 GPT-4o 技术&#xff0c;能够准确地识别检测有害文本图像。这一更新将为开发者提供强大…

Java | Leetcode Java题解之第445题两数相加II

题目&#xff1a; 题解&#xff1a; class Solution {public ListNode addTwoNumbers(ListNode l1, ListNode l2) {Deque<Integer> stack1 new ArrayDeque<Integer>();Deque<Integer> stack2 new ArrayDeque<Integer>();while (l1 ! null) {stack1.…

AI Agent应用出路到底在哪?

1 Agent/Function Call 的定义 Overview of a LLM-powered autonomous agent system&#xff1a; Agent学会调用外部应用程序接口&#xff0c;以获取模型权重中缺失的额外信息&#xff08;预训练后通常难以更改&#xff09;&#xff0c;包括当前信息、代码执行能力、专有信息源…

《深度学习》OpenCV 角点检测、特征提取SIFT 原理及案例解析

目录 一、角点检测 1、什么是角点检测 2、检测流程 1&#xff09;输入图像 2&#xff09;图像预处理 3&#xff09;特征提取 4&#xff09;角点检测 5&#xff09;角点定位和标记 6&#xff09;角点筛选或后处理&#xff08;可选&#xff09; 7&#xff09;输出结果 3、邻域…

深度学习反向传播-过程举例

深度学习中&#xff0c;一般的参数更新方式都是梯度下降法&#xff0c;在使用梯度下降法时&#xff0c;涉及到梯度反向传播的过程&#xff0c;那么在反向传播过程中梯度到底是怎么传递的&#xff1f;结合自己最近的一点理解&#xff0c;下面举个例子简单说明&#xff01; 一、…

Qt开发技巧(九)去掉切换按钮,直接传样式文件,字体设置,QImage超强,巧用Qt的全局对象,信号槽断连,低量数据就用sqlite

继续讲一些Qt开发中的技巧操作&#xff1a; 1.去掉切换按钮 QTabWidget选项卡有个自动生成按钮切换选项卡的机制&#xff0c;有时候不想看到这个烦人的切换按钮&#xff0c;可以设置usesScrollButtons为假&#xff0c;其实QTabWidget的usesScrollButtons属性最终是应用到QTabWi…

衡石分析平台系统管理手册-功能配置之AI 助手集成嵌入指南

AI 助手集成嵌入指南​ 本文档将引导您通过几个简单的步骤&#xff0c;将 AI 助手集成或嵌入到您的系统中。HENGSHI SENSE AI 助手提供了多种集成方式&#xff0c;您可以通过 iframe、JS SDK 或 API 调用等方式将 AI 助手嵌入集成到您的系统中。 1. 通过 iframe 集成​ ifra…

老板最想要的20套模板!基于 VUE 国产开源 IoT 物联网 Web 可视化大屏设计器

如有需求&#xff0c;文末联系小编 Cola-Designer 是一个基于VUE开发&#xff0c;实现拖拽和配置方式生成数据大屏&#xff0c;提供丰富的可视化模板&#xff0c;满足客户业务监控、数据统计、风险预警、地理信息分析等多种业务的展示需求。Cola-Designer 帮助工程师通过图形化…

MySQL - 单表增删改

1. MySQL 概述 MySQL 是一种流行的开源关系型数据库管理系统 (DBMS)&#xff0c;广泛应用于互联网公司和企业开发中。它支持 SQL 语句操作数据&#xff0c;并提供多种版本供选择。 1.1 MySQL 安装和连接 社区版&#xff1a;免费版本&#xff0c;适合开发者使用。商业版&…

sizeof 和 strlen

一 . sizeof 关键字 这个是我们的老朋友了昂&#xff0c;经常都在使用&#xff0c;它是专门用来计算变量所占内存空间大小的&#xff0c;单位是字节&#xff0c;当然&#xff0c;如果我们的操作对象是类型的话&#xff0c;计算的就是类型所创建的变量所占内存的大小&#xff0…