第8章 泛型程序设计

news2024/11/23 19:04:56

文章目录

  • 为什么要使用泛型程序设计
    • 类型参数的好处
    • 谁想成为泛型程序员
  • 定义简单泛型类
  • 泛型方法
  • 类型变量的限定
  • 泛型代码和虚拟机
    • 类型擦除
    • 转换泛型表达式
    • 转换泛型方法
      • 类型擦除与多态会发生冲突
      • 桥方法实现多态
        • 桥方法与可协变的返回类型
    • 调用遗留代码
  • 限制与局限性
  • 泛型类型的继承规则
  • 8.8 通配符类型
    • 8.8.1通配符概念
    • 8.8.2 通配符的超类型限定
    • 8.8.3 无限定通配符
    • 8.8.4 通配符捕获
  • 8.9 反射和泛型
    • 8.9.1 泛型 Class 类
    • 8.9.2使用Class\<T>参数进行类型匹配
    • 8.9.3 虚拟机中的泛型类型信息
    • 8.9.4 类型字面量
    • 个人理解:为什么要擦除?

为什么要使用泛型程序设计

泛型程序设计(generi c programming)意味着编写的代码可以对多种不同类型的对象重
用。

类型参数的好处

它们会让你的程序更易读,也更安全。

谁想成为泛型程序员

实现泛型类典型问题:
一个程序员可能想要将一个ArrayList<Manager>中的所有元素添加到一个ArrayList<Employee>中去。不过,当然反过来就不行了。如何允许前一个调用,而不允许后一个调用呢?
Java设计者为此发明了通配符类型

定义简单泛型类

  1. 类型变量在整个类定义中用于指定方法的返回类型以及字段和局部变量的类型。变量E表示集
    合的元素类型,K和V分别表示表的键和值的类型。T、U和S表示“任意类型”。

泛型方法

  1. 类型变量放在修饰符的后面,并在返回类型的前面。
class ArrayAlg
{
    public static <T> T getMiddle(T... a)
    {
        return a[a.length / 2];
    }
}
  1. 泛型方法可以在普通类中定义,也可以在泛型类中定义。
  2. 调用一个泛型方法时,可以把具体类型包围在尖括号中,放在方法名前面。大多数情况下,方法调用中可以省略/类型参数。编译器有足够的信息推断出你想要的方法。
String middle = ArrayAlg.<String>getMiddle("John", "Q. " , "Public"); 
String middle = ArrayAlg.getMiddle("John", "Q. " , "Public"); 
  1. 泛型方法的类型推导常见错误:
double middle = ArrayAIg.getMiddle(3.14, 1729, 0);

编译器将把参数自动装箱为1个Double和2个Integer对象,然后寻找这些类的共同超类型。事实上,它找到了 2个超类型:Number和Comparable接口,Comparable接口本身也是一个泛型类型。在这种情况下,可以采取的补救措施是将所有的参数都写为double值。

类型变量的限定

  1. 限制T只能是实现了 Comparable接口。
public static <T extends Comparable> T min(T[] a)
  1. 为什么使用关键字extends而不是implements ?毕竟,Comparable是一个接口。下面的记法
    <T extends BoundingType>表示T应该是限定类型的子类型。T和限定类型可以是,也可
    以是接口。选择关键字extends的原因是它更接近子类型的概念,并且Java的设计者也不打
    算在语言中再添加一个新的关键字(如sub)。

  2. 一个类型变量或通配符可以有多个限定,例如:T extends Comparable & Serializable。限定类型用“&”分隔,而逗号用来分隔类型变量。

  3. 可以根据需要拥有多个接口超类型,但最多有一个限定可以是。如果有一个类作为限定,它必须是限定列表中的第一个限定。

泛型代码和虚拟机

对于Java泛型的转换,需要记住以下几个事实:

  1. 虚拟机中没有泛型,只有普通的类和方法。
  2. 所有的类型参数都会替换为它们的限定类型。(类型擦除)
  3. 会合成桥方法来保持多态。
  4. 为保持类型安全性,必要时会插人强制类型转换

类型擦除

  1. 对于泛型类,类型变量会被擦除,并替换为其原始类型

Java泛型与C++模板有很大的区别。C++为每个模板的实例化产生不同的类型,这一现象称为“模板代码膨胀”。Java不存在这个问题的困扰。

  1. 原始类型用第一个限定来替换类型变量,或者,如果没有给定限定,就替换为0bject
    在这里插入图片描述

转换泛型表达式

  1. 调用泛型方法时,如果擦除了返回类型,编译器会插人强制类型转换
 Pair<Employee> buddies =...;
  Employee buddy = buddies.getFirst();

getFirst擦除类型后的返回类型是0bject。编译器自动插人转换到Employee的强制类型转换。也就是说,编译器把这个方法调用转换为两条虚拟机指令:
• 对原始方法Pair.getFirst的调用。
• 将返回的Object类型强制转换为Employee类型。

  1. 变量擦除与参数约束无关。

private T first; public void setFirst(T newValue) { first = newValue; }擦除后 private Object first; public void setFirst(Object newValue) { first = newValue; }。但Pair<Employee>调用setFirst(T newValue)方法时,编译器要求只能传入Employee及其子类型(Manager)的对象。不可能传入一个Object对象,保证了泛型的安全性。
在这里插入图片描述

  1. 泛型安全闭环:f(T t)方法传入参数T t有严格的参数类型控制,保证了更改器方法一定是安全的。这样一来,保证了调用get方法时编译器自动进行强制转换的安全。

转换泛型方法

  1. 类型擦除也会出现在泛型方法中。

public static <T extends Comparable> T min(T[] a)是整个一组方法,而擦除类型之后,只剩下一个方法:public static Comparable min(Comparable[] a)。其中,类型参数T已经被擦除了,只留下了限定类型Comparable。

  1. 类型擦除与多态会发生冲突,编译器会使用桥方法

类型擦除与多态会发生冲突

public class Pair<T>
{
    private T first;
    private T second;

    public Pair() { first = null; second = null; }
    public Pair(T first, T second) { this.first = first;  this.second = second; }

    public T getFirst() { return first; }
    public T getSecond() { return second; }

    public void setFirst(T newValue) { first = newValue; }
    public void setSecond(T newValue) { second = newValue; }
}

(1)DateInterval类是Pair的子类,Pair是个泛型类。

public class DateInterval extends Pair<LocalDate> {
    //重写的方法
    public void setSecond(LocalDate second) {
        System.out.println("DateInterval.setScond() is called");
        if(second.compareTo(getFirst())>=0)
            super.setSecond(second);
    }
}

(2)DateInterval类被擦除后变成:

public class DateInterval extends Pair {  //Pair<LocalDate>被擦除
    //重写的方法
    public void setSecond(LocalDate second) {
        System.out.println("DateInterval.setScond() is called");
        if(second.compareTo(getFirst())>=0)
            super.setSecond(second);
    }
}

(3)此时,DateInterval还有一个从Pair继承的setSecond方法:

public void setSecond(Object second) // 泛型类Pair的泛型方法在JVM中被擦除成object

(4)显然,DateInterval的public void setSecond(LocalDate second) 方法,不是对Pair的public void setSecond(Object second) 方法的重写。因为参数类型不同。所以无法自动实现多态。理论上,DateInterval有两个不相关的方法。即类型擦除与多态发生了冲突
在这里插入图片描述

桥方法实现多态

(1) 下面的例子可以看出,泛型实现了多态。

此处实现了多态,调用的是DateInterval.setScond()方法。只有second的日期在first之后才会给second赋值。年份换成1999年,输出的就是null。

 public static void main(String[] args) {
        DateInterval dateInterval = new DateInterval();
        Pair<LocalDate> superDateInterval=dateInterval;   // 子类对象赋值给父类变量
        superDateInterval.setFirst(LocalDate.of(2000, 12, 22));
        superDateInterval.setSecond(LocalDate.of(1999, 12, 22)); //not work,second为null
        superDateInterval.setSecond(LocalDate.of(2008, 12, 22));  // work,second为2008-12-22
        System.out.println(superDateInterval.getSecond());

    }

(2) 为了解决类型擦除与多态的冲突,编译器在DateInterval类中生成了一个桥方法

public void setSecond(Object second) { setSecond((LocalDate) second); }

DateInterval的桥方法public void setSecond(Object second) { setSecond((LocalDate) second); }是Pair的public void setSecond(Object second) 方法的重写。所以,多态性让superDateInterval调用的是superDateInterval的桥方法,而桥方法会调用DateInterval的public void setSecond(LocalDate second) 方法。

(3)利用反射查看桥方法
在这里插入图片描述

public static void main(String[] args) {
        DateInterval dateInterval = new DateInterval();
        Pair<LocalDate> superDateInterval=dateInterval;   // 子类对象赋值给父类对象
        superDateInterval.setFirst(LocalDate.of(2000, 12, 22));
        //此处实现了多态,调用的是DateInterval.setScond()方法。只有second的日期在first之后才会给second赋值。年份换成1999年,输出的就是null。
        superDateInterval.setSecond(LocalDate.of(2008, 12, 22));
        System.out.println(superDateInterval.getSecond());
        Method[] declaredMethods = dateInterval.getClass().getDeclaredMethods();
        for (Method declaredMethod : declaredMethods) {
            System.out.println(Modifier.toString(declaredMethod.getModifiers())+" "+declaredMethod.getReturnType()
                    +" "+declaredMethod.getName()
                   +"("+declaredMethod.getParameterTypes()[0]+")");
        }
    }

桥方法与可协变的返回类型

假设Datelnterval类也覆盖了 getSecond方法:

class Datelnterval extends Pair<LocalDate>
{
public LocalDate getSecond() { return (LocalDate) super.getSecond(); }
}

在Datelnterval类中,有两个getSecond方法:
LocalDate getSecond() // defined in Datelnterval
Object getSecond() // overrides the method defined in Pair to call the first method

  • 不能这样编写Java代码(两个方法有相同的参数类型是不合法的,在这里,两个方法都没有参数)。但是,在虚拟机中,会由参数类型和返回类型共同指定一个方法。因此,编译器可以为两个仅返回类型不同的方法生成字节码,虚拟机能够正确地处理这种情况。
  • 桥方法会调用新定义的方法LocalDate getSecond()
    在这里插入图片描述

调用遗留代码

设计Java泛型时,主要目标是允许泛型代码和遗留代码之间能够互操作。

  1. Swing用户界面工具包提供了一个JSlider类,有方法void setLabelTable(Dictionary table)。
  2. 在Java 5之前,Dictionary类实现为一个Object实例映射。Java 5把Dictionary实现为一个泛型类,不过JSlider从未更新,JSlider使用的依旧是没有类型参数,是原始类型的Dictionary类。
    Dictionary类是泛型类
    JSlider使用原始类型的Dictionary类
  3. 这种不匹配,会带来兼容性问题。

Dictionary<Integer, Components> labelTable = slider.getLabelTablel); // warning
警告: 确保标签表确实包含Integer和Component对象。

限制与局限性

  1. 不能用基本类型实例化类型参数,用对应的包装器类。

基本类型不是对象,无法擦除为object。

  1. 运行时类型查询只适用于原始类型

虚拟机中的对象总有一个特定的非泛型类型。因此,所有的类型查询只产生原始类型。
在这里插入图片描述

  1. 不能创建参数化类型的数组.var table = new Pair<String>[10]; //error

在这里插入图片描述
4. Varargs 警告
在这里插入图片描述

  1. 不能实例化类型变量,不能在类似newT(…)的表达式中使用类型变量。
    在这里插入图片描述
    在这里插入图片描述
public class PairTest2
{
   public static void main(String[] args)
   {

      Pair p1=Pair.makePair(new Supplier<String>() {
         public String get()
         {
            return "hhh";
         }
      });
      Pair p2=Pair.makePair(String.class);
   }

}

public class Pair<T> 
{
   private T first;
   private T second;

   public Pair() { first = null; second = null; }
   public Pair(T first, T second) { this.first = first;  this.second = second; }

   public T getFirst() { return first; }
   public T getSecond() { return second; }

   public void setFirst(T newValue) { first = newValue; }
   public void setSecond(T newValue) { second = newValue; }

   public static <T> Pair<T> makePair(Supplier<T> constr) {   //用Supplier创建
      return new Pair<>(constr.get(),constr.get());
   }

   public static <T> Pair<T> makePair(Class<T> cl) {    //用反射创建
      try {
         return new Pair<>(cl.getConstructor(String.class).newInstance("123"),cl.getConstructor(String.class).newInstance("456"));
      } catch (Exception e) {
         throw new RuntimeException(e);
      } 
   }
}
  1. 不能构造泛型数组
    在这里插入图片描述

理解问题:为什么return (E) elements[n];可以用强制类型转换。而不能:T a = new T(); return (T) a;

  • 实际上new T()会报错,因为不能实例化类型变量,同时,(T) a这个强制类型转换也不满足强转的条件: a instanceof T为假。经过类型擦除后,new T() 变成new object() , 所以a实际是一个object对象,不能把父类对象强转成子类对象。进一步揭示了为什么不能实例化类型变量。
  • 观察上面的代码,发现elements是对象变量而不是new出来的对象。elements[n]擦除为object,但elements[n]指向了某个T类型,所以可以进行强制转换。

在这里插入图片描述

数组父类为object,但B继承了A,那么A[]类型的引用就可以指向B[]类型的对象

在这里插入图片描述

  1. 泛型类的静态上下文中类型变量无效
    在这里插入图片描述

静态字段属于类,两个不同对象只有一个属于类的singleInstance字段,不可能既是A类型又是B类型。

  1. 不能抛出或捕获泛型类的实例
    在这里插入图片描述

  2. 可以取消对检查型异常的检查
    在这里插入图片描述

  3. 注意擦除后的冲突
    在这里插入图片描述

当Pair<T>重写equals方法时,即使加override注解也会报错。原因:泛型类重写equals方法时,为了实现多态,JVM会自动生成桥方法:boolean equals(Object),桥方法会调用重写的方法boolean equals(T),但由于擦除后T就是Object,所以重写的方法与巧方法冲突。、在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

泛型类型的继承规则

  1. 无论S与T有什么关系,通常,Pair<S>与Pair都没有任何关系
    在这里插入图片描述

没有继承关系,不可能多态或者强制转型。

在这里插入图片描述

  1. 总是可以将参数化类型转换为一个原始类型。例如,Pair<Employee>是原始类型Pair的一个子类型。

会会产生类型错误:
Pair pair=new Pair<Integer>(1,2); pair.setFirst("牛"); // only a compile-time warning
运行时才会报错ClassCastException。
但失去的只是泛型程序设计提供的附加安全性,更重要的是与遗留代码交互。

  1. 泛型类可以扩展或实现其他的泛型类。

ArrayList<T>类实现了List<T>接口。这意味着,一个ArrayList<Manager>可以转换为一个List<Manager>。但是,如前面所见,ArrayList<Manager>不是一个ArrayList<Employee>或List<Employee>。
在这里插入图片描述

8.8 通配符类型

8.8.1通配符概念

  • 在通配符类型中,允许类型参数发生变化。例如,通配符类型
    Pair <? extends Employee>
    在这里插入图片描述

  • 类型 Pair<Manager> 是 Pair<? extends Employee〉的子类型
    在这里插入图片描述

  • <? extends T>: 可调用get方法,不能调用set方法。get方法的返回值赋值给一个T类型或者T的子类型的对象变量。

在这里插入图片描述

在这里插入图片描述

8.8.2 通配符的超类型限定

    • <? superT>: 可调用get方法,不能调用set方法。调用set方法时,只能传递T类型的对象。get返回值只能赋给一个object。

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

8.8.3 无限定通配符

  1. Pair<?>
  2. get方法的返回值只能赋值给一个Object。setFirst方法不能被调用,甚至不能用Object
    调用。
  3. Pair<?>和Pair本质的不同在于:可以用任意Object对象调用原始Pair类的setFirst
    方法。
    在这里插入图片描述
    在这里插入图片描述

在这里插入图片描述

8.8.4 通配符捕获

  1. 通配符不是类型变量,因此,不能在编写代码中使用“?”作为一种类型。
 ? t = p.getFirst(); // ERROR
  p.setFirst(p.getSecond());
  p.setSecond(t);
  1. swapHelper方法的参数T捕获通配符。
   public static void swap(Pair<?> p) { swapHelper(p); }

   public static <T> void swapHelper(Pair<T> p)
   {
      T t = p.getFirst();
      p.setFirst(p.getSecond());
      p.setSecond(t);
   }
  1. 编译器必须能够保证通配符表示单个确定的类型。例如,ArrayList<Pair<T>>中的T永远不能捕获ArrayList<Pair<?>>中的通配符。数组列表可以保存两个Pair<?>,其中的?分别有不同的类型。 (有待考证)
   var ceo = new Manager("Gus Greedy", 800000, 2003, 12, 15);
      var cfo = new Manager("Sid Sneaky", 600000, 2003, 12, 15);
      Employee employee1=new Employee("employee1",80000,1999,11,22);
      Employee employee2=new Employee("employee2",80000,1999,11,22);
      var buddies = new Pair<Manager>(ceo, cfo);
      ceo.setBonus(1000000);
      cfo.setBonus(500000);
      Manager[] managers = { ceo, cfo };
      ArrayList<Pair<?>> arrayList=new ArrayList<>();  // ArrayList存储了不同类型的两个pair
      arrayList.add(new Pair<Manager>(ceo,cfo));
      arrayList.add(new Pair<Employee>(employee1,employee2));
      PairAlg.swap(arrayList.get(1));    //可行
      PairAlg.swap(arrayList.get(0));    //可行
      System.out.println();

8.9 反射和泛型

  1. 反射允许你在运行时分析任意对象。
  2. 泛型类型参数你将得不到太多信息,因为它们已经被擦除了。
    在下面的小节中,我们将学习利用反射可以获得泛型类的哪些信息。

8.9.1 泛型 Class 类

Class类是泛型的。Class类的常见方法:
在这里插入图片描述

8.9.2使用Class<T>参数进行类型匹配

Class<T>参数用于解决无法new T()的局限,创建T对象。
在这里插入图片描述

8.9.3 虚拟机中的泛型类型信息

  1. 原始的Pair类知道它源于泛型类Pair<T>,尽管一个Pair类型的对象无法区分它是构造为Pair<String>还是Pair<Employee>。 即可通过反射获得泛型类的所有信息。
  2. java.lang.reflect包中的接口Type
    在这里插入图片描述
...
public static void printType(Type type, boolean isDefinition)
   {
      if (type instanceof Class)
      {
         var t = (Class<?>) type;
         System.out.print(t.getName());
      }
      else if (type instanceof TypeVariable)   //类型变量
      {
         var t = (TypeVariable<?>) type;
         System.out.print(t.getName());
         if (isDefinition)
            printTypes(t.getBounds(), " extends ", " & ", "", false);
      }
      else if (type instanceof WildcardType)   //通配符
      {
         var t = (WildcardType) type;  
         System.out.print("?");
         printTypes(t.getUpperBounds(), " extends ", " & ", "", false);
         printTypes(t.getLowerBounds(), " super ", " & ", "", false);
      }
      else if (type instanceof ParameterizedType)  // 泛型类或接口
      {
         var t = (ParameterizedType) type;
         Type owner = t.getOwnerType();
         if (owner != null)
         {
            printType(owner, false);
            System.out.print(".");
         }
         printType(t.getRawType(), false);
         printTypes(t.getActualTypeArguments(), "<", ", ", ">", false);
      }
      else if (type instanceof GenericArrayType)   // 泛型数组
      {
         var t = (GenericArrayType) type;
         System.out.print("");
         printType(t.getGenericComponentType(), isDefinition);
         System.out.print("[]");
      }
   }
   ...

8.9.4 类型字面量

在这里插入图片描述

个人理解:为什么要擦除?

  1. 一个泛型类和泛型方法里,由于参数是T类型,不可能调用任何具体类的个性化函数。比如String.subString方法。因为当T不是String而是Integer时,T没有subSTring方法。
  2. 擦除后变为原始类型,也保证了不会调用任何具体类的个性化函数。
  3. 泛型具有的是各种类型的共同之处抽取出来的,不关心个性化的东西,核心在不同类型的共同之处。
  4. 调用泛型方法时,传入一个String,擦除后,调用泛型方法时,参数类型是object。而泛型方法类不会有任何String个性化方法,保证了object可以完成泛型方法,不可能出现调用String的subSTring方法,而object无法提供的情况。而当返回时,想要String对象,编译器会自动强制类型转换。
  5. 假如说,你想个性化泛型类的方法,只能继承,使用多态

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

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

相关文章

基于SpringBoot+Mybatis+Mysql+vue校园二手交易市场

基于SpringBootMybatisMysqlvue校园二手交易市场 一、系统介绍1、系统主要功能&#xff1a;2、环境配置 二、功能展示1.主页(客户)2.登陆、注册&#xff08;客户&#xff09;3.我的购物车(客户)4.我的商品详情(客户)5.我的商铺&#xff08;客户、商家&#xff09;6.我的信息&am…

zabbix5配置QQ邮件告警

1、服务端配置 编写邮件发送脚本 [fieldyangwww alertscripts]$ pwd /usr/lib/zabbix/alertscripts [fieldyangwww alertscripts]$ ll 总用量 8 -rwxr-xr-x 1 root root 136 5月 16 23:28 mail.sh -rwxr-xr-x 1 root root 751 5月 16 23:56 send_mail.py [fieldyangw…

信息与编码 SCUEC DDDD 期末考试整理(2)

1.求下面三种信道的信道容量 行列数量相等的情况 行比列多的情况 列比行多的情况 小贴士 2.客观世界三大基本要素&#xff1a;物质&#xff0c;能量&#xff0c;信息。 3.信息&#xff1a;是对事物运动状态和变化方式的表征&#xff0c;它存在于任何事物之中&#xff0c;可以…

机器学习算法系列(六)-- 朴素贝叶斯

.# 机器学习算法系列之 – 朴素贝叶斯 朴素贝叶斯法是基于概率统计&#xff0c;特征条件独立假设的分类方法&#xff0c;是一种非常常用的机器学习算法&#xff1b;通常用于处理文本分类和情感分析等自然语言处理任务中。相对于其他复杂的模型&#xff0c;朴素贝叶斯算法具有简…

提防利用 zip 域的新型网络钓鱼技术“浏览器中的文件归档器”

“浏览器中的文件存档器”是一种新的网络钓鱼技术,当受害者访问 .ZIP 域时,网络钓鱼者可以利用该技术。当受害者访问 .ZIP 域时,网络钓鱼者可以使用一种称为“浏览器中的文件存档器”的新型网络钓鱼技术在 Web 浏览器中“模拟”文件存档器软件。安全研究员 mr.d0x 详细介绍了…

A-可达鸭数学

题目链接 示例1 输入 9 1 -1 0 6 54 -8 520 1907 -2023输出 w m b wmb wmbbb mbw wmbwwmw wbmbmmbm mbwmbbwm备注: 请注意&#xff0c;在可达鸭数学里是没有负号的。 请注意&#xff0c;不要输出多余的前导b&#xff0c;否则会被判Wrong Answer。&#xff08;例如&#xff…

英文论文(sci)解读复现【NO.17】旋转至参加:卷积三重注意力模块

此前出了目标检测算法改进专栏&#xff0c;但是对于应用于什么场景&#xff0c;需要什么改进方法对应与自己的应用场景有效果&#xff0c;并且多少改进点能发什么水平的文章&#xff0c;为解决大家的困惑&#xff0c;此系列文章旨在给大家解读发表高水平学术期刊中的 SCI论文&a…

Elasticsearch第一天学习笔记

目录 一、Elasticsearch概述 二、elasticsearch入门 一、Elasticsearch概述 &#xff08;一&#xff09;elasticsearch是什么&#xff1f; The Elastic Stack, 包括 Elasticsearch 、 Kibana 、 Beats 和 Logstash &#xff08;也称为 ELK Stack &#xff09;。 Elaticsear…

数据库之主键、联合主键

参考文章&#xff1a;数据库之主键、联合主键 一、主键、联合主键简介 数据库主键是用来标记数据记录唯一性的列&#xff0c;不能为空&#xff0c;不能重复。 主键具有的特点&#xff1a;唯一性、非空性。 数据库联合主键&#xff1a;可以将多个列同时作为主键。&#xff0…

SQL语句之DQL语言

说明&#xff1a;DQL&#xff08;Data Query Language&#xff0c;数据查询语言&#xff09;&#xff0c;用来查询数据库表中的记录。有的书中&#xff0c;会把DQL语言放入到DML&#xff08;Data Manipulation Language&#xff0c;数据操作语言&#xff1a;数据的增删改&#…

金融数据获取:通过Ajax跳转的网页怎么爬?以东方财富基金净值数据为例

你是否碰到过点击网站上的按钮或链接&#xff0c;网页数据进行了刷新&#xff0c;但浏览器上显示的网址却没有任何变化的情况&#xff0c;这其实就是利用Ajax跳转的网页。本期笔者将以东方财富网为例展示如何获取Ajax跳转的网页内容&#xff0c;本文主要内容如下&#xff1a; 目…

MyBatis(多表查询,动态SQL的使用)

目录 多表查询 查询文章详情 查询一个用户底下的所有文章 动态SQL的使用 if 标签 trim 标签 where 标签 set 标签 foreach 标签 多表查询 现在有俩张表,一张是文章表,一张是用户表.如下: 查询文章详情 我们现在想查询得到一张表,表里面的内容和文章表大多一致,只是要在…

银行从业资格证(初级) 5天零基础拿证攻略【个人经历,亲测】

我的科目&#xff1a;《法律法规》、《个人理财》 方法 直接做真题&#xff0c;做真题错题总结归纳。 教材可买可不买&#xff0c;时间不够可以不看教材。 可以买天一金融的真题汇编&#xff0c;它附赠一个考点整理的小册子。 一定要自己整理知识框架。 看个人习惯选择网上做…

chatgpt赋能python:Python列表中随机抽取6个元素的简单方法

Python列表中随机抽取6个元素的简单方法 在Python编程中&#xff0c;列表是一种常用的数据类型&#xff0c;它允许我们存储和操作一系列数据。有时候&#xff0c;我们需要从列表中随机抽取一些元素&#xff0c;比如从一组名字中随机选择几个人参加抽奖等。本文将介绍如何使用P…

MIT 6.824 lab distributed system 分布式系统

https://youtu.be/cQP8WApzIQQ 概念 为什么需要分布式系统&#xff1f; high performanceparallelism&#xff1a;分布式系统可以实现CPU、内存、硬盘的并行运行fault tolerancephysical&#xff1a;security / isolated 分布式系统的困难 concurrency&#xff1a;各个并行的…

【unity小技巧】使用贝塞尔曲线实现导弹随机攻击敌人,也可以用于拾取物品

文章目录 先看效果代码实现1.导弹代码2.玩家生成导弹代码3.玩家挂载代码4.导弹挂载代码先看效果 代码实现 1.导弹代码 记得配置敌人为enemy标签 using System.Collections; using System.Collections.Generic; using

Nautilus Chain:独特且纯粹的创新型 Layer3

以 Layer3 架构为主要特点的模块化公链 Nautilus Chain 即将在近期上线主网&#xff0c;这也进一步引发了行业关于 Layer3 的讨论。 实际上&#xff0c;在2022年以太坊的创始人 Vitalik 提出了三大目标&#xff1a;Layer2 用于扩展&#xff0c;Layer3 用于定制功能&#xff0c;…

Transformer模型原理—论文精读

文章目录 前言模型架构Encoder和DecoderEncoderDecoder AttentionFFNEmbeddings和Positional EncodingEmbeddingsPositional Encoding 总结 前言 今天来看一下Transformer模型&#xff0c;由Google团队提出&#xff0c;论文名为《Attention Is All You Need》。论文地址。 正如…

Openwrt_XiaoMiR3G路由器_刷入OpenWrt

刷入Openwrt之前请保证小米R3G路由器已经刷入Breed控制台固件。 刷入Breed请参考&#xff1a; Openwrt_XiaoMiR3G路由器_刷入Breed固件 路由器具体配置参考 小米路由器3G参数 - 小米商城 既然要刷入OpwnWrt就需要线编译固件&#xff0c;使用lede的OpenWrt源码编译。 进入 …

K8S集群安装(二)

目录 1 安装说明.... 1 1.1 安装环境.... 1 1.2 生产环境可部署Kubernetes集群的两种方式.... 2 1.3 操作系统初始化配置.... 3 2 安装Docker/kubeadm/kubelet【所有节点】.... 4 2.1 安装Docker. 4 2.2 添加阿里云YUM软件源.... …