神刀安全网

java反射机制

基本概念

java的反射机制是动态获取类的信息以及动态调用对象的方法。这种机制允许程序在运行时通过reflection apis获取一个已知名称的class的内部信息。包括:modifiers(如public,static等),superclass(如Object),实现了的Interfaces(如Serializable),也包括其fields和methods的所有信息,并可以在运行时动态改变fields或调用methods。

  • 主要功能
    • 在运行时判断任意一个对象所属的类。
    • 在运行时构造任意一个类的对象。
    • 在运行时判断任意一个类所具有的成员变量和方法。
    • 在运行时调用任意一个对象的方法。

Java Reflection API简介

在JDK中,主要由以下类来实现Java反射机制,这些类(除了第一个)都位于java.lang.reflect包中。

  • Class类:代表一个类,位于java.lang包下。
  • Field类:代表类的成员变量(及类的属性)。
  • Method:代表类的方法。
  • Constructor类:代表类的构造方法。
  • Array类:提供了动态创建数组,以及访问数据的元素的静态方法。

Class对象

要想使用放射,首先需要获得带操作的类所对应的Class对象。java中,无论生成某个类的多少对象,这些对象都会对应于同一个Class对象。这个Class对象是有JVM生成的,通过它能够获取整个类的结构。
获取Class对象的3中方法

  1. 使用Class类的静态方法
    Class<?> stringClass = Class.forName("java.lang.String");
  2. 使用类的.class方法
    Class<?> stringClass2 = String.class;
  3. 使用对象的getClass方法
    String str = "aa"; Class<?> stringClass3 = str.getClass();

    getClass方法定义在Object类中,不是静态方法,声明为final,不能被子类覆写。

使用方法

  • 打印类所有方法信息
    public static void main(String[] args) throws ClassNotFoundException { // 获取class对象 Class<?> classType = Class.forName("java.lang.String"); // 返回class对象所对应类或接口中声明德尔所有方法的数组【包括私有方法】 Method[] methods = classType.getDeclaredMethods(); // 输出所有方法的声明 for (Method method : methods) {    System.out.println(method); } }

通过反射调用方法

public class Reflect {     public int add(int param1, int param2) {         return param1 + param2;     }      public String echo(String message) {         return "Hello " + message;     }      public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {         // 正常的执行手段         Reflect reflect = new Reflect();         System.out.println(reflect.add(1, 2));         System.out.println(reflect.echo("suys"));         System.out.println("================================");         // 反射的方法         // 获取class对象         Class<?> classType = Reflect.class;         // 生成新的对象         Reflect reflect1 = (Reflect) classType.newInstance();         System.out.println(reflect1 instanceof Reflect); // 判断是否是Reflect类的对象         // 通过反射调用方法         // 首先需要获得与该方法对应的Method对象,第一个参数是方法名,第二个参数是所需要的参数Class对象的数组         Method addMethod = classType.getMethod("add", new Class[]{int.class, int.class});         // 调用目标方法         Object result = addMethod.invoke(reflect1, new Object[]{1, 2});         System.out.println(result); //此时返回的是Integer类型          // 第二个方法调用采用类似的方法     } }

两种用法

// 接口 public interface CarInterface {     public String getName();     public String getSpeed(); } // 实现类1 public class SmallCar implements CarInterface {     @Override     public String getName() {         return "small car";     }     @Override     public String getSpeed() {         return "100-200";     } } // 实现类2 public class BigCar implements CarInterface{     @Override     public String getName() {         return "big car";     }     @Override     public String getSpeed() {         return "10-50";     } } // 测试类 public class Test {     public static void main(String[] args) throws Exception {         List<Class<?>> carInterfaces = Lists.newArrayList();         carInterfaces.add(SmallCar.class);         carInterfaces.add(BigCar.class);         for(Class<?> carInterface : carInterfaces) {             if (carInterface instanceof Class) {                 Object obj = carInterface.newInstance();                 System.out.println(carInterface.getName());                 Method method= carInterface.getMethod("getName");                 Object result = method.invoke(obj);                 System.out.println(result);             }         }     }     public static void main2(String[] args) throws Exception {         List<CarInterface> carInterfaces = Lists.newArrayList();         carInterfaces.add(new BigCar());         carInterfaces.add(new SmallCar());         for(CarInterface carInterface : carInterfaces) {             if (carInterface instanceof CarInterface) {                 Class<?> cc = carInterface.getClass();                 System.out.println(cc.getName());                 Method method= cc.getMethod("getName");                 Object result = method.invoke(carInterface);                 System.out.println(result);             }         }     } }

生成对象

如果像拖过类的不带参数的构造方法来生成对象,有两种方式:

  1. 先获得Class对象,然后通过该Class对象的newInstance()方法直接生成
    Class<?> classType = String.class; Object obj = classType.newInstance;
  2. 先获得Class对象,然后通过改对象获得对应的Constructor对象,在通过Constructor对象的newInstance()方法生成(其中Customer是一个自定义的类,有一个无参数的构造函数,也有带参数的构造方法)
    Class<?> classType = Customer.class; Constructor con = classType.getConstructor(new Class[]{}); Object obj = con.newInstance(new Object[]{}); Object result = classType.getMethod("getName").invoke(obj);

    如果想通过类的带参数的构造方法生成对象,只能使用上面的第二种方法。
    可以看出调用构造方法生成对象的方法和调用一般方法的类似,不同的是从Constructor对象时不需要指定名字,而获取Method对象时需要指定名字。

    利用反射实现对象拷贝的例子

    // customer类 public class Customer {  private String name;  private String email;  public Customer() {  }  public Customer(String a, String b) {      name = a;      email = b;  }  public String getEmail() {      return email;  }  public void setEmail(String email) {      this.email = email;  }  public String getName() {      return name;  }  public void setName(String name) {      this.name = name;  } } // 类copy以及测试方法 public Object copy(Object object) throws Exception {  Class<?> classType = object.getClass();  Object objectCopy = classType.getConstructor(new Class[]{}).newInstance(new Object[]{});  // 获得对象的所有成员变量  Field[] fields = classType.getDeclaredFields();  for (Field field : fields) {      // 获取成员变量的名字      String name = field.getName();      // 获取get和set方法      String firstLetter = name.substring(0, 1).toUpperCase(); // 将属性的首字母转换为大写      String getMethodName = "get" + firstLetter + name.substring(1);      String setMethodName = "set" + firstLetter + name.substring(1);      // 获取方法对象      Method getMethod = classType.getMethod(getMethodName, new Class[]{});      Method setMethod = classType.getMethod(setMethodName, new Class[]{field.getType()}); //注意set方法需要传入参数类型      // 调用get方法获取旧的对象的值      Object value = getMethod.invoke(object, new Object[]{});      // 调用set方法将这个值赋值到新的对象中去      setMethod.invoke(objectCopy, new Object[]{value});  }  return objectCopy; } public static void main(String[] args) throws Exception {  Customer customer = new Customer("Tom", "tom@163.com");  Reflect reflect = new Reflect();  Customer customer1 = (Customer)reflect.copy(customer);  System.out.println(customer1.getEmail() + "," + customer1.getName()); }

    反射与数组

    java.lang.Array类提供了动态创建和访问数组元素的各种静态方法。
    下面例子创建了一个长度为10的字符串数组,接着把索引位置为5的元素设为Hello,然后在读取索引位置为5的元素的值。

    public static void main(String[] args) throws Exception {  Class<?> classType = Class.forName("java.lang.String");  // 生成数组,指定元素类型和数组长度  Object array = Array.newInstance(classType, 10);  Array.set(array, 5, "hello");  System.out.println((String)Array.get(array, 5)); }

    多维数组
    首先区别一下下面两者

    System.out.println(Integer.TYPE); ==> int System.out.println(Integer.class); ==>java.lang.Integer
    public static void main(String[] args) {  int[] dims = new int[]{5, 10, 15};  // 注意区分以下两种  System.out.println(Integer.TYPE); // int  System.out.println(Integer.class); // Integer  // 创建一个三维数组,这个数组的三个维度分别是5,10,15  Object array = Array.newInstance(Integer.TYPE, dims);  // 可变参数,也可以这样写  //Object array = Array.newInstance(Integer.TYPE, 5, 10, 15);  System.out.println(array instanceof int[][][]);  Class<?> classType = array.getClass().getComponentType(); //返回数组元素类型  System.out.println(classType); // 三维数组的元素为二维数组,输出:CLass[[  // 获得第一层的索引为3的数组,返回的是一个二维数组  Object arrayObject = Array.get(array, 3);  Class<?> classType1 = arrayObject.getClass().getComponentType(); // 返回数组的元素类型  System.out.println(classType1); // 二维数组的元素为一维数组,输出:class [  // 此处返回的是一个一维数组  arrayObject = Array.get(arrayObject, 5);  Class<?> classType2 = arrayObject.getClass().getComponentType(); //返回数组元素类型  System.out.println(classType2); // 一维数组的元素为int  // 给一维数组下标为10的位置设置值为37  Array.setInt(arrayObject, 10, 37);  int[][][] arrayCast = (int[][][]) array;  System.out.println(arrayCast[3][5][10]); }

    利用反射调用私有方法、访问私有属性

    利用反射,首先是Class对象的获取,之后是Method和Field的udixiang的获取。
    以Method为例,可以看到getMethod()返回的是public的Method对象,而getDeclaredMethod()返回Method对象可以是非public的。
    Field的方法同理。
    访问私有属性和方法,在使用前要通过AccessibleObject类(Constructor、Field和Method类的基类)中的setAccesible()方法来抑制Java访问权限的检查。
    实例1.调用私有方法

    // 有私有方法的类 public class PrivateClass {  private String sayHello(String name) {      return "Hello: " + name;  } } // 利用反射机制访问该方法 public class TestPrivate {  public static void main(String[] args) throws Exception {      PrivateClass privateClass = new PrivateClass();      Class<?> classType = privateClass.getClass();      // 获取Method对象      Method method = classType.getDeclaredMethod("sayHello", new Class[]{String.class});      // 抑制Java的访问控制检查      method.setAccessible(true);      // 如果不加上面语句,报错      // Class com.suys.java.reflect.TestPrivate can not access a member of class com.suys.java.reflect.PrivateClass with modifiers "private"      String str = (String)method.invoke(privateClass, new Object[]{"suys"});      System.out.println(str);  } }

    实例2.访问私有属性

    // 有私有属性的类 public class PrivateClass2 {  private String name = "zhangsan";  public String getName() {      return name;  } } //测试方法 public static void main(String[] args) throws Exception {  PrivateClass2 privateClass2 = new PrivateClass2();  Class<?> classType = privateClass2.getClass();  Field field = classType.getDeclaredField("name");  field.setAccessible(true);  field.set(privateClass2, "lisi");  System.out.println(privateClass2.getName()); }

    Class类

    java中的Object是所有类的继承根源,其中getClass()方法返回一个Class Object。所有的类都有这个方法。
    Class类十分特殊,它和其他类一样继承自Object,其实体用以表达java程序运行时的classes和interfaces,也用来表达enum、array、primitive Java types(boolean,byte,char,short,int,long,float,double)以及关键词void。
    当一个class被加载,或当加载器(class loader)的defineClass()被JVM调用,JVM便自动产生一个Class对象(Class object)。
    如果想借由修改Java标准库源码来观察Class对象的实际生成时机,例如在Class的constructor内添加println,那是不可以的,因为Class没有public constructor。
    Class是Reflection的起源,针对任何类,唯有先为它产生一个Class object,接下来才能经由后者唤起为数十多个的Reflection APIs。

参考:http://www.cnblogs.com/doudouxiaoye/p/5819813.html

转载本站任何文章请注明:转载至神刀安全网,谢谢神刀安全网 » java反射机制

分享到:更多 ()

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址