Java中的反射机制详解

反射的简单demo

  1. 声明的原始类

    java 复制代码
    class Cat {
    	private String name = "猫猫";
        public int age = 10;
    
        public Cat(){}
    
        public Cat(String name) {
            this.name = name;
        }
    
        public void hi() {
            System.out.println("hi~ " + name);
        }
        public void hi() {
            System.out.println(name + " cry");
        }
    }
  2. src目录下的配置文件re.properties

    properties 复制代码
    classfullpath=com.zhl.Cat
    method=hi
  3. 编写反射代码

    java 复制代码
    public class Reflection01 {
        public static void main(String[] args) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
    		// 加载配置文件中的信息
    		Properties properties = new Properties();
            InputStream inputStream = ReflectionQuestion.class.getClassLoader().getResourceAsStream("re.properties");
            properties.load(inputStream);
            String classfullpath = properties.getProperty("classfullpath");
            String methodName = properties.getProperty("method");
            
            // 使用反射创建对象
            Class<?> aClass = Class.forName(classfullpath);
            Object o = aClass.newInstance();
            System.out.println(o.getClass());
            
            // 通过反射获取成员方法
            Method method = aClass.getMethod(methodName);
            method.invoke(o);
    		
    		// 通过反射获取成员变量(不能获取私有的)
            Field ageField = aClass.getField("age");
            System.out.println(ageField.get(o));
    		
    		// 通过反射获取构造器
            // 无参
            Constructor<?> constructor = aClass.getConstructor();
            System.out.println(constructor);
            // 有参
            Constructor<?> constructor2 = aClass.getConstructor(String.class);
            System.out.println(constructor2);
    	}
    }

反射机制和传统调用方法的对比

java 复制代码
public class Reflection02 {
    public static void main(String[] args) throws ClassNotFoundException, InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException {
        m1();
        m2();
    }
    // 传统方法:直接创建对象
    public static void m1() {
        Cat cat = new Cat();
        long begin = System.currentTimeMillis();
        for (int i = 0; i < 1000000; i++) {
            cat.hi();
        }
        long end = System.currentTimeMillis();
        System.out.println("传统方案耗时:" + (end - begin) + "ms");
    }

    // 反射机制调用方法
    public static void m2() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        Class<?> aClass = Class.forName("com.zhl.Cat");
        // 无参构造
        Constructor<?> constructor = aClass.getConstructor();
        // 关闭访问检查的开关
        constructor.setAccessible(true);
        Object o = constructor.newInstance();

        Method method = aClass.getMethod("hi");
        method.setAccessible(true);
        long begin = System.currentTimeMillis();
        for (int i = 0; i < 1000000; i++) {
            method.invoke(o);
        }
        long end = System.currentTimeMillis();
        System.out.println("使用反射方案耗时:" + (end - begin) + "ms");
    }
}

反射的优点和缺点:

  • 优点:可以动态的创建和使用对象(也是框架底层核心),使用灵活。
  • 缺点:使用反射基本是解释执行,对执行速度有影响。

Class类

Class类的对象不是new出来的,是系统创建的,在类加载阶段生成的。

对于某个类的Class类对象,类只加载一次,在内存中只有一份。

每个类的实例都会记得自己是由哪个 Class 实例所生成。

  • 已知一个类的全类名 ,且该类在类路径下,可通过Class类的静态方法forName()获取

    • 应用场景:通过配置文件读取类的全路径,加载类
    java 复制代码
    String className;
    Class<?> clazz = Class.forName(className);
  • 若已知具体的类,通过 类的class获取,该方式最为安全可靠,程序性能最高

    • 应用场景:多用于参数传递,比如通过反射得到对应构造器对象
    java 复制代码
    Class<Cat> cat = Cat.class;
  • 已知某个类的实例,调用该实例的getClass()方法获取Class对象,实例:

    • 应用场景:通过创建好的对象,获取Class对象
    java 复制代码
    // 运行类型
    Class clazz = 对象.getClass();	
  • 其他方式
    获取类加载器,加载指定类 。

    java 复制代码
    ClassLoader loader = 类.class.getClassLoader();	
    Class<?> clazz = loader.loadClass("com.zhl.Cat");

类加载

  • 基本说明

    反射机制是 java实现动态语言的关键,也就是通过反射实现类动态加载

    1. 静态加载:编译时加载相关的类,如果没有则报错。
      依赖性太强

    2. 动态加载:运行时加载需要的类,如果运行时不用该类,则不报错。
      降低了依赖性

    3. 举例说明 (伪代码)

      String key = scan.next();
      switch(key) {
      case "1" :
      Dog dog = new Dog();
      dog.hi();
      break;
      case "2":
      Class<?> clazz = Class.forName("Person");
      Object o = clazz.newInstance();
      Method methodName = clazz.getMethod("hi");
      method.invoke(o);
      break;
      default:
      break;
      }

    注意这段代码中,并没有创建 DogPerson类(当前module中没有Dog或Person类),但是编译时,只会报 Dog 类没有创建,而Person类并没有监测出来是否被创建,只有真正被执行到这行代码时才会被加载,判断是否创建过Person类。

  • 类加载时机

    1. 当创建对象时(new)
    2. 当子类被加载时,父类也加载
    3. 调用类中的静态成员时
    4. 通过反射(动态加载)

    关于JVM的类加载可参考https://blog.csdn.net/m0_72406127/article/details/137058092?spm=1001.2014.3001.5501

通过反射获取类的结构信息

  • java.lang.Class

    1. getName:获取全类名
    2. getSimpleName:获取简单类名
    3. getFields:获取所有public修饰的属性,包含本类以及父类的
    4. getDeclaredFields:获取本类中所有属性
    5. getMethods:获取所有public修饰的方法,包含本类以及父类的
    6. getDeclaredMethods:获取本类中所有方法
    7. getConstructors:获取所有public修饰的构造器,包含本类
    8. getDeclaredConstructors:获取本类中所有构造器
    9. getPackage:以Package形式返回包信息
    10. getSuperClass:以Class形式返回父类信息
    11. getInterfaces:以Class[]形式返回接口信息
    12. getAnnotations:以Annotation[]形式返回注解信息
  • java.lang.reflect.Field

    1. getModifiers: 以int形式返回修饰符
      默认修饰符是0,public 是 1,private 是 2,protected 是 4,static 是 8 ,final是 16,多个不同的修饰符可以相加
    2. getType:以Class形式返回类型
    3. getName:返回属性名
  • java.lang.reflect.Method

    1. getModifiers:以int 形式返回修饰符
    2. getName:返回方法名
    3. getReturnType:以Class形式获取 返回类型
    4. getParameterTypes:以Class[]返回参数类型数组
  • java.lang.reflect.Constructor

    1. getModifiers: 以int形式返回修饰符
    2. getName:返回构造器名(全类名)
    3. getParameterTypes:以Class[]返回参数类型数组

通过反射创建对象

  • 方式一:调用类中的publi 修饰的无参构造器
  • 方式二:调用类中的指定构造器
  • Class类相关方法
    • newlnstance:调用类中的无参构造器,获取对应类的对象
    • getConstructor(Class...clazz):根据参数列表,获取对应的public构造器对象
    • getDeclaredConstructor(Class...clazz):根据参数列表,获取对应的所有构造器 对象
  • Constructor类相关方法
    • setAccessible: 设为true,可以访问private修饰的构造器
    • newinstance(Object...obj): 调用构造器

在构造器/方法/属性中使用 getDeclaredXxx方法可以获取本类中的所有构造器/方法/属性,

通过setAccessible()方法,设置参数为true,关闭访问检查的开关,就可以直接访问和修改被 private 修饰的构造器/方法/属性。

相关推荐
进阶的架构师1 分钟前
互联网Java工程师面试题及答案整理(2024年最新版)
java·开发语言
黄俊懿2 分钟前
【深入理解SpringCloud微服务】手写实现各种限流算法——固定时间窗、滑动时间窗、令牌桶算法、漏桶算法
java·后端·算法·spring cloud·微服务·架构
易辰君3 分钟前
python爬虫 - 深入requests模块
开发语言·爬虫·python
木子020410 分钟前
java高并发场景RabbitMQ的使用
java·开发语言
无夜_11 分钟前
Prototype(原型模式)
开发语言·c++
看到请催我学习11 分钟前
内存缓存和硬盘缓存
开发语言·前端·javascript·vue.js·缓存·ecmascript
夜雨翦春韭21 分钟前
【代码随想录Day29】贪心算法Part03
java·数据结构·算法·leetcode·贪心算法
大霞上仙44 分钟前
jmeter学习(1)线程组与发送请求
java·学习·jmeter
笃励1 小时前
Java面试题二
java·开发语言·python
jyan_敬言1 小时前
【Linux】Linux命令与操作详解(一)文件管理(文件命令)、用户与用户组管理(创建、删除用户/组)
linux·运维·服务器·c语言·开发语言·汇编·c++