Java 反射

反射 Reflection

将类的各个组成部分封装为其他对象, 称为反射机制

反射是框架设计的灵魂

优点: 

  1. 可以在程序运行过程中, 操作这些对象 (例如对某个实例一无所知的情况下, 尝试调用其方法)
  2. 可以解耦, 提高程序的可扩展性

 

Class 类

每加载一种 class,JVM就为其创建一个 Class 类型的实例,并关联起来。注意:这里的 Class 类型是一个名叫 Class 的 class。它长这样:

public final class Class {
    private Class() {}
}

  

获取一个 class 的 反射对象(Class 实例), 有三种方法:

1) Class.forName(全类名): 将字节码文件(.class) 加载进内存, 返回 Class 对象

  • 多用于配置文件, 读取文件, 加载类

2) 类名.Class: 通过类的静态属性 class 获取

  • 多用于参数的传递

3) 对象.getClass(): 在 Object 类中定义

  • 多用于对象的获取字节码的方式

 

同一个字节码文件(.class) 在程序里只被加载一次, 不论通过哪一种方式获取都是同一个

demo:

package com.company;

public class Main {
    public static void main(String[] args) throws ClassNotFoundException {
        // forName()
        Class cls1 = Class.forName("com.company.Person");
        System.out.println(cls1);

        // .class
        System.out.println(Person.class);

        // .getClass()
        System.out.println(new Person().getClass());
    }
}

class Person {
    private String name;

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

    public String getName() {
        return name;
    }

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

 

反射对象的功能

获取的功能:

  1. 获取成员变量

    • Field[] getFields(): 获取所有 public 修饰的成员变量

    • Field getField(String name)

       

    • Field[] getDeclaredFields(): 获取所有的成员变量 (不考虑修饰符)

    • Field getDeclaredField(String name)

  2. 获取构造方法

    • Constructor<?>[] getConstructors()

    • Constructor<T> getConstructor(Class<?>... parameterTypes)

       

    • Constructor<?>[] getDeclaredConstructor()

    • Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)

  3. 获取成员方法

    • Method[] getMethods()

    • Method getMethod(String name, Class<T>... parameterTypes)

       

    • Method[] getDeclaredMethods()

    • Method[] getDeclaredMethods(String name, Class<T>... parameterTypes)

  4. 获取类名, 方法名

    • String getName()

 

获取 / 设置字段(Field)的值

Field[] getFields(): 获取所有 public 修饰的成员变量

Field getField(String name)

 

Field[] getDeclaredFields(): 获取所有的成员变量 (不考虑修饰符)

Field getDeclaredField(String name)

 

Field 对象操作成员变量:

  1. 获取值:
    • void get(Object obj);
  2. 设置值:
    • void set(Object obj, Object value)
  3. 忽略访问修饰符的安全检查:
    • setAccessible(true)

 

demo: 

import java.lang.reflect.Field;

public class Main {
    public static void main(String[] args) throws Exception {
        // .class
        Class pc = Person.class;
        // 获取所有的字段 (忽略修饰符)
        Field[] farray = pc.getDeclaredFields();
        for (Field field:farray) {
            System.out.println(field.getName());  //name hobby
        }

        // 获取指定名称的字段, 并修改值
        Field fh = pc.getDeclaredField("hobby");
        Field fn = pc.getDeclaredField("name");
        Person p = new Person();
        fn.set(p, "johny");
        System.out.println(fn.get(p));  // johny
        // 私有字段需要暴力反射
        fh.setAccessible(true);
        fh.set(p, "我的爱好");
        System.out.println(fh.get(p));  // 我的爱好
        System.out.println(p.toString());  // Person{name='johny', hobby='我的爱好'}
    }
}

class Person {
    public String name;
    private String hobby;

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", hobby='" + hobby + '\'' +
                '}';
    }
}

运行结果:
name
hobby
johny
我的爱好
Person{name='johny', hobby='我的爱好'}

 

获取 Constructor 构造器 来创建对象

Constructor<?>[] getConstructors(): 获取所有 public 修饰的构造方法

Constructor<T> getConstructor(Class<?>... parameterTypes)

 

Constructor<?>[] getDeclaredConstructor(): 获取所有的构造方法 (不考虑修饰符)

Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)

 

Constructor 构造器来创建对象: 

  1. public T newInstance(Object ... initargs)

 

demo:

import java.lang.reflect.Constructor;

public class Main {
    public static void main(String[] args) throws Exception {
        // .class
        Class pc = Person.class;
        // 获取构造器
        Constructor c = pc.getConstructor(String.class, String.class);
        // 创建对象
        Object person = c.newInstance("johny", "my hobby");
        System.out.println(person);  // Person{name='johny', hobby='my hobby'}

        // 空参构造
        Object person2 = pc.newInstance();
        System.out.println(person2);  // Person{name='null', hobby='null'}
    }
}


class Person {
    public String name;
    private String hobby;

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

    public Person() {
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", hobby='" + hobby + '\'' +
                '}';
    }
}

 

获取成员方法

Method[] getMethods(): 获取所有 public 修饰的成员方法
Method getMethod(String name, Class<T>... parameterTypes)

Method[] getDeclaredMethods(): 获取所有的成员方法 (不考虑修饰符)
Method[] getDeclaredMethods(String name, Class<T>... parameterTypes)

getName():           

  • Method.getName(): 获取方法名;
  • Class.getName: 获取类名

 

demo:

import java.lang.reflect.Method;

public class Main {
    public static void main(String[] args) throws Exception {
        // .class
        Class pc = Person.class;
        // 获取指定的方法
        Method m1 = pc.getMethod("testMethod");
        Method m2 = pc.getDeclaredMethod("argsMethod", String.class);
        // 调用方法 (和操作字段的形式相似)
        Person p = new Person();
        m1.invoke(p);
        // 暴力反射
        m2.setAccessible(true);
        m2.invoke(p, "hhhhhh");

        // 获取方法名
        System.out.println(m1.getName());  // testMethod
        System.out.println(m2.getName());  // argsMethod
        // 获取类名
        System.out.println(pc.getName());  // com.company.Person
    }
}


class Person {
    public void testMethod(){
        System.out.println("测试方法");  // 测试方法
    }

    private void argsMethod(String args){
        System.out.println("带有参数的方法, args: " + args);  // 带有参数的方法, args: hhhhhh
    }
}

 

一个反射的案例

需求: 写一个'框架', 不能改变该类的任何代码的前提下, 可以帮我们创建任意类的对象, 并且执行其中任意方法

实现:

  1. properties 配置文件

  2. 反射机制

步骤:

  1. 将需要创建的对象的全类名和需要执行的方法定义在配置文件中

  2. 在程序中加载读取配置文件

  3. 使用反射技术来加载类文件进内存

  4. 创建对象

  5. 执行方法

 

code:

import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.Properties;

public class Main {
    public static void main(String[] args) throws Exception {
        // 创建配置文件对象
        Properties pro = new Properties();
        // 获取加载 class 目录下的配置文件
        ClassLoader clsL = Main.class.getClassLoader();
        InputStream is = clsL.getResourceAsStream("pro.properties");
        pro.load(is);

        // 获取配置文件中的参数
        String className = pro.getProperty("className");
        String methodName = pro.getProperty("methodName");
        // 创建对象,
        System.out.println(className);  // com.company.Student
        Class cls = Class.forName(className);
        Object obj = cls.newInstance();
        // 执行方法
        Method m = cls.getMethod(methodName);
        m.invoke(obj);  // i love leaning
    }


}


class Student {
    public void study(){
        System.out.println("i love leaning");
    }
}

 

pro.properties:  (参数不要带引号)

className=com.company.Student
methodName=study

 

 

ending ~ 

 

 

posted @ 2019-11-08 18:22  kaichenkai  阅读(153)  评论(0)    收藏  举报