• java反射所需要了解的基本知识点


    反射概述

    反射:通过运行时操作元数据或对象,Java 可以灵活地操作运行时才能确定的信息指程序可以访问、检测和修改它本身状态或行为的一种能力。
    其相关类在下面这个包里面:java.lang.reflect.*;

    反射机制相关的重要的类:

    1. java.lang.Class 代表整个字节码。代表一个类型,代表整个类。
    2. java.lang.reflect.Method 代表字节码中的方法字节码。代表类中的方法。
    3. java.lang.reflect.Constructor 代表字节码中的构造方法字节码。代表类中的构造方法
    4. java.lang.reflect.Field 代表字节码中的属性字节码。代表类中的成员变量(静态变量+实例变量)

    必须先获得Class才能获取Method、Constructor、Field。

    Class文件(反射原理)

    每个类都会产生一个对应的Class对象,一般保存在.class文件中,Class文件是一组以8字节为基础单位的二进制文件

    Class对象仅在需要的时候才会加载,static初始化是在类加载时进行的

    类加载时,类加载器首先会检查这个类的Class对象是否已被加载过,如果尚未加载,默认的类加载器就会根据类名查找对应的.class文件。

    • 虚拟机为每种类型管理一个独一无二的Class对象,每个类(型)都有一个Class对象
    • 运行程序时,Java虚拟机(JVM)首先检查所要加载的类对应的Class对象是否已经加载
    • 如果没有加载,JVM就会根据类名查找.class文件,并将其Class对象载入
      在这里插入图片描述

    特点

    1. java八个基本数据类型和关键字 void 也都对应一个 Class 对象

      • 八个基本数据类型:boolean、byte、char、short、int、long、float 和 double
    2. 每个数组属于被映射为 Class 对象的一个类,所有具有相同元素类型和维数的数组都共享该 Class 对象

    3. 一般某个类的Class对象被载入内存,它就用来创建这个类的所有对象。

    该方法的执行会导致类加载,类加载时,静态代码块执行

    推荐阅读:https://juejin.cn/post/6917050648563777544#comment

    获取Class对象

    Object类的getClass()方法

    Person person = new Person();  
    Class<?> class1 = person.getClass();
    System.out.println( class1 );
    
    • 1
    • 2
    • 3

    运行结果

    class com.app.Person
    
    • 1

    Class类的中静态forName()方法

    Class<?> class2 = Class.forName("com.app.Person");
    System.out.println( class2 );
    
    • 1
    • 2

    运行结果

    class com.app.Person
    
    • 1

    T.class

    如果T是一个Java类型,那么T.class就代表了匹配的类对象。

    Class<?> class3 = Person.class;
    System.out.println( class3 );
    
    • 1
    • 2

    运行结果

    class com.app.Person
    
    • 1

    区别

    使用”.class”来创建Class对象的引用时,不会自动初始化该Class对应类,使用forName()会自动初始化该Class对应类。

    获取所有的方法:getMethods( )

    //创建类
    Class<?> class1 = Class.forName("com.app.Person");
    //获取所有的公共的方法
    Method[] methods = 	class1.getMethods() ;
    for (Method method : methods) {
    	System.out.println( method );
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    运行结果会将该方法的自定义的所有方法和父类Object的所有方法进行输出

    获取某个方法:getMethod(“方法名”)

    //创建类
    Class<?> class1 = Class.forName("com.app.Person");
    //获取所有的公共的方法
    Method method = class1.getMethod("getName");
    System.out.println( method );
    
    • 1
    • 2
    • 3
    • 4
    • 5

    运行结果会将该方法的getName方法输出

    获取所有实现的接口:getInterfaces()

    //创建类
    Class<?> class1 = Class.forName("com.app.Person");
    //获取所有的接口
    Class<?>[] interS = class1.getInterfaces() ;
    for (Class<?> class2 : interS ) {
    	System.out.println( class2 );
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    获取父类:getSuperclass()

    /创建类
    Class<?> class1 = Class.forName("com.app.Person");
    //获取父类
    Class<?> superclass = class1.getSuperclass() ;
    System.out.println( superclass );
    
    • 1
    • 2
    • 3
    • 4
    • 5

    获取所有具有public属性的构造函数:getConstructors()

    返回所有具有public属性的构造函数数组

    //创建类
    Class<?> class1 = Class.forName("com.app.Person");
    //获取所有的构造函数
    Constructor<?>[] constructors = class1.getConstructors() ;
    for (Constructor<?> constructor : constructors) {
    	System.out.println( constructor );
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    获取所有的属性:getDeclaredFields();

    获得某个类的所有申明的字段,即包括public、private和proteced,但是不包括父类的申明字段。

    //创建类
    Class<?> class1 = Class.forName("com.app.Person");
    //取得本类的全部属性
    Field[] field = class1.getDeclaredFields();
    for (Field field2 : field) {
    	System.out.println( field2 );
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    输出的结果也可以看出来其修饰符

    获取公共属性:getFields();

    getFields()获得某个类的所有的公共(public)的字段,包括父类。

    //创建类
    Class<?> class1 = Class.forName("com.app.Person");
    //取得本类的全部属性
    Field[] field = class1.getFields();
    for (Field field2 : field) {
    	System.out.println( field2 );
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    输出的结果也可以看出来其修饰符,但是只有public的内容

    创建实例:newInstance()

    //创建类
    Class<?> class1 = Class.forName("com.app.Person");;
    //创建实例化:相当于 new 了一个对象
    Object object = class1.newInstance() ;
    //向下转型
    Person person = (Person) object ;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    newInstance()和new的区别

    区别1:

    1. 前者是使用类加载机制,后者是创建一个新类
    2. 为什么会有两种创建对象方式:
      • 要考虑到软件的可伸缩、可扩展和可重用等软件设计思想:Java中工厂模式经常使用newInstance()方法来创建对象

    区别2:

    1. 从JVM的角度看,我们使用关键字new创建一个类的时候,这个类可以没有被加载
    2. 但是使用newInstance()方法的时候,就必须保证:(两个步骤的正是Class的静态方法forName()所完成
      • 这个类已经加载;
      • 这个类已经连接了。

    区别3:

    1. 在调用class的静态加载方法forName时获得更好的灵活性,提供给了一种降耦的手段。

    一句话描述:

    1. newInstance: 弱类型。低效率。只能调用无参构造
    2. new: 强类型。相对高效。能调用任何public构造

    执行某对象的某个方法:invoke()

    作用:调用包装在当前Method对象中的方法。

    原型:

    Object invoke(Object obj,Object...args)
    
    • 1

    参数解释:

    1. obj:实例化后的对象
    2. args:用于方法调用的参数

    返回:根据obj和args调用的方法的返回值

    方法说明

    一:如果底层方法是静态的,那么可以忽略指定的 obj 参数,该参数可以为 null如果底层方法所需的形参数为 0,则所提供的 args 数组长度可以为 0 或 null

    二:第一个参数是隐式参数(比如this,传进你要委托的对象);其余的可变参数提供了显式参数(Java SE 5.0以前没有可变参数,必须传递对象数组或者null);对于静态方法(属于类),第一个参数设置为null,作为隐式参数来传递

    三:如果有返回类型,invoke方法将返回一个名义上的Object类型,实际类型由方法内部决定,所以还要进行强制类型转换

    1. 如果返回类型是基本类型,为了统一返回类型,将会返回其包装器类型,如下
      //省略显式参数,因为没有形参,不需要实参传入
      double s = (Double)m2.invoke(harry) 
      
      • 1
      • 2

    举例

    执行某个类的静态方法

    nvoke的一个参数是null,因为这是静态方法,不需要借助实例运行

    // 获取Integer.parseInt(String)方法,参数为String:
    Method m = Integer.class.getMethod("parseInt", String.class);
    // 调用该静态方法并获取结果:
    Integer n = (Integer) m.invoke(null, "12345");
    // 打印调用结果:
    System.out.println(n);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    执行某对象的方法

    owner对象中带有参数args的method方法。返回值是Object,也既是该方法的返回值

    public Object newInstance(String className, Object[] args) throws Exception { 
    	//这个对象的Class
    	Class ownerClass = owner.getClass();
    
    	// 配置参数的Class数组,作为寻找Method的条件
    	Class[] argsClass = new Class[args.length];  
    	for (int i = 0, j = args.length; i < j; i++) {  
    	    argsClass[i] = args[i].getClass();  
    	}
    	//过methodName和参数的argsClass(方法中的参数类型集合)数组得到要执行的Method。
    	Method method = ownerClass.getMethod(methodName, argsClass); 
    	// 执行该Method.invoke方法的参数是执行这个方法的对象owner,和参数数组args
    	return method.invoke(owner, args);
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
  • 相关阅读:
    阿里云配置ECS实例的IPv6地址,开通公网IPv6
    C# 使用 RSA 加密算法生成证书签名产生“The system cannot find the file specified”异常
    Windbg 快速定位C# 动态库依赖问题
    Spring5之IOC容器中IOC操作之Bean管理(二)之p名称空间注入、外部bean、内部bean、级联赋值
    2022牛客多校八 F-Longest Common Subsequence(思维)
    SpringBoot开发之SpringJDBC
    JAVA基础(JAVA SE)学习笔记(六)面向对象编程(基础)
    【算法总结】归并排序专题(刷题有感)
    卡奥斯低代码平台新版本体验活动
    JVM的几个面试重点
  • 原文地址:https://blog.csdn.net/yyuggjggg/article/details/128194570