初探反射机制

如果需要修改Test类对象的private testName属性,我们会怎么做呢?

class Test {
    private String testName = "hello";

    public void setTestName(String name) {
        testName = name;
    }
}

通常我们可以提供一个public setTestName方法去修改testName值。
当然,还可以使用本文的主角\color{red}{反射机制}达到修改testName值。

反射机制常用方法之一:修改对象中private属性
创建Test类对象test,test对象中有私有属性testName,testName初始值为hello。通过反射,将testName值修改成world。

class Test {
    private String testName = "hello";

    public String getTestName() {
        return testName;
    }

    public static void main(String[] args) {
        try {
            Test test = new Test();
            System.out.println("testName:" + test.getTestName());//testName:hello
            Class<?> clazz = test.getClass();
            Field testNameField = clazz.getDeclaredField("testName");
            testNameField.setAccessible(true);
            testNameField.set(test, "world");
            System.out.println("testName:" + test.getTestName());//testName:world
        } catch (Exception e) {
        }
    }
}

通过例子我们感受到反射机制的作用,到底什么是反射,为什么需要反射,反射常用方法还有哪些。

  • 什么是反射?

在计算机科学中,反射是指计算机程序在运行时(Run time)可以访问、检测和修改它本身状态或行为的一种能力。
用比喻来说,反射就是程序在运行的时候能够“观察”并且“修改”自己的行为。

在Java中,
反射能“观察”自己的具体表现就是:对于任意一个类,都能够知道这个类的所有方法和属性。
反射能“修改”自己的具体表现就是:对于任意一个对象,都能够调用它的所有方法和改变所有的属性。

  • 为什么需要反射?

1.反射使得设计更优雅易懂,例如日常使用的Spring框架,注解的形式就相当优雅易懂而且实用。
关于“注解”详细介绍请点击//TODO

@Controller
class Test {
    public static void main(String[] args) {
        Class clazz = Test.class;
        Annotation[] annotations = clazz.getAnnotations();
        for (Annotation annotation : annotations) {
            System.out.println(annotation);
        }
    }
}

2.Java中的动态代理、拦截器等,都是使用反射的“观察”和“修改”能力。

通过反射的“观察”和“修改”能力,可以设计出更优雅易读而且实用的应用程序。为什么需要反射?因为在设计程序时,需要“观察”和“修改”的能力,而拥有这些能力的我们定义为反射。

  • 反射的常用方法

获取Class对象的三种方式

1.通过对象获取

class Test {
    public static void main(String[] args) {
        Test test = new Test();
        Class clazz = test.getClass();
    }
}

2.通过类获取

class Test {
    public static void main(String[] args) {
        Class clazz = Test.class;
    }
}

3.通过Class.forName获取

class Test {
    public static void main(String[] args) {
        try {
            Class clazz = Class.forName("Test");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}
获取类字段
class Test {
    private String nickname;

    public static void main(String[] args) {
        Class clazz = Test.class;
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            System.out.println(field);
        }
    }
}
获取类方法
class Test {
    private String nickname;

    public String getNickname() {
        return nickname;
    }

    public static void main(String[] args) {
        Class clazz = Test.class;
        Method[] methods = clazz.getDeclaredMethods();
        for (Method method : methods) {
            System.out.println(method);
        }
    }
}
获取类构造器
class Test {
    private String nickname;

    public Test(String nickname) {
        this.nickname = nickname;
    }

    public static void main(String[] args) {
        try {
            Class clazz = Test.class;
            Constructor constructor = clazz.getConstructor(String.class);
            Test test = (Test) constructor.newInstance("tom");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

更多反射的方法可以查看Class类的源码。查看源码时,例如Class.forName,里面调用forName0方法,会发现forName0方法是一个native方法,如何查看native方法的源码,JDK native源码查看

    @CallerSensitive
    public static Class<?> forName(String className)
                throws ClassNotFoundException {
        Class<?> caller = Reflection.getCallerClass();
        return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
    }

    /** Called after security check for system loader access checks have been made. */
    private static native Class<?> forName0(String name, boolean initialize,
                                            ClassLoader loader,
                                            Class<?> caller)
        throws ClassNotFoundException;

我们会发现,Java中,反射基本围绕着Class类展开,去实现反射的“观察”和“修改”能力,关于Class类文件的结构,参考Class类文件的结构。了解Class类文件的结构后,需要再去了解虚拟机类加载机制以及类加载器//TODO。
而本文主要介绍反射机制基础概念,反射的意义及反射常用方法。
想深入理解反射在JVM中的实现原理,除了需要阅读源码(包括阅读native方法源码),还需要理解Class类文件的结构,以及虚拟机类加载机制,做完这些工作,才算比较明白反射机制。

深入理解反射机制//TODO

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。