反射

1、定义

百度百科:Java的反射(reflection)机制是指在程序的运行状态中,可以构造任意一个类的对象,可以了解任意一个对象所属的类,可以了解任意一个类的成员变量和方法,可以调用任意一个对象的属性和方法。这种动态获取程序信息以及动态调用对象的功能称为Java语言的反射机制。反射被视为动态语言的关键。

维基百科:反射指计算机程序在运行时(runtime)可以访问、检测和修改它本身状态或行为的一种能力。[1]用比喻来说,反射就是程序在运行的时候能够“观察”并且修改自己的行为。
Java的反射机制的实现要借助于4个类:Class,Constructor,Field,Method;

2、应用场景

(1)、在Java类里面解析xml或properties里面的内容,载入类的Class实例。
这样就可以动态配置一些东西,不用每一次都要在代码里面去new或者做其他的事情,以后要改的话直接改配置文件,代码维护起来就很方便了。

(2)、在编码阶段不知道那个类名,要在运行期从配置文件读取类名, 这时候就没有办法硬编码new 一个实例,而必须用到反射才能创建这个对象.反射的目的就是为了扩展未知的应用。
比如你写了一个程序,这个程序定义了一些接口,只要实现了这些接口的jar都可以作为插件来插入到这个程序中。那么怎么实现呢?就可以通过反射来实现。就是把jar加载进内存,然后通过反射的方式来调用jar中的方法。

(3)、动态修改一些系统级别的API
比如某系统API类的某个属性设置了固定默认值,子类无法直接访问,而我们在自定义类的时候可能需要去修改这个默认值,就可以通过反射来实现。

3、常用API

1、获取反射中的class对象
    private static void callApi1() throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        Class clz = Class.forName("template.ClassA");//类所在包下的路径
        Class clz2 = ClassA.class;
        ClassA classA = new ClassA();
        Class clz3 = classA.getClass();

        System.out.println("类对象:" + clz);
        System.out.println("类对象2:" + clz2);
        System.out.println("类对象3:" + clz3);

    }
2、通过反射创建类实例
    private static void callApi2() throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
        //方式1
        Class clz = ClassA.class;
        ClassA classA = (ClassA) clz.newInstance();
        System.out.println("通过 Class 对象创建类实例:" + classA);
        //方式2
        Constructor constructor = clz.getConstructor();
        ClassA classA2 = (ClassA) constructor.newInstance();
        System.out.println("通过 Constructor 对象创建类实例:" + classA2);
		//3、自定义构造函数的类实例
        Constructor constructor2 = clz.getConstructor(String.class);
        ClassA classA3 = (ClassA) constructor2.newInstance("Lily");
        System.out.println("通过 Constructor 对象创建类有参构造函数实例:" + classA3);
    }

通过 Constructor 对象创建类实例可以选择特定构造方法,而通过 Class 对象则只能使用默认的无参数构造方法(public)。

3、遍历方法和属性及调用
    private static void callApi3() throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        //反射实例化
        Class clz = Class.forName("template.ClassA");
        Method[] methods = clz.getDeclaredMethods();
        for (Method method : methods) {
            System.out.println("包含的方法:" + method);
        }
        
        Method[] publicMethods = clz.getMethods();
        for (Method method : publicMethods) {
            System.out.println("包含的公有方法:" + method);
        }

        Field[] fields = clz.getDeclaredFields();
        for (Field field : fields) {
            System.out.println("包含的属性:" + field);
        }

        Field[] publicFields = clz.getFields();
        for (Field field : publicFields) {
            System.out.println("包含的公有属性:" + field);
        }

        Method setNameMethod = clz.getMethod("setName", String.class);
        Constructor constructor = clz.getConstructor();
        Object obj = constructor.newInstance();
        setNameMethod.invoke(obj, "Tom.Hanks");
        Method getNameMethod = clz.getMethod("getName");
        System.out.println("反射实例化调用类的方法:" + getNameMethod.invoke(obj));
    }
方法名称描述
getDeclaredMethods获取的是类自身声明的所有方法,包含public、protected和private方法 ;不能继承父类的方法
getMethods获取的是类的所有共有方法,这就包括自身的所有public方法,和从基类继承的、从接口实现的所有public方法。
getDeclaredFields获取的是类自身声明的所有属性 ,包含public、protected和private属性;不能继承父类的属性
getFields获取的是类的所有共有属性,这就包括自身的所有public属性,和从基类继承的、从接口实现的所有public属性。

4、案例

ClassA类:
package template;

/**
 * Created by Administrator on 2020/5/11.
 */
public class ClassA {
    private String name;
    private final String DEFAULT_NAME;//JVM 在编译阶段得到的 .class 文件已经将常量优化为具体的值,在运行阶段就直接使用具体的值了,所以即使修改了常量的值也已经毫无意义了。
    public int TAG;

    public ClassA() {
        DEFAULT_NAME = "无";//将赋值放在构造函数中,构造函数是我们运行时 new 对象才会调用的,所以就不会直接为常量赋值。
    }

    public ClassA(String name) {
        this.name = name;
        this.DEFAULT_NAME = "无";//将赋值放在构造函数中,构造函数是我们运行时 new 对象才会调用的,所以就不会直接为常量赋值。
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    private String getDefaultName() {
        return DEFAULT_NAME;
    }
}
TestReflect类:
package template;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * Created by Administrator on 2020/5/11.
 */
public class TestReflect {
    public static void main(String[] args) {

        try {
            callApi1();
            callApi2();
            callApi3();

        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }

    }


    //1、获取反射中的class对象
    private static void callApi1() throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        Class clz = Class.forName("template.ClassA");
        Class clz2 = ClassA.class;
        ClassA classA = new ClassA();
        Class clz3 = classA.getClass();

        System.out.println("类对象:" + clz);
        System.out.println("类对象2:" + clz2);
        System.out.println("类对象3:" + clz3);

    }

    //2、通过反射创建类实例
    private static void callApi2() throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {

        //方式1
        Class clz = ClassA.class;
        ClassA classA = (ClassA) clz.newInstance();
        System.out.println("通过 Class 对象创建类实例:" + classA);
        //方式2
        Constructor constructor = clz.getConstructor();
        ClassA classA2 = (ClassA) constructor.newInstance();
        System.out.println("通过 Constructor 对象创建类实例:" + classA2);

        //通过 Constructor 对象创建类实例可以选择特定构造方法,而通过 Class 对象则只能使用默认的无参数构造方法(public)。
        Constructor constructor2 = clz.getConstructor(String.class);
        ClassA classA3 = (ClassA) constructor2.newInstance("Lily");
        System.out.println("通过 Constructor 对象创建类有参构造函数实例:" + classA3);

    }

    //3、遍历方法和属性及调用
    private static void callApi3() throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        //反射实例化
        Class clz = Class.forName("template.ClassA");
        Method[] methods = clz.getDeclaredMethods();
        for (Method method : methods) {
            System.out.println("包含的方法:" + method);
        }
        System.out.println("------------------------------分隔线------------------------------");

        Method[] publicMethods = clz.getMethods();
        for (Method method : publicMethods) {
            System.out.println("包含的公有方法:" + method);
        }

        System.out.println("------------------------------分隔线------------------------------");
        Field[] fields = clz.getDeclaredFields();
        for (Field field : fields) {
            System.out.println("包含的属性:" + field);
        }
        System.out.println("------------------------------分隔线------------------------------");
        Field[] publicFields = clz.getFields();
        for (Field field : publicFields) {
            System.out.println("包含的公有属性:" + field);
        }
        System.out.println("------------------------------分隔线------------------------------");

        Method setNameMethod = clz.getMethod("setName", String.class);
        Constructor constructor = clz.getConstructor();
        Object obj = constructor.newInstance();
        setNameMethod.invoke(obj, "Tom.Hanks");
        Method getNameMethod = clz.getMethod("getName");
        System.out.println("反射实例化调用:" + getNameMethod.invoke(obj));
    }
}
输出:

在这里插入图片描述

5、总结

防止硬编码,更多的体现则是一种灵活性。

当在为私有属性赋值的时候需要设置setAccessible(true);

getDeclaredMethods:不能继承父类(或接口)的所有方法
getMethods:能继承父类(或接口)的公开方法
getDeclaredFields:不能继承父类(或接口)的所有属性
getFields:能继承父类(或接口)的公开属性

当子类想获取父类中的私有属性时,可以通过:getSuperclass的getDeclaredFields()方法来获取。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值