第十三章:Java反射机制

news2025/7/13 12:46:17

第十三章:Java反射机制

13.1:Java反射机制概述

  1. Java Reflection

    Reflection(反射)是被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。

    ​ 加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息。我们可以通过这个对象看到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,所以,我们形象的称之为: 反射。

  2. Java反射机制研究及应用

    • Java反射机制提供的功能
      1. 在运行时判断任意一个对象所属的类
      2. 在运行时构造任意一个类的对象
      3. 在运行时判断任意一个类所具有的成员变量和方法
      4. 在运行时获取泛型信息
      5. 在运行时调用任意一个对象的成员变量和方法
      6. 在运行时处理注解
      7. 生成动态代理
    • 反射相关的主要API
      1. java.lang.Class: 代表一 个类
      2. java.lang.reflect.Method: 代表类的方法
      3. java.lang.reflect.Field: 代表类的成员变量
      4. java.lang.reflect.Constructor: 代表类的构造器

13.2:理解Class类并获取Class实例

  1. Class

    • Object类中定义了以下的方法,此方法将被所有子类继承:public final Class getClass()
    • 以上的方法返回值的类型是一个Class类,此类是Java反射的源头,实际上所谓反射从程序的运行结果来看也很好理解,即:可以通过对象反射求出类的名称。
    • 对象照镜子后可以得到的信息:某个类的属性、方法和构造器、某个类到底实现了哪些接口。对于每个类而言,JRE都为其保留一个不变的Class类型的对象。
    • 一个Class对象包含了特定某个结构(class/interface/enum/annotation/primitive type/void/[])的有关信息
      1. Class本身也是一个类
      2. Class对象只能由系统建立对象
      3. 一个加载的类在JVM中只会有一个Class实例
      4. 一个Class对象对应的是一个加载到JVM中的一个.class文件
      5. 每个类的实例都会记得自己是由哪个Class实例所生成
      6. 通过Class可以完整地得到一个类中的所有被加载的结构
      7. Class类是Reflection的根源,针对任何你想动态加载、运行的类,唯有先获得相应的Class对象
  2. Class 类的常用方法

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

    private String name;
    public int age;
    
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    private Person(String name) {
        this.name = name;
    }
    public Person() {
        System.out.println("Person()");
    }
    
    // 省略get/set方法
    // 省略toString方法
    
    public void show(){
        System.out.println("你好,我是一个人");
    }
    private String showNation(String nation){
        System.out.println("我的国籍是:" + nation);
        return nation;
    }
    
    Class clazz = Person.class;
    //1.通过反射,创建Person类的对象
    Constructor cons = clazz.getConstructor(String.class,int.class);
    Object obj = cons.newInstance("Tom", 12);
    Person p = (Person) obj;
    System.out.println(p.toString());
    //2.通过反射,调用对象指定的属性、方法
    Field age = clazz.getDeclaredField("age");
    age.set(p,10);
    System.out.println(p.toString());
    //调用方法
    Method show = clazz.getDeclaredMethod("show");
    show.invoke(p);
    //通过反射,可以调用Person类的私有结构的
    //调用私有的构造器
    Constructor cons1 = clazz.getDeclaredConstructor(String.class);
    cons1.setAccessible(true);
    Person p1 = (Person) cons1.newInstance("Jerry");
    System.out.println(p1);
    //调用私有的属性
    Field name = clazz.getDeclaredField("name");
    name.setAccessible(true);
    name.set(p1,"HanMeimei");
    System.out.println(p1);
    //调用私有的方法
    Method showNation = clazz.getDeclaredMethod("showNation", String.class);
    showNation.setAccessible(true);
    String nation = (String) showNation.invoke(p1,"中国");
    System.out.println(nation);
    
  3. 获取Class类的实例

    //方式一:调用运行时类的属性:.class
    Class clazz1 = Person.class;
    System.out.println(clazz1);
    //方式二:通过运行时类的对象,调用getClass()
    Person p1 = new Person();
    Class clazz2 = p1.getClass();
    System.out.println(clazz2);
    //方式三:调用Class的静态方法:forName(String classPath)
    Class clazz3 = Class.forName("com.wang.java.Person");
    System.out.println(clazz3);
    //方式四:使用类的加载器:ClassLoader
    ClassLoader classLoader = ReflectionTest.class.getClassLoader();
    Class clazz4 = classLoader.loadClass("com.wang.java.Person");
    System.out.println(clazz4);
    
    System.out.println(clazz1 == clazz2); // true
    System.out.println(clazz1 == clazz3); // true
    System.out.println(clazz1 == clazz4); // true
    
  4. 哪些类型可以有Class对象

    • class:外部类,成员(成员内部类,静态内部类),局部内部类,匿名内部类。
    • interface:接口
    • []:数组
    • enum:枚举
    • annotation:注解@interface
    • primitive type:基本数据类型
    • void
    Class c1 = Object.class;
    Class c2 = Comparable.class;
    Class c3 = String[].class;
    Class c4 = int[][].class;
    Class c5 = ElementType.class;
    Class c6 = Override.class;
    Class c7 = int.class;
    Class c8 = void.class;
    Class c9 = Class.class;
    
    int[] a = new int[10];
    int[] b = new int[100];
    Class c10 = a.getClass();
    Class c11 = b.getClass();
    // 只要数组的元素类型与维度一样,就是同一个Class
    System.out.println(c10 == c11); // true
    System.out.println(c4 == c11); // false
    

13.3:类的加载与ClassLoader的理解

  1. 类的加载过程

    当程序主动使用某个类时,如果该类还未被加载到内存中,则系统会通过如下三个步骤来对该类进行初始化。
    在这里插入图片描述

    • 加载:将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后生成一个代表这个类的java.lang.Class对象,作为方法区中类数据的访问入口(即引用地址)。所有需要访问和使用类数据只能通过这个Class对象。这个加载的过程需要类加载器参与。
    • 链接:将Java类的二进制代码合并到JVM的运行状态之中的过程。
      1. 验证:确保加载的类信息符合JVM规范,例如:以cafe开头,没有安全方面的问题
      2. 准备:正式为类变量(static)分配内存并 设置类变量默认初始值的阶段,这些内存都将在方法区中进行分配
      3. 解析:虚拟机常量池内的符号引用(常量名)替换为直接引用(地址)的过程
    • 初始化:
      1. 执行类构造器<clinit>()方法的过程。类构造器<clinit>()方法是由编译期自动收集类中所有类变量的赋值动作和静态代码块中的语句合并产生的。(类构造器是构造类信息的,不是构造该类对象的构造器)。
      2. 当初始化一个类的时候,如果发现其父类还没有进行初始化,则需要先触发其父类的初始化
      3. 虚拟机会保证一个类的<clinit>()方法在多线程环境中被正确加锁和同步
  2. 什么时候会发生类初始化

    • 类 的主动引用 ( 一定会发生类的初始化 )
      1. 当虚拟机启动,先初始化main方法所在的类
      2. new一个类的对象
      3. 调用类的静态成员(除了final常量)和静态方法
      4. 使用java.lang.reflect包的方法对类进行反射调用
      5. 当初始化一个类,如果其父类没有被初始化,则先会初始化它的父类
    • 类的被动引用 ( 不会发生类的初始化 )
      1. 当访问一个静态域时,只有真正声明这个域的类才会被初始化
        • 当通过子类引用父类的静态变量,不会导致子类初始化
      2. 通过数组定义类引用,不会触发此类的初始化
      3. 引用常量不会触发此类的初始化(常量在链接阶段就存入调用类的常量池中了
  3. 类加载器的作用:

    • 类加载的作用:将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后在堆中生成一个代表这个类的java.lang.Class对象,作为方法区中类数据的访问入口。

    • 类缓存:标准的JavaSE类加载器可以按要求查找类,但一旦某个类被加载到类加载器中,它将维持加载(缓存)一段时间。不过JVM垃圾回收机制可以回收这些Class对象

    • 类加载器作用是用来把类(class)装载进内存的。JVM 规范定义了如下类型的类的加载器。
      在这里插入图片描述

      //对于自定义类,使用系统类加载器进行加载
      ClassLoader classLoader = ClassLoaderTest.class.getClassLoader();
      System.out.println(classLoader);
      //调用系统类加载器的getParent():获取扩展类加载器
      ClassLoader classLoader1 = classLoader.getParent();
      System.out.println(classLoader1);
      //调用扩展类加载器的getParent():无法获取引导类加载器
      //引导类加载器主要负责加载java的核心类库,无法加载自定义类的。
      ClassLoader classLoader2 = classLoader1.getParent();
      System.out.println(classLoader2);
      
      ClassLoader classLoader3 = String.class.getClassLoader();
      System.out.println(classLoader3);
      

13.4:创建运行时类的对象

  1. 调用Class对象的newInstance()方法

    • 类必须有一个无参数的构造器。
    • 类的构造器的访问权限需要足够。
    Class<Person> clazz = Person.class;
    Person obj = clazz.newInstance();
    System.out.println(obj);
    
  2. **调用Class类的getDeclaredConstructor(Class ...parameterTypes)**方法

    Class clazz = Person.class;
    Constructor con = clazz.getConstructor(String.class,Integer.class);
    Person p2 = (Person) con.newInstance("Peter",20);
    System.out.println(p2);
    

13.5:获取运行时类的完整结构

  1. 实现的全部接口

    • public Class<?>[] getInterfaces()
    Class clazz = Person.class;
    Class[] interfaces = clazz.getInterfaces();
    for(Class c : interfaces){
        System.out.println(c);
    }
    System.out.println();
    //获取运行时类的父类实现的接口
    Class[] interfaces1 = clazz.getSuperclass().getInterfaces();
    for(Class c : interfaces1){
        System.out.println(c);
    }
    
  2. 所继承的父类

    • public Class<? Super T> getSuperclass()
    Class clazz = Person.class;
    Class superclass = clazz.getSuperclass();
    System.out.println(superclass);
    
  3. 全部构造器

    • public Constructor<T>[] getConstructors():返回Class对象所表示的类的所有public构造方法。
    • public Constructor<T>[] getDeclaredConstructors():返回Class对象表示的类声明的所有构造方法。
    • public int getModifiers(): 取得修饰符
    • public String getName():取得方法名称
    • public Class<?>[] getParameterTypes(): 取得参数的类型
    Class clazz = Person.class;
    // getConstructors():获取当前运行时类中声明为public的构造器
    Constructor[] constructors = clazz.getConstructors();
    for(Constructor c : constructors){
        System.out.println(c);
    }
    System.out.println();
    // getDeclaredConstructors():获取当前运行时类中声明的所有的构造器
    Constructor[] declaredConstructors = clazz.getDeclaredConstructors();
    for(Constructor c : declaredConstructors){
        System.out.println(c);
    }
    
  4. 全部的方法

    • public Method[] getDeclaredMethods():返回此Class对象所表示的类或接口的全部方法
    • public Method[] getMethods():返回此Class对象所表示的类或接口的public的方法
    Class clazz = Person.class;
    //getMethods():获取当前运行时类及其所有父类中声明为public权限的方法
    Method[] methods = clazz.getMethods();
    for(Method m : methods){
        System.out.println(m);
    }
    System.out.println();
    //getDeclaredMethods():获取当前运行时类中声明的所有方法。(不包含父类中声明的方法)
    Method[] declaredMethods = clazz.getDeclaredMethods();
    for(Method m : declaredMethods){
        System.out.println(m);
    }
    
    • public Class<?> getReturnType():取得全部的返回值
    • public Class<?>[] getParameterTypes():取得全部的参数
    • public int getModifiers():取得修饰符
    • public Class<?>[] getExceptionTypes():取得异常信息
    Class clazz = Person.class;
    Method[] declaredMethods = clazz.getDeclaredMethods();
    for(Method m : declaredMethods){
        //1.获取方法声明的注解
        Annotation[] annos = m.getAnnotations();
        for(Annotation a : annos){
            System.out.println(a);
        }
        //2.权限修饰符
        System.out.print(Modifier.toString(m.getModifiers()) + "\t");
        //3.返回值类型
        System.out.print(m.getReturnType().getName() + "\t");
        //4.方法名
        System.out.print(m.getName());
        System.out.print("(");
        //5.形参列表
        Class[] parameterTypes = m.getParameterTypes();
        if(!(parameterTypes == null && parameterTypes.length == 0)){
            for(int i = 0;i < parameterTypes.length;i++){
                if(i == parameterTypes.length - 1){
                    System.out.print(parameterTypes[i].getName() + " args_" + i);
                    break;
                }
                System.out.print(parameterTypes[i].getName() + " args_" + i + ",");
            }
        }
        System.out.print(")");
        //6.抛出的异常
        Class[] exceptionTypes = m.getExceptionTypes();
        if(exceptionTypes.length > 0){
            System.out.print("throws ");
            for(int i = 0;i < exceptionTypes.length;i++){
                if(i == exceptionTypes.length - 1){
                    System.out.print(exceptionTypes[i].getName());
                    break;
                }
                System.out.print(exceptionTypes[i].getName() + ",");
            }
        }
        System.out.println();
    }
    
  5. 全部的Field

    • public Field[] getFields():返回此Class对象所表示的类或接口的publicField
    • public Field[] getDeclaredFields():返回此Class对象所表示的类或接口的全部Field
    Class clazz = Person.class;
    //获取属性结构
    //getFields():获取当前运行时类及其父类中声明为public访问权限的属性
    Field[] fields = clazz.getFields();
    for(Field f : fields){
        System.out.println(f);
    }
    System.out.println();
    //getDeclaredFields():获取当前运行时类中声明的所有属性。(不包含父类中声明的属性)
    Field[] declaredFields = clazz.getDeclaredFields();
    for(Field f : declaredFields){
        System.out.println(f);
    }
    
    • public int getModifiers(): 以整数形式返回此Field的修饰符
    • public Class<?> getType(): 得到Field的属性类型
    • public String getName(): 返回Field的名称
     Class clazz = Person.class;
    Field[] declaredFields = clazz.getDeclaredFields();
    for(Field f : declaredFields){
        //1.权限修饰符
        int modifier = f.getModifiers();
        System.out.print(Modifier.toString(modifier) + "\t");
        //2.数据类型
        Class type = f.getType();
        System.out.print(type.getName() + "\t");
        //3.变量名
        String fName = f.getName();
        System.out.print(fName);
        System.out.println();
    }
    
  6. 泛型相关

    • Type getGenericSuperclass():获取父类泛型类型
    Class clazz = Person.class;
    Type genericSuperclass = clazz.getGenericSuperclass();
    System.out.println(genericSuperclass);
    
    • ParameterizedType:泛型类型
    • getActualTypeArguments():获取实际的泛型类型参数数组
    Class clazz = Person.class;
    Type genericSuperclass = clazz.getGenericSuperclass();
    ParameterizedType paramType = (ParameterizedType) genericSuperclass;
    //获取泛型类型
    Type[] actualTypeArguments = paramType.getActualTypeArguments();
    // System.out.println(actualTypeArguments[0].getTypeName());
    System.out.println(((Class)actualTypeArguments[0]).getName());
    
  7. 类所在的包

    • Package getPackage()
    Class clazz = Person.class;
    Package pack = clazz.getPackage();
    System.out.println(pack);
    

13.6:调用运行时类的指定结构

  1. 调用指定方法

    通过反射,调用类中的方法,通过Method类完成。步骤:

    • 通过Class类的getMethod(String name,Class…parameterTypes)方法取得一个Method对象,并设置此方法操作时所需要的参数类型。
    • 之后使用Object invoke(Object obj, Object[] args)进行调用,并向方法中传递要设置的obj对象的参数信息。
    Class clazz = Person.class;
    //创建运行时类的对象
    Person p = (Person) clazz.newInstance();
    Method show = clazz.getDeclaredMethod("show", String.class);
    //保证当前方法是可访问的
    show.setAccessible(true);
    Object returnValue = show.invoke(p,"CHN");
    System.out.println(returnValue);
    System.out.println("*************如何调用静态方法*****************");
    Method showDesc = clazz.getDeclaredMethod("showDesc");
    showDesc.setAccessible(true);
    //如果调用的运行时类中的方法没有返回值,则此invoke()返回null
    //Object returnVal = showDesc.invoke(null);
    Object returnVal = showDesc.invoke(Person.class);
    System.out.println(returnVal);
    
  2. Object invoke(Object obj, Object … args)

    • Object对应原方法的返回值,若原方法无返回值,此时返回null
    • 若原方法若为静态方法,此时形参Object obj可为null
    • 若原方法形参列表为空,则Object[] argsnull
    • 若原方法声明为private,则需要在调用此invoke()方法前,显式调用方法对象的setAccessible(true)方法,将可访问private的方法。
  3. 调用指定属性

    • 在反射机制中,可以直接通过Field类操作类中的属性,通过Field类提供的set()get()方法就可以完成设置和取得属性内容的操作
    • public Field getField(String name) :返回此Class对象表示的类或接口的指定的publicField
    • public Field getDeclaredField(String name):返回此Class对象表示的类或接口的指定的Field
    • public Object get(Object obj): 取得指定对象obj上此Field的属性内容
    • public void set(Object obj,Object value) :设置指定对象obj上此Field的属性内容
    Class clazz = Person.class;
    //创建运行时类的对象
    Person p = (Person) clazz.newInstance();
    //1. getDeclaredField(String fieldName):获取运行时类中指定变量名的属性
    Field name = clazz.getDeclaredField("name");
    //2.保证当前属性是可访问的
    name.setAccessible(true);
    //3.获取、设置指定对象的此属性值
    name.set(p,"Tom");
    System.out.println(name.get(p));
    
  4. 关于setAccessible 方法的使用

    • Method和FieldConstructor对象都有setAccessible()方法。

    • setAccessible启动和禁用访问安全检查的开关。

    • 参数值为true则指示反射的对象在使用时应该取消Java语言访问检查。

      1. 提高反射的效率。如果代码中必须用反射,而该句代码需要频繁的被调用,那么请设置为true

      2. 使得原本无法访问的私有成员也可以访问

    • 参数值为false则指示反射的对象应该实施Java语言访问检查

13.7:动态代理

  1. 代理设计模式的原理

    • 使用一个代理将对象包装起来, 然后用该代理对象取代原始对象。任何对原始对象的调用都要通过代理。代理对象决定是否以及何时将方法调用转到原始对象上。
    • 动态代理是指客户通过代理类来调用其它对象的方法,并且是在程序运行时根据需要动态创建目标类的代理对象
    • 动态代理使用场合
      1. 调试
      2. 远程方法调用
    • 动态代理相比于静态代理的优点
      抽象角色中(接口)声明的所有方法都被转移到调用处理器一个集中的方法中处理,这样,我们可以更加灵活和统一的处理众多的方法
  2. Java动态关代理相关API

    • Proxy :专门完成代理的操作类,是所有动态代理类的父类。通过此类为一个或多个接口动态地生成实现类。
    • 提供用于创建动态代理类和动态代理对象的静态方法
      1. static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces):创建一个动态代理类所对应的Class对象
      2. static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h) :直接创建一个动态代理对象
  3. 静态代理

    interface ClothFactory{
        void produceCloth();
    }
    
    //代理类
    class ProxyClothFactory implements ClothFactory{
    
        private ClothFactory factory;//用被代理类对象进行实例化
        public ProxyClothFactory(ClothFactory factory){
            this.factory = factory;
        }
    
        @Override
        public void produceCloth() {
            System.out.println("代理工厂做一些准备工作");
            factory.produceCloth();
            System.out.println("代理工厂做一些后续的收尾工作");
        }
    }
    
    //被代理类
    class NikeClothFactory implements ClothFactory{
        @Override
        public void produceCloth() {
            System.out.println("Nike工厂生产一批运动服");
        }
    }
    
    public class StaticProxyTest {
        public static void main(String[] args) {
            //创建被代理类的对象
            ClothFactory nike = new NikeClothFactory();
            //创建代理类的对象
            ClothFactory proxyClothFactory = new ProxyClothFactory(nike);
            proxyClothFactory.produceCloth();
    
        }
    }
    
  4. 动态代理

    interface Human{
        String getBelief();
        void eat(String food);
    }
    
    //被代理类
    class SuperMan implements Human{
        @Override
        public String getBelief() {
            return "I believe I can fly!";
        }
        @Override
        public void eat(String food) {
            System.out.println("我喜欢吃" + food);
        }
    }
    
    class HumanUtil{
        public void method1(){
            System.out.println("====================通用方法一====================");
        }
        public void method2(){
            System.out.println("====================通用方法二====================");
        }
    }
    
    class ProxyFactory{
        //调用此方法,返回一个代理类的对象。解决问题一
        public static Object getProxyInstance(Object obj){//obj:被代理类的对象
            MyInvocationHandler handler = new MyInvocationHandler();
            handler.bind(obj);
            return Proxy.newProxyInstance(
                obj.getClass().getClassLoader(),obj.getClass().getInterfaces(),handler);
        }
    }
    
    class MyInvocationHandler implements InvocationHandler{
        private Object obj;//需要使用被代理类的对象进行赋值
        public void bind(Object obj){
            this.obj = obj;
        }
        //当我们通过代理类的对象,调用方法a时,就会自动的调用如下的方法:invoke()
        //将被代理类要执行的方法a的功能就声明在invoke()中
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            HumanUtil util = new HumanUtil();
            util.method1();
            //method:即为代理类对象调用的方法,此方法也就作为了被代理类对象要调用的方法
            //obj:被代理类的对象
            Object returnValue = method.invoke(obj,args);
            util.method2();
            //上述方法的返回值就作为当前类中的invoke()的返回值。
            return returnValue;
        }
    }
    
    public class ProxyTest {
        public static void main(String[] args) {
            SuperMan superMan = new SuperMan();
            //proxyInstance:代理类的对象
            Human proxyInstance = (Human) ProxyFactory.getProxyInstance(superMan);
            //当通过代理类对象调用方法时,会自动的调用被代理类中同名的方法
            String belief = proxyInstance.getBelief();
            System.out.println(belief);
            proxyInstance.eat("四川麻辣烫");
            System.out.println("*****************************");
            NikeClothFactory nikeClothFactory = new NikeClothFactory();
    ClothFactory proxyClothFactory = (ClothFactory)ProxyFactory.getProxyInstance(nikeClothFactory);
            proxyClothFactory.produceCloth();
        }
    }
    

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

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

相关文章

Spring Boot 实现多文件上传

文件上传 Spring Boot代码 代码结构&#xff1a; Controller层 package com.yqifei.upload.controller;import io.swagger.annotations.Api; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile;import javax.serv…

ETL --事实表

每一个事实表通过表的粒度来定义。事实表的粒度是事件度量的定义。我们必须至始至终按照度量如何在 现实世界中理解来规定事实表的粒度。 所有的事实表包含了一组关联到维表的外键&#xff0c;而这些维表提供了事实表度量的上下文。大多数的事实表还 包括了一个或者多个数值型…

小樽C++ 多章⑧ (贰) 指针与数组

目录 1.C中数组变量名某些情况可以看成是指针 2.C语言的scanf 输入语句&#xff0c;printf 输出语句 3.用指针来当动态数组 小樽C 多章⑧ (壹) 指针变量https://blog.csdn.net/weixin_44775255/article/details/129031168 小樽C 多章⑧ (叁) 指针与字符串、(肆) 函数与指针…

GitLab 凭借什么连续 3 年上榜 Gartner 应用程序安全测试魔力象限?听听 GitLab 自己的分析

本文来源&#xff1a;about.gitlab.com 作者&#xff1a;Sandra Gittlen 译者&#xff1a;极狐(GitLab) 市场部内容团队 应用程序安全测试&#xff08;AST&#xff09;对于应用程序研发来说&#xff0c;是一个正在快速发展并且十分重要的领域。DevOps 方法论提到&#xff1a;需…

Java基础之《dubbo(1)—dubbo基础入门》

一、为什么要使用dubbo 1、dubbo是什么 dubbo是一个分布式服务框架&#xff0c;致力于提供高性能和透明化的RPC远程服务调用方案&#xff0c;以及SOA服务治理方案。 2、dubbo有何特点 &#xff08;1&#xff09;远程通讯&#xff1a;提供透明化的远程方法调用&#xff0c;提供…

入门JAVA第十七天 Oracle的JDBC技术

一、数据库JDBC技术学习内容与方法 1.1 学习内容 &#xff08;1&#xff09; Oracle数据库 目前最好的关系型数据库。 基本的CRUD命令 SQL语句。select(R),update(U),delete(D),insert(C) &#xff08;2&#xff09; MySQL数据库 中小型项目非常好用的关系型数据库。 灵活&…

【零基础入门前端系列】—浮动(十八)

【零基础入门前端系列】—浮动&#xff08;十八&#xff09; 一、浮动的定义 float属性定义元素在哪个方向&#xff0c;以往这个属性总应用于图像&#xff0c;使得文本围绕在图像的周围&#xff0c;不过在CSS中&#xff0c;任何元素都可以浮动&#xff0c;浮动的元素会生成一…

【Git】P5 Git 远程仓库(3)pull 发生冲突

pull 发生冲突冲突在什么场景下发生&#xff1f;为什么要先 pull 再 push构建一个冲突场景初始开始操作&#xff1a;程序员2&#xff1a;程序员1&#xff1a;程序员2&#xff1a;发生冲突&#xff1a;查看冲突&#xff1a;解决冲突&#xff1a;冲突在什么场景下发生&#xff1f…

[手写OS]动手实现一个OS 之 准备工作以及引导扇区

[手写OS]动手实现一个OS之第一步-环境以及引导扇区 环境准备 一台可用计算机&#xff08;linux我不知道&#xff0c;我用的Windows&#xff09;汇编编译器NASM一个方便的软盘读写工具VirtualBox 汇编编译器NASM 官网地址&#xff1a;https://www.nasm.us/pub/nasm/snapshot…

java 中的equals()示例代码

Java中的equals()是十分重要的&#xff0c;和要区别开来简述public booleanequals(Object obj)作用&#xff1a;判断其他的对象是否和该对象相等其比较规则为&#xff1a;当参数obj引用的对象与当前对象为同一个对象时&#xff0c;就返回true,否则返回false.简单示例equals()方…

代码随想录算法训练营第二十二天 | 235. 二叉搜索树的最近公共祖先 、701.二叉搜索树中的插入操作、450.删除二叉搜索树中的节点

打卡第22天&#xff0c;平衡二叉树&#xff0c;难&#xff0c;难&#xff0c;难。 今日任务 235.二叉搜索树的最近公共祖先701.二叉搜索树中的插入操作450.删除二叉搜索树中的节点 235.二叉搜索树的最近公共祖先 给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。 百…

BeanFactory接口

目录 概述 接口方法 BeanFactory重要的子类 概述 BeanFactory是容器的顶层接口,也是spring最核心的容器,管理bean的核心方法都在BeanFactory接口中定义。像ApplicationContext接口,ConfigurableApplicationContext接口都间接继承BeanFactory接口,既ApplicationContext调用ge…

Wwise集成到unreal

1、Wwise集成到Unreal 1.1 安装必要的软件 安装unreal 5.1&#xff1b;安装Audiokinetic Launcher&#xff1b;集成版本是Wwise 2021.1.12.7973。Audiokinetic Launcher下载地址&#xff1a; https://www.audiokinetic.com/zh/thank-you/launcher/windows/?refdownload&pl…

Go语言学习的第三天--下部分(Gin框架的基础了解)

每天都会分享Go的知识&#xff0c;喜欢的朋友关注一下。每天的学习分成两部分基础&#xff08;必要的&#xff0c;基础不牢地动山摇&#xff09;&#xff0c;另一部分是Go的一些框架知识&#xff08;会不定时发布&#xff0c;因为小Wei也是一名搬砖人&#xff09;。但是可以保证…

哪个牌子的蓝牙耳机最好?质量最好的蓝牙耳机排行榜

随着蓝牙耳机的发展越来越快速&#xff0c;蓝牙耳机市场涌现出五花八门的产品&#xff0c;外观不同、性能不一。最近看到很多人问&#xff0c;哪个牌子的蓝牙耳机最好&#xff1f;接下来&#xff0c;我来给大家推荐几款质量最好的蓝牙耳机排行榜&#xff0c;一起来看看吧。 一…

电商使用CRM系统有什么好处,如何选择

数据显示&#xff0c;使用电商CRM客户管理系统后&#xff0c;企业销售额提高了87%&#xff0c;客户满意度提高了74%&#xff0c;业务效率提高了73%。要在竞争激烈的电商市场取得成功&#xff0c;与目标受众的有效沟通是有效的方法。下面说说什么是电商CRM系统&#xff1f;电商C…

如何使用ArcGIS生成剖面图(附练习数据)

1、概述地形剖面图指沿地表某一直线方向上的垂直剖面图&#xff0c;以显示剖面线上断面地势起伏状况。能够制作剖面图的软件有很多&#xff0c;作为GIS行业的老大&#xff0c;ArcGIS当然也是可以的&#xff0c;这里给大家详细介绍一下ArcGIS中制作剖面图的知识&#xff0c;希望…

2.详解DEBUG模式

文章目录DEBUG模式解决了两个问题四种开启DEBUG的方式第一种第二种第三种第四种DEBUG的PIN码可以在浏览器端调试代码使用&#xff08;不推荐使用&#xff0c;了解就可以&#xff09;DEBUG模式解决了两个问题 flask代码中如果出现了异常&#xff0c;我们在浏览器中不会提示具体…

图床搭建,使用typora上传

1. 准备gitee作为图床的仓库 新建仓库 准备仓库的私人令牌&#xff0c;后面配合使用 点击个人设置——》私人令牌 注意私人令牌&#xff0c;复制保存好&#xff0c;后面不能再看了 2. 准备PicGO&#xff0c;并进行相关配置 PicGo官方下载链接 下载安装好node.js,下载网址 安…

【银行测试】必看的四类题型:这可是最经典的一套题目了

目录&#xff1a;导读 一、根据题目要求写出具体LINUX操作命令 二、JMETER题目 三、根据题目要求写出具体SQL语句 四、测试案例设计题 金三银四面试面对大厂面试官提问&#xff0c;如何回答&#xff1a;花3天背完这100道软件测试面试题&#xff01;银行测试的offer还不是手…