一、反射机制概念

主要是指程序可以访问,检测和修改它本身状态或行为的一种能力,并能根据自身行为的状态和结果,调整或修改应用所描述行为的状态和相关的语义。在java中,只要给定类的名字, 那么就可以通过反射机制来获得类的所有信息。

反射是Java中一种强大的工具,能够使我们很方便的创建灵活的代码,这些代码可以再运行时装配,无需在组件之间进行源代码链接。但是反射使用不当会成本很高!

二、反射机制的作用

  1. 在运行时判断任意一个对象所属的类;
  2. 在运行时获取类的对象;
  3. 在运行时访问java对象的属性,方法,构造方法等。

三、反射机制的优点与缺点

优点:可以实现动态创建对象和编译,体现出很大的灵活性.

编译方式说明:

  1. 静态编译:在编译时确定类型 & 绑定对象。如常见的使用new关键字创建对象

  2. 动态编译:运行时确定类型 & 绑定对象。动态编译体现了Java的灵活性、多态特性 & 降低类之间的藕合性

缺点:对性能有影响。使用反射基本上是一种解释操作,我们可以告诉JVM,我们希望做什么并且它 满足我们的要求。这类操作总是慢于只直接执行相同的操作。

四、具体使用

  • Class类

反射机制的实现主要通过操作java.lang.Class类。

在Java程序运行时,Java运行环境会为所有类维护一个java.lang.Class对象,存放着对应类型对象的运行时信息。

Java反射机制的实现除了依靠Java.lang.Class类,还需要依靠:Constructor类、Field类、Method类,分别作用于类的各个组成部分:

  • 使用步骤

在使用Java反射机制时,主要步骤包括:

  1. 获取目标类型的Class对象
  2. 通过Class对象分别获取Constructor类对象、Method类对象 & Field 类对象
  3. 通过Constructor类对象、Method类对象 & Field类对象分别获取类的构造函数、方法&属性的具体信息,并进行后续操作
1、获得class对象

(1) 使用 Class 类的 forName 静态方法

    Class<?> classtype3 = Class.forName("java.lang.Boolean");
    System.out.println(classtype3);

(2) 直接获取某一个对象的 class

    Class<?> classtype2 = Boolean.class;
    System.out.println(classtype2);
    Class<?> classtype4 = Boolean.TYPE;
    System.out.println(classtype4);

(3) 调用某个对象的 getClass() 方法

    Boolean flg = true;
    Class<?> classtype = flg.getClass();
    System.out.println(classtype);

(4) 获取父类 getSuperclass();

    Class<?> classtype4 = Class.forName("java.lang.Boolean");
    classtype4.getSuperclass();
2、判断是否为某个类的实例

一般地,我们用 instanceof 关键字来判断是否为某个类的实例。同时我们也可以借助反射中 Class 对象的 isInstance() 方法来判断是否为某个类的实例,它是一个 native 方法:

public native boolean isInstance(Object obj);
3、创建实例以及获取构造方法

(1) 使用Class对象的newInstance()方法来创建Class对象对应类的实例。

(2) 先通过Class对象获取指定的Constructor对象,再调用Constructor对象的newInstance()方法来创建实例。这种方法可以用指定的构造器构造类的实例。

4、获取方法

(1) getDeclaredMethods 方法返回类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。

public Method[] getDeclaredMethods() throws SecurityException

(2) getMethods 方法返回某个类的所有公用(public)方法,包括其继承类的公用方法。

public Method[] getMethods() throws SecurityException

(3) getMethod 方法返回一个特定的方法,其中第一个参数为方法名称,后面的参数为方法的参数对应Class的对象。

public Method getMethod(String name, Class<?>... parameterTypes)
  1. getDeclaredMethod 方法返回一个特定的方法,但不包括继承的方法。
5、获取类的成员变量(字段)信息

(1) getFiled:获得指定的公有成员变量

(2) getDeclaredField:获得指定已声明的成员变量,但不能得到其父类的成员变量

(3) getFileds:获得所有公有的成员变量

(4) getDeclaredFields:获得所有已声明的成员变量,但不能得到其父类的成员变量

(5) 获取指定对象的指定字段的值 field.get(obj)

(6) 设置指定对象的指定对象Field值 field.set(obj, “123”);

6、调用方法

当我们从类中获取了一个方法后,我们就可以用 invoke() 方法来调用这个方法。invoke 方法的原型为:

public Object invoke(Object obj, Object... args)
        throws IllegalAccessException, IllegalArgumentException,
           InvocationTargetException
7、获得注解

Class类、Field类、Method类 & Constructor类对象都有如下方法

getAnnotations();

getDeclaredAnnotations();

getAnnotation(annotationClass);

8、关于private

反射机制的默认行为受限于Java的访问控制,无法访问( private )私有的方法、字段。

Java安全机制只允许查看任意对象有哪些域,而不允许读它们的值若强制读取,将抛出异常。

使用Field类、Method类 & Constructor类对象的setAccessible()可以脱离Java程序中安全管理器的控制、屏蔽Java语言的访问检查。

五、代码

public class ReflectionTest {

    public static void main(String[] args) throws Exception {

        // 1、获得class
        // 1.1 使用 Class 类的 forName 静态方法
        Class<?> class1 = Class.forName("java.lang.Boolean");
        System.out.println(class1);
        // 1.2 直接获取某一个对象的 class
        Class<?> class2 = Boolean.class;
        System.out.println(class2);
        Class<?> class3 = Boolean.TYPE;
        System.out.println(class3);
        // 1.3 调用某个对象的 getClass() 方法
        Boolean flg = true;
        Class<?> class4 = flg.getClass();
        System.out.println(class4);
        // 1.4 获取父类调用getSuperclass() 方法
        Class<?> superclass = class1.getSuperclass();
        System.out.println(superclass);

        // 2、判断是否为某个类的实例
        System.out.println(Boolean.class.isInstance(flg));

        // 3、创建实例以及获取构造器
        // 3.1 直接通过class对象创建实例
        Class<?> m4 = M4A1.class;
        Object m4obj1 = m4.newInstance();
        
        System.out.println(m4obj1.toString());
        // 3.2 通过class对象获得构造器来实例化对象
        // 获得参数的指定构造器
        Constructor constructor = m4.getConstructor(String.class,Double.class);

        // 获得所有构造器
        Constructor<?>[] constructors = m4.getConstructors();
        for(Constructor c:constructors){
            System.out.println(c);
        }
        Object m4obj2 = constructor.newInstance("步枪",38.89d);
        System.out.println(m4obj2.toString());
        
        // 4、获取方法
        // 4.1 获取所有公用方法(public)
        Method[] methods = m4.getMethods();
        for(Method m:methods){
            System.out.println(m);
        }
        // 4.2 获取所有非继承的方法
        Method[] methods2 = m4.getDeclaredMethods();
        for(Method m:methods2){
            System.out.println(m);
        }
        // 4.3 由方法名和参数类型获取一个指定方法
        Method method = m4.getMethod("setConment", String.class);
        System.out.println(method);
        Method method2 = m4.getDeclaredMethod("toString");
        System.out.println(method2);

        // 5、获取成员变量
        // 5.1 获取指定变量
        Field field = m4.getDeclaredField("conment");
        System.out.println(field);
        Field field2 = m4.getField("name");
        System.out.println(field2);
        // 5.2 获取所有变量
        Field[] fields = m4.getFields();
        for(Field f:fields){
            System.out.println(f);
        }
        Field[] fields2 = m4.getDeclaredFields();
        for(Field f:fields2){
            System.out.println(f);
        }
        // 5.3 设定指定字段的值
        // 设置可以访问private
        field.setAccessible(true);
        field.set(m4obj1, "使用Field类的set方法设定conment的值");
        // 5.4 获取指定字段的值
        Object value = field.get(m4obj1);
        System.out.println(value);
        // 6、调用方法
        // 获得class对象
        Class<?> clazz = M4A1.class;
        // 实例化
        Object m4a1 = clazz.newInstance();
        // 获得方法
        Method setConmentMethod = clazz.getMethod("setConment",String.class);
        Method getConmentMethod = clazz.getMethod("getConment");
        Method tostringMethod = clazz.getMethod("toString");
        // 调用方法
        setConmentMethod.invoke(m4a1, "测试一下invoke调用方法");
        Object result = getConmentMethod.invoke(m4a1);
        System.out.println(result);
        System.out.println(tostringMethod.invoke(m4a1));
    }
}
public class M4A1 {

    private String conment;
    private Double might;
    public String name;

    /**
     * @Title:M4A1
     * @Description:TODO
     */
    public M4A1() {
        super();
        // TODO Auto-generated constructor stub
        System.out.println("执行无参构造方法");
    }

    /**
     * @Title:M4A1
     * @Description:TODO
     * @param conment
     * @param might
     */
    public M4A1(String conment, Double might) {
        super();
        this.conment = conment;
        this.might = might;
        System.out.println("执行带参构造方法");
    }

    /**
     * @return the conment
     */
    public String getConment() {
        return conment;
    }

    /**
     * @param conment
     *            the conment to set
     */
    public void setConment(String conment) {
        this.conment = conment;
    }

    /**
     * @return the might
     */
    public Double getMight() {
        return might;
    }

    /**
     * @param might
     *            the might to set
     */
    public void setMight(Double might) {
        this.might = might;
    }

    /*
     * (non Javadoc)
     * 
     * @Title: toString
     * 
     * @Description: TODO
     * 
     * @return
     * 
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString() {
        return "M4A1 [conment=" + conment + ", might=" + might + "]";
    }

}

参考

https://www.jianshu.com/p/4c07f1824cd8

https://www.sczyh30.com/posts/Java/java-reflection-1/

https://www.cnblogs.com/tech-bird/p/3525336.html