Java ”框架 = 注解 + 反射 + 设计模式“ 之 反射详解

news2024/9/25 15:26:07

Java ”框架 = 注解 + 反射 + 设计模式“ 之 反射详解

在这里插入图片描述

每博一文案

无论幸福还是苦难,无论光荣还是屈辱,你都要自己遭遇与承受。
                       —————— 《平凡的世界》 孙少平
                       
多少美好的东西消失和毁灭了,世界还像什么事也没有发生,是的,生活在继续着。
可是,生活中的每一个却在不断地失去自己最珍贵的东西。
生活永远是美好的;人的痛苦却时时在发生。
                        —————— 《平凡的世界》
 我时常在想:第二天天一亮,一切都是新的。
                        —————— 《平凡的世界》

文章目录

  • Java ”框架 = 注解 + 反射 + 设计模式“ 之 反射详解
    • 每博一文案
    • 1. 反射的概念
    • 2. 动态语言 vs 静态语言
    • 3. Class 类
      • 4. Class 类中常用的方法
      • 5. 获取Class类的实例(三种方式)
        • 5.1 方式一:
        • 5.2 方式二:
        • 5.3 方式三:重点 ⭐⭐⭐
    • 6. 类加载器的理解
      • 6.1 类的加载过程:
      • 6.2 类加载的作用
        • 6.2.1 双亲委派
      • 6.3 ClassLoader 的使用
    • 7. 反射机制查看类
    • 8. 反射机制调用构造器
        • 8.1 通过反射调用私有构造器 🌟🌟🌟
    • 9. 反射机制获取类中的属性
        • 9.1 通过反射机制调用私有的属性(set/get) 🌟🌟🌟
    • 10. 反射机制获取类中的方法
        • 10.1 通过反射调用类中私有的方法
    • 11. 反射读取文件信息
    • 12. 反射机制获取到类中的注解信息
    • 13. 23种设计模式:动态代理模式
    • 14. 总结:
    • 15. 最后:

1. 反射的概念

  • Reflection (反射)是被视为 动态语言的关键 ,反射机制允许程序在执行期借助 Reflection API 取得任何类的内部信息,并能直接操作任意对象内部属性以及方法。
  • 反射机制有什么用 ???
    • 通过 Java语言中的反射机制可以操作字节码文件,优点类似于黑客。(可以读和修改字节码文件)
  • 在Java中加载完类之后,会在堆内存的方法区中就产生一个 Class 类型的对象(一个类只有一个 Class 对象(因为类在内存当中只会生产一份,一个类对应的.class字节码文件,所有对象共用,这仅用的一份)),这个对象就包含了完整的类的结构信息。我们可以通过这个对象看到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,所以,我们形象的称之为 : 反射。

在这里插入图片描述

  • 反射机制的相关类在 **java.lang.reflect.* **包下
    • java.lang.Class : 代表整个字节码,代表一个类型,代表整个类。
    • java.lang.reflect.Constructor : 代表字节码中的方法字节码,代表类中的方法。
    • java.lang.reflect.Field : 代表字节码中的构造方法字节码,代表类中的构造方法。
    • java.lang.reflect.Field : 代表字节码中的属性字节码,代表类中的成员变量(静态变量 + 实例变量)。
public class User {
     // 这个是时属性 Field
     int no;
     public User(){
         
     }

     // 这个是构造器 Constructor
     public User (int no) {
         
     }

     // 这个是方法 Method()
     public void setNo(int no) {
         
     }

     public int getNo() {
     	return no;
         
     }
}

2. 动态语言 vs 静态语言

  • 动态语言:

动态语言: 是一类在运行时可以改变其结构的语言:例如:新的函数,对象,甚至代码可以被引进,已有的函数可以被删除或是其他结构上的变化。通俗点说就是在运行时代码可以根据某些条件自身结构

主要动态语言: Object-C,C#,JavaScript,PHP,Python,Erlang

  • 静态语言:

与动态语言相应的,运行时结构不可变的语言就是静态语言。如:Java,C,C++

Java不是动态语言,但Java可以称之为 ”准动态语言“。即 Java有一定的动态性,我们可以利用反射机制,字节码操作获得类似动态语言的特性。Java的动态性让编程的时候更加灵活!

3. Class 类

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

在这里插入图片描述

除了int等基本类型外,Java的其他类型全部都是class(包括interface)。例如:

  • String
  • Object
  • Runnable
  • Exception
  • interface 接口
  • enum 枚举
  • annotaion 注解
  • void
  • [] 数组
  • class 外部类,成员(成员内部类,静态内部类),局部内部类,匿名内部类

在这里插入图片描述

  • Class 本身也是一个类。注意是 大写的 C ,小写的表示的就成了 class 关键字了。
  • Class 对象只能由系统建立对象。
  • 一个加载的类 在 JVM 中只会有一个 Class 实例,因为 一个 Class 对象对应的是一个加载到 JVM 中的一个 .class 字节码文件,一个类只会加载一份对应的 .class 字节码文件,所有这个类的对象共用。
  • 每个类的的实例都会记得自己是由哪个 Class 实例所生成的。
  • 通过 Class 可以完整地得到一个类中的所有被加载的结构。
  • Class 类是 Reflection 的根源,针对任何你想要:动态加载,运行的类,唯有先获得相应的 Class 对象为前提。

4. Class 类中常用的方法

Class 这里类是没有公开的构造器的,所以是无法通过 new 的方式创建该 Class 对象的。

方法名作用
public static Class<?> forName(String className) throws ClassNotFoundException返回指定类名 name(全类名)的 Class 对象
public T newInstance() throws InstantiationException, IllegalAccessException调用无参构造器,返回该 Class 中对应的类的对象实例。等同于 new 对象
注意该方法并不会创建对应类中的实例对象,但是会调用该类中的静态代码块
public String getName()返回此 Class 对象所表示的实体(类,接口,数组类,基本数据类型或 void) 的名称
public Class<? super T> getSuperclass()返回当前Class 对象的父类的 Class 对象
public Class<?>[] getInterfaces()获取当前Class 对象的接口
public ClassLoader getClassLoader()返回该类的类加载器
public Constructor<?>[] getConstructors() throws SecurityException返回该类中的所有的构造器存储到 (Constructor[ ])对象的数组中
public Method[] getDeclaredMethods() throws SecurityException返回该类中的所有方法存储到Method[ ]对象数组
public Field[] getDeclaredFields() throws SecurityException返回该类中所有的属性存储到 Field[ ]对象数组中

5. 获取Class类的实例(三种方式)

5.1 方式一:

前提: 若已知具体的类,通过类的 class属性获取,该方法最为安全可靠, 程序性能最高。任何类型(除了基本数据类型不行,但是可以用对应的包装类代替)都可以 .class

举例:

package blogs.blog11;

public class ReflectTest01 {

    public static void main(String[] args) {
        // <User> 泛型
        Class<User> userClass = User.class;
        System.out.println(userClass);

        // <String> 泛型
        Class<String> stringClass = String.class;
        System.out.println(stringClass);

        //这里自动装箱了
        Class integerClass = int.class;
        System.out.println(integerClass);


    }
}

class User {
    String name;

    public User() {

    }


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

在这里插入图片描述

5.2 方式二:

前提: 已知某个类的实例,调用该实例的 getClass()方法获取 Class对象。

举例:

package blogs.blog11;

public class ReflectTest01 {
    public static void main(String[] args) {
        User user = new User();
        // <? extends User >上界通配符
        Class<? extends User> aClass = user.getClass();
        System.out.println(aClass);

        String s = new String();
        Class<? extends String> aClass2 = s.getClass();
        System.out.println(aClass2);

        Integer integer = new Integer(123);
        Class aClass3 = integer.getClass();
        System.out.println(aClass3);

    }
}

class User {
    String name;

    public User() {

    }


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

在这里插入图片描述

5.3 方式三:重点 ⭐⭐⭐

前提: 已知一个类的全类名,且该类在类路径下,可通过 Class类的静态方 法forName()获取,可能抛出ClassNotFoundException该类不存在异常

全类名 : 指的是将包含一个类的包名,比如:java.lang.Stringblogs.blog11.User 这样含有指定包下的类名。

这里使用 fonName(全类名) 的原因是:不同的包下,会存在同名的类名,这样Java程序无法确定你需要的是哪个包下的类了。所以需要指定包下的类。

可以使用 IDEA 工具,快速复制获取到一个类的:全类名,如下:
在这里插入图片描述

在这里插入图片描述

举例:

package blogs.blog11;

public class ReflectTest01 {

    public static void main(String[] args) {
        try {
            Class<?> clazz = Class.forName("blogs.blog11.User");
            System.out.println(clazz);

            Class<?> clazz2 = Class.forName("java.lang.String");
            System.out.println(clazz2);

            Class clazz3 = Class.forName("java.lang.Integer");
            System.out.println(clazz3);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

    }
}

class User {
    String name;

    public User() {

    }


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

在这里插入图片描述

这第三种方式还又一个好处: 会自动调用该类中的静态代码块,因为静态代码块,是和类一起加载到内存当中的,当类加载到内存时,该静态代码就会自动执行其中的语句,仅仅只会执行一次,因为类也仅仅只会加载到内存一次。而 Class.forName() 类/静态方法,就是加载对应类的。该方法并不会创建该类的实例化对象,而是将该类加载到内存当中。

如果你只想让一个类的 “静态代码块” 执行的话,你可以怎么做?
class.forName(“该类的类名”);
这样类就加载,类加载的时候,静态代码块执行!!!

应用实例:JDBC 中加载对应数据库的驱动,执行其中的静态代码块。

举例: 如下,我们可以看到,其中的构造器并没有被调用(也就没有创建实例对象了,),但是静态代码块执行了。

package blogs.blog11;


/**
 * 获取Class 对象的三种方式
 */
public class ReflectTest01 {

    public static void main(String[] args) {
        // 会执行blogs.blog11.User 类中的静态代码块,而不会创建器实例化对象(不会调用该类的构造器)
        Class clazz = null;
        try {
            clazz = Class.forName("blogs.blog11.User");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        System.out.println(clazz);
    }
}

class User {
    String name;

    public User() {
        System.out.println("User 的构造器");
    }

    // 静态代码块
    static {
        System.out.println("User 类中的 静态代表块");
    }


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

在这里插入图片描述

注意:每个类对应的.class 字节码文件只有一份,所以对应的 每个类的 Class 对象也只有一个,所有的类对象共用

举例:

package blogs.blog11;


/**
 * 获取Class 对象的三种方式
 */
public class ReflectTest01 {
    public static void main(String[] args) {
        try {
            Class<?> clazz = Class.forName("java.lang.String");
            Class<String> clazz2 = String.class;
            System.out.println(clazz == clazz2);  // true
            
            // 因为每个类只有一个 Class 对象,类只会加载一次到内存当中,并只生成一份 .class 文件
            // 所有对象共用。

            Class clazz3 = Class.forName("blogs.blog11.User");
            Class clazz4 = User.class;
            System.out.println(clazz3 == clazz4);  // 返回 true 
            
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

    }
}

class User {
    String name;

    public User() {
        
    }



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

在这里插入图片描述

上述代码的内存图解析:

在这里插入图片描述

6. 类加载器的理解

在这里插入图片描述

6.1 类的加载过程:

当程序主动使用了某个类时,如果该类还未加载到内存中,则系统会通过,如下三个步骤对该类进行初始化:

在这里插入图片描述

  • 加载:

class 文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后生成一个代表这个类的 Java.lang.Class 对象,作为方法区中类数据的访问入口 (即引用地址)。所有需要访问和使用类数据只能通过 这个 Class 对象。这个加载的过程需要类加载器参与。

  • 链接:

Java 类的二进制代码合并到 JVM 的运行状态之中的过程。

  1. 验证:确保加载的类信息符合 JVM 规范,例如:以 cafe 开头,没有安全方面的问题。
  2. 准备:正式为类变量 (static)分配内存并设置类变量默认初始值的阶段,这些内存都将在方法区中进行分配。
  3. 解析:虚拟机常量池内的符号引用(常量名)替换为直接引用(地址)的过程。
  • 初始化:

执行 类构造器<clinit>() 方法的过程。类构造器<clinit>() 方法是由编译期自动收集类中所有类变量的赋值动作和静态代码块中的语句合并产生的 。(类构造器是构造类信息的,不是构造该类对象的构造器)。

当初始化一个类的时候,如果发现其父类还没有进行初始化,则需要先触发其父类的初始化。

虚拟机会保证一个类的<clinit> ()方法在多线程环境中被正确加锁和同步。

举例:

public class ClassLoadingTest {
	public static void main(String[] args) {
		System.out.println(A.m);
	}
}

class A {

  static {
	m = 300;
  }
  static int m = 100;
}

//第二步:链接结束后m=0
//第三步:初始化后,m的值由<clinit>()方法执行决定

// 这个A的类构造器<clinit>()方法由类变量的赋值和静态代码块中的语句按照顺序合并

产生,类似于

// <clinit>(){
// 	m = 300;
// 	m = 100;
// }

什么时候发生类初始化 ???

  • 类的主动引用(一定会发生类的初始化)

当虚拟机启动,先运行 main 方法所在的类,new 一个类的对象,调用类的静态成员(除了 final 常量)和静态方法。使用 java.lang.reflect 包的方法对类进行反射调用。当初始化一个类,如果其父类没有被初始化,则先会初始化它的父类。

  • 类的被动引用(不会发生类的初始化)

当访问一个静态域时,只有真正声明这个域的类才会被初始化,当通过子类引用父类的静态变量,不会导致子类初始化,

通过数组定义类引用,不会触发此类的初始化。引用常量不会触发此类的初始化(常量在链接阶段存入调用类的常量池中了。)

6.2 类加载的作用

在这里插入图片描述

类加载器: 负责将对应类生成的.class 文件(可能在磁盘上,也可能在网络上)加载到内存中,并为之在堆区中生成对应的 java.lang.Class对象,作为方法区中类数据的访问入口。

类加载器作用是用来把类(class)装载进内存的。JVM 规范定义了如下类型的 类的加载器。

在这里插入图片描述

类加载机制:

类加载器负责加载所有的类,系统为所有被载入内存中的类生成 java.lang.Class 实例对象。

一个类被载入 JVM 中,同一个类就不会再次载入了。正如一个对象有一个唯一的标识一样,一个载入 JVM 中的类也有一个唯一的标识。

在 Java 中,一个类用其全限定类名(包括包名和类名) 作为标识,但在JVM中,一个类用其全限定类名和其类加载器作为唯一标识。

例如:如果在 pg 的包中有一个名为 Person 的类,被类加载器 ClassLoader 实例 Loader 负责加载,则该 Person 类对应的 Class 对象在 JVM 中表示为(Person,pg,Loader)这意味着两个类加载器加载同名类:(Person,pg,Loader)和 (Person,pg,Loader)是不同的,它们所加载的类也是完全不同,互不兼容的。

JVM启动时,会形成由三个类加载器组成的初始类加载器层次结构:

  • 根类加载器(Bootstrap ClassLoader) :其负责加载Java的核心类,比如String,System 这些类,一般是在C:\Program Files\jdk1.8.0_101\jre\lib\rt.jar 路径下,rt.jar 中都是JDK最核心的类库
  • 扩展类加载器(Extension ClassLoader) :其负责加载 JRE 的扩展类库。一般是在:\Program Files\jdk1.8.0_101\jre\lib\*.jar路径下的
  • 系统类加载器(System ClassLoader) : 其负责加载 CLASSPATH 环境变量所指定的 JAR 包和类路径。

除了可以使用 Java 提供的类加载器之外, 开发者也可以实现自己的类加载器, 自定义的类加载器通过继承 ClassLoader 来实现。

  • 用户类加载器:用户自定义的加载器,以类加载器为父类

6.2.1 双亲委派

java中为了保证类加载的安全,使用了双亲委派机制。优先从启动类加载器中加载,这个称为"父",“父"无法加载到,再从扩展类加载器中加载。这个称为"母”。双亲委派,如果都加载不到,才会考虑从应用类加载器中加载,直到加载到为止。

如下是 :类加载器的层次结构图

在这里插入图片描述

双亲委派模型:如上图所示的类加载器之间的这种层次关系,就称为类加载器的双亲委派模型(Parent Delegation Model)。该模型要求除了顶层的启动类加载器外,其余的类加载器都应当有自己的父类加载器,子类加载器和父类加载器不是以继承 (Inheritance) 的关系实现,而是通过组合(Composition)关系来复用父加载器的代码。

双亲委派模型的工作过程为:

如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每个层次的加载器都是如此,因此所有的类加载请求都会传给顶层的启动类加载器 ,只有当父加载反馈自己无法完成该加载请求(该加载的搜索范围中没有找到对应的类)时,子加载器才会尝试自己去加载。

JVM 的类加载机制主要有如下三种:

  • 全盘负责: 所谓全盘负责,就是当一个类加载器负责加载某一个 Class 时,该 Class 所依赖的和引用的其他 Class 也将由该类加载器负责载入,除非显示使用另外一个类加载器来载入。
  • 父类委托: 所谓的父类委托,则是 先让 parent(父)类加载器试图加载该 Class,只有在父类加载器无法加载该类时,才尝试从自己的类路径中加载该类。
  • 缓存机制: 缓存机制将保证所有加载过的 Class 都会被缓存,当程序中需要使用某个 Class 时,类加载器先从缓存区中搜寻该 Class,只有当缓存区不存在该 Class 对象时,系统才会读取该类对应的二进制数据,并将其转换成 Class 对象,存入缓存区中。这就是为什么修改了 类后,必须重新启动 JVM ,程序所做的修改才会生效的原因。

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

6.3 ClassLoader 的使用

在这里插入图片描述

在这里插入图片描述

举例:

  • 获取一个系统类加载器 格式如下:
ClassLoader classLoader = 类名.class.getClassLoader(); 
package blogs.blog11;

public class ReflectTest02 {
    public static void main(String[] args) {
        // 对于自定义类,使用的是系统类加载进行加载的
        ClassLoader classLoader = ReflectTest02.class.getClassLoader();
        System.out.println(classLoader);
    }
}

在这里插入图片描述

  • 获取系统类加载器的父类加载器,即扩展类加载器

格式:

 // 首先通过: 类名.class 调用getClassLoader() 方法获取到该类的类加载器
// 再通过获取到的类加载器,调用 getParent()犯法,获取都器父类的类加载器。
ClassLoader classLoader = ReflectTest02.class.getClassLoader();
 ClassLoader classLoaderParent = classLoader.getParent();
package blogs.blog11;

public class ReflectTest02 {
    public static void main(String[] args) {
        // 对于自定义类,使用的是系统类加载进行加载的
        ClassLoader classLoader = ReflectTest02.class.getClassLoader();
        System.out.println(classLoader);

        // 调用对应类中父类中的getParent(): 获取扩展类加载器
        ClassLoader classLoaderParent = classLoader.getParent();
        System.out.println(classLoaderParent);
    }
}

在这里插入图片描述

  • 获取扩展类加载器的父类加强器,即引导类加载器
package blogs.blog11;

public class ReflectTest02 {
    public static void main(String[] args) {
        // 对于自定义类,使用的是系统类加载进行加载的
        ClassLoader classLoader = ReflectTest02.class.getClassLoader();

        // 调用对应类中父类中的getParent(): 获取扩展类加载器
        ClassLoader classLoaderParent = classLoader.getParent();

        // 调用扩展类加强器到getParent(): 是无法获取引导类加载器
        // 引导类加载器主要负责: 加载Java的核心类型,无法加载自定义类的
        // 所以一般是返回 null 的。我们无法获取到的。
        ClassLoader parent = classLoaderParent.getParent();
        System.out.println(parent);
    }
}

在这里插入图片描述

  • 测试当前类由哪个类加载器进行加载的
package blogs.blog11;

public class ReflectTest02 {
    public static void main(String[] args) {
        try {
            // Java内置的 String 类型
            ClassLoader classLoader = Class.forName("java.lang.String").getClassLoader();
            System.out.println(classLoader);

            // 自定义的类引用类型
            ClassLoader classLoader2 = Class.forName("blogs.blog11.Test").getClassLoader();
            System.out.println(classLoader2);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

class Test {
    public int num = 0;

    public Test() {

    }

    @Override
    public String toString() {
        return "Test{" +
                "num=" + num +
                '}';
    }
}

在这里插入图片描述

7. 反射机制查看类

通过反射机制获取到对应类中的 包信息,父类名,接口,修饰符,类名,泛型,类中实现的接口,父类中实现的接口

举例

package blogs.blog11;


import java.lang.reflect.Modifier;

public class ReflectTest03 {
    public static void main(String[] args) {
        // 1. 获取到对应类中的  Class 对象
        Class clazz = null;
        try {
            clazz = Class.forName("blogs.blog11.MyClass");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

        // 2. 通过该 Class 对象获取到对应类中的包名
        Package aPackage = clazz.getPackage();
        System.out.println(aPackage);

        // 3. 获取到该类中的修饰符
        int modifiers = clazz.getModifiers();  // 注意这里返回的是对应 修饰符中序号
        String s = Modifier.toString(modifiers);
        System.out.println(s);  // 如果默认是缺省的权限修饰符的话,返回的是 空

        // 4. 获取到该类的类名
        String name = clazz.getName();
        System.out.println(name);

        // 5. 获取该类中所继承的父类名
        String superName = clazz.getSuperclass().getName();
        System.out.println(superName);

        // 6. 获取该类中的所实现的接口.因为一个类中可以继承多个接口所以,返回的是Class[]类型的数组
        Class[] interfaces = clazz.getInterfaces();
        for (Class anInterface : interfaces) {
            System.out.println(anInterface);
        }
        
    }
}

@MyAnnotation
class MyClass<String> extends MySuperClass implements Comparable {

    @Override
    public int compareTo(Object o) {
        return 0;
    }
}


@interface MyAnnotation {
    String value() default "Hello";

}

class MySuperClass {  // protected

}

在这里插入图片描述

8. 反射机制调用构造器

  • public Constructor[] getConstructors():返回此 Class 对象所表示的类的所有public构造方法。

  • public Constructor[] getDeclaredConstructors():返回此 Class 对象表示的类声明的所有构造方法,包括私有的构造器

  • Constructor类中:

  • public int getModifiers() 获取到修饰符,返回的是对应Modifier 修饰符类中的 数值常量,可以通过 Modifier .toString(对应修饰符的数值) 获取对应修饰符的名称;

在这里插入图片描述

  • public String getName() 获取到方法名称

  • public Class[] getParameterTypes() 获取到参数的类型

通过反射获取到类中所有的构造器中的信息

举例:

package blogs.blog11;


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

/**
 * 反射处理构造器
 */
public class ReflectTest05 {
    public static void main(String[] args) {
        StringBuilder s = new StringBuilder(); // 用于字符串的拼接

        // 获取到对应的类中的 Class 对象.
        Class clazz = null;
        try {
            clazz = Class.forName("blogs.blog11.MyClass3");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

        // 获取该类中所有的构造方法
        // 因为一个类中可能会存在多个构造器,所有返回的是一个Constructor 的数组
        Constructor[] declaredConstructors = clazz.getDeclaredConstructors();

        for(Constructor c : declaredConstructors) {
            // 获取到构造方法的修饰符
            int modifiers = c.getModifiers();
            String s2 = Modifier.toString(modifiers);
            System.out.print(s2 + "\t");

            // 获取到构造器的名字,其实就是类名
            String simpleName = clazz.getSimpleName();
            System.out.println(simpleName);

            // 获取该构造方法中的实参列表
            // 因为一个方法中会存在多个参数。所以返回的是一个 Class[]数组
            Class[] parameterTypes = c.getParameterTypes();
            // 当该返回的参数数组,含有数据,不是 0 ,不为 null
            // 则说明该构造器是有参数的。
            if(!(parameterTypes.length == 0 || parameterTypes == null)) {
                // 则获取到该构造器中从参数
                for(Class aClass : parameterTypes) {
                    String type = aClass.getSimpleName();  // 实参类型
                    System.out.println(type);
                }
            }

            System.out.println();

        }

    }

}


class MyClass3 {
    String name;
    int age;

    public MyClass3() {
        // 定义无参构造器,用于反射调用
    }

    public MyClass3(String name) {
        this.name = name;
    }


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

在这里插入图片描述

8.1 通过反射调用私有构造器 🌟🌟🌟

  1. 通过反射调用无参构造器

使用方法:newInstance()

该方法的实际上是调用了类中的无参构造器,如果类中没有定义无参构造器,就无法通过该反射机制调用类中的构造器

举例:

package blogs.blog11;


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

/**
 * 反射处理构造器
 */
public class ReflectTest05 {
    public static void main(String[] args) {
        // 1.获取对应的Class 对象
        try {
            Class clazz =  Class.forName("blogs.blog11.MyClass3"); // 全类路径

            // 2.调用 newInstance();这个构造器
            // 该方法实际上是调用了类中的无参构造器
            Object o = clazz.newInstance();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

    }

}


class MyClass3 {
    String name;
    int age;

    public MyClass3() {
        System.out.println("无参构造器");
    }

    public MyClass3(String name) {
        this.name = name;
    }


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

在这里插入图片描述

注意: 如果该类中没有定义无参构造器,则无法反射,会异常:java.lang.InstantiationException。所以一般定义的类都会创建一个无参构造器,用于反射机制的获取。

在这里插入图片描述

  1. 通过反射机制调用私有的构造器

如果想要调用类中 权限范围小的,比如 private 私有的,必须使用 setAccessible(true) 方法将其中的值设置为 true

  • Method(方法) 和 Field(属性),Constructor(构造器) 都有 serAccessible() 方法
  • setAccessible() 启动和禁用访问安全检查的开关。
  • 参数值为 true 则指示反射的对象在使用时,应该取消 Java 语言访问检查。
    • 提高反射的效率。如果代码中必须用反射,而该代码需要频繁的被调用,那么请设置为 true
    • 可以使得原本无法访问的私有成员/方法/构造器也可以访问。
  • 参数值为 false 则指示反射的对象应该实施 Java语言访问检查。

举例: 这里访问私有的构造器,通过反射机制。

注意 需要将 setAccessible(true) 中设置为 true 是保证反射可以访问私有的构造器。

package blogs.blog11;


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

/**
 * 反射处理构造器
 */
public class ReflectTest05 {
    public static void main(String[] args) {
        // 1.获取对应的Class 对象
        try {
            Class clazz =  Class.forName("blogs.blog11.MyClass3"); // 全类路径

           
            // 第一步:先获取到这个类中有参数的构造器的Constructor对象
            // 注意对应调用的构造器中的参数类型要保持一致,用于后面的赋值
            Constructor declaredConstructor = clazz.getDeclaredConstructor(String.class, int.class);

            // 第二步: 设置将该获取到的Constructor(构造器)对象中的setAccessible(true)保证私有的可以被访问
            // 如果缺少这一步设置的话,是无法访问私有的构造器的
            declaredConstructor.setAccessible(true);

            // 第三步: 传构造器中对应的参数,调用构造器,创建对象
            // 通用是调用 newInstance() 方法调用其中类对应的构造器。注意这里我们调用的是带了参数的构造器了。
            Object tom = declaredConstructor.newInstance("Tom", 18);
            System.out.println(tom);


        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }

    }

}


class MyClass3 {
    String name;
    int age;

    public MyClass3() {
    }

    private MyClass3(String name) {
        this.name = name;

    }


    private MyClass3(String name, int age) {
        this.name = name;
        this.age = age;
    }

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

在这里插入图片描述

注意: 如果缺少了。setAccessible(true); 这一步设置的话,是无法调用到私有的构造器的,会报该异常:java.lang.IllegalAccessException: 非法访问异常。

在这里插入图片描述

9. 反射机制获取类中的属性

  • public Field[] getFields() : 返回此Class对象所表示的类或接口的public的Field。
  • public Field[] getDeclaredFields() : 返回此Class对象所表示的类或接口的全部Field。
  • public int getModifiers() : 以整数形式返回此Field的修饰符.同理可以使用 Modifier .toString(对应修饰符的数值) 获取对应修饰符的名称;
  • public Class getType() 得到Field的属性类型
  • public String getName() 返回Field的名称。

举例:

package blogs.blog11;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;

public class ReflectTest06 {
    public static void main(String[] args) {
        // 这里我们创建一个字符串,用于字符串的拼串
        StringBuilder s = new StringBuilder();
        Class clazz = null;

        try {
            // 1. 获取到该类的Class 字节码对象
            clazz = Class.forName("blogs.blog11.MyClass6");

            // 2. 实例化该对象:因为这里我们已经知道需要实例读的对象是什么了,所以可以直接强制转换
            // newInstance()方法调用的是该类中的无参构造器,所以无参构造器必须要有
            MyClass6 myClass = (MyClass6) clazz.newInstance();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

        // 将该类中的 修饰符 类名  添加到该字符串中
        s.append(Modifier.toString(clazz.getModifiers()) + "class \t" + clazz.getSimpleName() + "\t{ \n");

        // 3. 获取到该类中的所有属性,包括private 私有的
        // 因为一个类中的属性是多个,所以返回的是 Field 属性数组
        Field[] fields = clazz.getDeclaredFields();

        for (Field f : fields) {
            s.append("\t");
            // 获取到该属性中的修饰符
            int modifiers = f.getModifiers();
            String bedeck = Modifier.toString(modifiers);
            s.append(bedeck);  //添加到字符串中
            s.append(" ");

            // 获取到该属性的类型
            Class<?> type = f.getType();
            // 获取到该属性的类型名称
            String nameType = type.getName();
            s.append(nameType); // 添加
            s.append(" ");

            // 获取到属性名
            String nameAttribute = f.getName();
            s.append(nameAttribute); // 添加
            s.append("\n");

        }

        s.append("}");
        System.out.println(s);
    }
}


class MyClass6 {

    public String name;
    protected int age;
    private int id;
    boolean sex;

    public MyClass6() {
        // 定义无参构造器,用于反射调用
    }


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

在这里插入图片描述

9.1 通过反射机制调用私有的属性(set/get) 🌟🌟🌟

在反射机制中,可以直接通过Field类操作类中的属性,通过Field类提供的set()get()方法就可以完成设置和取得属性内容的操作。

  • public Field getField(String name) :返回此Class对象表示的类或接口的指定的 public 的Field 属性的字节码对象。只能获取到 public的属性

  • public Field getDeclaredField(String name) :返回此Class对象表示的类或接口的 指定的Field属性的字节码对象,所以的都可以,包括 private 私有的

  • public Object get(Object obj) :取得指定对象obj上此Field的属性内容,获取到该对应对象中指明的属性的值

  • public void set(Object obj,Object value) :设置指定对象obj上此Field的属性内容,修改该对应对象中指明的属性的值

核心代码:

 //获取需要访问的属性的 Field 对象((根据属性的名称来获取Field)) : 通过属性名找到你想要set/get的属性的 Field (属性)字节码对象
Field fieldName = clazz.getDeclaredField("name");


 // 通过将为setAccessible(true)反射可以访问私有的属性
 fieldName.setAccessible(true);


// 使用 set()对属性赋值
// set(哪个对象中的属性,赋的值)
 fieldName.set(myClass,"Tom"); // 注意所赋的值,需要符合该类中的这个属性的定义,不然报错


// 6.读取属性的值:
// 两个要素:MyClass对象的 name 属性的值,哪个对象中的属性值。获取到该对象中对应的属性值
Object o = fieldName.get(myClass);
System.out.println(o);

举例:

package blogs.blog11;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;

public class ReflectTest06 {
    public static void main(String[] args) {
        try {
            // 1. 创建该类的 Class 字节码对象
            Class clazz = Class.forName("blogs.blog11.MyClass6");

            // 2. 获取该类的实例化对象: 因为这里我们已经知道需要实例化的对象是什么了,所以可以直接强制转换
            // newInstance()实际调用的是该类中的无参构造器,所以该类必须要有无参构造器
            MyClass6 myClass = (MyClass6) clazz.newInstance();

            // 3. 获取需要访问的属性的 Field 对象((根据属性的名称来获取Field))
            Field fieldName = clazz.getDeclaredField("name");

            // 4. 给MyClass6 对象中的 私有 name 属性赋值
/*
        虽然使用了反射机制,但是三要素还是缺一不可:
        要素1: Object 对象,修改赋值的对象上的属性值
        要素2: no 属性
        要素3: 100属性值
         */
            // 通过将为setAccessible(true)反射可以访问私有的属性
            fieldName.setAccessible(true);

            // 5.使用 set()对属性赋值
            // set(对应的赋值的对象,该对象中想要赋的属性值)
            fieldName.set(myClass,"Tom"); // 注意所赋的值,需要符合该类中的这个属性的定义,不然报错


            // 6.读取属性的值:
            // 两个要素:MyClass 对象的 name 属性的值
            Object o = fieldName.get(myClass);
            System.out.println(o);

            System.out.println(myClass);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }


    }
}


class MyClass6 {

    private String name;
    protected int age;
    private int id;
    boolean sex;

    public MyClass6() {
        // 定义无参构造器,用于反射调用
    }


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

在这里插入图片描述

10. 反射机制获取类中的方法

  • public Method[] getDeclaredMethods():返回此Class对象所表示的类或接口的全部方法,包括私有的方法

  • public Method[] getMethods() :返回此Class对象所表示的类或接口的public的方法

  • public Class getReturnType():获取到该方法中的返回值类型

  • public Class[] getParameterTypes():获取到该方法全部的参数类型,

  • public int getModifiers():获取到该方法的修饰符,返回的是对应Modifier 修饰符类中的 数值常量,可以通过 Modifier .toString(对应修饰符的数值) 获取对应修饰符的名称;

  • public Class[] getExceptionTypes(): 获取到该方法的异常信息

举例:

package blogs.blog11;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

public class ReflectTest07 {
    public static void main(String[] args) {

        // 定义一个字符串,用于字符串的拼接
        StringBuilder stringBuilder = new StringBuilder();
        // 1. 获取到该类的 Class 字节码对象
        Class clazz = null;
        try {
            clazz = Class.forName("blogs.blog11.MyClass7");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        // 首先将该类名,信息添加到字符串中
        stringBuilder.append(Modifier.toString(clazz.getModifiers()) + "\tclass\t" + clazz.getSimpleName() +
                "\t{\n");

        // 2. 获取到该类中的所有的方法,包括private 私有的
        Method[] methods = clazz.getDeclaredMethods();

        // 遍历该 Method 数组中的值
        for (Method m : methods) {
            stringBuilder.append("\t");

            // 获取到该方法中的修饰符 public
            int modifiers = m.getModifiers();
            String s = Modifier.toString(modifiers);
            stringBuilder.append(s);  // 添加上
            stringBuilder.append(" ");

            // 获取到该方法的返回值类型
            Class<?> returnType = m.getReturnType();
            // 获取到该方法的返回值类型名
            String nameType = returnType.getName();
            stringBuilder.append(nameType);  // 添加上
            stringBuilder.append(" ");

            // 获取到该方法名称
            String name = m.getName();
            stringBuilder.append(name);
            stringBuilder.append(" ( ");

            // 获取到该方法的参数列表:
            // 因为方法中可能存在多个参数列表,也可能一个也没有
            Class<?>[] parameterTypes = m.getParameterTypes();
            // 如果该方法从实参数是 0 个,就不要获取该方法中的实参类型了
            if (!(parameterTypes.length == 0 || parameterTypes == null)) {
                // 有遍历该实参列表
                for (Class<?> p : parameterTypes) {
                    // 获取到该实参的类型
                    String simpleName = p.getSimpleName();
                    stringBuilder.append(simpleName);  // 添加上
                    stringBuilder.append(",");
                }


                // 删除指定下标位置上的字符,这里删除的是:最后一个参数多了一个 “,”逗号
                stringBuilder.deleteCharAt(stringBuilder.length() - 1);

            }
            stringBuilder.append("){}\n");
        }
        stringBuilder.append("}");
        System.out.println(stringBuilder);
    }
}


final class MyClass7 {
    public MyClass7() {
        // 定义无参构造器,用于反射调用
    }

    public static void fun2() {

    }

    static final void fun4(String s, double d) {

    }

    private int fun1(int num) {
        return num;
    }

    private String fun3() {
        return "Hello World";
    }
}

在这里插入图片描述

10.1 通过反射调用类中私有的方法

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

1.通过Class类的getMethod(String name,Class…parameterTypes)方法取得 一个Method 方法字节码对象,并设置此方法操作时所需要的参数类型。之后使用Object invoke(Object obj, Object[] args)进行调用,并向方法中传递要设置的obj对象的参数信息。

Object invoke(Object obj, Object … args) 方法的说明:

  • invoke()方法的返回值,就是调用的原方法的返回值,若原方法无返回值,此时返回null

  • 若原方法若为静态方法,此时形参Object obj 可为null

  • 若原方法形参列表为空,则 Object[] args 可为null

  • 若原方法声明为 private,则需要在调用此 **invoke()**方法前,显式调用 方法对象的 setAccessible(true)方法,将可访问private的方法。

举例:

package blogs.blog11;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

public class ReflectTest07 {
    public static void main(String[] args) {
        try {
            // 1. 获取到该类的 Class 字节码对象
            Class clazz = Class.forName("blogs.blog11.MyClass7");

            //2. 创建对应的类对象,通过反射调用其无参构造器
            // 这里因为我们知道,实例化的对象是什么类型的可以直接强制转换
            MyClass7 myClass = (MyClass7) clazz.newInstance();


            // 3. 获取指定想要调用的方法中的 Method 方法的字节码对象
            // 方法存在重载:所以想要确定一个方法的要素是:方法名,实参类型
            // 获取到 fun1()方法的 Method 对象
            Method method = clazz.getDeclaredMethod("fun1");

            // 获取到 fun3(String s ,int num) 方法的 Method 对象
            Method method2 = clazz.getDeclaredMethod("fun3", String.class, int.class); // 注意需要和实际调用的方法中的类型保持一致

            // 4. 设置为 true 保证反射可以调用私有的方法
            method.setAccessible(true);
            method2.setAccessible(true);

            // 5.
            /*
            调用方法
            1. fun1() fun2() 方法名
            2. myClass调用哪个对象中的方法
            3. 方法中的参数列表的赋值:“Tom" ,999实参
            4. o,o2 返回类型
            该 invoke()方法的返回类型就是,对应反射机制调用的对象中的方法的返回类型
         */

            // 调用 method 中的 fun1() 方法
            // 因为我们的 fun1()方法是没有实参的所以传个 null 就可以了
            Object o = method.invoke(myClass, null);
            System.out.println(o); // 因为fun1()方法是没有返回值的所以返回的是 null

            // 调用 method2 中的fun2()方法
            Object o2 = method2.invoke(myClass, "Tom", 999);
            System.out.println(o2); // invoke()方法的返回值,就是 fun2()方法的返回值
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }

    }
}


final class MyClass7 {
    public MyClass7() {
        // 定义无参构造器,用于反射调用
    }

    public static void fun2() {

    }

    static final void fun4(String s, double d) {

    }

    private void fun1() {
        System.out.println("你好世界");
    }

    private String fun3(String s, int num) {
        return "Hello World" + num;
    }
}

在这里插入图片描述

11. 反射读取文件信息

在这里插入图片描述

// 使用反射进行获取
    @Test
    public void test3() {
        // Map 集合
        Properties properties = new Properties();

        ClassLoader classLoader = ClassLoaderTest.class.getClassLoader();
        // 注意: 该文件路径需要在 src 目录下才行,注意: 再包下时需要说明包路径下的位置
        // 读取配置文件的方式二: 使用ClassLoader
        // 配置文件默认识别为: 当前module的src 下
        InputStream resourceAsStream = classLoader.getResourceAsStream("day28/jdbc.properties");

        try {
            properties.load(resourceAsStream);
        } catch (IOException e) {
            e.printStackTrace();
        }

        String user = properties.getProperty("user");
        System.out.println(user);
        String password = properties.getProperty("password");
        System.out.println(password);

    }

在这里插入图片描述

12. 反射机制获取到类中的注解信息

想要让反射可以读取到注解中的信息,则该反射中的元注解必须是: @Retention(RetentionPolicy.RUNTIME) 才行。

举例: 这里我们使用反射读取到 fun() 方法中的 注解中的 value 属性值:

注解

package blogs.blog10;


import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@Retention(RetentionPolicy.RUNTIME)  // 生命周期在:运行期,才可以被反射读取到
public @interface MyAnnotation {
    String value() default "Tom";

}

package blogs.blog10;


import java.lang.reflect.Method;

public class AnnotationTest {

    public static void main(String[] args) {
        Method method = null;

        try {
            // 获取类加载器,类对象
            Class clazz = Class.forName("blogs.blog10.AnnotationTest"); // 全类路径名

            // 获取 fun()方法
            method = clazz.getDeclaredMethod("fun");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }

        // 判断该方法是否存在该注解,存在才读取该注解上的属性值
        if(method.isAnnotationPresent(MyAnnotation.class)) {
            // 获取该注解对象
            MyAnnotation annotation = method.getAnnotation(MyAnnotation.class);
            // // 获取该注解的属性值,就像对象.属性一样
            String value = annotation.value();
            System.out.println(value);
        }

    }

    @MyAnnotation("lihua")
    public void fun() {
        int num = 0;
    }

}




在这里插入图片描述

13. 23种设计模式:动态代理模式

想要了解:动态代理模式的大家可以移步至:🔜🔜🔜 https://www.liaoxuefeng.com/wiki/1252599548343744/1264804593397984

14. 总结:

  1. 反射的作用:通过 Java语言中的反射机制可以操作字节码文件,优点类似于黑客。(可以读和修改字节码文件),可以调用私有的构造器,set/get 属性,调用私有的方法。
  2. 获取 Class 类实例的三种方式,重点掌握方式三:该方式会自动调用该类中的静态代码块,因为静态代码块,而不会实例化类的,可以应用于 JDBC 加载数据库驱动,执行其中的静态代码块,而不实例化。
  3. 类加载的过程。
  4. 类加载器的作用: 负责将对应类生成的.class 文件(可能在磁盘上,也可能在网络上)加载到内存中,并为之在堆区中生成对应的 java.lang.Class对象,作为方法区中类数据的访问入口。
  5. 双亲委派的原理。类加载器的种类,层次
  6. 反射机制中的:调用私有的构造器,调用私有的属性,调用私有的方法,获取注解中的属性值。

15. 最后:

👍👍👍 ✏️✏️✏️✏️✏️✏️✏️✏️✏️✏️ 感谢以下大佬提供的参考资料 ✏️✏️✏️✏️✏️✏️✏️✏️✏️✏️ 👍👍👍

【1】: https://fighter3.blog.csdn.net/article/details/103758938

【2】:https://www.liaoxuefeng.com/wiki/1252599548343744/1264804593397984

限于自身水平,其中存在的错误,希望大家给予指教,韩信点兵——多多益善 。谢谢大家,江湖再见,后会有期 !!!
在这里插入图片描述

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

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

相关文章

Head First设计模式---5.单例模式

2.2单例模式 单例模式运用的可能比其他几种简单&#xff0c;通俗点理解就是&#xff0c;我这个对象只能存在一个。 问题 保证一个类只有一个实例。 为什么会有人想要控制一个类所拥有的实例数量&#xff1f; 最常见的原因是控制某些共享资源 &#xff08;例如数据库或文件&am…

STL库实践

STL库实践1 写在最前面的话1.1 容器(container)1.2 算法(algorithm)1.3 迭代器(iterator)1.4 仿函数1.5 适配器1.6 空间配置器1.7 stl初试牛刀2 容器之 string2.1 string 构造函数2.2 string基本赋值操作2.3 string存取字符操作2.4 string拼接操作2.5 string查找和替换2.6 stri…

Windows安装 MySQL5.7(超详细)

Windows安装 MySQL5.7安装包下载安装步骤解压添加环境变量初始化MySQL初始登录MySQL并修改root密码注意&#xff0c;截至2023年2月23日&#xff0c;MySQL所有版本不提供ARM芯片架构的Windows版本(8.0.12开始支持Red Hat系统的ARM版本)&#xff0c;所以ARM架构的Windows无法安装…

什么是软件测试中的人工智能?

什么是软件测试中的人工智能&#xff1f;近日&#xff0c;由人工智能实验室OpenAI开发的全新“对话机器人”ChatGPT在各大中外媒体平台掀起了一阵狂热之风。从正式发布到风靡全球&#xff0c;不过100天&#xff0c;用户已突破1亿&#xff0c;成为史上用户增长最快的应用程序。C…

图解 | 工信部网络与数据安全57项“执法事项清单”来了

2023年2月&#xff0c;工业和信息化部根据《工业和信息化部全面推行行政执法公示制度执法全过程记录制度重大执法决定法制审核制度暂行实施方案》的相关要求&#xff0c;结合有关法律法规依据的修订情况及行政执法工作实际&#xff0c;编制发布了《工业和信息化部行政执法项目清…

高效制作知识库的软件工具,这6个都很不错哦!

任何工作流程都离不开文档管理&#xff0c;因此文档管理也是企业数字化转型中的重要环节。面对复杂的业务流程、频繁的文档编辑任务和跨区域的文件共享需求&#xff0c;优秀的文档管理体系能够帮助企业实现安全的文档存储&#xff0c;高效的文档搜索&#xff0c;便捷的文档协作…

CVE-2023-23752 Joomla未授权访问漏洞分析

漏洞概要 Joomla 在海外使用较多&#xff0c;是一套使用 PHP 和 MySQL 开发的开源、跨平台的内容管理系统(CMS)。 Joomla 4.0.0 至 4.2.7 版本中的 ApiRouter.php#parseApiRoute 在处理用户的 Get 请求时未对请求参数有效过滤&#xff0c;导致攻击者可向 Joomla 服务端点发送包…

大数据框架之Hadoop:MapReduce(三)MapReduce框架原理——MapTask工作机制

MapTask工作机制如下图所示。 &#xff08;1&#xff09;Read阶段&#xff1a;MapTask通过用户编写的RecordReader&#xff0c;从输入InputSplit中解析出一个个key/value。 &#xff08;2&#xff09;Map阶段&#xff1a;该节点主要是将解析出的key/value交给用户编写map()函数…

SDL2 简明教程(五):OpenGL 绘制

系列文章目录 SDL2 简明教程&#xff08;一&#xff09;&#xff1a;使用 Cmake 和 Conan 构建 SDL2 编程环境 SDL2 简明教程&#xff08;二&#xff09;&#xff1a;创建一个空的窗口 SDL2 简明教程&#xff08;三&#xff09;&#xff1a;显示图片 SDL2 简明教程&#xf…

DC220V冲击继电器RCJ-3

系列型号 RCJ-2型冲击继电器&#xff1b; RCJ-2/48VDC冲击继电器 RCJ-2/110VDC冲击继电器 RCJ-2/220VDC冲击继电器 RCJ-2/100VAC冲击继电器 RCJ-2/127VAC冲击继电器 RCJ-2/220VAC冲击继电器 RCJ-3/220VAC冲击继电器 RCJ-3型冲击继电器 RCJ-3/127VAC冲击继电器 RCJ-3/100VAC冲…

Jenkins集成Allure报告

Jenkins集成Allure报告 紧接上文&#xff1a;Jenkins部署及持续集成——傻瓜式教程 使用Allure报告 1、在插件库下载Allure插件Allure Jenkins Plugin 2、在构建后操作中加入allure执行的报告目录&#xff08;相对于项目的路径&#xff09; 3、run.py代码改成如下 import p…

2023年白酒行业研究报告

第一章 行业概况 白酒是中国传统的酿酒业之一&#xff0c;历史悠久&#xff0c;源远流长。白酒指以高粱等粮谷为主要原料&#xff0c;以大曲、小曲或麸曲及酒母等为糖化发酵剂&#xff0c;经蒸煮、糖化、发酵、蒸馏、陈酿、勾兑而制成的&#xff0c;酒精度(体积分数)在18%-68%…

【Spark分布式内存计算框架——离线综合实战】3. SparkSession 工具类、广告数据 ETL

SparkSession 工具类 在项目工程【cn.itcast.spark.utils】包下创建工具类&#xff1a;SparkUtils&#xff0c;专门构建SparkSession实例对象&#xff0c;具体步骤如下&#xff1a; 构建SparkConf对象、设置通用相关属性判断应用是否本地模式运行&#xff0c;如果是设置值mas…

04 DC-DC变换器(DCDC Converter / Switched-mode Power Supply)简介

文章目录0、DC-DC变换器概述1、DC-DC变换器的基本结构BuckBoostBuck-BoostBoost-Buck小结2、换流与特性分析分析Buck电路分析Boost电路分析Buck-Boost电路&#xff08;前级Buck后级Boost&#xff09;分析Cuk电路&#xff08;前级Boost后级Buck组合&#xff09;小结3、换流与特性…

OAuth2在项目的应用-扫码登录

业界提供了OAUTH的多种实现如PHP、JavaScript&#xff0c;Java&#xff0c;Ruby等各种语言开发包&#xff0c;Oauth协议目前发展到2.0版本&#xff0c;1.0版本过于复杂&#xff0c;2.0版本已得到广泛应用。参考&#xff1a;https://baike.baidu.com/item/oAuth/7153134?fralad…

2020蓝桥杯真题含2天数(填空题) C语言/C++

题目描述 本题为填空题&#xff0c;只需要算出结果后&#xff0c;在代码中使用输出语句将所填结果输出即可。 小蓝特别喜欢 2&#xff0c;今年是公元 2020 年&#xff0c;他特别高兴&#xff0c;因为每天日历上都可以看到 2。 如果日历中只显示年月日&#xff0c;请问从公元 …

转录组丨limma差异表达分析,绘制火山图和热图

limma差异表达分析 本篇笔记的内容是在R语言中利用limma包进行差异表达分析&#xff0c;主要针对转录组测序得到的基因表达数据进行下游分析&#xff0c;并将分析结果可视化&#xff0c;绘制火山图和热图 文章目录limma差异表达分析[toc]环境部署与安装输入数据准备差异表达分析…

java JMM 内存屏障

内存屏障的目的 每个CPU都会有自己的缓存&#xff08;有的甚至L1,L2,L3&#xff09;&#xff0c;缓存的目的就是为了提高性能&#xff0c;避免每次都要向内存取。但是这样的弊端也很明显&#xff1a;不能实时的和内存发生信息交换&#xff0c;分在不同CPU执行的不同线程对同一…

你真的需要文档管理软件吗?

什么是文档管理软件&#xff1f; 文档管理软件 (DMS) 是一种数字解决方案&#xff0c;可帮助组织处理、捕获、存储、管理和跟踪文档。 通过严格管理您的关键业务信息&#xff0c;您可以开发以稳定、可预测、可衡量的方式启动、执行和完成的流程。 如果没有功能齐全的文档管理软…

堆-优先队列priorityqueue原理和应用

java中PriorityQueue优先队列 优先队列 &#xff1a;底层是用数组实现的二叉堆&#xff0c;因为堆通常分为大顶堆或者小顶堆&#xff0c;所以优先队列可以获取每次出来的都是最大或者最小元素&#xff08;对象可以实现比较器&#xff0c;Java优先级队列默认每次取出来的为最小元…