Java反射

Java反射

Java反射介绍

Java反射是指在程序运行时动态获取类的信息、创建对象、调用方法等操作。其基本原理是通过Java虚拟机在运行时动态加载类,并通过类的信息获取类的结构信息,包括构造方法、字段、方法等,并对这些结构进行访问和操作。
在Java中,每一个类都对应一个Class对象,Class对象包含了该类的结构信息,包括类的构造方法、字段、方法等。Java反射机制就是通过Class对象来获取类的结构信息,并对其进行操作。

Java反射机制主要涉及以下几个类:

  1. Class类:表示一个类的结构信息,包括构造方法、字段、方法等。
  2. Constructor类:表示一个构造方法。
  3. Field类:表示一个类的成员变量。
  4. Method类:表示一个类的方法。

通过反射机制,可以动态地创建对象、获取对象的成员变量、调用对象的方法等。例如,可以通过反射机制实现对象的序列化和反序列化、动态代理等功能。但是,由于反射机制的使用需要大量的资源,所以在实际开发中应该尽量避免滥用反射。

Java反射机制主要的作用包括:

  1. 动态加载类和创建对象:反射机制可以在运行时动态加载类和创建对象,可以根据条件动态创建不同的对象,提高程序的灵活性和可扩展性。
  2. 获取类的信息:反射机制可以获取类的属性、方法、构造方法等信息,可以根据这些信息进行对象的操作,比如调用方法、获取和修改属性值等。
  3. 调用对象的方法:反射机制可以调用对象的方法,可以根据方法名和参数列表获取对应的方法,并执行该方法,实现动态调用对象的方法。
  4. 动态修改对象的属性值:反射机制可以获取和修改对象的属性值,可以根据属性名获取对应的属性,并动态修改属性值。
  5. 动态代理:反射机制可以实现动态代理,可以在运行时生成代理对象,并通过代理对象调用原始对象的方法,实现AOP编程。

总之,Java反射机制为Java程序提供了一种强大的动态编程方式,使得程序更加灵活、动态和可扩展。本来写死的类和方法可以增加复用性

Java中反射的优点:

  1. 灵活性:反射提供了一种灵活的机制,在运行时检查和修改程序的行为,而无需重新编译代码。
  2. 代码重用:反射可以用于创建可重用的代码,可以与任何对象或类一起使用,而不需要在编译时知道类型的具体细节。
  3. 调试:反射可用于调试复杂程序,允许开发人员在运行时检查和修改对象的状态。

Java中反射的缺点:

  1. 性能:由于动态检查和修改对象的开销,反射可能比直接访问对象的属性或方法要慢。
  2. 安全风险:反射可以用于访问和修改类的私有或受保护成员,如果不正确使用,可能会导致安全漏洞。
  3. 复杂性:反射可能会使代码更加复杂和难以理解,特别是如果过度或不正确使用。

注意:要有选择的使用反射功能,如果可以直接执行操作,那么最好不要使用反射。

Java反射获取Class的方法

  1. 使用对象的getClass()方法获取,例如:Object obj = new Object(); Class<?> objClass = obj.getClass();
  2. 使用类名.class获取,例如:Class<?> objClass = Object.class;
  3. 使用Class.forName()方法获取,例如:Class<?> objClass = Class.forName("java.lang.Object");

注意
使用Class.forName()方法时需要指定类的全限定名(包括包名),否则会抛出ClassNotFoundException异常。此外,还可以使用ClassLoader来获取Class对象,例如:Class<?> objClass = ClassLoader.getSystemClassLoader().loadClass("java.lang.Object");

Java反射中比较常用的类和方法

描述
Class 表示一个Java类,可以获取和设置类的信息。
Constructor 表示Java类的构造函数,可以创建新的对象。
Field 表示Java类的字段,可以获取和设置字段的值。
Method 表示Java类的方法,可以调用方法并获取返回值。
Modifier 提供了访问修饰符的方法,例如public、private、static等。
方法 使用类型 返回值 描述
newInstance() Class,Constructor T 默认调用无参构造创建一个Java类的新实例,但是可以通过下面两个方法调用newInstance()创建对象
getConstructor() Class Constructor 获取Java类public的特定构造函数。
getDeclaredConstructor() Class Constructor 获取Java类的特定构造函数。
getField() Class Field 参数获取指定字段,后面加s获取Java类的public字段。
getDeclaredField() Class Field 参数获取指定字段,后面加s获取Java类的所有字段。
getMethod() Class Method 获取指定方法,后面加s获取Java类的public方法(包括从父类继承的)。
getDeclaredMethod() Class Method 获取指定方法,后面加s获取Java类的所有方法。
getModifiers() Member,Field,Method int 获取Java类、字段或方法的访问修饰符(整型常量)。
getName() Member,Field,Method String 获取Java类、字段或方法的名称。
getSimpleName() Member,Field,Method String 获取Java类、字段或方法的简单名称(没有包名)。
getType() Field Class<?> 获取Java字段的数据类型。
getValue() Field Object 获取Java字段的值。
invoke() Method Object 调用Java方法并返回其返回值。
setAccessible() AccessibleObject void 可以用来将一个成员变量或成员方法的可访问性改为可访问,不一定是改为 public 访问权限
getAnnotation(注解类型) Member,Field,Method 注解类型 getAnnotation方法只能获取到直接作用于当前元素上的注解,无法获取到间接作用于当前元素上的注解(例如继承、实现、重载等情况下的注解),后面加s获取所有注解。

Java反射案例

import java.lang.reflect.*;

public class ReflectionExample {

    public static void main(String[] args) {
        // 通过Class.forName获取Person类的Class对象
        Class<?> clazz = null;
        try {
            clazz = Class.forName("Person");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

        // 通过Class对象获取Constructor对象并创建实例
        Constructor<?> constructor = null;
        try {
            constructor = clazz.getConstructor(String.class, int.class);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
        Person person = null;
        try {
            person = (Person) constructor.newInstance("张三", 20);
        } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
            e.printStackTrace();
        }

        // 通过Class对象获取Method对象并调用方法
        Method method = null;
        try {
            method = clazz.getMethod("sayHello");
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
        try {
            method.invoke(person);
        } catch (IllegalAccessException | InvocationTargetException e) {
            e.printStackTrace();
        }

        // 通过Class对象获取Field对象并修改属性值
        Field field = null;
        try {
            field = clazz.getDeclaredField("name");
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }
        field.setAccessible(true);
        try {
            field.set(person, "李四");
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        System.out.println("修改后的名字为:" + person.getName());
    }
}

class Person {
    private String name;
    private int age;

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

    public void sayHello() {
        System.out.println("Hello, I'm " + name + ", " + age + " years old.");
    }

    public String getName() {
        return name;
    }
}

反射案例解析

通过Class.forName获取Person类的Class对象

Class<?> clazz = Class.forName("Person")
Class<?> :表示一个未知类型的类,用于表示任意类型的类,包括基本数据类型和引用类型
Class.forName("Person"):Java 中的一个静态方法,它返回一个类或接口的 Class 对象

通过Class对象获取Constructor对象

Constructor<?> constructor = clazz.getConstructor(String.class, int.class);
Constructor<?>使用可以调用一个类的构造方法来创建一个对象的Constructor对象
clazz.getConstructor(String.class, int.class):通过clazz对象获取该类的构造方法,比如案例中返回Person(String name, int age)这个构造方法

通过Constructor对象创建实例

Person person = (Person) constructor.newInstance("张三", 20);
Person:实体类
constructor.newInstance("张三", 20):调用该类的构造方法创建对象

通过Class对象获取Method对象

Method method = clazz.getMethod("sayHello");
Method:一个类,用于表示类的方法。一个方法对象包含了一个方法的所有信息,包括方法的名称、返回类型、参数类型、修饰符等。
clazz.getMethod("sayHello"):使用 Java 反射机制获取类中指定方法的实例对象,其中 "sayHello" 是方法名。该方法返回一个 Method 对象,可以用来调用该方法。

通过Method对象调用方法person对象的方法

method.invoke(person):使用 method 对象(根据method中的方法信息) 调用 person对象的方法

通过Class对象获取Field对象

Field field = clazz.getDeclaredField("name");
Field:表示一个类的成员变量
clazz.getDeclaredField("name"):获取clazz类的name属性字段

修改属性值

field.setAccessible(true);:将一个私有属性的可访问性改为public( true)。但是这种做法是有风险的,非必要最好不要使用
field.set(person, "李四");:通过set修改属性的值

Java反射学习路线

Java反射是Java中一个重要的概念,能够让你在程序运行期间获取类的信息、访问对象的属性和方法,实现代码的灵活性和扩展性。下面是学习Java反射的一些路线:

  1. 首先,你需要对Java的基本语法和面向对象编程有一定的了解,掌握类、对象、方法、属性等概念。
  2. 然后学习Java反射的基本概念和原理,了解反射是如何在运行期间获取和使用类信息的。
  3. 掌握Class类和其常用方法,如forName()方法、newInstance()方法等。
  4. 了解Constructor、Field、Method等反射API的基本使用方法,掌握其在实际开发中的应用。
  5. 学习注解和反射的结合使用,了解注解的原理和使用方法,学习如何通过反射读取和解析注解信息。
  6. 学习泛型和反射的结合使用,了解泛型的基本概念和使用方法,学习如何通过反射读取和使用泛型信息。
  7. 了解Java反射的局限性和使用注意事项,如反射的性能问题、安全问题等。
  8. 最后,结合实际项目,学习Java反射的实际应用方法和技巧,提高自己的开发效率和代码质量。