关于反射我又知道了一些新的东西

任何未知类型对象,都可以通过反射获取它的类信息。

1. 原理

说到反射,我们就不能不提Class这个类:

public final class Class<T> implements java.io.Serializable, GenericDeclaration, Type, AnnotatedElement {
                                  
}

在运行一个项目的时候,JVM不会一次性的将全部class文件加载到内存中,而是在需要用到某一个class文件的时候,才将它加载到内存中,这种方式叫做JVM的动态加载。

当JVM第一次把一个class文件加载进内存的时候,会为它创建一个Class类实例,并将它和该实例关联起来。这个Class实例具有这个class文件的所有信息,包括它所在的包、它的名称、它继承的父类、它实现的接口、它包含的属性,以及它拥有的方法。

那么,为什么我们在聊反射之前,就一定要聊Class类呢?因为,反射其实就是一种通过Class实例获取它关联的class文件信息的方法。

具体的通过代码来体现:

private void testReflection(Object obj) {

    Class<?> clazz = obj.getClass();

    Field[] fields = clazz.getDeclaredFields();

    Method[] methods = clazz.getDeclaredMethods();

}

通过objgetClass()方法,获取Class实例,该实例有obj的类的全部信息。如上代码,在未知obj的真实类型的情况下,透过反射,我们获取到了obj的类的所有属性及所有方法。

2. 作用

第一个作用,获取类的信息。在讲原理的时候,我们已经看到这样的例子了。

第二个作用,是获取某个属性的值,或者执行某个方法。代码示例如下:

我们先创建一个Desk类:

package obj.desk;

import lombok.Getter;
import lombok.Setter;

@Setter
@Getter
public class Desk {

    /**
     * 形状
     */
    private String shape;

    /**
     * 桌子腿条数
     */
    protected int legs;

    /**
     * 桌子高度
     */
    protected double height;

    /**
     * 额外描述
     */
    protected String desc;

    /**
     * 放置东西在桌子上
     * @param thing
     */
    public void place(String thing) {
        System.out.println("place on desk");
    }

    /**
     * 从桌子上移走东西
     * @param thing
     */
    public void remove(String thing) {
        System.out.println("remove from desk");
    }

}

然后,通过field.get(obj),就能拿到field的值。其中field是通过反射获取的,obj是那个对象。这样做当然是有意义的,因为我们并不是所有时候都能知道field是什么。有的时候,我们只是知道这些field的特征,比如是某个注解修饰的属性,而我们需要通过这些特征获取field的值。此外,通过代码field.setAccessible(true);,我们可以关闭访问权限控制,操纵private类型的属性,很cool是吧。

以下代码打印出String类型的属性的值:

import obj.desk.Desk;

import java.lang.reflect.Field;

public class TestWhatever {
    public static void main(String[] args) throws IllegalAccessException {
        Desk desk = new Desk();
        desk.setDesc("this is a desk");

        testReflection(desk);
    }

    private static void testReflection(Object obj) throws IllegalAccessException {
        // 1.获取obj的Class实例
        Class<?> clazz = obj.getClass();

        // 2.通过Class实例获取类的全部属性
        Field[] fields = clazz.getDeclaredFields();

        // 3.判空
        if (fields == null || fields.length == 0) {
            return;
        }

        // 4.匹配 & 打印
        for (Field field : fields) {
            // 匹配String类型的属性。注意:是通过'=='判断的两个Class实例是否相同的
            if (field.getType() == String.class) {
                // 这行代码可以打破访问权限控制的壁垒,即使非public的属性也能访问
                field.setAccessible(true);

                // 通过field.get(obj)可以获取到该属性的值
                String value = field.getName() + ": " + field.get(obj);

                System.out.println(value);
            }
        }
    }
}

// 输出
// shape: null
// desc: this is a desk

类似的,通过反射我们可以修改方法访问权限,可以调用方法。

import obj.desk.Desk;

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

public class TestWhatever {
    public static void main(String[] args) throws IllegalAccessException, InvocationTargetException {
        Desk desk = new Desk();
        desk.setDesc("this is a desk");

        testReflection(desk);
    }

    private static void testReflection(Object obj) throws IllegalAccessException, InvocationTargetException {
        // 1.获取obj的Class实例
        Class<?> clazz = obj.getClass();

        // 2.获取obj的类的全部属性
        Method[] methods = clazz.getDeclaredMethods();

        // 3.判空
        if (methods == null && methods.length == 0) {
            return;
        }

        // 4.调用所有get方法 & 打印执行结果
        for (Method method : methods) {
            // 找到所有get方法
            if (method.getName() != null && method.getName().startsWith("get")) {

                // 这行代码可以打破访问权限控制的壁垒,即使非public的方法也能访问
                method.setAccessible(true);

                // 通过method.invoke(obj)可以调用obj的方法
                String result = method.getName() + "() return: " + method.invoke(obj);

                System.out.println(result);

            }
        }
    }
}

// 输出
// getDesc() return: this is a desk
// getLegs() return: 0
// getHeight() return: 0.0
// getShape() return: null

3. API

在我们理解了反射的原理之后,反射对于我们来说,其实只是一系列的API。而类具有什么信息,我们就有获取什么信息的API,这两个是对应的。以下是几个API示例:

3.1.获取Class实例的三种方式
// 1.通过调用对象的getClass方法获取Class实例
Class<?> clazz = obj.getClass();

// 2.通过类的静态属性获取Class实例
Class<?> clazz2 = Desk.class;

// 3.通过类全名获取Class实例
Class<?> clazz3 = Class.forName("obj.desk.Desk");
3.2.属性相关API
import obj.desk.Desk;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;

public class TestWhatever {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
        Desk desk = new Desk();
        desk.setDesc("this is a desk");

        testReflection(desk);
    }

    private static void testReflection(Object obj) throws IllegalAccessException {
        Class<?> clazz = obj.getClass();

        // 1.获取field
        // 获取类的全部属性
        Field[] fields = clazz.getDeclaredFields();

        // 获取类的全部public属性,及父类的全部public属性
        Field[] fields1 = clazz.getFields();

        // 获取类的属性
        Field field = null;
        try {
            field = clazz.getDeclaredField("desc");
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }

        // 获取类的public属性,或父类的public属性
        Field field1 = null;
        try {
            field1 = clazz.getField("desc");
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }

        if (field == null)
            return;

        // 2.获取field的信息
        // 获取属性类型
        Class<?> type = field.getType();
        System.out.println("type: " + type);
        // 获取属性名
        String name = field.getName();
        System.out.println("name: " + name);
        // 获取属性的修饰符:private, static, final,通过数值表示
        int modifiers = field.getModifiers();
        System.out.println("modifiers: " + modifiers);

        // 3.获取属性值
        field.setAccessible(true);
        Object value = field.get(obj);
        System.out.println("属性desc的值: " + value);

        // 4.设置字段值
        field.set(obj, "Set a new desc");
        System.out.println("属性desc的新值: " + field.get(obj));

    }
}

// 输出
// java.lang.NoSuchFieldException: desc
//	at java.lang.Class.getField(Class.java:1690)
//	at TestWhatever.testReflection(TestWhatever.java:35)
//	at TestWhatever.main(TestWhatever.java:11)
// type: class java.lang.String
// name: desc
// modifiers: 4
// 属性desc的值: this is a desk
// 属性desc的新值: Set a new desc
3.3.方法相关API
import obj.desk.Desk;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;

public class TestWhatever {
    public static void main(String[] args) throws IllegalAccessException, InvocationTargetException {
        Desk desk = new Desk();
        desk.setDesc("this is a desk");

        testReflection(desk);
    }

    private static void testReflection(Object obj) throws IllegalAccessException, InvocationTargetException {
        Class<?> clazz = obj.getClass();

        // 1.获取method
        // 获取类的全部方法
        Method[] methods = clazz.getDeclaredMethods();

        // 获取类的全部public方法,及父类的全部public方法
        Method[] methods1 = clazz.getMethods();

        // 获取类的方法
        Method method = null;
        try {
            method = clazz.getDeclaredMethod("place", String.class);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }

        // 获取类的public方法,或父类的public方法
        Method method1 = null;
        try {
            method1 = clazz.getMethod("place");
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }

        if (method == null)
            return;

        // 2.获取method的信息
        // 获取方法返回值类型
        Class<?> returnType = method.getReturnType();
        System.out.println("returnType: " + returnType);
        // 获取方法名
        String name = method.getName();
        System.out.println("name: " + name);
        // 获取方法的修饰符:private, static, final,通过数值表示
        int modifiers = method.getModifiers();
        System.out.println("modifiers: " + modifiers);
        // 获取方法参数列表
        Parameter[] parameters = method.getParameters();
        if (parameters != null && parameters.length > 0) {
            for (Parameter parameter : parameters) {
                // 获取参数的数据类型
                Class<?> parameterType = parameter.getType();
                System.out.println("parameterType: " + parameterType);

                // 获取参数的名称(在编译后的class文件中的名称)
                String parameterName = parameter.getName();
                System.out.println("parameterName: " + parameterName);
            }
        }

        // 3.调用方法
        method.setAccessible(true);
        method.invoke(obj, "a book");
    }
}

// 输出
// java.lang.NoSuchMethodException: obj.desk.Desk.place()
//	at java.lang.Class.getMethod(Class.java:1773)
//	at TestWhatever.testReflection(TestWhatever.java:36)
//	at TestWhatever.main(TestWhatever.java:12)
// returnType: void
// name: place
// modifiers: 1
// parameterType: class java.lang.String
// parameterName: arg0
// place a book on desk
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值