Java反射机制剖析

Posted on

Java反射机制剖析

(一)-定义和API

  1. 什么是Java反射机制 Java的反射机制是在程序运行时,能够完全知道任何一个类,及其它的属性和方法,并且能够任意调用一个对象的属性和方法。这种运行时的动态获取就是Java的反射机制。其实这也是Java是动态语言的一个象征。

用一句话来概括反射就是加载一个运行时才知道的类以及它的完整内部结构。

  1. 为什么要有Java反射机制 我们为什么要用Java的反射机制呢?

我认为有两种:

第一种:反射的目的就是为了扩展未知的应用。比如你写了一个程序,这个程序定义了一些接口,只要实现了这些接口的dll都可以作为插件来插入到这个程序中。那么怎么实现呢?就可以通过反射来实现。就是把dll加载进内存,然后通过反射的方式来调用dll中的方法。

第二种:在编码阶段不知道那个类名,要在运行期从配置文件读取类名, 这时候就没有办法硬编码new ClassName(),而必须用到反射才能创建这个对象。

一个生活中常看到的例子有助于理解我们为什么要用Java的反射机制:你进了一家饭店,你不知道他们都有那些菜,要多少钱。那么你第一件事情是干啥“服务员拿个菜单过来”,然后指着菜单说“我要这个,我要那个”。

  1. 一起来看ReflectionAPI

在生活中,我们使用一个未知的东西的时候总会用帮助来解决我们的使用问题,电视机有帮助,电脑有帮助,几乎所有的事物都携带着它的一本帮助,Java的反射机制也不例外。

在JDK中有Reflection API的帮助,它主要说明了什么是Java反射机制,这种反射机制提供了什么样的属性和方法,进一步我们能够知道能够通过它完成什么样的工作。

下面咱就一起看看这部分的API。这些接口和类都位于lang包里。

如图:

接口:

类:

简单介绍一些类和接口的用法。

1) Member成员是一种接口,反映有关单个成员(字段或方法)或构造方法的标识信息

2) InvocationHandler是代理实例的调用处理程序 实现的接口(这个接口的具体用法等到java反射机制剖析4着重介绍)

3) Method提供一个类的方法的信息以及访问类的方法的接口。

示例:

[java] view plaincopyprint?

  1. import java.lang.reflect.Method;
  2. public class TestMethod {
  3. ///
  4. /* @param args
  5. /* @throws Exception
  6. /*/
  7. public static void main(String[] args) throws Exception {
  8. // TODO Auto-generated method stub
  9. Class classType=Class.forName(args[0]);
  10. Method methods[]=classType.getDeclaredMethods();
  11. for(int i=0;i<methods.length;i++){
  12. System.out.println(methods[i].toString());
  13. }
  14. }
  15. }

4) Filed提供一个类的域的信息以及访问类的域的接口。

示例:

[java] view plaincopyprint?

  1. import java.lang.reflect.Field;
  2. public class TestField {
  3. ///
  4. /* @param args
  5. /* @throws Exception
  6. /*/
  7. public static void main(String[] args) throws Exception {
  8. // TODO Auto-generated method stub
  9. Class classType=Class.forName(args[0]);
  10. Field[] fields = classType.getFields();
  11. for(int i=0;i<fields.length;i++){
  12. System.out.println(fields[i].toString());
  13. }
  14. }
  15. }

5) Array 类提供了动态创建和访问 Java 数组的方法。

示例:

[java] view plaincopyprint?

  1. import java.lang.reflect.Array;
  2. public class TestArray {
  3. public TestArray(){
  4. }
  5. ///
  6. /* @param args
  7. /* @throws Exception
  8. /*/
  9. public static void main(String[] args) throws Exception {
  10. Class<?> classType = Class.forName("java.lang.String");
  11. Object array = Array.newInstance(classType, 10);
  12. Array.set(array, 5, "hello");
  13. String s = (String)Array.get(array, 5);
  14. System.out.println(s);
  15. }
  16. }

6) Proxy提供动态地生成代理类和类实例的静态方法(这个方法在java放射机制剖析4着重介绍)。

其余的类和接口的使用方法详见API 来源: [http://blog.csdn.net/lfsf802/article/details/7239652](http://blog.csdn.net/lfsf802/article/details/7239652)

(二)-功能以及举例

《java反射机制剖析(一)》的API我们看到了许多接口和类,我们能够通过这些接口做些什么呢?

从上篇API中我们能看到它能够完成下面的这些功能:

1) 获得类 A. 运用getClass() (每个class都有此函数)

                                i.    String str = "abc";

                              ii.    Class c1 = str.getClass();

B. 运用Class.forName() (static method 最常被使用)

                                i.    Class c1 = Class.forName ("java.lang.String");

                              ii.    Class c2 = Class.forName ("java.awt.Button");

C. 运用 .class 语法

                                i.    Class c1 = String.class;

                              ii.    Class c2 = java.awt.Button.class;

                            iii.    Class c4 = int.class;

                              iv.    Class c5 = int[].class;

D. 运用 TYPE语法 ( primitive wrapper classes 基本数据类型包装类的 TYPE语法)

                                i.    Class c1 = Boolean.TYPE;

2) 获得属性(这个功能的实例见Java反射机制剖析(2)) 以下四个方法可以获得属性,主要参见的是Java.lang.class

Public Field getField

(String name) 返回一个 Field 对象,它反映此 Class 对象所表示的类或接口的指定公共成员字段 public Field[] getFields() 返回一个包含某些 Field 对象的数组,这些对象反映此 Class 对象所表示的类或接口的所有可访问公共字段 Public Field

getDeclaredField(String name) 返回一个 Field 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明字段 public Field[]

getDeclaredFields() 返回 Field 对象的一个数组,这些对象反映此 Class 对象所表示的类或接口所声明的所有字段

3) 获得方法() 以下四个方法可以获得方法,要参见的是Java.lang.class

public Method

getMethod(String name,

... )

返回一个 Method 对象,它反映此 Class 对象所表示的类或接口的指定公共成员方法 public Method[] getMethods()

返回一个包含某些 Method 对象的数组,这些对象反映此 Class 对象所表示的类或接口(包括那些由该类或接口声明的以及从超类和超接口继承的那些的类或接口)的公共 member 方法 public Method

getDeclaredMethod(String name,…)

返回一个 Method 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明方法 public Method[]

getDeclaredMethods()

返回 Method 对象的一个数组,这些对象反映此 Class 对象表示的类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法

4) 获取类的构造器 以下四个方法可以获得方法,要参见的是Java.lang.class

public Constructor

getConstructor(Class<?>... )

返回一个 Constructor 对象,它反映此 Class 对象所表示的类的指定公共构造方法 public Constructor<?>[]

getConstructors()

返回一个包含某些 Constructor 对象的数组,这些对象反映此 Class 对象所表示的类的所有公共构造方法 Public Constructor

getDeclaredConstructor(Class<?>...)

返回一个 Constructor 对象,该对象反映此 Class 对象所表示的类或接口的指定构造方法 public Constructor<?>[]

getDeclaredConstructors()

返回 Constructor 对象的一个数组,这些对象反映此 Class 对象表示的类声明的所有构造方法。它们是公共、保护、默认(包)访问和私有构造方法 示例代码:

[java] view plaincopyprint?

  1. import java.lang.reflect.Constructor;
  2. public class TestConstructor {
  3. ///
  4. /* @param args
  5. /* @throws ClassNotFoundException
  6. /* @throws SecurityException
  7. /* @throws NoSuchMethodException
  8. /*/
  9. public static void main(String[] args) throws Exception {
  10. // TODO Auto-generated method stub
  11. Class classType=Class.forName(args[0]);
  12. Constructor Constructor= classType.getConstructor();
  13. System.out.println(Constructor.toString());
  14. }
  15. }

5) 新建类的实例

调用类的Class对象的newInstance方法

示例:

[java] view plaincopyprint?

  1. import java.lang.reflect.Constructor;
  2. public class TestConstructor {
  3. ///
  4. /* @param args
  5. /* @throws ClassNotFoundException
  6. /* @throws SecurityException
  7. /* @throws NoSuchMethodException
  8. /*/
  9. public static void main(String[] args) throws Exception {
  10. // TODO Auto-generated method stub
  11. Class classType=Class.forName(args[0]);
  12. //Constructor Constructor= classType.getConstructor();
  13. Object inst=classType.newInstance();
  14. System.out.println(inst);
  15. }
  16. } 调用默认Constructor对象的newInstance方法

示例:

[java] view plaincopyprint?

  1. import java.lang.reflect.Constructor;
  2. public class TestConstructor {
  3. ///
  4. /* @param args
  5. /* @throws ClassNotFoundException
  6. /* @throws SecurityException
  7. /* @throws NoSuchMethodException
  8. /*/
  9. public static void main(String[] args) throws Exception {
  10. // TODO Auto-generated method stub
  11. Class classType=Class.forName(args[0]);
  12. Constructor Constructor= classType.getConstructor();
  13. Object inst=Constructor.newInstance();
  14. System.out.println(inst);
  15. }
  16. } 调用带参数Constructor对象的newInstance方法

示例:

[java] view plaincopyprint?

  1. Class classType=User.class
  2. Constructor constructor2 =
  3. classType.getDeclaredConstructor(int.class, String.class);
  4. Object inst = constructor2.newInstance(1, "123");
  5. System.out.println(inst);

来源: [http://blog.csdn.net/lfsf802/article/details/7239711](http://blog.csdn.net/lfsf802/article/details/7239711)

(三)-简单谈谈动态代理 通过Java反射机制剖析(一)Java反射机制剖析(二)的学习,已经对反射有了一定的了解,这一篇通过动态代理的例子来进一步学习反射机制。

  1. 代理模式

代理模式就是为其他对象提供一种代理来控制对这个对象的访问。其实代理模式是在访问的对象时引入一定程度的间接性,这种间接性可以附加多种用途。

它的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务。

  1. 分类

代理类按照创建时期可以分为两种,静态代理类和动态代理类。

静态代理类:由程序员创建或由特定工具自动生成源代码,再对其编译。在程序运行前,代理类的.class文件就已经存在了。

动态代理类:在程序运行时,运用反射机制动态创建而成。

  1. 静态代理和动态代理举例

静态代理:

业务接口类:

[java] view plaincopyprint?

  1. package com.bjpowernode.pattern;
  2. public interface UserManager {
  3. public void addUser(String userId, String userName);
  4. public void delUser(String userId);
  5. public void modifyUser(String userId, String userName);
  6. public String findUser(String userId);
  7. }
    业务接口实现类:

[java] view plaincopyprint?

  1. package com.bjpowernode.pattern;
  2. public class UserManagerImpl implements UserManager {
  3. public void addUser(String userId, String userName) {
  4. //System.out.println("start-->>addUser() userId-->>" + userId);
  5. try {
  6. System.out.println("UserManagerImpl.addUser() userId-->>" + userId);
  7. //System.out.println("success-->>addUser()");
  8. }catch(Exception e) {
  9. e.printStackTrace();
  10. //System.out.println("error-->>addUser()");
  11. throw new RuntimeException();
  12. }
  13. }
  14. public void delUser(String userId) {
  15. System.out.println("UserManagerImpl.delUser() userId-->>" + userId);
  16. }
  17. public String findUser(String userId) {
  18. System.out.println("UserManagerImpl.findUser() userId-->>" + userId);
  19. return "张三";
  20. }
  21. public void modifyUser(String userId, String userName) {
  22. System.out.println("UserManagerImpl.modifyUser() userId-->>" + userId);
  23. }
  24. }
    业务代理类:

[java] view plaincopyprint?

  1. package com.bjpowernode.pattern;
  2. public class UserManagerImplProxy implements UserManager {
  3. private UserManager userManager;
  4. public UserManagerImplProxy(UserManager userManager) {
  5. this.userManager = userManager;
  6. }
  7. public void addUser(String userId, String userName) {
  8. try {
  9. System.out.println("start-->>addUser() userId-->>" + userId);
  10. userManager.addUser(userId, userName);
  11. System.out.println("success-->>addUser()");
  12. }catch(Exception e) {
  13. e.printStackTrace();
  14. System.out.println("error-->>addUser()");
  15. }
  16. }
  17. public void delUser(String userId) {
  18. }
  19. public String findUser(String userId) {
  20. return null;
  21. }
  22. public void modifyUser(String userId, String userName) {
  23. }
  24. }
    客户端类:

[java] view plaincopyprint?

  1. package com.bjpowernode.pattern;
  2. public class Client {
  3. ///
  4. /* @param args
  5. /*/
  6. public static void main(String[] args) {
  7. //UserManager userManager = new UserManagerImpl();
  8. UserManager userManager = new UserManagerImplProxy(new UserManagerImpl());
  9. userManager.addUser("0001", "张三");
  10. }
  11. }
    运行结果:

[java] view plaincopyprint?

  1. start-->>addUser() userId-->>0001
  2. UserManagerImpl.addUser() userId-->>0001
  3. success-->>addUser()

动态代理:

业务接口类:

[java] view plaincopyprint?

  1. package com.bjpowernode.pattern;
  2. public interface UserManager {
  3. public String test(String userId);
  4. }
    业务接口实现类:

[java] view plaincopyprint?

  1. package com.bjpowernode.pattern;
  2. public class UserManagerImpl implements UserManager {
  3. public String test(String userId) {
  4. System.out.println("UserManagerImpl.findUser() userId-->>" + userId);
  5. return "张三";
  6. }
  7. }
    BusinessHandler类:

[java] view plaincopyprint?

  1. package com.bjpowernode.pattern;
  2. import java.lang.reflect.InvocationHandler;
  3. import java.lang.reflect.Method;
  4. import java.lang.reflect.Proxy;
  5. public class BusinessHandler implements InvocationHandler {
  6. private Object targetObject;
  7. public Object newProxyInstance(Object targetObject) {
  8. this.targetObject = targetObject;
  9. return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),
  10. targetObject.getClass().getInterfaces(), this);
  11. }
  12. public Object invoke(Object proxy, Method method, Object[] args)
  13. throws Throwable {
  14. System.out.println("start-->>" + method.getName());
  15. for (int i=0; i<args.length; i++) {
  16. System.out.println(args[i]);
  17. }
  18. Object ret = null;
  19. try {
  20. //调用目标方法
  21. ret = method.invoke(targetObject, args);
  22. System.out.println("success-->>" + method.getName());
  23. }catch(Exception e) {
  24. e.printStackTrace();
  25. System.out.println("error-->>" + method.getName());
  26. throw e;
  27. }
  28. return ret;
  29. }
  30. }
    客户端类:

[java] view plaincopyprint?

  1. package com.bjpowernode.pattern;
  2. import java.lang.reflect.Field;
  3. public class Client {
  4. ///
  5. /* @param args
  6. /*/
  7. public static void main(String[] args) {
  8. BusinessHandler businessHandler = new BusinessHandler();
  9. UserManager userManager = (UserManager)businessHandler.newProxyInstance(new UserManagerImpl());
  10. //userManager.addUser("0001", "张三");
  11. //userManager.delUser("0001");
  12. // System.out.println(userManager.getClass().getName());
  13. String name = userManager.test("0001");
  14. //String name = ((UserManagerImpl) logHandler.newProxyInstance(new UserManagerImpl())).test("0001");
  15. System.out.println("Client.main() --- " + name);
  16. }
  17. }
    运行结果:

[java] view plaincopyprint?

  1. start-->>test
  2. 0001
  3. UserManagerImpl.findUser() userId-->>0001
  4. success-->>test
  5. Client.main() --- 张三

来源: [http://blog.csdn.net/lfsf802/article/details/7239766](http://blog.csdn.net/lfsf802/article/details/7239766)

(四)-深度剖析动态代理原理及总结 动态代理类原理(示例代码参见java反射机制剖析(三)) a) 理解上面的动态代理示例流程

b) 代理接口实现类源代码剖析

咱们一起来剖析一下代理实现类($Proxy0)的源代码和整个动态代理的流程。

$Proxy0生成的代码如下:

[html] view plaincopyprint?

  1. import java.lang.reflect.InvocationHandler;
  2. import java.lang.reflect.Method;
  3. import java.lang.reflect.Proxy;
  4. import java.lang.reflect.UndeclaredThrowableException;
  5. public final class $Proxy0 extends Proxy implements Manager {
  6. private static Method m1;
  7. private static Method m0;
  8. private static Method m3;
  9. private static Method m2;
  10. static {
  11. try {
  12. m1 = Class.forName("java.lang.Object").getMethod("equals",
  13. new Class[] { Class.forName("java.lang.Object") });
  14. m0 = Class.forName("java.lang.Object").getMethod("hashCode",
  15. new Class[0]);
  16. m3 = Class.forName("com.ml.test.Manager").getMethod("test",
  17. new Class[0]);
  18. m2 = Class.forName("java.lang.Object").getMethod("toString",
  19. new Class[0]);
  20. } catch (NoSuchMethodException nosuchmethodexception) {
  21. throw new NoSuchMethodError(nosuchmethodexception.getMessage());
  22. } catch (ClassNotFoundException classnotfoundexception) {
  23. throw new NoClassDefFoundError(classnotfoundexception.getMessage());
  24. }
  25. }
  26. public $Proxy0(InvocationHandler invocationhandler) {
  27. super(invocationhandler);
  28. }
  29. @Override
  30. public final boolean equals(Object obj) {
  31. try {
  32. return ((Boolean) super.h.invoke(this, m1, new Object[] { obj }))
  33. .booleanValue();
  34. } catch (Throwable throwable) {
  35. throw new UndeclaredThrowableException(throwable);
  36. }
  37. }
  38. @Override
  39. public final int hashCode() {
  40. try {
  41. return ((Integer) super.h.invoke(this, m0, null)).intValue();
  42. } catch (Throwable throwable) {
  43. throw new UndeclaredThrowableException(throwable);
  44. }
  45. }
  46. public final void test() {
  47. try {
  48. super.h.invoke(this, m3, null);
  49. return;
  50. } catch (Error e) {
  51. } catch (Throwable throwable) {
  52. throw new UndeclaredThrowableException(throwable);
  53. }
  54. }
  55. @Override
  56. public final String toString() {
  57. try {
  58. return (String) super.h.invoke(this, m2, null);
  59. } catch (Throwable throwable) {
  60. throw new UndeclaredThrowableException(throwable);
  61. }
  62. }
  63. }

引入眼帘的是这个代理接口实现类实现了业务类的接口(也就是例子中的UserManager接口),又继承了基类Proxy类;

接着就是构造函数,在构造方法中把BusinessHandler传过去,接着$Proxy0调用父类Proxy的构造器,为h赋值(这里要看Proxy的构造方法);

随后看到的就是这个类重写了Proxy类的Equals、hashCode、toString方法,又实现了业务类接口的方法(即UserManager的test方法),具体重写和实现都是用到的super.h.invoke(即Proxy.h.invoke)这个方法。

简单分析完这个代理接口实现类,咱们下面来整体看一下这个动态代理是怎么实现的:

首先客户端初始化了BusinessHandler类,调用这个类的newProxyInstance(new UserManagerImpl())方法来初始化了上面的代理接口实现类;

接下来代理接口实现类通过构造函数把BusinessHandler传过去(也就是代码中的this),并通过Proxy的构造函数给h赋值;

随后再客户端就能实例化出代理接口实现类$Proxy0,我们把它强制转换为业务实现接口(UserManager)类型的(为什么要强制转换,这里非常有意思,如果不强制转换就会报错,这里很好解释,因为当前的环境根本不会知道这个代理接口实现类$Proxy0既继承Proxy又实现业务实现接口UserManager,但是强制转换成UserManager它是可以做到的,因为当前环境中就有UserManager。这就是反射的厉害之处,可以在运行时动态调用任何一个类并可以使用这个类的具体细节。);

之后当我们调用test方法的时候其实是调用了$Proxy0中的test方法,这个方法的实现是通过Proxy.h的invoke方法实现的(即调用了BusinessHandler.invoke方法);

之后在调用了Method的invoke方法(这时的参数是this,和args)。

这样就调用了UserManagerImpl的对应方法,之后返回给客户端。

到此就完成了整个的调用关系。

反射,反射,程序员的快乐 通过上篇文章对动态代理进行了深度剖析,现在想起来还感觉非常有意思,这里面其实最根本的机制就是反射机制,运行时动态实例化任何一个类,并且调用它的具体细节。现在反看动态代理的示例,其实发现这里最关键的还是在就在Proxy.newProxyInstance(..)方法执行时生成了$Proxy0的内存字节码这一点上,当我们有了内存字节码,我们的反射就会大显威力,这样才有了我们之后的一系列的调用关系。

通过反射机制的分析和动态代理示例的剖析,发现编程是一件多么有意思的事情,以至于我们沉浸其中不能自拔。

最后总结一下:反射,反射,程序员的快乐! 来源: [http://blog.csdn.net/lfsf802/article/details/7239799](http://blog.csdn.net/lfsf802/article/details/7239799)

希望本站内容对您有点用处,有什么疑问或建议请在后面留言评论
转载请注明作者(RobinChia)和出处 It so life ,请勿用于任何商业用途