Java反射调用与面向对象结合使用产生的惊艳
Posted 李新杰的博客园
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java反射调用与面向对象结合使用产生的惊艳相关的知识,希望对你有一定的参考价值。
缘起
我在看Spring的源码时,发现了一个隐藏的问题,就是父类方法(Method)在子类实例上的反射(Reflect)调用。
初次看到,感觉有些奇特,因为父类方法可能是抽象的或私有的,但我没有去怀疑什么,这可是Spring的源码,肯定不会有错。
不过我去做了测试,发现确实是正确的,那一瞬间竟然给我了一丝的惊艳。
这其实是面向对象(继承与重写,即多态)和反射结合的产物。下面先来看测试,最后再进行总结。
友情提示:测试内容较多,不过还是值得一看。
具体方法的继承与重写
先准备一个父类,有三个方法,分别是public,protected,private。
public class Parent {
public String m1() {
return "Parent.m1";
}
protected String m2() {
return "Parent.m2";
}
private String m3() {
return "Parent.m3";
}
}
再准备一个子类,继承上面的父类,也有三个相同的方法。
public class Child extends Parent {
@Override
public String m1() {
return "Child.m1";
}
@Override
protected String m2() {
return "Child.m2";
}
private String m3() {
return "Child.m3";
}
}
public和protected是对父类方法的重写,private自然不能重写。
首先,通过反射获取父类和子类的方法m1,并输出:
Method pm1 = Parent.class.getDeclaredMethod("m1");
Method cm1 = Child.class.getDeclaredMethod("m1");
Log.log(pm1);
Log.log(cm1);
输出如下:
public java.lang.String org.cnt.java.reflect.method.Parent.m1()
public java.lang.String org.cnt.java.reflect.method.Child.m1()
可以看到,一个是父类的方法,一个是子类的方法。
其次,比较下这两个方法是否相同或相等:
Log.log("pm1 == cm1 -> {}", pm1 == cm1);
Log.log("pm1.equals(cm1) -> {}", pm1.equals(cm1));
输入如下:
pm1 == cm1 -> false
pm1.equals(cm1) -> false
它们既不相同也不相等,因为一个在父类里,一个在子类里,它们各有各的源码,互相独立。
然后,实例化父类和子类对象:
Parent p = new Parent();
Child c = new Child();
接着,父类方法分别在父类和子类对象上反射调用:
Log.log(pm1.invoke(p));
Log.log(pm1.invoke(c));
输出如下:
Parent.m1
Child.m1
父类方法在父类对象上反射调用输出Parent.m1,这很好理解。
父类方法在子类对象上反射调用输出Child.m1,初次看到的话,还是有一些新鲜的。
明明调用的是父类版本的Method,输出的却是子类重写版本的结果。
然后,子类方法分别在父类和子类对象上反射调用:
Log.log(cm1.invoke(p));
Log.log(cm1.invoke(c));
输出如下:
IllegalArgumentException
Child.m1
子类方法在父类对象上反射调用时报错。
子类方法在子类对象上反射调用时输出Child.m1,这很好理解
按照同样的方式,对方法m2进行测试,得到的结果和m1一样。
它们一个是public的,一个是protected的,对于继承与重写来说是一样的。
然后再对方法m3进行测试,它是private的,看看会有什么不同。
首先,父类方法分别在父类和子类对象上反射调用:
java反射机制