`
ydbc
  • 浏览: 718387 次
  • 性别: Icon_minigender_1
  • 来自: 大连
文章分类
社区版块
存档分类
最新评论

java 运行时类型识别(RTTI) - 2 - 反射

 
阅读更多

本文将叙述如何运行时查看类信息,其中包括变量,方法,以及通过反射修改变量,执行方法等

包括如何反射匿名内部类及如何执行其方法,但是笔者强烈不建议这么做,这里只是演示反射而已


下面是一个测试类

  1. packagereflect;
  2. publicclassOuter{
  3. static{
  4. System.out.println("Testing..");
  5. }
  6. TestInterfaceti=newTestInterface(){
  7. publicStringtest(){
  8. return"thisisti";
  9. }
  10. };
  11. publicOuter(Stringname){
  12. System.out.println("Outer");
  13. }
  14. publicStringtoString(){
  15. System.out.println("OutertoString");
  16. returnnewTestInterface(){
  17. publicStringtest(){
  18. return"thisisatest!";
  19. }
  20. }.test();
  21. }
  22. @SuppressWarnings("unused")
  23. privatevoidprivateMethod(){
  24. System.out.println("privateMethod");
  25. }
  26. protectedvoidprotectedMethod(){
  27. System.out.println("protectedMethod");
  28. }
  29. voidpackageMethod(){
  30. System.out.println("packageMethod");
  31. }
  32. publicstaticvoidstaticMethod(){
  33. System.out.println("staticMethod");
  34. }
  35. publicinterfaceTestInterface{
  36. publicStringtest();
  37. }
  38. publicstaticclassStaticInner{
  39. privatestaticfinalStringTAG="StaticInnerTAG";
  40. publicStaticInner(){
  41. System.out.println("StaticInner");
  42. }
  43. publicStringtoString(){
  44. System.out.println("StaticInnertoString");
  45. returnTAG;
  46. }
  47. }
  48. privateclassInner{
  49. Stringname;
  50. publicInner(){
  51. System.out.println("Inner");
  52. }
  53. publicInner(Stringname){
  54. System.out.println("reflect.Outer.Inner.Inner(Stringname)");
  55. this.name=name;
  56. }
  57. }
  58. }

其中包括了普通内部类,静态内部类,内部匿名类,接口等

其中外部类Outer没有默认构造器,String reflect.Outer.Inner.getName()是private的……


外部类

Constructor,Method,Class.forName,newInstance

下面来看如何对上述的类进行反射

首先先看外部类Outer

  1. publicstaticvoidmain(String[]args){
  2. try{
  3. Class<?>outer=Class.forName("reflect.Outer");
  4. outer=Outer.class;
  5. Constructor<?>[]constructors=outer.getConstructors();
  6. for(Constructor<?>constructor:constructors){
  7. Class<?>[]types=constructor.getParameterTypes();
  8. System.out.println(constructor.getName()+"("+arrayToString(types)+")");
  9. }
  10. Constructor<?>outerConstructor=outer.getConstructor(String.class);
  11. OuterouterInstance=(Outer)outerConstructor.newInstance("aouter");
  12. System.out.println(outerInstance);
  13. }catch(ClassNotFoundExceptione){
  14. e.printStackTrace();
  15. }catch(SecurityExceptione){
  16. e.printStackTrace();
  17. }catch(NoSuchMethodExceptione){
  18. e.printStackTrace();
  19. }catch(IllegalArgumentExceptione){
  20. e.printStackTrace();
  21. }catch(InstantiationExceptione){
  22. e.printStackTrace();
  23. }catch(IllegalAccessExceptione){
  24. e.printStackTrace();
  25. }catch(InvocationTargetExceptione){
  26. e.printStackTrace();
  27. }
  28. }
  29. publicstaticStringarrayToString(Class<?>[]classes){
  30. StringBuilderstringBuilder=newStringBuilder();
  31. for(Class<?>type:classes){
  32. stringBuilder.append(type.getCanonicalName());
  33. stringBuilder.append(",");
  34. }
  35. if(classes.length>0){
  36. stringBuilder.deleteCharAt(stringBuilder.length()-1);
  37. }
  38. returnstringBuilder.toString();
  39. }

打印结果

  1. Testing..
  2. reflect.Outer(java.lang.String)
  3. Outer
  4. OutertoString
  5. thisisatest!

下面简单分析下

  1. Class<?>outer=Class.forName("reflect.Outer");
  2. //outer=Outer.class;

我们对类reflect.Outer进行反射,这里使用Outer.class也可以获得Class对象

之前说过使用类字面量不会初始化该类,而Class.forName则会初始化,当使用Outer.class时候log如下

  1. reflect.Outer(java.lang.String)
  2. Testing..
  3. Outer
  4. OutertoString
  5. thisisatest!

顺序改变了,打印的时候Testing..实际是下面这句触发的

  1. OuterouterInstance=(Outer)outerConstructor.newInstance("aouter");

这时候加载Outer这个类,然后进行初始化


接下来我们查看了Outer的所有构造器Constructor,并打印出所需要的参数(与普通方法区别开来)

构造器只有一个,参数是String

如何执行这个带有String作为参数的构造器来返回一个Outer实例

  1. Constructor<?>outerConstructor=outer.getConstructor(String.class);
  2. OuterouterInstance=(Outer)outerConstructor.newInstance("aouter");
  3. System.out.println(outerInstance);

我们先取得我们需要的Constructor,然后,Constructor提供了newInstance方法,这样就可以获得Outer实例

最后打印实例,调用了toString函数

如果你尝试调用

  1. outer.newInstance();

则会看到如下异常

java.lang.InstantiationException: reflect.Outer

at java.lang.Class.newInstance0(Class.java:357)

at java.lang.Class.newInstance(Class.java:325)

at reflect.Test.main(Test.java:30)


这是因为Outer没有提供默认构造器

在获得Constructor时其实是有下面两个选择的,至于区别,我们放在Method里说

  1. outer.getConstructors();
  2. outer.getDeclaredConstructors()

现在看看如何获得类的方法,顺便看看Declared的作用

不如直接打印出来,这样比较直观

  1. Method[]methods=outer.getMethods();
  2. for(Methodmethod:methods){
  3. Class<?>[]types=method.getParameterTypes();
  4. System.out.println(method.getReturnType().getName()+""+outer.getName()+"."+method.getName()+"("+arrayToString(types)+")");
  5. }
  6. System.out.println("--------------------------------------");
  7. methods=outer.getDeclaredMethods();
  8. for(Methodmethod:methods){
  9. Class<?>[]types=method.getParameterTypes();
  10. System.out.println(method.getReturnType().getName()+""+outer.getName()+"."+method.getName()+"("+arrayToString(types)+")");
  11. }Class<?>outer=Outer.class;

结果如下

  1. voidreflect.Outer.staticMethod()
  2. java.lang.Stringreflect.Outer.toString()
  3. voidreflect.Outer.wait(long)
  4. voidreflect.Outer.wait()
  5. voidreflect.Outer.wait(long,int)
  6. booleanreflect.Outer.equals(java.lang.Object)
  7. intreflect.Outer.hashCode()
  8. java.lang.Classreflect.Outer.getClass()
  9. voidreflect.Outer.notify()
  10. voidreflect.Outer.notifyAll()
  11. --------------------------------------
  12. voidreflect.Outer.privateMethod()
  13. voidreflect.Outer.protectedMethod()
  14. voidreflect.Outer.packageMethod()
  15. voidreflect.Outer.staticMethod()
  16. java.lang.Stringreflect.Outer.toString()

调用getMethods时,Outer的方法只打印出了toString,其余结果均为父类Object的公有方法

调用getDeclaredMethods时,则只打印出在Outer中声明定义的方法

他们均打印出了公共的静态方法

所有带Declared和不带Declared的成对的方法都和上面的类似


如何利用反射执行一个方法,下面以void reflect.Outer.privateMethod()为例

  1. Methodmethod=outer.getDeclaredMethod("privateMethod");
  2. method.setAccessible(true);
  3. method.invoke(outerInstance,(Object[])null);
  4. method.setAccessible(false);

首先privateMethod是私有方法,所以为了能得到对应的Method对象,我们需要调用getDeclaredMethod


为了能访问私有方法,我们需要进行setAccessible(true)的设置

然后需要有个对象来执行,我们选择刚才的outerInstance

这时控制台输出

  1. privateMethod

干完活后,恢复其设置setAccessible(false)

下面是静态方法的执行

  1. Methodmethod=outer.getDeclaredMethod("staticMethod");
  2. method.setAccessible(true);
  3. method.invoke(Outer.class,(Object[])null);
  4. method.setAccessible(false);

ps:使用method.invoke(outerInstance, (Object[])null);也能执行,但是不该这样


内部类

isInterface,Field,isAccessible,setAccessible,getModifiers,Modifier.toString()

下面看看内部类的反射

  1. Class<?>[]classes=outer.getDeclaredClasses();
  2. for(Class<?>clazz:classes){
  3. if(clazz.isInterface()){
  4. System.out.println("interface:"+clazz.getName());
  5. }else{
  6. System.out.println("class:"+clazz.getName());
  7. }
  8. }

输出

  1. class:reflect.Outer$Inner
  2. class:reflect.Outer$StaticInner
  3. interface:reflect.Outer$TestInterface

看来反射提供的api功能还是很全的


下面是如何反射静态内部类

  1. try{
  2. Class<StaticInner>staticInner=StaticInner.class;
  3. StaticInnerstaticInnerInstance=staticInner.newInstance();
  4. }catch(SecurityExceptione){
  5. e.printStackTrace();
  6. }catch(IllegalArgumentExceptione){
  7. e.printStackTrace();
  8. }catch(InstantiationExceptione){
  9. e.printStackTrace();
  10. }catch(IllegalAccessExceptione){
  11. e.printStackTrace();
  12. }

也可以使用Class.forName方式,如果StaticInner是不可见的,那么上面的代码则无法编译,下面看看Class.forName

  1. try{
  2. Class<StaticInner>staticInner=(Class<StaticInner>)Class.forName("reflect.Outer.StaticInner");
  3. StaticInnerstaticInnerInstance=staticInner.newInstance();
  4. }catch(SecurityExceptione){
  5. e.printStackTrace();
  6. }catch(IllegalArgumentExceptione){
  7. e.printStackTrace();
  8. }catch(InstantiationExceptione){
  9. e.printStackTrace();
  10. }catch(IllegalAccessExceptione){
  11. e.printStackTrace();
  12. }catch(ClassNotFoundExceptione){
  13. e.printStackTrace();
  14. }

如果你和上面的写法一样,那么很不幸,运行时会得到如下错误

java.lang.ClassNotFoundException: reflect.Outer.StaticInner

at java.net.URLClassLoader$1.run(URLClassLoader.java:217)

at java.security.AccessController.doPrivileged(Native Method)

at java.net.URLClassLoader.findClass(URLClassLoader.java:205)

at java.lang.ClassLoader.loadClass(ClassLoader.java:321)

at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:294)

at java.lang.ClassLoader.loadClass(ClassLoader.java:266)

at java.lang.Class.forName0(Native Method)

at java.lang.Class.forName(Class.java:186)

at reflect.Test.main(Test.java:11)


我们去bin目录下可以发现这样的文件Outer$StaticInner.class

反射代码只需做如下改动

  1. Class<StaticInner>staticInner=(Class<StaticInner>)Class.forName("reflect.Outer$StaticInner");

.变成了$

这里newInstance之所以能执行,是因为StaticInner有默认构造器

下面看看StaticInner中生命了哪些变量

  1. Class<StaticInner>staticInner=StaticInner.class;
  2. Field[]fields=staticInner.getDeclaredFields();
  3. for(Fieldfield:fields){
  4. Class<?>type=field.getType();
  5. field.setAccessible(true);
  6. System.out.println(Modifier.toString(field.getModifiers())+""+type.getName()+""+field.getName());
  7. }
  8. StaticInnerStaticInnerInstance=staticInner.newInstance();
  9. Fieldfield=staticInner.getDeclaredField("TAG");
  10. booleanaccessible=field.isAccessible();
  11. if(!accessible){
  12. field.setAccessible(true);
  13. }
  14. System.out.println(field.get(StaticInnerInstance));
  15. field.set(StaticInnerInstance,"TAG");
  16. System.out.println(field.get(StaticInnerInstance));
  17. if(!accessible){
  18. field.setAccessible(false);
  19. }

结果如下

  1. privatejava.lang.StringTAG
  2. StaticInner
  3. StaticInnerTAG
  4. TAG

StaticInner只声明了一个变量,名字为TAG,类型为String

即使是private变量,设置setAccessible(true)之后也能改变其值

普通内部类的反射和上面类似


最后说下匿名内部类

匿名内部类的作用是仅仅使用一次,所以也没有必要进行反射,实际上也很难进行反射

例子中有两个匿名内部类

  1. TestInterfaceti=newTestInterface(){
  2. publicStringtest(){
  3. return"thisisti";
  4. }
  5. };
  6. publicStringtoString(){
  7. System.out.println("OutertoString");
  8. returnnewTestInterface(){
  9. publicStringtest(){
  10. return"thisisatest!";
  11. }
  12. }.test();
  13. }

在bin目录下发现有Outer$1.class和Outer$2.class


getDeclaredClasses发现也没有匿名内部类的信息


  1. try{
  2. Class<Outer>outer=Outer.class;
  3. Class<?>[]classes=outer.getDeclaredClasses();
  4. for(Class<?>clazz:classes){
  5. System.out.println(clazz.getName());
  6. }
  7. }catch(SecurityExceptione){
  8. e.printStackTrace();
  9. }catch(IllegalArgumentExceptione){
  10. e.printStackTrace();
  11. }

结果

  1. reflect.Outer$Inner
  2. reflect.Outer$StaticInner
  3. reflect.Outer$TestInterface

只有这三个,包括一个接口

如果非要对匿名内部类进行反射,也不是不可能

下面演示如何反射一个匿名内部类,仅仅是演示,没有任何意义,也不推荐这么做

我们以上面的toString方法里面的内部类做例子

  1. try{
  2. Class<?>clazz=Class.forName("reflect.Outer$2");
  3. Constructor<?>[]constructors=clazz.getDeclaredConstructors();
  4. constructors[0].setAccessible(true);
  5. Class<?>[]types=constructors[0].getParameterTypes();
  6. for(Class<?>type:types){
  7. System.out.println("ParameterTypes:"+type.getName());
  8. }
  9. TestInterfacetestInterface=(TestInterface)constructors[0].newInstance(newOuter(""));
  10. Method[]methods=clazz.getDeclaredMethods();
  11. for(Methodmethod:methods){
  12. Class<?>[]parameterTypes=method.getParameterTypes();
  13. System.out.println(method.getName()+"("+arrayToString(parameterTypes)+")");
  14. }
  15. Methodmethod=clazz.getDeclaredMethod("test");
  16. Stringstring=(String)method.invoke(testInterface);
  17. System.out.println(string);
  18. }catch(SecurityExceptione){
  19. e.printStackTrace();
  20. }catch(IllegalArgumentExceptione){
  21. e.printStackTrace();
  22. }catch(ClassNotFoundExceptione){
  23. e.printStackTrace();
  24. }catch(IllegalAccessExceptione){
  25. e.printStackTrace();
  26. }catch(InvocationTargetExceptione){
  27. e.printStackTrace();
  28. }catch(InstantiationExceptione){
  29. e.printStackTrace();
  30. }catch(NoSuchMethodExceptione){
  31. e.printStackTrace();
  32. }

结果如下

  1. ParameterTypes:reflect.Outer
  2. Testing..
  3. Outer
  4. test()
  5. thisisatest!

下面看看代码都做了什么,依次说明

首先获取Outer$2的Class

然后获取其构造器,查看所需要的参数,我们发现内部匿名类也需要一个外部类的支持

然后我们newInstance生成实例,声明为一个接口,由于要传入外部类,这里我们直接new一个,所以会初始化外部类,打印第2,3句

接下来查看内部类的方法,找到test(),这是第4句打印的结果

最后用实例化好的内部匿名类执行这个方法,所以打印出了最后一句


执行第一个匿名内部类的方法代码简单些,同样不推荐这么做

  1. try{
  2. Class<Outer>clazz=Outer.class;
  3. Outero=newOuter("");
  4. //System.out.println(o.ti.test());
  5. Fieldfield=clazz.getDeclaredField("ti");
  6. TestInterfacetestInterface=(TestInterface)field.get(o);
  7. System.out.println(testInterface.test());
  8. }catch(SecurityExceptione){
  9. e.printStackTrace();
  10. }catch(IllegalArgumentExceptione){
  11. e.printStackTrace();
  12. }catch(NoSuchFieldExceptione){
  13. //TODOAuto-generatedcatchblock
  14. e.printStackTrace();
  15. }catch(IllegalAccessExceptione){
  16. e.printStackTrace();
  17. }

为了演示反射,这里没有使用System.out.println(o.ti.test());


至此,反射演示结束

由于写本篇blog的时候多次修改代码,所以上面可能有些地方不太一致,如果发现,敬请指正


分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics