Java反射探索研究(转)

时间:2021-05-01 15:50:02

林炳文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的三种方法

  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,
但是不包括父类的申明字段。

下面来看一个例子说明:

动物接口

  1. package com.lin;
  2. public interface Aminal {
  3. public String eat(String obj);
  4. public int run(int obj);
  5. }

实现类:

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

来看看各自的调用:

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

来看下结果:

getMethods方法

Java反射探索研究(转)

getDeclareMethos方法:

Java反射探索研究(转)

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

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

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

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

输出结果:

Java反射探索研究(转)

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

  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. System.out.println("====================通过newInstance(参数)方法一来生成对象====================");
  7. Dog dog2 = (Dog)constructors[0].newInstance("狗狗2号");
  8. System.out.println(dog2);
  9. System.out.println("====================通过newInstance(参数)方法二来生成对象====================");
  10. Constructor con1 = dogClass.getConstructor(new  Class[]{String.class,int.class});     //主要就是这句了
  11. Dog dog3 = (Dog) con1.newInstance(new Object[]{"狗狗3号",14});
  12. System.out.println(dog3);

输出结果:

Java反射探索研究(转)

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

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

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

  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. }

输出结果:

Java反射探索研究(转)

其中函数类型对应表如下:
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)方法调用

这是当前狗类的方法:

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

不同方法的调用过程:

  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. Method method2 = dogClass.getDeclaredMethod("play");//不要用getMethod,它只能取到public方法
  8. method2.setAccessible(true);//私有方法一定要加这句
  9. method2.invoke(dogClass.newInstance());
  10. //调用公共方法
  11. Method method3 = dogClass.getMethod("eat", String.class);//这里也可以用getDeclaredMethod
  12. Dog dog3 = new Dog("狗狗3号", 45);
  13. method3.invoke(dog3, "苹果~");

输出结果:

Java反射探索研究(转)

方法调用这里一定要记住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)进行属性获取更改

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

输出结果:

Java反射探索研究(转)

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

五、反射常用工具类

(1)bean复制工具

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

1、toString的基类

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

2、bean复制工具

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

使用:

新建两个类:

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

另一个:

  1. package com.lin;
  2. import java.util.Date;
  3. public class Bus extends BaseBean{
  4. private String name;
  5. private String id;
  6. private Boolean sellFlag;
  7. private int age;
  8. private double maxSpeed;
  9. private double minSpeed;
  10. private long driverPeople;//和car類型不同
  11. private int driverYear;//car沒有這個
  12. private Date date;
  13. public String getName() {
  14. return name;
  15. }
  16. public void setName(String name) {
  17. this.name = name;
  18. }
  19. public String getId() {
  20. return id;
  21. }
  22. public void setId(String id) {
  23. this.id = id;
  24. }
  25. public Boolean getSellFlag() {
  26. return sellFlag;
  27. }
  28. public void setSellFlag(Boolean sellFlag) {
  29. this.sellFlag = sellFlag;
  30. }
  31. public int getAge() {
  32. return age;
  33. }
  34. public void setAge(int age) {
  35. this.age = age;
  36. }
  37. public double getMaxSpeed() {
  38. return maxSpeed;
  39. }
  40. public void setMaxSpeed(double maxSpeed) {
  41. this.maxSpeed = maxSpeed;
  42. }
  43. public double getMinSpeed() {
  44. return minSpeed;
  45. }
  46. public void setMinSpeed(double minSpeed) {
  47. this.minSpeed = minSpeed;
  48. }
  49. public long getDriverPeople() {
  50. return driverPeople;
  51. }
  52. public void setDriverPeople(long driverPeople) {
  53. this.driverPeople = driverPeople;
  54. }
  55. public int getDriverYear() {
  56. return driverYear;
  57. }
  58. public void setDriverYear(int driverYear) {
  59. this.driverYear = driverYear;
  60. }
  61. public Date getDate() {
  62. return date;
  63. }
  64. public void setDate(Date date) {
  65. this.date = date;
  66. }
  67. }

调用:

  1. public static void test5() {
  2. Car car = new Car();
  3. car.setAge(12);
  4. car.setDriverPeople(4);
  5. car.setId("YU1234");
  6. car.setMaxSpeed(13.66);
  7. car.setMinSpeed(1.09);
  8. car.setName("小车");
  9. car.setSellFlag(false);
  10. car.setDate(new Date());
  11. Bus bus = new Bus();
  12. BeanCopy.copy(car,bus);
  13. System.out.println(car);
  14. System.out.println(bus);
  15. }
Java反射探索研究(转)
除了两个不同的字段外,其它的都复制过去了,这在DTO、VO、DOMAIN对象转换时经常用到。

http://blog.csdn.net/evankaka/article/details/49978481