• Java基础—— 反射


    【引入】

    动态语言
            是一类在运行时可以改变其结构的语言:例如新的函数、对象、甚至代码可以被引进,已有的函数可以被删除或是其他结构上的变化。通俗点说就是在运行时代码可以根据某些条件改变自身结构
    主要动态语言: Object--c、c JavaScript PHP、 Python等
    静态语言
            与动态语言相对应的,运行时结构不可变的语言就是静态语言。如ava、C、C++。
    虽然 java不是动态语言,但Java可以称之为“准动态语言”即java有一定的动态性我们可以利用反射机制获得类似动态语言的特性。java的动态性让编程的时候更加灵活!

    一、反射概述

    反射(Reflect)是Java被视为动态语言的关键,反射机制允许程序在执行期间,通过Reflect ApI取得任何类的内部信息,同时直接操作对象的内部属性以及方法。

     获取Class对象的三种方式:

    1. Class.forName("全类名"):将字节码加载进内存,返回class对象。
    2. 类名.class :通过类型属性class获取。
    3. 对象.getClass(): 通过方法调用获取。
    1. public class Reflect1 {
    2. public static void main(String[] args) throws ClassNotFoundException {
    3. //1、forName() ------> source(源代码阶段)
    4. Class<?> person1 = Class.forName("com.reflect.model.Person");
    5. System.out.println(person1);
    6. //2、.Class文件 ------> class (字节码阶段)
    7. Class<Person> person2 = Person.class;
    8. System.out.println(person2);
    9. //3、getClass ------> runtime(运行时阶段)
    10. Person person3=new Person();
    11. Class<? extends Person> person3Class = person3.getClass();
    12. System.out.println(person3Class);
    13. System.out.println("=============================");
    14. System.out.println(person1==person2);
    15. System.out.println(person2==person3Class);
    16. }
    17. }

    二、Class对象的主要功能概述

    1、获取成员变量

    Field[] getFields()返回一个包含Field对象的数组,反映由这个class对象表示的类或接口的所有可访问的公共字段。
    Field getField(String name)返回一个Field对象,该对象反映由这个class对象表示的类或接口的指定公共成员字段。name参数是一个String,指定所需字段的简单名称。
    Field[] getDeclaredFields()返回一个Field对象数组,反映由这个class对象表示的类或接口声明的所有字段。这包括公共、受保护、默认(包)访问和私有字段,但不包括继承字段。
    Field getDeclaredField返回一个Field对象,该对象反映由这个class对象表示的类或接口的指定声明字段。name参数是一个String,它指定所需字段的简单名称。
    1. public class Reflect2_1 {
    2. public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException, NoSuchMethodException, InvocationTargetException, InstantiationException {
    3. Class cls = Class.forName("com.reflect.model.Person");
    4. //1、变量
    5. System.out.println("================1、变量===============");
    6. Field msg = cls.getField("msg");
    7. Person person=new Person();
    8. System.out.println(msg.get(person));
    9. msg.set(person,"hello");
    10. System.out.println(msg.get(person));
    11. Field age = cls.getDeclaredField("age");
    12. age.setAccessible(true);
    13. System.out.println(age.get(person));
    14. age.set(person,12);
    15. System.out.println(age.get(person));
    16. }
    17. }

    2、获取成员方法

    Method getMethod(String name, Class<?>... parameterTypes)返回一个Method对象,该对象反映由这个class对象表示的类或接口的指定公共成员方法。name参数是一个String,用于指定所需方法的简单名称。parameterTypes参数是一个Class对象数组,按声明的顺序标识方法的形式参数类型。如果parameterTypes为空,则将其视为空数组。
    Method[] getMethods()返回一个包含Method对象的数组,该数组反映由这个class对象表示的类或接口的所有公共方法,包括由类或接口声明的方法和从超类和超接口继承的方法。
    Method[] getDeclaredMethods()返回一个包含Method对象的数组,该数组反映由这个class对象表示的类或接口的所有声明方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。
    Method getDeclaredMethod(String name, Class<?>... parameterTypes)返回一个Method对象,该对象反映由这个class对象表示的类或接口的指定声明方法。name参数是一个String,用于指定所需方法的简单名称,parameterTypes参数是一个Class对象数组,按声明的顺序标识方法的形式参数类型。
    1. public class Reflect2_2 {
    2. public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException, NoSuchMethodException, InvocationTargetException, InstantiationException {
    3. Class cls = Class.forName("com.reflect.model.Person");
    4. //2、方法
    5. System.out.println("=================2、方法==============");
    6. Method setAge = cls.getMethod("setAge", int.class);
    7. System.out.println(person.toString());
    8. setAge.invoke(person, 23);
    9. System.out.println(person.toString());
    10. }
    11. }

    3、获取构造方法

    Constructor<?>[] getConstructors()返回一个包含构造函数对象的数组,该对象反映由这个class对象表示的类的所有公共构造函数。如果类没有公共构造函数,或者类是数组类,或者类反映了原始类型或void,则返回长度为O的数组。
    Constructor<T> getConstructor(Class<?>... parameterTypes)返回一个构造函数对象,该对象反映由这个class对象表示的类的指定公共构造函数。parameterTypes形参是一个Class对象数组,按声明的顺序标识构造函数的形式形参类型。如果此Class对象表示在非静态上下文中声明的内部类,则形式形参类型包括显式的封闭实例作为第一个形参。
    1. public class Reflect2_3 {
    2. public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException, NoSuchMethodException, InvocationTargetException, InstantiationException {
    3. Class cls = Class.forName("com.reflect.model.Person");
    4. //3、构造器
    5. System.out.println("================3、构造器=============");
    6. Person person2 = (Person) cls.newInstance();
    7. System.out.println(person2.toString());
    8. Constructor constructor = cls.getConstructor();
    9. Person person3 = (Person) constructor.newInstance();
    10. System.out.println(person3.toString());
    11. }
    12. }

    三、反射应用实例

    【需求】不改变代码,可以创建任意对象,运行任一方法

    src目录下创建 info.properties文件

    读取配置文件,获取对象,执行方法

    1. public class Reflect3 {
    2. public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
    3. //1、获取配置信息
    4. Properties pro=new Properties();
    5. ClassLoader classLoader = Reflect3.class.getClassLoader();
    6. InputStream input = classLoader.getResourceAsStream("info.properties");
    7. pro.load(input);
    8. //2、获取配置文件数据
    9. String className = pro.getProperty("className");
    10. String methodName = pro.getProperty("methodName");
    11. //3、将该类加载进入内存
    12. Class cls= Class.forName(className);
    13. //4、通过反射执行方法
    14. Cat cat= (Cat) cls.newInstance();
    15. Method method = cls.getMethod(methodName,String.class);
    16. method.invoke(cat,"你好!");
    17. }
    18. }

     四、反射的应用场景

    1、在运行时分析代码

            Field类有一个getType方法,用来返回描述字段类型的一个对象,这个对象的类型同样是Class Method 和Constructor类有报告参数类型的方法,Method类还有一个报告返回类型的方法这三个类都有一个名为 getModifiers方法,它将返回一个整数,用不同的0/1位描述所使用的修饰符,如 public和 static另外,还可以利用javalang. reflect包中 Modifier类的静态方法分析 getModifiers返回的这个整数。例如,可以使用 Nodifier类中的 isPublic、 isPrivate或isFinal判断方法或构造器是 public、 private还是 final我们需要做的就是在 getModifiers回的整数上调用 Modifier类中适当的方法,另外,还可以利用 Modifier. toString方法将修饰符打印出来。

    2、运行时检查对象

    如利用Field[] getDeclaredFields() 方法,获取全部子段,编写一个通用的toString方法( )。

    3、实现泛型数组操作代码

    Class<?> getComponentType() :返回表示数组组件类型的Class。如果这个类不表示数组类,这个方法返回null。

    五、反射破坏单例模式 

            由于反射的特殊性,导致很多时候,需要单例模式的场景下,反射直接回破坏单例模式。如通过getConstructors( ) 获取构造器,利用构造器方法newInstance()直接创建对象,单例模式由此失效。但是有一种对象,天生就是单例模式——枚举

    1. public enum SingleEnum {
    2. INSTANCE;
    3. private static SingleEnum singleEnum=null;
    4. private SingleEnum(){
    5. }
    6. static class User{
    7. private static User user=null;
    8. private User(){
    9. }
    10. public static User getInstance() {
    11. if(user!=null){
    12. user=new User();
    13. }
    14. return user;
    15. }
    16. }
    17. public SingleEnum getInstance() {
    18. return INSTANCE;
    19. }
    20. }
    21. class Newtest{
    22. public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
    23. Constructor<SingleEnum> userConstructor = SingleEnum.class.getDeclaredConstructor(String.class,int.class);
    24. userConstructor.setAccessible(true);
    25. SingleEnum u1 = userConstructor.newInstance();
    26. System.out.println(u1);
    27. }
    28. }

    【运行结果】

     无法通过反射创建对象。

  • 相关阅读:
    mybatis学习(15):mybatis连接mysql数据库
    java项目_第168期ssm二手车交易网站-_计算机毕业设计
    day32贪心算法part02| 122.买卖股票的最佳时机II 55. 跳跃游戏 45.跳跃游戏II
    Golang sync.WaitGroup
    易观分析:2022年Q2中国网络零售B2C市场交易规模达23444.7亿元
    架构师spring boot 面试题
    Typora 窗口总数跳动问题
    java进阶复习题.doc
    四、K8S资源清单
    java-net-php-python-jsp大学生兼职平台计算机毕业设计程序
  • 原文地址:https://blog.csdn.net/m0_46013789/article/details/125559102