java反射机制(1)- 知识点总结Java Reflection API操作

来源:互联网 发布:黄金价格数据 编辑:程序博客网 时间:2024/06/06 16:52

  转载需注明出处:java反射机制(1)- 知识点总结Java Reflection API操作

1 什么是反射机制

  什么是反射机制?简单点说就是程序在运行时能够获取自身的信息。在java中,只要给定类的全名,就可以通过反射机制来获取类的所有信息。
  复杂点说就是:通过Class类,可以在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法;这种动态获取的信息以及动态调用对象的方法的功能酒称为java的反射机制。

  Java反射机制主要提供了一下功能:
- 在运行时判断任意一个对象所属的类;
- 在运行时构造任意一个类的对象;
- 在运行时判断任意一个类所具有的成员变量和方法;
- 在运行时调用任意一个对象的方法;
- 生成动态代理;

2 先认识下静态加载类与动态加载类

  静态加载类(编译时加载类):大多数情况下都是使用这种形式。比如我们定义了一个类A,实例化采用A a = new A()接着就可以通过a对象调用相关方法或属性,这就是静态加载类的过程。
  动态加载类(运行时加载类):所谓动态加载类,只需要通过Class clazz = Class.forName("类的全名")即可获得类类型,然后通过调用A a = clazz.newInstance()方法即可实例化这个类。
  本质的区别在于静态加载的类的源程序在编译时期加载(必须存在),而动态加载的类在编译时期可以缺席(源程序不必存在)。

3 Java Reflection API简介

  JDK中,主要通过一下类实现java反射机制,这些类抖位于java.lang.reflect包中。

  • Class:代表一个类
  • Filed:代表类的成员变量或者说成员属性
  • Method:代表类的方法
  • Constructor:代表类的构造方法
  • Array:提供了动态创建数组,以及访问数组元素的静态方法
    这里写图片描述
    这里写图片描述
      下面直接通过几个案例说明相关API操作:

      案例1:获取类名、方法、属性已经构造函数等基本信息。
      在 java.lang.Object 类中定义了 getClass()方法, 因此对于任意一个 Java 对象, 都可以通过此方法获得对象的类型。Class 类是 Reflection API 中的核心类,它有以下方法。

  • getName():获得类的完整名字。

  • getFields():获得类的 public 类型的属性。
  • getDeclaredFields():获得类的所有属性。
  • getMethods():获得类的 public 类型的方法。
  • getDeclaredMethods():获得类的所有方法。
  • getMethod(String name, Class[] parameterTypes):获得类的特定方法,name参数指定方法的名字,parameterTypes参数指定方法的参数类型。
  • getConstrutors():获得类的 public类型的构造方法。
  • getConstrutor(Class[] parameterTypes):获得类的特定构造方法, parameterTypes参数指定构造方法的参数类型。
  • newInstance():通过类的不带参数的构造方法创建这个类的一个对象。
package com.markliu.reflection.getinfo;import java.lang.reflect.Constructor;import java.lang.reflect.Field;import java.lang.reflect.Method;public class DumpMethodsConstructorsFields {    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, SecurityException {        /* Returns the Class object associated with the classname */        Class<?> clazz = Class.forName("java.util.Stack");        Class<?> clazz1 = Class.forName("java.io.FileInputStream", true, java.io.FileInputStream.class.getClassLoader());        System.out.println(clazz);        System.out.println(clazz1);        System.out.println("--------interfaces------");        Class<?>[] interfaces = clazz1.getInterfaces();        for(Class<?> iClass : interfaces) {            System.out.println(iClass.getName());        }        System.out.println("--------------");        /*         *  获取所有声明的方法(不包括构造函数),public,protected,缺省和private类型,         *  包括返回类型、方法名,参数和抛出异常.         *  getDeclaredMethods():获得类的所有方法。         */        Method[] methods = clazz.getDeclaredMethods();        for(Method m : methods) {            System.out.println(m);        }        System.out.println("--------------");        /*         * getMethods():获得类的 public 类型的方法。         */        Method[] methods1 = clazz1.getMethods();        for(Method m : methods1) {            System.out.println(m);        }        System.out.println("--------------");        /*         * getMethod(String name, Class[] parameterTypes):获得类的特定方法,         * name 参数指定方法的名字         * parameterTypes 参数指定方法的参数类型。         * 获取:public int java.io.FileInputStream.read(byte[],int,int) throws java.io.IOException         */        Method method = clazz1.getMethod("read", new Class[]{byte[].class, int.class, int.class});        System.out.println(method);        System.out.println("-------public Constructors-------");        Constructor<?>[] publicConstructors = clazz1.getConstructors();        for(Constructor<?> c : publicConstructors) {            System.out.println(c);        }        System.out.println("-------constructors-------");        Constructor<?>[] constructors = clazz1.getDeclaredConstructors();        for(Constructor<?> c : constructors) {            System.out.println(c);        }        /* 根据参数数组获取指定的public型构造函数*/        Constructor<?> constructor = clazz1.getConstructor(new Class[]{java.io.File.class});        System.out.println(constructor);        System.out.println("-------public field-------");        /* 获得类的所有public类型的属性。*/        Field[] fields = clazz1.getFields();        for(Field field : fields) {            System.out.println(field);        }        System.out.println("-------field-------");        /* 获得类的所有属性。*/        Field[] fields1 = clazz1.getDeclaredFields();        for(Field field : fields1) {            System.out.println(field);        }        System.out.println("-------field's setters and getters methods-------");        /*         * 获取属性的set和get方法         * 我们不能直接获取setter和getter方法,需要获取到所有的方法,         * 再进行循环选择。注意对于boolean类型,默认采用的是isXxx()         */        Class<?> clazz2 = Class.forName("com.markliu.reflection.invoke.Person");        Method[] methods2 = clazz2.getMethods();        for(Method m : methods2) {            if (isGetter(m))                System.out.println(m);            if (isSetter(m))                 System.out.println(m);        }    }    public static boolean isGetter(Method method) {        String methodName = method.getName();        Class<?> returnType = method.getReturnType();        Class<?> parameterTypes[] = method.getParameterTypes();        if(returnType.equals(void.class)) {            return false;        }        if ( (methodName.startsWith("get") || methodName.startsWith("is"))                 && parameterTypes.length == 0) {            return true;        }        return false;    }    public static boolean isSetter(Method method) {        String methodName = method.getName();        Class<?> parameterTypes[] = method.getParameterTypes();        if (methodName.startsWith("set") && parameterTypes.length == 1) {            return true;        }        return false;    }}

  输出结果为(考虑篇幅,只列出获取setter和getter方法的结果,其他的省略):

public boolean com.markliu.reflection.invoke.Person.isGoodman()public void com.markliu.reflection.invoke.Person.setGoodman(boolean)public java.lang.Integer com.markliu.reflection.invoke.Person.getAge()public void com.markliu.reflection.invoke.Person.setAge(java.lang.Integer)public java.lang.String com.markliu.reflection.invoke.Person.getName()public void com.markliu.reflection.invoke.Person.setName(java.lang.String)public final native java.lang.Class java.lang.Object.getClass()

  案例2:获取类的私有属性和私有方法。
  person类的私有属性和方法:

private String name;private Integer age;private void introduce() {    System.out.println("My name is " + name + ", I'm " + age + " years old!");}
System.out.println("-------private fields and methods-------");Person person = new Person("SunnyMarkLiu", 22);System.out.println("name:" + person.getName());Field privateField = Person.class.getDeclaredField("name");// !important 设置标志为true后,被反射的类会抑制java的访问检查机制privateField.setAccessible(true);privateField.set(person, "HasChanged!");System.out.println("反射修改后:" + person.getName());Method privateMethod = Person.class.getDeclaredMethod("introduce", new Class[]{});// !important 设置标志为true后,被反射的类会抑制java的访问检查机制privateMethod.setAccessible(true);privateMethod.invoke(person, new Object[]{});

  注意:在访问私有属性和私有方法时,需要对访问的私有属性或方法设置setAccessible(true)使被反射的类抑制java的访问检查机制。否则会报IllegalAccessException异常!
  运行结果如下:

name:SunnyMarkLiu反射修改后:HasChanged!My name is HasChanged!, I'm 22 years old!

  案例3:通过调用构造方法创建一个新的对象:。
  先调用 Class 类的 getConstructor方法获得一个 Constructor 对象,然后调用 Constructor 对象的 newInstance方法构造一个实例。

package com.markliu.reflection.newInstance;import java.lang.reflect.Constructor;import java.lang.reflect.InvocationTargetException;public class NewInstance {    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException,     SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException,     InvocationTargetException {        Class<?> clazz = Class.forName("com.markliu.reflection.newInstance.PersonBean");        System.out.println(clazz);        // 获取有参构造函数        Constructor<?> constructor = clazz.getConstructor(new Class[]{String.class, Integer.class});        PersonBean person = (PersonBean) constructor.newInstance(new Object[]{"SunnyMarkLiu", 20});        person.info();        // 获取有参构造函数        Constructor<?> constructor1 = clazz.getConstructor(new Class[]{});        PersonBean person1 = (PersonBean) constructor1.newInstance(new Object[]{});        person1.info();    }}class PersonBean implements Person{    private String name;    private Integer age;    public PersonBean() {}    public PersonBean(String name, Integer age) {        this.name = name;        this.age = age;    }    // 省略了get和set方法    public void info() {        System.out.println(this.name + ":" +this.age);    }}interface Person {    public void info();}

  案例4:运用反射机制调用对象的方法。
  Method 类的 invoke(Object obj,Object args[])方法接收的参数必须为对象, 如果参数为基本类型数据, 必须转换为相应的包装类型的对象。 invoke()方法的返回值总是对象,如果实际被调用的方法的返回类型是基本类型数据, 那么 invoke()方法会把它转换为相应的包装类型的对象,再将其返回。

package com.markliu.reflection.invoke;import java.lang.reflect.Method;public class MethodInvoke {    public static void main(String[] args) throws Exception {        Class<?> clazz = Class.forName("com.markliu.reflection.invoke.PersonService");        PersonService personService = (PersonService) clazz.getConstructor(new Class<?>[]{}).newInstance(new Object[]{});        // 获取要调用的方法        Method method = clazz.getMethod("add", new Class[]{Person.class});        /*         * 调用personService对象的method方法,同时传递参数。         * 返回值result为调用该函数的返回值,如果函数返回类型为void,则result为null         */        Object result = method.invoke(personService,                                 new Object[]{new Person("SunnyMarkLiu", 22)});        System.out.println(result);        Method method1 = clazz.getMethod("get", new Class[]{int.class});        Object result1 = method1.invoke(personService,                                 new Object[]{0});        System.out.println(result1);        Person p = (Person) result1;        System.out.println(p.getName()+":"+p.getAge());    }}// PersonService类package com.markliu.reflection.invoke;import java.util.List;import java.util.ArrayList;public class PersonService {    private List<Person> persons = new ArrayList<Person>();    public String add(Person p) {        persons.add(p);        return "add-done";    }    public Person get(int i) {        return persons.get(i);    }}// Person类package com.markliu.reflection.invoke;public class Person {    private String name;    private Integer age;    public Person(String name, Integer age) {        this.name = name;        this.age = age;    }    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    public Integer getAge() {        return age;    }    public void setAge(Integer age) {        this.age = age;    }}

  案例5:Array 类提供了动态创建和访问数组元素的各种静态方法。

package com.markliu.reflection.array;import java.lang.reflect.*;public class ArrayTester {    public static void main(String args[]) throws Exception {        // 设置数组的维度为5x10        int dims[] = new int[] { 5, 10, 15 };        Class<?> classType = Class.forName("java.lang.String");        Object array = Array.newInstance(classType, dims);        // Object array = Array.newInstance(Integer.TYPE, dims);        // 使 arrayObj 引用 array[3]        Object arrayObj = Array.get(array, 3);        Class<?> cls = arrayObj.getClass().getComponentType();        System.out.println("ComponentType" + cls);        // 使 arrayObj 引用 array[3][5]        Object arrayObj1 = Array.get(arrayObj, 5);        // 把元素 array[3][5][10]设为 37        Array.set(arrayObj1, 10, "hello");        String arrayCast[][][] = (String[][][]) array;        System.out.println(arrayCast[3][5][10]);        /*         * 输出:          * ComponentTypeclass [Ljava.lang.String;          * hello         */    }}

4 小结

  Java 反射机制是 Java 语言的一个重要特性。考虑实现一个 newInstance(String className)方法,它的作用是根据参数 className 指定的类名,通过该类的不带参数的构造方法创建这个类的对象,并将其返回。如果不运用 Java 反射机制,必须在newInstance()方法中罗列参数 className 所有可能的取值,然后创建相应的对象:

public Object newInstance(String className) throws Exception{    if(className.equals("HelloService1"))        return new HelloService1();    if(className.equals("HelloService2"))        return new HelloService2();    if(className.equals("HelloService3"))        return new HelloService3();    if(className.equals("HelloService4"))        return new HelloService4();    ...    if(className.equals("HelloService1000"))        return new HelloService1000();}

  以上程序代码很冗长,而且可维护性差。如果在以后软件的升级版本中去除了一个 HelloService4 类,或者增加了一个 HelloService1001 类,都需要修改以上 newInstance()方法。如果运用反射机制,就可以简化程序代码,并且提高软件系统的可维护性和可扩展性:

public Object newInstance(String className) throws Exception{    Class classType=Class.forName(className);    return classType.newInstance();}

  Java 反射机制在服务器程序和中间件程序中得到了广泛运用。在服务器端,往往需要根据客户的请求 ,动态调用某一个对象的特定方法。此外 ,有一种ORM(Object-Relation Mapping,对象-关系映射,例如Hibernate)中间件能够把任意一个 JavaBean 持久化到关系数据库中。在 ORM 中间件的实现中,运用 Java 反射机制来读取任意一个JavaBean 的所有属性, 或者给这些属性赋值。 (小结摘自:孙卫琴《Java网络编程精解》第10章)

4 0
原创粉丝点击