java--反射

65 阅读4分钟

「这是我参与11月更文挑战的第10天,活动详情查看:2021最后一次更文挑战

什么是反射

2019042011494188.png

对于运行阶段的一个类,可以动态的获取这个类的所有属性、方法和构造方法等信息。对于运行阶段的一个对象,也可以动态的获取这个对象的属性、方法、构造方法等信息的机制,称之为反射机制。 反射就是在运行时才知道要操作的类是什么,并且可以在运行时获取类的完整构造,并调用对应的方法。

 Java反射机制主要提供了以下功能:

  • 在运行时构造任意一个类的对象
  • 在运行时获取任意一个类所具有的成员变量和方法
  • 在运行时调用任意一个对象的方法(属性)
  • 生成动态代理

Class类

主要方法

getName():获得类的完整名字。
​
getFields():获得类的public类型的属性。
​
getDeclaredFields():获得类的所有属性。包括private 声明的和继承类
​
getMethods():获得类的public类型的方法。
​
getDeclaredMethods():获得类的所有方法。包括private 声明的和继承类
​
getMethod(String name, Class[] parameterTypes):获得类的特定方法,name参数指定方法的名字,parameterTypes 参数指定方法的参数类型。
​
getConstructors():获得类的public类型的构造方法。
​
getConstructor(Class[] parameterTypes):获得类的特定构造方法,parameterTypes 参数指定构造方法的参数类型。
​
newInstance():通过类的构造方法创建这个类的一个对象。
​

class获取示例

public class Apple {
​
    private int price;
​
    public int getPrice() {
        return price;
    }
​
    public void setPrice(int price) {
        this.price = price;
    }
​
    public static void main(String[] args) throws Exception{
        //正常的调用
        Apple apple = new Apple();
        apple.setPrice(5);
        System.out.println("Apple Price:" + apple.getPrice());
        //通过.class方式调用
        Class class1 = Apple.class;
        //使用class对象的forName方法调用
        Class clz = Class.forName("com.demo.api.Apple");
        Method setPriceMethod = clz.getMethod("setPrice", int.class);
        Constructor appleConstructor = clz.getConstructor();
        Object appleObj = appleConstructor.newInstance();
        setPriceMethod.invoke(appleObj, 14);
        Method getPriceMethod = clz.getMethod("getPrice");
        System.out.println("Apple Price:" + getPriceMethod.invoke(appleObj));
    }
}

从这个简单的例子可以看出,一般情况下我们使用反射获取一个对象的步骤:

  • 获取类的 Class 对象实例
Class clz = Class.forName("com.demo.api.Apple");
  • 根据 Class 对象实例获取 Constructor 对象
Constructor appleConstructor = clz.getConstructor();
  • 使用 Constructor 对象的 newInstance 方法获取反射类对象
Object appleObj = appleConstructor.newInstance();

而如果要调用某一个方法,则需要经过下面的步骤:

  • 获取方法的 Method 对象
Method setPriceMethod = clz.getMethod("setPrice", int.class);
  • 利用 invoke 方法调用方法
setPriceMethod.invoke(appleObj, 14);

Class能实现的功能

1判断对象属于哪个类

Person person = new Person();

Class class2= person.getClass();

System.out.println("class2:"+class2);

输出:class2:class reflect.Person

2获取类信息

Class class1 = Person.class;

Method[] methods = class1.getMethods();

Method[] declaredMethods = class1.getDeclaredMethods();

Field[] declaredFields = class1.getDeclaredFields();

3构建对象

Person person = new Person();

Class class2= person.getClass();

Object o = class2.newInstance();

//强转前先用instanceof判断

if(o instanceof Person){undefined

((Person) o).workIng();

}

4动态执行方法

Class class1 = Class.forName("reflect.Person");

Method work = class1.getDeclaredMethod("work");

Person person = new Person();

work.invoke(person);

5动态操作属性

Class class1 = Class.forName("reflect.Person");

Person person = new Person();

Field field = class1.getDeclaredField("username");

field.set(person,"pine");

代码示例

public class Consumer {
    private long id;//私有的
    public String name;//共有的
    /*没参数构造体*/
    public Consumer() {
    }
    /*有参数构造体*/
    public Consumer(long id, String name) {
        this.id = id;
        this.name = name;
    }
    /*getter setter*/
    public long getId() {
        return id;
    }
​
    public void setId(long id) {
        this.id = id;
    }
​
    public String getName() {
        return name;
    }
​
    public void setName(String name) {
        this.name = name;
    }
    /*私有的无参buy方法*/
    private static void buy() {
        System.out.println("私有的无参buy方法");
    }
    /*共有的有参有返回值consume方法*/
    public String consume(String giftName) {
        System.out.println("买了一件礼物: " + giftName);
        return giftName;
    }
}
​
​
​
public class TestReflect {
    public static void getProperty() throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
        try {
            /* 获取对象类型 */
         Class<?> clazz  = Class.forName("com.Dan.Consumer");
         Object object = clazz.newInstance();
            /* 获取到所有属性 */
            Field[] field = clazz.getDeclaredFields();
            for (Field f:field) {
                //String fieldName = f.getName();// 取到属性名字
                //System.out.println(fieldName);
                System.out.println(f);
            }
            /* 获取到所有的方法,包括私有的,但不包括父类的 */
            Method[] methods = clazz.getDeclaredMethods();
            for (Method m:methods){
                //String methodName = m.getName();
                //System.out.println(methodName);
                System.out.println(m);
            }
            /* 所有的构造体 */
            Constructor[] constructors = clazz.getDeclaredConstructors();
            for (Constructor c:constructors){
                System.out.println(c);
            }
            // 调用方法
            Method method = clazz.getMethod("info", String.class, long.class);//获取方法
            method.invoke(object, "隔壁老王",2017032009);
            //得到属性
            Field aField = clazz.getDeclaredField("name"); //因为name变量是private的,所以不能用getField方法
            aField.setAccessible(true);
            aField.set(object,"二大爷");
            Object obj = aField.get(object);
            System.out.println(obj);
            // 得到构造器
            Constructor constructor = clazz.getDeclaredConstructor(long.class, String.class);
            constructor.newInstance(2016040221, "王小二");
            
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
​
    public static void main(String[] args) throws InvocationTargetException, NoSuchMethodException,
            InstantiationException, IllegalAccessException, NoSuchFieldException {
        getProperty();
    }
}

总结

  1. 在运行期间,如果我们要产生某个类的对象,Java虚拟机(JVM)会检查该类型的Class对象是否已被加载。如果没有被加载,JVM会根据类的名称找到.class文件并加载它。一旦某个类型的Class对象已被加载到内存,就可以用它来产生该类型的所有对象,虚拟机只会产生一份字节码, 用这份字节码可以产生多个实例对象。
  2. 一个Java类中用一个Class类的对象来表示,一个类中的组成部分:成员变量,方法,构造方法,包等等信息也用一个个的Java类来表示,就像汽车是一个类,汽车中的发动机,变速箱等等也是一个个的类。表示Java类的Class类显示要提供一系列的方法,来获得其中的变量,方法,构造方法,修饰符,包等信息,这些信息就是用相应类的实例对象来表示,它们是Field、Method、Contructor、Package等等。

\