【Java高级语法】(八)反射机制:有朋友问反射到底是怎样玩的?看完这篇文章你就清楚了~

news2024/11/25 15:50:04

Java高级语法详解之反射机制

  • :one: 概念
  • :two: 优势和缺点
  • :three: 使用
    • 3.1 Class类
    • 3.2 获取类的结构信息- 构造函数
    • 3.3 获取类的结构信息- 方法
    • 3.4 获取类的结构信息- 字段
    • 3.5 动态创建对象、调用方法和设置属性
    • 3.6 动态代理
  • :four: 底层原理
  • :five: 应用场景
  • :ear_of_rice: 总结
  • :bookmark_tabs: 本文源码下载地址

1️⃣ 概念

反射机制是Java语言的一个重要特性,它使得程序可以在运行时获取和操作编译时未知的类的相关信息。在传统的开发模式中,我们需要在编译时确定类的结构,并且通过编码方式直接使用类的方法和属性。而反射机制则打破了这种限制,可以在运行时动态地创建对象、获取类的成员和调用方法。

可以说反射提供了强大的工具,使得开发人员能够编写更加灵活和通用的代码。

2️⃣ 优势和缺点

关于反射机制的优缺点的详细介绍如下:

🍎 优点:

  • 动态性:反射机制允许程序在运行时动态地获取和操作类型信息,而不需要在编译时就确定。这使得程序可以根据运行时环境的变化进行适应和调整,具有更大的灵活性和可扩展性;
  • 通用性:反射机制可以被广泛地用于处理各种类型的对象和数据结构。它提供了一种统一的方式来处理不同类型的对象,而不需要针对每个类型实现特定的代码逻辑;
  • 功能强大:通过反射,程序可以动态地创建新的对象实例、调用方法、访问和修改成员变量,甚至可以访问和调用私有成员。这样可以实现很多复杂的功能,例如依赖注入、插件系统、框架扩展等。

🍏 缺点:

  • 性能影响:由于反射机制需要在运行时进行类型检查和动态调用,相比直接调用编译时已知的类型,它会引入额外的性能开销。反射操作通常比直接访问更慢,因此在性能敏感的场景下不适宜过度依赖反射;
  • 安全问题:反射机制破坏了面向对象编程的封装性原则。它可以绕过访问修饰符的限制,访问和修改私有成员变量或调用私有方法,这可能造成安全隐患;
  • 复杂性增加:由于反射机制提供了灵活性,但也带来了一定的复杂性。使用反射需要对类型的结构和操作有较深入的了解,错误的使用反射可能导致代码难以理解、维护困难。

综上所述,反射机制的优点包括动态性、通用性和功能强大,但同时也存在性能影响、安全问题和增加复杂性的缺点。 在开发中,应该根据具体需求谨慎使用反射,并权衡其带来的好处和代价。

3️⃣ 使用

3.1 Class类

Java中的每个类都有一个Class对象对应,该对象保存了与该特定类相关的类的结构信息。

获取Class类对象的三种方式:

  • 通过类的class属性获取,例如String.class。适用于已知类的名称,但没有创建对象实例的情况;
  • 通过对象的getClass()方法获取,例如str.getClass()。适用于已经具有对象实例的情况;
  • 通过Class类的静态方法forName()获取。该方法需要传入完全限定名(Fully Qualified Name)作为参数,例如Class.forName("java.lang.String")。适用于只知道类的名称字符串,并且在运行时需要动态地加载和使用该类的情况。

示例如下:

public class UseDemo {
    public static void main(String[] args) {
        // 通过类名获取Class对象
        Class<?> class1 = String.class;
        System.out.println(class1.getName());

        // 通过对象的getClass()方法获取Class对象
        String str = "Hello";
        Class<?> class2 = str.getClass();
        System.out.println(class2.getName());

        // 通过Class类的forName()方法获取Class对象
        try {
            Class<?> class3 = Class.forName("java.lang.String");
            System.out.println(class3.getName());
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

在这段代码中,使用了不同的三种方式来获取Class类对象。使用了 getName()方法来获取类的名字。输出结果:

java.lang.String
java.lang.String
java.lang.String

3.2 获取类的结构信息- 构造函数

反射机制允许我们获取类的各种结构信息,如方法、字段、构造函数等。其中,可以使用以下方法获取构造函数:

  • getConstructors():获取所有公共构造方法;
  • getDeclaredConstructors():获取所有构造方法(包括私有的);
  • getConstructor(Class<?>... parameterTypes):获取指定参数类型的构造方法。

以下是一个Java演示代码,展示了通过反射机制获取类的构造方法的不同方式:

import java.lang.reflect.Constructor;
import java.lang.reflect.Modifier;

public class ReflectionDemo {
    public static void main(String[] args) {
        try {
            // 获取类的Class对象
            Class<?> clazz = MyClass.class;
            
            // 获取所有公共构造方法
            Constructor<?>[] publicConstructors = clazz.getConstructors();
            System.out.println("所有公共构造方法:");
            printConstructors(publicConstructors);
            
            // 获取所有构造方法(包括私有的)
            Constructor<?>[] allConstructors = clazz.getDeclaredConstructors();
            System.out.println("所有构造方法(包括私有的):");
            printConstructors(allConstructors);
            
            // 获取指定参数类型的构造方法
            Class<?>[] parameterTypes = { int.class, String.class };
            Constructor<?> constructor = clazz.getConstructor(parameterTypes);
            System.out.println("指定参数类型的构造方法:");
            printConstructor(constructor);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    // 打印构造方法信息
    private static void printConstructors(Constructor<?>[] constructors) {
        for (Constructor<?> constructor : constructors) {
            // 获取构造方法的修饰符,然后使用Modifier.toString()将修饰符转换成字符串形式
            String modifiers = Modifier.toString(constructor.getModifiers());
            // 获取构造方法的名称
            String name = constructor.getName();
            // 获取构造方法的参数类型,返回一个Class<?>数组。每个元素都表示一个参数类型
            Class<?>[] parameterTypes = constructor.getParameterTypes();
            
            System.out.print(modifiers + " " + name + "(");
            for (int i = 0; i < parameterTypes.length; i++) {
                if (i > 0) {
                    System.out.print(", ");
                }
                System.out.print(parameterTypes[i].getName());
            }
            System.out.println(")");
        }
        System.out.println();
    }
    
    // 打印单个构造方法信息
    private static void printConstructor(Constructor<?> constructor) {
        String modifiers = Modifier.toString(constructor.getModifiers());
        String name = constructor.getName();
        Class<?>[] parameterTypes = constructor.getParameterTypes();
        
        System.out.print(modifiers + " " + name + "(");
        for (int i = 0; i < parameterTypes.length; i++) {
            if (i > 0) {
                System.out.print(", ");
            }
            System.out.print(parameterTypes[i].getName());
        }
        System.out.println(")");
        System.out.println();
    }
}

class MyClass {
    public MyClass(int val, String str) {}
    
    private MyClass(double val) {}
    
    protected MyClass(boolean flag) {}

    MyClass() {}
}

运行上述代码,将输出以下结果:

所有公共构造方法:
public com.xiaoshan.demo2.MyClass(int, java.lang.String)

所有构造方法(包括私有的):
 com.xiaoshan.demo2.MyClass()
protected com.xiaoshan.demo2.MyClass(boolean)
private com.xiaoshan.demo2.MyClass(double)
public com.xiaoshan.demo2.MyClass(int, java.lang.String)

指定参数类型的构造方法:
public com.xiaoshan.demo2.MyClass(int, java.lang.String)

上面这段代码展示了获取类的所有公共构造方法、所有构造方法和特定参数类型构造方法的方式,并使用反射机制将构造方法的相关信息打印出来。

3.3 获取类的结构信息- 方法

反射机制允许我们获取类的各种结构信息,如方法、字段、构造函数等。其中,可以使用以下方法获取类中的方法:

  • getMethods():获取类中的公共方法;
  • getDeclaredMethods():获取类中所有声明的方法(包括私有方法);
  • getMethod(String name, Class<?>... parameterTypes):根据方法名和参数类型获取指定的公共方法;
  • getDeclaredMethod(String name, Class<?>... parameterTypes):根据方法名和参数类型获取指定的方法。

以下是一个Java演示代码,展示了通过反射机制获取类中方法的不同方式:

import java.lang.reflect.Method;

public class UseDemo {
    public static void main(String[] args) {
        // 获取类对象
        Class<MyClass> myClass = MyClass.class;

        // 获取声明的所有方法(包括公共、保护、默认和私有方法)
        Method[] declaredMethods = myClass.getDeclaredMethods();
        System.out.println("Declared methods:");
        printMethods(declaredMethods);

        // 获取所有公共方法(包括继承的和实现的父接口的方法)
        Method[] publicMethods = myClass.getMethods();
        System.out.println("Public methods:");
        printMethods(publicMethods);

        // 获取指定方法
        try {
            Method specificMethod = myClass.getMethod("specificMethod");
            System.out.println("Specific method:");
            System.out.println(specificMethod);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
    }

    private static void printMethods(Method[] methods) {
        for (Method method : methods) {
            System.out.println(method);
        }
        System.out.println("Total methods: " + methods.length);
        System.out.println();
    }
}

class MyClass {

    public void publicMethod() {}

    private void privateMethod() {}

    protected void protectedMethod() {}

    void defaultMethod() {}

    public static void staticMethod() {}

    public void specificMethod() {}
}

运行上述代码,将输出以下结果:

Declared methods:
public void com.xiaoshan.demo3.MyClass.publicMethod()
private void com.xiaoshan.demo3.MyClass.privateMethod()
protected void com.xiaoshan.demo3.MyClass.protectedMethod()
void com.xiaoshan.demo3.MyClass.defaultMethod()
public static void com.xiaoshan.demo3.MyClass.staticMethod()
public void com.xiaoshan.demo3.MyClass.specificMethod()
Total methods: 6

Public methods:
public void com.xiaoshan.demo3.MyClass.publicMethod()
public static void com.xiaoshan.demo3.MyClass.staticMethod()
public void com.xiaoshan.demo3.MyClass.specificMethod()
public final void java.lang.Object.wait() throws java.lang.InterruptedException
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
public boolean java.lang.Object.equals(java.lang.Object)
public java.lang.String java.lang.Object.toString()
public native int java.lang.Object.hashCode()
public final native java.lang.Class java.lang.Object.getClass()
public final native void java.lang.Object.notify()
public final native void java.lang.Object.notifyAll()
Total methods: 12

Specific method:
public void com.xiaoshan.demo3.MyClass.specificMethod()

上面这段代码展示了获取类的所有公共方法、所有方法和特定参数类型方法的方式,并将方法的相关信息打印出来。需要注意的是,从结果中可以观察到,用于获取所有公共方法的 getMethods() 不仅包括本类公共方法,还包括了继承的和实现的父接口中的方法。

3.4 获取类的结构信息- 字段

反射机制允许我们获取类的各种结构信息,如方法、字段、构造函数等。其中,可以使用以下方法获取类中的字段:

  • getFields():获取类中声明的公共字段;
  • getDeclaredFields():获取类中声明的所有字段(包括私有字段);
  • getDeclaredField(String name):根据名称获取类中的指定字段(包括私有字段)。

以下是一个Java演示代码,展示了通过反射机制获取类中字段的不同方式:

import java.lang.reflect.Field;

public class UseDemo {

    public static void main(String[] args) {
        try {
            // 获取类的Class对象
            Class<?> clazz = MyClass.class;

            // 获取所有公共字段(包括继承自父类的公共字段)
            Field[] publicFields = clazz.getFields();
            System.out.println("公共字段:");
            for (Field field : publicFields) {
                System.out.println(field.getName());
            }

            // 获取所有字段(包括公共、私有和受保护字段)
            Field[] declaredFields = clazz.getDeclaredFields();
            System.out.println("\n所有字段:");
            for (Field field : declaredFields) {
                System.out.println(field.getName());
            }

            // 获取指定字段
            Field privateField = clazz.getDeclaredField("privateField");
            System.out.println("\n指定字段:");
            System.out.println(privateField.getName());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

class MyClass {
    private String privateField;
    public int publicField;
}

运行上述代码,将输出以下结果:

公共字段:
publicField

所有字段:
privateField
publicField

指定字段:
privateField

3.5 动态创建对象、调用方法和设置属性

利用反射机制,我们可以在运行时动态地创建对象和调用其方法。通过获取类的构造函数并传入相应的参数来创建实例,然后可以对该实例调用方法。

以下是一些常用的动态创建对象和调用其方法的方法:

  • Class对象.newInstance():通过Class对象创建类对象;
  • Constructor对象.newInstance():通过Constructor对象创建类对象;
  • setAccessible(boolean flag):通过设置参数为true,可以绕过Java语言的访问控制检查,从而访问并修改对象的私有成员变量、方法或构造函数;
  • invoke(Object obj, Object... args):动态调用一个对象的方法。第一个参数是要调用方法的对象实例,第二个可变参数是方法的参数;
  • set(Object obj, Object value):将指定对象的字段设置为指定的值。需要注意此方法只能修改非final修饰的字段的值。第一个参数是要修改字段的对象实例,第二个参数是要设置的值。

示例如下:

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class UseDemo {

    public static void main(String[] args) {
        Class<Cat> clazz = Cat.class;
        try {
            // 通过Class对象创建类对象
            Cat cat1 = clazz.newInstance();
            System.out.println(cat1);

            // 通过构造方法创建类对象
            Constructor<Cat> constructor = clazz.getConstructor(String.class, int.class);
            Cat cat2 = constructor.newInstance("橘猫", 2);
            System.out.println(cat2);

            // 调用方法
            Method method = clazz.getDeclaredMethod("eat", String.class);
            method.setAccessible(true);
            method.invoke(cat2, "猫粮");

            // 获取并修改私有字段的值
            Field field = clazz.getDeclaredField("name");
            field.setAccessible(true);
            field.set(cat1, "折耳猫");
            System.out.println(cat1);
            
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }

    }
}

class Cat{
    private String name;
    private int age;

    private void eat(String food){
        System.out.println("吃东西:" + food);
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public Cat() {
    }

    public Cat(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "Cat{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

运行结果:

Cat{name='null', age=0}
Cat{name='橘猫', age=2}
吃东西:猫粮
Cat{name='折耳猫', age=0}

3.6 动态代理

反射机制还常用于实现动态代理。动态代理是Java提供的一种强大的机制,允许程序运行时创建一个实现一组接口的代理类。通过实例化代理对象,并能够拦截到代理对象所调用的方法,从而实现额外的操作。

首先,定义一个接口MyInterface,其中包含了一个无返回值的方法myMethod()

interface MyInterface {
    void myMethod();
}

接着,定义一个RealObject类实现MyInterface接口,并且实现myMethod()方法:

public class RealObject implements MyInterface {
    public void myMethod() {
        System.out.println("RealObject method called.");
    }
} 

接着,定义一个DynamicProxy类实现InvocationHandler接口,作为代理对象的处理程序。它接收一个真实对象作为参数,在方法调用前后进行处理。在这里使用了反射机制的invoke()方法调用真实对象的方法,在方法调用之前和调用之后做了一些处理操作:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class DynamicProxy implements InvocationHandler {
    private Object subject;

    public DynamicProxy(Object subject) {
        this.subject = subject;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("Before method call.");
        Object result = method.invoke(subject, args);
        System.out.println("After method call.");
        return result;
    }

}

接下来,通过Proxy.newProxyInstance()方法创建一个代理对象proxy。该方法接收三个参数:类加载器、接口数组和代理对象的处理程序。
最后,通过代理对象proxy调用myMethod()方法时,将执行代理类的增强处理,然后调用真实对象的方法。

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

public class ProxyDemo {
    public static void main(String[] args) {
        RealObject realObject = new RealObject();
        InvocationHandler handler = new DynamicProxy(realObject);

        MyInterface proxy = (MyInterface) Proxy.newProxyInstance(
                realObject.getClass().getClassLoader(),
                new Class<?>[]{MyInterface.class},
                handler
        );

        proxy.myMethod();
    }
}

运行结果:

Before method call.
RealObject method called.
After method call.

4️⃣ 底层原理

反射机制是面向对象编程语言中的一项重要特性,它允许程序在运行时动态地获取和操作类的成员信息,包括类的方法、属性、构造函数等。通过反射,程序可以直接操作类的成员,无需在编译时明确引用这些成员。

底层原理主要涉及三个方面:反射的元数据、动态加载和运行时调用。

  1. 元数据:元数据是描述类的结构和成员信息的数据。在Java中,每个类都有一个Class对象来表示它自身,这个Class对象持有了类的元数据。类的元数据包括类名、父类、实现的接口、字段名称和类型、方法名称和参数等信息。

  2. 动态加载:反射利用ClassLoader机制动态加载类。ClassLoader负责读取字节码文件,并生成对应的Class对象。当需要使用某个类时,ClassLoader会根据类名查找并载入字节码文件,然后创建对应的Class对象。通过Class对象,可以获取类的各种信息。

  3. 运行时调用:通过反射获取到类的Class对象后,就可以在运行时动态调用类的方法、访问或修改对象的属性等。调用方法时,先获取Method对象,然后通过invoke()方法传递实例和参数来调用方法。更新或获取属性值时,通过Field对象的get()和set()方法进行操作。

需要注意以下几点:

  • 反射机制的底层实现依赖于字节码的解析和动态加载等 技术,因此在运行时使用反射会带来一定的性能损失;
  • 反射机制能够突破访问权限限制,即使某个成员是私有的,也可以通过反射获取和操作它。但这种方式容易破坏封装性,应谨慎使用;
  • 使用反射机制进行编程时,应特别注意类型的安全性,避免出现类型错误。

5️⃣ 应用场景

通过反射,程序可以动态地获取类的信息并使用这些信息实现一些灵活性较高的功能。下面是几个反射机制的应用场景:

  1. 应用程序结构分析:反射可以在运行时分析一个类的成员(字段、方法、属性等),并据此生成文档、自动生成代码或进行其它类似的操作。它可以用于构建工具、集成开发环境(IDE)和测试框架等。

  2. 插件系统:反射机制可以使应用程序支持插件式的架构,通过在运行时动态加载扩展,从而实现动态扩展功能。比如,一个图像处理软件可以通过反射机制在运行时根据插件提供的类和方法来进行图像滤镜的选择和应用。

  3. 框架开发:使用反射可以实现通用且灵活的框架,使框架能够适应各种不同的类和对象,并提供基于配置文件的动态行为。例如,Java的Spring框架就广泛使用了反射机制,通过读取配置文件中定义的类和方法以及它们的属性,实现依赖注入和不同模块之间的松耦合。

  4. 动态代理:通过反射机制可以动态地创建代理类以及其实例。代理模式在实现中经常使用反射来实现,它将对被代理对象的方法调用进行拦截,并在方法调用前后执行一些额外的操作。动态代理广泛应用于AOP(面向切面编程)和RPC(远程过程调用)等方面。

  5. 序列化/反序列化:某些场景下,我们可能需要将对象以二进制或其他格式进行存储、传输或保存到文件中。反射提供了一种机制来获取对象的内部信息,并将其转换为可进行持久化处理的格式。

这些仅是反射机制的一些常见应用场景,还有许多其他领域也能发挥反射的作用,比如动态配置、ORM框架、动态加载类等。反射的强大功能使得程序在运行时具备更高的灵活性和扩展性,但也要注意合理使用,因为反射操作相比直接调用会引入一些性能开销。

🌾 总结

Java的反射机制为开发人员提供了一种灵活而强大的工具,可以在运行时检查和操作类的结构信息。通过反射,我们可以动态地创建对象、调用方法、访问字段等,并能够实现一些高级功能如动态代理。然而,过度使用反射可能会增加代码的复杂性和性能开销,因此需要慎重使用并仔细权衡利弊。

📑 本文源码下载地址

Java的反射机制讲解案例代码 Class类、 获取类的结构信息:构造函数、方法和字段,动态创建对象、调用方法和设置属性

在这里插入图片描述

在这里插入图片描述

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

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

相关文章

SedonaSQL 聚合函数使用说明

ST_Envelope_Aggr 函数说明: 返回几何的外边界 语法: ST_Envelope_Aggr (A:geometryColumn) 支持版本: v1.0.0 Spark SQL 举例说明: SELECT ST_Envelope_Aggr(pointdf.arealandmark) FROM pointdf运行示例(AggregateFunctionTest.java): ST_Intersection_Aggr 函数说明: …

大文件如何传输到电脑?亲测又快又简单!

我们平时可以因为各种原因&#xff0c;如更换新电脑、高清视频分享等&#xff0c;需要将大文件传输到另一台电脑上。大文件如何传输到电脑?相信这是很多朋友都想知道如何实现吧。我们为您提供了2种轻松将大文件从PC传输到PC的方法。话不多说&#xff0c;上技巧&#xff01; 方…

腾讯云服务器地域有什么区别?怎么选比较好

腾讯云服务器地域有什么区别&#xff1f;云服务器地域怎么选择&#xff1f;地域是指云服务器所在机房的地理位置&#xff0c;用户距离地域越近网络延迟越低&#xff0c;速度越快&#xff0c;所以地域就近选择即可。广州上海北京等地域网站域名需要备案&#xff0c;中国香港或其…

SpringBoot使用MockMVC单元测试Controller

前言&#xff1a; 在SpringBoot应用程序中&#xff0c;Controller是接受客户端请求并返回响应数据的核心组件。为了保证Controller的正确性和稳定性&#xff0c;我们可以使用MockMVC框架进行单元测试。MockMVC是Spring框架提供的一个HTTP客户端&#xff0c;用于模拟HTTP请求和响…

2023年智能优化算法之——增长优化器Growth Optimizer(GO),附MATLAB代码

增长优化器的主要设计灵感来源于个人在社会成长过程中的学习和反思机制。学习是个体通过从外部世界获得知识而成长的过程。反思是检查个人自身不足并调整个人学习策略以帮助个人成长的过程。参考文献如下&#xff1a; Zhang, Qingke, et al. “Growth Optimizer: A Powerful M…

二维码在固定资产实物盘点中的应用

很多企业为了掌握固定资产的后续使用情况和状态&#xff0c;会定期对投入使用的固定资产进行盘点&#xff0c;然而固定资产常会出现分散情况&#xff0c;在这种情况下让财务人员到达每个固定资产的所在处进行实地盘点显得极为不现实。 也有不少企业会在盘点过程中使用到固定资…

聊天室(一)___常见的基本功能实现

最近搞聊天室的人还挺多&#xff0c;正好自己也弄就总结自己遇到必不可少的一些功能&#xff0c;本篇文章主要为自己和看到我文章的人一种思路&#xff0c;希望大家不要把聊天室想的太复杂。 上图是我自己做的一个聊天室&#xff0c;类似微信的单聊群聊收藏等功能&#xff0c;根…

python+requests接口自动化框架详解,没有比这个更详细的了

目录 为什么要做接口自动化框架 正常接口测试的流程是什么&#xff1f; 一、接口框架如下&#xff1a; 二、接口的数据规范设计---Case设计 2.1注册接口用例 2.2登录接口用例 三、创建utils包&#xff1a;用来存放公共的类 3.1 ParseExcel.py 操作封装excel的类&#xf…

【AI工具】-Stable Diffusion本地化部署教程

前言 今天我们要介绍的是时下最流行的AI绘图软件Stable Diffusion&#xff0c;虽然Diffusion.ai已经开放api&#xff0c;但是长时间的商业化调用我们需要购买很多的金币。所以我们需要找一个平替的AI绘图平台&#xff0c;现在主流市场中AI绘图软件主要就是OpenAI的DALLE、midj…

SSM会议管理系统

SSM会议管理系统 后端基于SSM、前端基于Freemarker写的会议管理系统、使用JDK8、mysql使用5.7版本 技术栈 Spring SpringMVC MyBatis Mysql Freemarker jqueryajax[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JGo0luHu-1687163482019)(img.png)] …

【Python 随练】打印水仙花数

题目&#xff1a; 打印出所有的"水仙花数"&#xff0c;所谓"水仙花数"是指一个三位数&#xff0c;其各位数字立方和等于该数 简介&#xff1a; 在本篇博客中&#xff0c;我们将解决一个经典的数学问题&#xff1a;打印出所有的水仙花数。水仙花数是指一…

Unity核心5——Tilemap

Tilemap 一般称之为瓦片地图或者平铺地图&#xff0c;是 Unity2017 中新增的功能&#xff0c;主要用于快速编辑 2D 游戏中的场景&#xff0c;通过复用资源的形式提升地图多样性 ​ 工作原理就是用一张张的小图排列组合为一张大地图 ​ 它和 SpriteShape 的异同 共同点&#x…

浅析GPT2中的autoregressive和BERT的autoencoding源码实现

经常使用BERT来做研究&#xff0c;因此对Encoder的架构较为熟悉&#xff0c;但是从来没有了解过GPT这样的Decoder架构&#xff0c;尤其对自回归的形式不知道源码是如何实现的。 为了方便对比和讨论&#xff0c;接来下所探讨的源码都是基于HuggingFace这个框架的。 Bert注意力…

基于亚马逊云科技Serverless,朝夕光年和Second Dinner携手打造年度手游

经典的漫威IP&#xff0c;酷炫的卡牌对战&#xff0c;丰富的故事情节&#xff0c;这款移动游戏《MARVEL SNAP》一经上线就深得全球玩家喜爱。在The Game Awards 2022的年度颁奖典礼上&#xff0c;它更是以出色的表现&#xff0c;一举斩获最佳移动游戏奖项。 其研发公司Second …

【ElasticSearch】中文分词器

ES默认的analyzer&#xff08;分词器&#xff09;&#xff0c;对英文单词比较友好&#xff0c;对中文分词效果不好。不过ES支持安装分词插件&#xff0c;增加新的分词器。 1、如何指定analyzer&#xff1f; 默认的分词器不满足需要&#xff0c;可以在定义索引映射的时候&#…

Python自动办公之合并多个PDF文件

本文基于使用pycharm平台&#xff0c;使用glob库和PyPDF2库实现 首先将需要合并的文件放于一个文件中 如下图 addpdf文件夹为需要合并的文件位置 good.py为代码块 代码如下 print(这个小代码仅仅用于合并pdf文件数量小于10的情况) print() from PyPDF2 import PdfMerger,P…

深度解析DuckDB的ScheduleEvents

深度解析DuckDB的ScheduleEvents 1.ScheduleEventData2.ScheduleEventsInternal3.SchedulePipeline 3.1 Event3.2 PipelineEventStack3.3 主逻辑4.可视化总结 书接上回熬夜三晚之深度解析DuckDB MetaPipeline&#xff0c;MetaPipeline在初始化的时候会构建出下面几个&#xff1…

【ARMv8/v9 异常模型入门及渐进2 - 系统控制寄存器 SCTRL_ELx 介绍】

文章目录 SCTRL_ELx 介绍背景ARMv8 SCTLR_ELx 介绍ARMv9 SCTLR_ELx 介绍 SCTRL_ELx 介绍背景 由于在做DFD 测试过程中需要测试 EL1 状态下的 self-hosted trace 功能&#xff0c;但是这个测试是在UEFI中做的&#xff0c;在开发验证阶段UEFI默认是运行在EL3 下的&#xff0c;所…

mac安装VsCode遇到的问题

万事开头难&#xff0c;头一次在安装生产工具的时候&#xff0c;就遇到了这么棘手的问题。百度和Google都试过了&#xff0c;网上的所有方式对我都没效果。最终自己阴差阳错解决了&#xff0c;我看内外网反馈这个问题的还挺多&#xff0c;在这里记录一下&#xff0c;希望可以帮…

Docker专题系列之十三:docker容器内安装vim编辑器

在使用docker时&#xff0c;有时候我们需要编辑配置文件&#xff0c;需要使用vim或者vi命令&#xff0c;但是会发现&#xff1a; root20ab69bedcdb:/etc/mysql# vim my.cnf bash: vim: command not found这是因为vim没有安装&#xff0c;使用如下命令安装&#xff1a; apt-ge…