文档章节

Java反射

清风伴月
 清风伴月
发布于 2017/09/03 21:34
字数 1101
阅读 4
收藏 0

这一节课,我们来讲一下反射。反射是Java中非常重要的一个机制。虽然在实际使用中我们很少直接通过反射写代码。但是凡是用过spring的,一定听说过动态代理,动态代理的实现原理中,反射占据了非常重要的地位。还有各种RPC实现,它们的背后也有反射的影子。

反射的概念

反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。

要理解反射最好的办法就是看代码:

public class TestReflection {
    public static void main(String args[]) {
        try {
            Test obj = Test.class.newInstance();
            BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
            String s = br.readLine();

            Method m;
            if (s.equals("sayh")) {
                m = Test.class.getDeclaredMethod("sayHello");
            } else
                m = Test.class.getDeclaredMethod("sayWorld");

            m.invoke(obj);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }
}

class Test {
    public void sayHello() {
        System.out.println("hello");
    }

    public void sayWorld() {
        System.out.println("world");
    }
}

运行这个程序,输入 sayh,就会打印出 hello。

我来解释一下这个程序,首先,我们使用Test.class创建了一个Test类型的实例obj。然后再通过getDeclaredMethod找到Test的某一个方法,如果我们在控制台上输入的是 "sayh",就调用它的sayHello方法,否则就调用sayWorld方法。

我把这个代码,不使用反射再写一遍:

    public static void show() throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        String s = br.readLine();

        Test obj = new Test();
        if (s.equals("sayh")) {
            obj.sayHello();
        } else
            obj.sayWorld();
    }

这两个的逻辑是等价的。那我们还要反射干啥呢?

反射的作用

考虑下面的例子:我要调用的函数名和函数参数都是通过IO库读入的,可能是从文件中,也可能是从网络上,而且在调用某个函数之前,我希望打印一行LOG,可想而知,如果是直接写,就会变得很难。但通过反射就会很轻松,我们来演示一下:

    public static void main(String args[]) {
        try {
            Test obj = Test.class.newInstance();
            BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
            String s = br.readLine();

            Method m = Test.class.getDeclaredMethod(s, int.class, int.class);
            Integer a = Integer.valueOf(br.readLine());
            Integer b = Integer.valueOf(br.readLine());

            System.out.println("Test." + m.getName() + " has been called with " + a + ", " + b);
            System.out.println(m.invoke(obj, a, b));
        } catch (IOException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }

运行结果:

比如说,我要对系统中的每一个函数都加上打印,来跟踪参数的变化和函数的调用情况,如果按照原来直接的写法,那么我真的是只能一个函数一个函数的修改了。但是使用反射呢?我可以在invoke之前统一加。这就带来了很大的方便了。

动态加载类和属性

我们上面看了关于方法的例子,再来看一个动态加载类和属性的例子:

public class TestReflection {
        public static void main(String args[]) {
            try {
                Test obj = Test.class.newInstance();
                BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
                String fieldName = br.readLine();

                Field f = Test.class.getDeclaredField(fieldName);
                f.set(obj, Class.forName("cn.hinus.sort." + br.readLine()).newInstance());

                obj.sort();
            } catch (IOException e) {
                e.printStackTrace();
            } catch (NoSuchFieldException e) {
                e.printStackTrace();
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
    }

    class Test {
        public Sorter sorter;

        public void sort() {
            int[] a = {5, 7, 3, 1, 4, 6, 2};
            sorter.sort(a);
            for(int i : a) {
                System.out.println(i);
            }
        }
}

运行后输入sorter和BubbleSorter,就会看到:

这里,我们使用了,第十二周课程里的排序的例子。这个例子的重点在于,如果我们想在运行时修改sorter的具体实现,我们根本就不需要再去改代码了。直接通过输入参数,就能改变sorter的具体实现。

注意这里面的两个要点:通过getDeclaredField获取 obj 指定的属性,通过Class.forName动态地加载一个类,并通过newInstance动态创建这个类的实例。

好了。今天的讲解就到这里了,关于反射的更多知识,我们下节课再看。

1. 把我们今天的例子的从控制台上读取输入,变成从文件中读取,并尝试一下,只通过修改文本文件的内容就可以改变程序行为,而不必重新编译代码。

2. 学一下,spring的bean的定义,再思考一下这种动态组合对象的能力是怎么做到的。学习完反射,你是不是对依赖注入有了直观的理解?

本文转载自:https://zhuanlan.zhihu.com/p/26032547

共有 人打赏支持
清风伴月
粉丝 1
博文 129
码字总数 255659
作品 0
海淀
程序员
私信 提问

暂无文章

混合模型---logistic模型的混合

专家混合

中国龙-扬科
12分钟前
1
0
自定义参数校验注解 (实现ConstraintValidator方法)

Hibernate Validator常用注解(图网上找的) 2.自定义校验器 a.注解类 @Target({FIELD, METHOD, PARAMETER, ANNOTATION_TYPE})@Retention(RUNTIME)@Documented@Constraint(validatedB......

INSISTQIAO
15分钟前
1
0
Integer 实现

Integer 封装类型,参数传递传的是值,不是引用 内带缓存,-128 到127 -128 到127 直接数值 IntegerCache 如果不在这个范围,才会new Integer () public static Integer valueOf(int ...

Java搬砖工程师
16分钟前
1
0
数字IT基础-数据采集总线

数字化运营基础 在如今“双十一”不再是线上活动的代名词,而逐步变为一场线上线下同时进行的消费者盛宴。销售、运营、物流、生产商等都在开足马力在各大渠道备战,据统计: 消费者在期间被平...

阿里云官方博客
23分钟前
0
0
三次握手四次挥手

背景 和女朋友异地恋,为了保持感情我提议每天晚上视频聊天一次。 从好上开始,到现在,一年多也算坚持下来了。 问题: 有时候聊天的过程中,我的网络或者她的网络可能会不好,视频就会卡住,...

瑞查德-Jack
26分钟前
3
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部