本帖最后由 刘蕴学 于 2012-6-12 07:58 编辑
给你个建议,你可以试着阅读并自己实现一套collection framework 的所有成员,你这个帖子的问题在这些数据结构的迭代器实现那里看代码更直观
1.反射时有个很蛋疼的问题,即引用类型和实例类型,当然这个得看具体情况。
比如下边这段代码,你更应该看是在什么情况下才选择那种方式。- Collection a = new ArrayList();
- Collection.class.getMethod("add", null);
- a.getClass().getMethod("add", null);
复制代码 2.反射的另外一个弊端是,基本类型处理方面,你需要将类型进行转换为包装类型,反射时并不会自动装箱和拆箱。
这段代码是动态代理的代理代码生成中的基本类型处理代码,功能是将返回值返回到代理方法调用处,问题就是这么产生的,返回值因为可能存在范型,所以是Object的,但是基本类型不能被转成基本类型,所以如此蛋疼的代码就产生了。- //写入返回值,不过要注意的是基本类型
- //如果直接返回不强制转换为包装类型的
- //话会出现ClassCastException
- //Object cannot be cast to primitive type
- if(returnType != void.class)
- {
- if(returnType == boolean.class)
- sb.append(" (Boolean)");
- else if(returnType == int.class)
- sb.append(" (Integer)");
- else if(returnType == byte.class)
- sb.append(" (Byte)");
- else if(returnType == short.class)
- sb.append(" (Short)");
- else if(returnType == long.class)
- sb.append(" (Long)");
- else if(returnType == float.class)
- sb.append(" (Float)");
- else if(returnType == double.class)
- sb.append(" (Double)");
- else if(returnType == char.class)
- sb.append(" (Character)");
- else
- sb.append(" ("+returnType.getCanonicalName()+")");
- sb.append("obj");
- }
复制代码 3.关于变量,被 static final 标识的将无法被反射。
4.关于方法,反射时的Method类型实例,也依赖于类实例的类型,并不是说我有一个跟他一样的方法,就可以使用该method实例来进行相同的访问。
比如下面的代码,方法的反射是错误的,当然变量的访问也不行。- public class asd
- {
- public static void main(String[] args) throws Exception
- {
- A.class.getField("a").get(new B());
- A.class.getMethod("asd", new Class[]{}).invoke(new B(), new Object[]{});
- }
-
- public static class A
- {
- public int a = 0;
- public void asd(){}
- }
-
- public static class B
- {
- public int a = 0;
- public void asd(){}
- }
- }
复制代码 5.还有一些限定方法很复杂,比如Object的clone,如果你的类不实现这个Cloneable的接口(虽然这个clone方法已经被Object声明了),反射时会有让你发狂的问题。- public class asd
- {
- public static void main(String[] args) throws Exception
- {
- B.class.getMethod("clone", new Class<?>[]{}).invoke(new B(), new Object[]{});
- A.class.getMethod("clone", new Class<?>[]{}).invoke(new A(), new Object[]{});
- }
-
- public static class A{}
- public static class B implements Cloneable
- {
- @Override
- //Overrides: clone() in Object
- public Object clone()
- {
- return this;
- }
- }
- }
复制代码 如果猜想Object中的clone方法是protected的,使用暴力反射来试着访问,就像下边这段代码,结果大吃一惊,还是NoSuchMethod。。。- public class asd
- {
- public static void main(String[] args) throws Exception
- {
- B.class.getMethod("clone", new Class<?>[]{}).invoke(new B(), new Object[]{});
- Method clone = A.class.getDeclaredMethod("clone", new Class<?>[]{});
- clone.setAccessible(true);
- clone.invoke(new A(), new Object[]{});
- }
-
- public static class A{}
- public static class B implements Cloneable
- {
- @Override
- //Overrides: clone() in Object
- public Object clone()
- {
- return this;
- }
- }
- }
复制代码 6.暴力反射是不推荐的,而且在程序中大量应用反射也不对,暴力反射会影响程序稳定性,导致不可预期的问题,比如我声明了一个私有成员,我以为你可以按照我的封装方式来安全访问,结果就是可能你的暴力反射会导致我的代码崩溃。反射的带价还是很高,在无必要的情况一般不推荐使用反射,效率只是一方面。
7.访问权限,这个实际上很好理解,你必须保证你的反射过程中,所有路径都是可行的,仅目的地可以到达是不行的。
8.底层名字,这个我也不知道应该怎么说,或许sun本身就很蛋疼,举例来说像数组,你如果使用getName方法,会拿到一个[L开头的名字,所以getCanonicalName才是正确的做法。
9.反射可以帮助我们来解开很多神秘的面纱,比如匿名内部类为什么不能访问非final的局部变量这个问题,只要是百度的,99%的人的答案都是错的,那1%中99%虽然知道是什么原理,但是他们也没有办法证明。但这并不证明我们要在程序中大量应用反射。
下边这段代码就是这个问题的根本- /* 定义一个局部内部类的上层接口,用以允许返回局部内部类实例 */
- interface SuperInner
- {
- void inner();
- }
- /* 定义一个外部类,包括一个outer方法,用以返回局部内部类实例 */
- class Outer
- {
- int y = 0;
- //out方法将返回inner实例
- SuperInner outer()
- {
- //假设我们这里x可以被inner访问,那么outer函数返回之后
- //x已经被销毁,inner对象就无法访问x
-
- //换句话说就是Inner对象的生存周期超出了x的边界,而能保证
- //生存周期一致的情况只有inner对象自己的成员,所以通过在
- //inner里边复制一个x来保存这个局部变量x的值以达到目的
- //就像val$x一样
-
- //但这种办法有个致命点,如果在inner里对val$x进行赋值操作
- //外边的x并不会被改变,所以为了保证两个对象的一致性,该变量
- //x以及其复制变量val$x必须为终态的,也就是final,不能再次
- //赋值
-
- //反编译inner可以证明以上结论,在字节码里
- // question6.Outer$1Inner(question6.Outer, java.lang.String)
- //这是构造方法,传outer不奇怪,内部类都需要外部类的引用指针
- //这个String字符串就值得思考了
-
- //4 getfield val$y
- //在inner方法中可以看到这个字节码,是val$y 而不是y
- int x = 4;
- final String y = new String("asd");
- class Inner implements SuperInner
- {
- // int val$x = 4;
- public void inner()
- {
- try
- {
- //这里通过反射,可以发现证明此y非彼y,因为我们并没有
- //在inner里定义val$y变量,而我们却可以拿得到,没异常
- //而我们改的是val$y的值为qqq,下边的syso(y)居然会输出
- //qqq,结论可以证实
-
- //通过代码也可以看出来,这个val$y肯定是private的,并且
- //他也是final的
- Field field = getClass().getDeclaredField("val$y");
- field.setAccessible(true);
- System.out.println(field.get(Inner.this));
- field.set(Inner.this, "qqq");
- }
- catch (Exception e)
- {
- e.printStackTrace();
- }
- // System.out.println(x);本行编译报错,提示局部内部类访问必须是终态的
- System.out.println(y);//输出y的值
- }
- }
- //当inner实例被返回时,x将被销毁
- return new Inner();
- }
- }
复制代码 10.反射与范型是不可分割的,至少目前来看是这样的,典型的例子就是Class<?> clazz = ...;这样的声明方式,如果你单写Class clazz 这样的声明是不安全的,至少编译器是这么认为的,黄线真的很难看。在实际使用代码时,范型要清晰的写出来,避免一些类型不确定的问题,而在反射时就要把这种假范型完全扔出去,在所有范型的地方,本质仍然是Object。
11.多接口的实现类的方法反射,这个在动态代理会出现,代理是根据你声明接口带反射方法的,如果单一接口实现不会出问题,但是多接口的情况就会很复杂,你需要知道他依赖于哪一个接口。 |