• Java中的反射机制


    传统情况下,在Java中创建对象的方法:new对象-->调用指定方法

    但是如果通过配置文件读取文件信息进行创建对象并调用相关方法呢??

    1. Properties properties = new Properties();
    2. properties.load(new FileReader("src\\re.properties"));
    3. // 获取相关信息
    4. String classPath = properties.get("classPath").toString(); // 文件路径
    5. String method = properties.get("method").toString(); // 方法名

    但是显然此时虽然得到了文件路径还有方法名,但是并不能通过new来创建对象。因为classPath、method都是String

    此时需要使用Java中的反射机制

    这样需求(利用配置文件配置信息,不修改源代码情况下控制程序)在学习框架时特别多,同时这样设计也符合OCP(开闭原则)。

    1. // 通过反射机制实现
    2. // 加载类,得到Class类型的对象
    3. Class cls = Class.forName(classPath);
    4. // 通过Class对象获得你加载类的对象实例
    5. Object object = cls.newInstance();
    6. // 通过Class对象获得你加载的类方法实例
    7. Method method = cls.getMethod(method);
    8. // 通过method对象调用方法。即:通过方法对象调用方法
    9. method.invoke(object);

    反射的核心思想:“万物皆对象”。

            即:class类本身就是一个对象;method方法也是一个对象。

    使用反射机制,调用不同的方法,不需要修改源代码,仅仅需要修改配置文件

            调用不同的方法:仅仅修改配置文件中:method=A方法名 --> method=B方法名

    Java程序在计算机中存在三个阶段:

            编译阶段(代码阶段):用户输入的源代码 通过Javac编译生成.class字节码文件

            加载阶段:“反射的产生”,编译得到的.class字节码文件通过类加载器ClassLoader生成class类对象

            运行阶段:“反射的应用”,通过对象实例获得相关的Class类对象,得到Class类对象后,可以操作属性、调用对象方法。

    加载完类后,会在堆中产生一个Class类型的对象。(一个类仅仅产生一个Class对象)

    这个类对象包含了类的完整结构信息。通过这个类对象可以看到完整的类结构。相当于“镜子”,称为“反射”。

    反射机制允许程序借助于Reflection API取得任何类的内部信息(比如成员变量、构造器、成员方法等),操作类中的属性和方法。

    反射机制涉及到的类:

            java.lang.Class:表示一个类。Class对象表示某类加载到堆中的类对象

            java.lang.reflect.Method:表示类的方法。Method对象表示某个类的方法

            java.lang.reflect.Field:表示类中的成员变量。Field对象表示某个类中的成员变量。

            java.lang.reflect.Constructor:表示类中的构造器。Constructor对象表示某个类的构造器

    Class类细节:

            1.继承了Object类,实现了Serializable、Type、GenericDeclaration接口

            2.Class类对象并不是new出来的,而是系统创建的

    1. // ClassLoader中的loadClass()方法:
    2. // 创建对象时都会进入ClassLoader中的loadClass方法,该方法返回class类对象
    3. public Class loadClass(String name) throws ClassNotFoundException {
    4. return loadClass(name, false);
    5. }

            3.对于某个类的class对象,在内存中仅仅存在一份。因为类仅仅加载一次

            4.每个类的实例对象都会记得自己是由哪个Class实例产生的

            5.Class对象可以通过一系列的API得到一个类的完整结构

            6.Class类对象存放在堆内存中

    类的二进制字节码文件(xxx.class(类的元数据):方法代码、变量名、方法名等)存放在方法区

    1. // 得到Class类对象的几种方式:
    2. // 1.Class.forName():Class类中的静态方法forName()
    3. String classAllPath = "com.kxg.Car.car";
    4. Class carClass1 = Class.forName(classAllPath);
    5. System.out.println(carClass1);
    6. // 2.类名.class()
    7. Class carClass2 = car.class;
    8. System.out.println(carClass2);
    9. // 3.对象.getClass()
    10. Class carClass3 = car.class;
    11. System.out.println(carClass3);
    12. // 4.通过类加载器得到类对象:先得到类加载器,通过类加载器得到类对象
    13. ClassLoader classLoader = car.getClass().getClassLoader();
    14. Class carClass4 = classLoader.loadClass(classAllPath);
    15. System.out.println(carClass4);
    1. // 演示class类中的常用方法:
    2. // 1.获取对应的car类对象
    3. Class carClass = Class.forName("com.kxg.Car.car");
    4. // 2.输出carClass类对象,显示该class对象是哪个类的class对象
    5. System.out.println(carClass); //class com.kxg.Car.car
    6. System.out.println(carClass.getClass()); // 得到运行时类
    7. // 3.得到包名:getPackage().getName()
    8. System.out.println(carClass.getPackage().getName()); //com.kxg.Car
    9. // 4.得到全类名:getName()
    10. System.out.println(carClass.getName()); //com.kxg.Car.car
    11. // 5.通过carClass类创建实例对象:newInstance()
    12. car object = (car) carClass.newInstance();
    13. System.out.println(object); // 调用car类中的toString()方法
    14. // 6.获取属性:getField("属性名")
    15. Field carBrand = carClass.getField("brand");
    16. System.out.println(carBrand.get(obj)); // 输出obj对象的carBrand 宝马
    17. // 7.改变属性的值:属性对象.set(实例对象, "值")
    18. carBrand.set(obj, "奔驰"); // 改变obj对象的brand 宝马-->奔驰
    19. System.out.println(carBrand.get(obj)); // 奔驰
    20. // 8.得到所有的字段属性
    21. Field[] fields = carClass.getFields();
    22. for (Field field : fields) {
    23. System.out.println(field.getName()); // brand price color
    24. }

    反射的使用:

    反射机制获取实例对象:

    1. Class userClass = Class.forName("com.kxg.reflection.Accessible.User");
    2. // 1.newInstance():通过类中的无参构造器创建实例对象
    3. Use user1 = (User) userClass.newInstance();
    4. user1.setName("john");
    5. user1.setAge(22);
    6. System.out.println(user1);
    7. // 2.getConstructor(Class... parameterTypes):通过类中的public有参构造器创建实例对象
    8. Constructor constructor = userClass.getConstructor(int.class);
    9. /* 此时constructor对象就是制定的构造器:
    10. public User(int age) {
    11. this.age = age;
    12. }
    13. */
    14. User user2 = (User) constructor.newInstance(21);
    15. System.out.println(user2);
    16. // 3.getDeclaredConstructor(Class... parameterTypes):通过类中的非public有参构造器
    17. Constructor constructor1 = userClass.getDeclaredConstructor(int.class, String.class);
    18. // 得到的私有构造器不能直接newInstance,而是暴破:setAccessible()
    19. constructor1.setAccessible(true);
    20. // 设置暴破后,constructor1也相当于对应的构造器
    21. User user3 = (User) constructor1.newInstance(23, "jack");
    22. System.out.println(user3);

    反射机制暴破操作属性:

    1. Class studentClass = Class.forName("com.kxg.reflection.Accessible.Student");
    2. // 创建实例对象
    3. Student student = studentClass.newInstance();
    4. // getField(FieldName):得到public属性
    5. Field age = studentClass.getField("age");
    6. age.set(student, 20);
    7. System.out.println(student); // 【null,20】
    8. // getDeclaredField(FieldName):得到private属性
    9. Field name = studentClass.getDeclaredField("name");
    10. // 由于name是private的,所以不能直接设置。需要进行暴破
    11. name.setAccessible(true);
    12. // 此外,如果name是static,则设置时:name.set(null, "jack"); --即可。
    13. name.set(student, "jack");
    14. System.out.println(student); // 【jack, 20】

    反射机制暴破操作方法:

    1. Class bossClass = Class.forName("com.kxg.reflection.Accessible.Boss");
    2. Object object = bossClass.newInstance();
    3. // getDeclaredMethod(MethodName, Class... parameterTypes)
    4. // 1.访问public并调用
    5. Method method1 = bossClass.getDeclaredMethod("hi");
    6. method1.invoke(object);
    7. // 2.访问private并调用
    8. Method method2 = bossClass.getDeclaredMethod("say", String.class);
    9. method2.setAccessible(true); // 进行暴破处理
    10. method2.invoke(object, "今天发工资吗");
    11. // 3.访问private、static并调用
    12. Method method3 = bossClass.getDeclaredMethod("getSalary", double.class);
    13. method3.setAccessible(true);
    14. method3.invoke(null, 10000.0);
    15. // 调用方法返回类型为Object,但是运行类型与具体方法中定义的类型相同

  • 相关阅读:
    Linux基础知识——tmux和vim
    devops-6-pipline
    利用URL语法命令,API 接口获取淘宝天猫,拼多多,1688 商品详情等平台,其他API接口
    算法通关村 | 透彻理解动态规划
    数字孪生和数据分析:数字化时代的力量结合
    如何高效的开展app的性能测试?
    第二届中国未来交通产业发展峰会在深举办 聚焦智能网联、低空经济
    Spark基本概念
    Harbor使用公网证书
    高可用双机GPFS集群的的自动化部署脚本
  • 原文地址:https://blog.csdn.net/m0_63404275/article/details/127318384