Java反射探索研究(转)

Posted 沧海一滴

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java反射探索研究(转)相关的知识,希望对你有一定的参考价值。

         林炳文Evankaka原创作品。转载请注明出处http://blog.csdn.net/evankakay

          摘要:本文详细深入讲解是Java中反射的机制,并介绍了如何通过反射来生成对象、调用函数、取得字段、设置字段的方法。最后,给出了一些反射常用到的实例。

一、反射

(1)概念

   反射含义:可以获取正在运行的Java对象。
(2)功能
        1)在运行时判断任意一个对象所属的类
        2)在运行时构造任意一个类的对象
        3) 在运行时判断任意一个类所具有的成员变量和方法
       4)在运行时调用任意一个对象的方法
(3)实现Java反射的类
  1)Class:它表示正在运行的Java应用程序中的类和接口
  2)Field:提供有关类或接口的属性信息,以及对它的动态访问权限
  3)Constructor:提供关于类的单个构造方法的信息以及对它的访问权限
  4)Method:提供关于类或接口中某个方法信息
  注意:Class类是Java反射中最重要的一个功能类,所有获取对象的信息(包括:方法/属性/构造方法/访问权限)都需要它来实现

(4)取得class的三种方法

 

[java] view plain copy
 
  1. Dog dog = new Dog();                   
  2.  Class<?> dogClass = dog.getClass();  
  3.  Class<?> dogClass1 = Dog.class;  
  4.  Class<?> dogClass2 = Class.forName("com.lin.Dog");//注意要添加异常抛出  

 

(5)关键方法

 

方法关键字

含义

getDeclaredMethods()

获取所有的方法

getReturnType()

获得方法的放回类型

getParameterTypes()

获得方法的传入参数类型

getDeclaredMethod("方法名",参数类型.class,……)

获得特定的方法

 

 

构造方法关键字

含义

getDeclaredConstructors()

获取所有的构造方法

getDeclaredConstructor(参数类型.class,……)

获取特定的构造方法

 

 

父类和父接口

含义

getSuperclass()

获取某类的父类

getInterfaces()

获取某类实现的接口

 

 

(6)一些区别函数

public Method[] getMethods()返回某个类的所有公用(public)方法包括其继承类的公用方法,当然也包括它所实现接口的方法。

public Method[] getDeclaredMethods()对象表示的类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。当然也包括它所实现接口的方法。

getFields()获得某个类的所有的公共(public)的字段,包括父类。 

getDeclaredFields()获得某个类的所有申明的字段,即包括public、private和proteced,
但是不包括父类的申明字段。 

下面来看一个例子说明:

动物接口

 

[java] view plain copy
 
  1. package com.lin;  
  2.   
  3. public interface Aminal {  
  4.       
  5.     public String eat(String obj);  
  6.       
  7.     public int run(int obj);  
  8.   
  9. }  


实现类:

 

 

[java] view plain copy
 
  1. <pre name="code" class="java">package com.lin;  
  2.   
  3. import java.util.jar.Attributes.Name;  
  4.   
  5. public class Dog implements Aminal {  
  6.       
  7.     private String name;  
  8.       
  9.     private int age;  
  10.       
  11.     public Dog() {  
  12.         // TODO 自动生成的构造函数存根  
  13.     }  
  14.       
  15.     public Dog(String name,int age) {  
  16.         this.name = name;  
  17.         this.age = age;  
  18.     }     
  19.       
  20.     public Dog(String name) {  
  21.         this.name = name;  
  22.         this.age = 10;  
  23.     }  
  24.       
  25.     private void sleep(int x) {  
  26.         System.out.println(name + "睡觉" + x + "分钟");  
  27.     }  
  28.       
  29.     public String getName() {  
  30.         return name;  
  31.     }  
  32.   
  33.     public void setName(String name) {  
  34.         this.name = name;  
  35.     }  
  36.   
  37.     public int getAge() {  
  38.         return age;  
  39.     }  
  40.   
  41.     public void setAge(int age) {  
  42.         this.age = age;  
  43.     }  
  44.   
  45.     @Override  
  46.     public String eat(String obj) {  
  47.         System.out.println(name + "吃"+ obj);  
  48.         return null;  
  49.     }  
  50.   
  51.     @Override  
  52.     public int run(int obj) {  
  53.         System.out.println("跑,速度:"+ obj);  
  54.         return 0;  
  55.     }  
  56.       
  57.     @Override  
  58.     public String toString() {  
  59.         return "狗名:" + name + "  狗的年纪:" + age;  
  60.     }  
  61.       
  62.     private static void play() {  
  63.         System.out.println("狗狗自己玩啊玩");  
  64.     }  
  65. }  
  66. </pre><br>  

来看看各自的调用:

 

 

[java] view plain copy
 
  1. package com.lin;  
  2.   
  3. import java.lang.reflect.Method;  
  4.   
  5. public class ReflectLearning {  
  6.   
  7.     public static void main(String[] args) throws ClassNotFoundException {  
  8.         Dog dog = new Dog();          
  9.          System.out.println(dog.getClass());   
  10.          System.out.println(dog.getClass().getName());   
  11.            
  12.          Class<?> dogClass = dog.getClass();  
  13.          Class<?> dogClass1 = Dog.class;  
  14.          Class<?> dogClass2 = Class.forName("com.lin.Dog");  
  15.            
  16.          Method[] methods1 = dogClass.getMethods();  
  17.          System.out.println("====================通过getMethods取得方法开始====================");  
  18.          for (Method method : methods1) {  
  19.              System.out.println(method);   
  20.         }  
  21.          System.out.println("====================通过getMethods取得方法结束====================");  
  22.            
  23.            
  24.          Method[] methods2 = dogClass.getDeclaredMethods();  
  25.          System.out.println("====================通过getDeclaredMethods取得方法开始====================");  
  26.          for (Method method : methods2) {  
  27.              System.out.println(method);   
  28.         }  
  29.          System.out.println("====================通过getDeclaredMethods取得方法结束====================");  
  30.   
  31.   
  32.   
  33.     }  
  34.       
  35.   
  36. }  


来看下结果:

 

getMethods方法

技术分享

getDeclareMethos方法:

技术分享

从上面可以看出getMethods()返回某个类的所有公用(public)方法包括其继承类的公用方法,当然也包括它所实现接口的方法。getDeclaredMethods()对象表示的类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。当然也包括它所实现接口的方法。

二、通过反射调用构造函数

(1)、列出所有的构造函数:

 

[java] view plain copy
 
  1. Constructor<?>[] constructors = dogClass.getConstructors();  
  2.   
  3. System.out.println("====================列出所有的构造函数结束====================");  
  4. for (Constructor<?> constructor : constructors) {           
  5.  System.out.println(constructor);  
  6. }  
  7. System.out.println("====================列出所有的构造函数结束====================");  

输出结果:

 

技术分享

(2)、通过反射生成对象

 

[java] view plain copy
 
  1. System.out.println("====================通过newInstance()来生成对象,一定在有默认构造函数====================");  
  2. Dog dog1 = (Dog) dogClass.newInstance();  
  3. dog1.setName("狗狗1号");  
  4. dog1.setAge(7);  
  5. System.out.println(dog1);  
  6.   
  7. System.out.println("====================通过newInstance(参数)方法一来生成对象====================");  
  8. Dog dog2 = (Dog)constructors[0].newInstance("狗狗2号");  
  9. System.out.println(dog2);  
  10.   
  11. System.out.println("====================通过newInstance(参数)方法二来生成对象====================");  
  12. Constructor con1 = dogClass.getConstructor(new  Class[]{String.class,int.class});     //主要就是这句了  
  13. Dog dog3 = (Dog) con1.newInstance(new Object[]{"狗狗3号",14});  
  14. System.out.println(dog3);  

输出结果:

 

技术分享

从上面可以看出,先通过getConstructor(new  Class[]{xxxx.class,yyy.class}),再通过con1.newInstance(new Object[]{"xxxxx",...});的方式是最灵活的,可以自动根据输入的参数类型和个数,找到对应的构造函数来调用。第二种方法需要得到构造函数的数组,并且需要知道对应哪一个构造函数。第一种就只能调用无参构造函数。

三、通过反射调用普通函数、静态函数

(1)取得函数的一些基本信息

 

[java] view plain copy
 
  1. Class<?> dogClass = Dog.class;  
  2. Method[] methods = dogClass.getDeclaredMethods();  
  3. for (Method method : methods) {  
  4.     System.out.println("函数名:"+method.getName() +"        函数类型:"+ method.getModifiers() + "         函数返回: "+ method.getReturnType() + "        函数参数个数:" + method.getParameterCount());  
  5.       
  6. }  

输出结果:

 

技术分享

其中函数类型对应表如下:
PUBLIC: 1
PRIVATE: 2
PROTECTED: 4
STATIC: 8
FINAL: 16
SYNCHRONIZED: 32
VOLATILE: 64
TRANSIENT: 128
NATIVE: 256
INTERFACE: 512
ABSTRACT: 1024
STRICT: 2048

(2)方法调用

这是当前狗类的方法:

 

[java] view plain copy
 
  1. package com.lin;  
  2.   
  3. import java.util.jar.Attributes.Name;  
  4.   
  5. public class Dog implements Aminal {  
  6.       
  7.     private String name;  
  8.       
  9.     private int age;  
  10.       
  11.     public Dog() {  
  12.         // TODO 自动生成的构造函数存根  
  13.     }  
  14.       
  15.     public Dog(String name,int age) {  
  16.         this.name = name;  
  17.         this.age = age;  
  18.     }     
  19.       
  20.     public Dog(String name) {  
  21.         this.name = name;  
  22.         this.age = 10;  
  23.     }  
  24.       
  25.     private void sleep(int x) {  
  26.         System.out.println(name + "睡觉" + x + "分钟");  
  27.     }  
  28.       
  29.     public String getName() {  
  30.         return name;  
  31.     }  
  32.   
  33.     public void setName(String name) {  
  34.         this.name = name;  
  35.     }  
  36.   
  37.     public int getAge() {  
  38.         return age;  
  39.     }  
  40.   
  41.     public void setAge(int age) {  
  42.         this.age = age;  
  43.     }  
  44.   
  45.     @Override  
  46.     public String eat(String obj) {  
  47.         System.out.println(name + "吃"+ obj);  
  48.         return null;  
  49.     }  
  50.   
  51.     @Override  
  52.     public int run(int obj) {  
  53.         System.out.println("跑,速度:"+ obj);  
  54.         return 0;  
  55.     }  
  56.       
  57.     @Override  
  58.     public String toString() {  
  59.         return "狗名:" + name + "  狗的年纪:" + age;  
  60.     }  
  61.       
  62.     private static void play() {  
  63.         System.out.println("狗狗自己玩啊玩");  
  64.     }  
  65.       
  66.       
  67.   
  68. }  


不同方法的调用过程:

 

 

[java] view plain copy
 
  1. //调用私有方法  
  2. Method method1 = dogClass.getDeclaredMethod("sleep", int.class);//不要用getMethod,它只能取到public方法  
  3. Dog dog1 = (Dog) dogClass.getConstructor(new Class[] {String.class}).newInstance(new Object[]{"狗狗1号"});  
  4. method1.setAccessible(true);//私有方法一定要加这句  
  5. method1.invoke(dog1, 12);  
  6.   
  7. //调用私有静态方法  
  8.               Method method2 = dogClass.getDeclaredMethod("play");//不要用getMethod,它只能取到public方法   
  9.               method2.setAccessible(true);//私有方法一定要加这句  
  10.              method2.invoke(dogClass.newInstance());   
  11.         
  12.              //调用公共方法  
  13. Method method3 = dogClass.getMethod("eat", String.class);//这里也可以用getDeclaredMethod  
  14. Dog dog3 = new Dog("狗狗3号", 45);  
  15. method3.invoke(dog3, "苹果~");  


输出结果:

 

技术分享

方法调用这里一定要记住getMethod和getDeclaredMethod的区别,并且在调用私有的方法之前一定要加setAccessible(true)这一句,要不会报错!

四、通过反射取得字段、设置字段值

(1)怎么通过反射获取类的属性
a)Class.getDeclaredField(String name);
返回一个 Field 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明字段。
b)Class.getDeclaredFields();
返回 Field 对象的一个数组,这些对象反映此 Class 对象所表示的类或接口所声明的所有字段。
c)Class.getField(String name);
返回一个 Field 对象,它反映此 Class 对象所表示的类或接口的指定公共成员字段。
d)Class.getField();
返回一个包含某些 Field 对象的数组,这些对象反映此 Class 对象所表示的类或接口的所有可访问公共字段。

(2)进行属性获取更改

[java] view plain copy
 
  1. Dog dog1 = new Dog("狗狗1号", 12);  
  2. System.out.println(dog1);  
  3.   
  4. Class<?> dogClass = dog1.getClass();  
  5. Field field1 = dogClass.getDeclaredField("name");//注意,getField只能取得public的字段  
  6. field1.setAccessible(true);//私有变量必须先设置Accessible为true  
  7.    System.out.println("原本狗名:" + field1.get(dog1));  
  8.   
  9. field1.set(dog1,"狗狗2号");  
  10.   
  11. System.out.println(dog1);     


输出结果:

 

技术分享

值得注意的是获取私有属性的时候必须先设置Accessible为true,然后才能获取。

 

五、反射常用工具类

(1)bean复制工具

这里可以使用commons-beanutils中的copyProperties()方法,自己写是为了加深对反射的理解。

1、toString的基类

 

[java] view plain copy
 
  1. package com.lin;  
  2.   
  3. import java.lang.reflect.Field;  
  4. import java.text.SimpleDateFormat;  
  5. import java.util.Date;  
  6.   
  7. /** 
  8.  * bean基類 
  9.  * @author lin 
  10.  * 
  11.  */  
  12. public class BaseBean {  
  13.   
  14.     public String toString() {   
  15.       StringBuffer sb = new StringBuffer();    
  16.       SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");    
  17.       Class<?> cls = this.getClass();  
  18.       Field[] fields = cls.getDeclaredFields();  
  19.       sb.append(cls.getName() + "{");  
  20.       for (Field field : fields) {  
  21.            try {    
  22.           field.setAccessible(true);    
  23.           sb.append(field.getName());    
  24.           sb.append("=");    
  25.           sb.append(field.get(this));    
  26.           sb.append(" ");    
  27.       } catch (Exception e) {    
  28.           e.printStackTrace();    
  29.       }   
  30.       }  
  31.       sb.append("}");  
  32.       return sb.toString();  
  33.     }  
  34. }  


2、bean复制工具

 

 

[java] view plain copy
 
  1. package com.lin;  
  2.   
  3. import java.lang.reflect.Field;  
  4. import java.lang.reflect.InvocationTargetException;  
  5. import java.lang.reflect.Method;  
  6.   
  7. /** 
  8.  * 将一个JavaBean风格对象的属性值拷贝到另一个对象的同名属性中 (如果不存在同名属性的就不拷贝) 
  9.  **/  
  10.   
  11. public class BeanCopy {  
  12.     private static String GET = "get";  
  13.     private static String SET = "set";  
  14.       
  15.     /** 
  16.      *  
  17.      * @param source 
  18.      * @param target 
  19.      * @throws Exception 
  20.      */  
  21.     public static void copy(Object source,Object target){  
  22.         Class<?> sourceClz = source.getClass();  
  23.         Class<?> targetClz = target.getClass();  
  24.         // 得到Class对象所表征的类的所有属性(包括私有属性)  
  25.         Field[] sourceFields = sourceClz.getDeclaredFields();  
  26.         if (sourceFields.length == 0) {  
  27.             sourceFields = sourceClz.getSuperclass().getDeclaredFields();  
  28.         }  
  29.           
  30.         int len = sourceFields.length;  
  31.         for (int i = 0; i < len; i++) {  
  32.             String fieldName = sourceFields[i].getName();  
  33.             Field targetField = null;  
  34.             // 得到targetClz对象所表征的类的名为fieldName的属性,不存在就进入下次循环  
  35.             try {  
  36.                 targetField = targetClz.getDeclaredField(fieldName);  
  37.             } catch (NoSuchFieldException e) {  
  38.                 try {  
  39.                     targetField = targetClz.getSuperclass().getDeclaredField(fieldName);  
  40.                 } catch (NoSuchFieldException e1) {  
  41.                     e1.printStackTrace();  
  42.                 } catch (SecurityException e1) {  
  43.                     e1.printStackTrace();  
  44.                 }  
  45.             }  
  46.               
  47.             if (targetField == null) {  
  48.                 continue;  
  49.             }  
  50.               
  51.             // 判断sourceClz字段类型和targetClz同名字段类型是否相同  
  52.             if (sourceFields[i].getType() == targetField.getType()) {  
  53.                 // 由属性名字得到对应get和set方法的名字  
  54.                 String getMethodName = GET + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);  
  55.                 String setMethodName = SET + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);  
  56.                 // 由方法的名字得到get和set方法的Method对象  
  57.                 Method getMethod;  
  58.                 Method setMethod;  
  59.                 try {  
  60.                     try {  
  61.                         getMethod = sourceClz.getDeclaredMethod(getMethodName,new Class[] {});//get方法入參為空  
  62.                     } catch (NoSuchMethodException e) {  
  63.                         getMethod = sourceClz.getSuperclass().getDeclaredMethod(getMethodName,new Class[] {});  
  64.                     }  
  65.                     try {  
  66.                         setMethod = targetClz.getDeclaredMethod(setMethodName,sourceFields[i].getType());//set方法入參不為空  
  67.   
  68.                     } catch (NoSuchMethodException e) {  
  69.                         setMethod = targetClz.getSuperclass().getDeclaredMethod(setMethodName,sourceFields[i].getType());  
  70.                     }  
  71.                     // 调用source对象的getMethod方法  
  72.                     Object result = getMethod.invoke(source, new Object[] {});  
  73.                     // 调用target对象的setMethod方法  
  74.                     setMethod.invoke(target, result);  
  75.   
  76.                 } catch (SecurityException e) {  
  77.                     e.printStackTrace();  
  78.   
  79.                 } catch (NoSuchMethodException e) {  
  80.                     e.printStackTrace();  
  81.   
  82.                 } catch (IllegalArgumentException e) {  
  83.                     e.printStackTrace();  
  84.   
  85.                 } catch (IllegalAccessException e) {  
  86.                     e.printStackTrace();  
  87.   
  88.                 } catch (InvocationTargetException e) {  
  89.                     e.printStackTrace();  
  90.   
  91.                 }  
  92.             } else {  
  93.                 continue;  
  94.   
  95.             }  
  96.   
  97.         }  
  98.   
  99.     }  
  100.   
  101. }  

使用:

 

新建两个类:

 

[java] view plain copy
 
  1. package com.lin;  
  2.   
  3. import java.util.Date;  
  4.   
  5. public class Car extends BaseBean{  
  6.       
  7.     private String name;  
  8.       
  9.     private String id;  
  10.        
  11.     private Boolean sellFlag;  
  12.       
  13.     private int age;  
  14.       
  15.     private double maxSpeed;  
  16.       
  17.     private double minSpeed;  
  18.       
  19.     private int driverPeople;  
  20.       
  21.     private Date date;  
  22.   
  23.     public String getName() {  
  24.         return name;  
  25.     }  
  26.   
  27.     public void setName(String name) {  
  28.         this.name = name;  
  29.     }  
  30.   
  31.     public String getId() {  
  32.         return id;  
  33.     }  
  34.   
  35.     public void setId(String id) {  
  36.         this.id = id;  
  37.     }  
  38.   
  39.     public Boolean getSellFlag() {  
  40.         return sellFlag;  
  41.     }  
  42.   
  43.     public void setSellFlag(Boolean sellFlag) {  
  44.         this.sellFlag = sellFlag;  
  45.     }  
  46.   
  47.     public int getAge() {  
  48.         return age;  
  49.     }  
  50.   
  51.     public void setAge(int age) {  
  52.         this.age = age;  
  53.     }  
  54.   
  55.     public double getMaxSpeed() {  
  56.         return maxSpeed;  
  57.     }  
  58.   
  59.     public void setMaxSpeed(double maxSpeed) {  
  60.         this.maxSpeed = maxSpeed;  
  61.     }  
  62.   
  63.     public double getMinSpeed() {  
  64.         return minSpeed;  
  65.     }  
  66.   
  67.     public void setMinSpeed(double minSpeed) {  
  68.         this.minSpeed = minSpeed;  
  69.     }  
  70.   
  71.     public int getDriverPeople() {  
  72.         return driverPeople;  
  73.     }  
  74.   
  75.     public void setDriverPeople(int driverPeople) 

以上是关于Java反射探索研究(转)的主要内容,如果未能解决你的问题,请参考以下文章

探索Java无反射机制,如何节省启动内存资源消耗!

java反射机制(转)

[转]Java反射机制详解

Java 反射机制深入研究

[Java安全] 反射

[Java安全] 反射