Java反射

Java基础 - 反射

一、作用

“反射是框架设计的灵魂”,它可以将类的各个组成部分封装为一个个的对象,通过 Java 的反射机制,程序员可以更深入地控制程序的运行过程。

二、好处

  1. 可以在程序运行过程中操作这些封装的对象

  2. 可以解耦,提高程序的可扩展性

三、使用

首先准备一个Person类

public class Person {
    private String name;
    private Integer age;

    public String a;
    protected String b;
    String c;
    private String d;

    public Person() {
    }

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

    private void eat(String food){
        System.out.println("吃" + food);
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", a='" + a + '\'' +
                ", b='" + b + '\'' +
                ", c='" + c + '\'' +
                ", d='" + d + '\'' +
                '}';
    }
}

1. 获取Class对象的方式

@Test
public void test() throws ClassNotFoundException {
    //1. Class.forName("全类名"):将字节码文件加入进内存,获取Class对象
    Class<?> clazz1 = Class.forName("com.sjl.Person");
    System.out.println(clazz1);

    //2. 类名.class:通过类的属性class获取
    Class<Person> clazz2 = Person.class;
    System.out.println(clazz2);

    //3. 类对象.getClass():通过Object中的方法getClass()获取
    Person person = new Person();
    Class<? extends Person> clazz3 = person.getClass();
    System.out.println(clazz3);

    //同一字节码文件(*.class)在程序运行过程中只被加载一次,所以无论哪种方式获得的Class对象都是同一个
    System.out.println(clazz1 == clazz2); //true
    System.out.println(clazz2 == clazz3); //true
}

2. Class中的API

Class类的API,大致分为一下几类(参考jdk1.8 api)

  1. 获取类名
    • String getName()
  2. 获取成员变量们
    • Field getField(String name) 获取指定名称的公有方法
    • Field[] getFields() 获取公有方法列表
    • Field getDeclaredField(String name) 获取指定名称的方法
    • Field[] getDeclaredFields() 获取所有方法列表
  3. 获取构造方法们
    • Constructor< T > getConstructor(Class<?>... parameterTypes)
    • Constructor<?>[] getConstructors()
    • Constructor< T > getDeclaredConstructor(Class<?>... parameterTypes)
    • Constructor<?>[] getDeclaredConstructors()
  4. 获取成员方法们
    • Method getMethod(String name, Class<?>... parameterTypes)
    • Method[] getMethods()
    • Method getDeclaredMethod(String name, Class<?>... parameterTypes)
    • Method[] getDeclaredMethods()

3. 代码实现

@Test
public void test1(){
    //获取Class对象
    Class<Person> clazz = Person.class;
    //获取类的全名称
    String name = clazz.getName();
    System.out.println("类的全名称:" + name);
}

@Test
public void test2() throws Exception {
    //获取Class对象
    Class<?> clazz = Class.forName("com.sjl.Person");
    //获取Person的公有属性列表
    Field[] fields = clazz.getFields();
    for(Field field : fields){
        System.out.println("公共属性:" + field);
    }
    //获取指定的公有属性
    Field a = clazz.getField("a");
    System.out.println("指定公共属性a:" + a);

    System.out.println("================================");

    //获取Person的所有属性列表,不论权限修饰符
    Field[] declaredFields = clazz.getDeclaredFields();
    for (Field declaredField : declaredFields) {
        System.out.println("所有属性:" + declaredField);
    }

    //获取指定的属性
    Field d = clazz.getDeclaredField("d");
    System.out.println("指定属性d:" + d);

    System.out.println("================================");

    //为私有属性 设置值 和 获取值
    Field name = clazz.getDeclaredField("name");
    //暴力反射,忽略类的权限修饰符
    name.setAccessible(true);
    //设值,需要传一个person对象
    Person person = new Person();
    name.set(person, "张三");
    //取值,也需要传一个person对象
    Object value = name.get(person);
    System.out.println(value);
}

@Test
public void test3() throws Exception {
    //获取Class对象
    Class<?> clazz = Class.forName("com.sjl.Person");
    //获取公有构造方法
    Constructor<?>[] constructors = clazz.getConstructors();
    for (Constructor<?> constructor : constructors) {
        System.out.println(constructor);
    }

    //获取空参构造器
    Constructor<?> constructor1 = clazz.getConstructor();
    //通过构造器创建对象 newInstance
    Object c1 = constructor1.newInstance();
    System.out.println(c1);

    //如果是空参构造器还可以简写,直接用Class对象创建
    Object c2 = clazz.newInstance();
    System.out.println(c2);
}

@Test
public void test4() throws Exception {
    //获取Class对象
    Class<?> clazz = Class.forName("com.sjl.Person");
    //获取成员方法
    Method eat_method = clazz.getDeclaredMethod("eat", String.class);
    //eat是私有方法,因此需要暴力反射
    eat_method.setAccessible(true);
    //执行方法,传入对象和参数
    Person person = new Person();
    eat_method.invoke(person, "火锅");
}

四、应用小案例

实现:在不改变当前代码的情况下,创建任意一个类的实例并调用它的方法

思路:

  1. 编写一个配置文件存放要创建的类的全类名,方法名
  2. 读取配置文件内容获得对应的信息
  3. 通过反射,根据全类名获得Class对象,创建其实例,调用其方法

配置文件:

className=com.sjl.Person
methodName=eat

Java代码:

public class ReflectDemo {
    //实现:在不改变当前代码的情况下,创建任意一个类的实例并调用它的方法
    public static void main(String[] args) throws Exception {
        //获取Class对象
        Class<?> clazz = Class.forName(getValue("className"));
        //创建实例
        Object o = clazz.newInstance();
        //获取方法对象
        Method eat_method = clazz.getDeclaredMethod(getValue("methodName"), String.class);
        //执行
        eat_method.setAccessible(true);
        eat_method.invoke(o, "火锅");
    }

    //根据key值读取配置文件内容
    public static String getValue(String key) throws IOException {
        Properties properties = new Properties();
        InputStream is = ReflectDemo.class.getClassLoader().getResourceAsStream("config.properties");
        properties.load(is);
        assert is != null;
        is.close();
        return properties.getProperty(key);
    }
}

运行结果:

吃火锅

新建一个类Person2,将eat方法的输出稍作修改,将配置文件修改为

className=com.sjl.Person2
methodName=eat

运行结果

吃火锅,我是二号选手
posted @ 2020-02-07 19:33  农夫三拳有点疼~  阅读(285)  评论(0编辑  收藏  举报