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

  转载需注明出处: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:提供了动态创建数组,以及访问数组元素的静态方法
    java反射机制(1)- 知识点总结Java Reflection API操作_第1张图片
    java反射机制(1)- 知识点总结Java Reflection API操作_第2张图片
      下面直接通过几个案例说明相关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 persons = new ArrayList();
    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章)

你可能感兴趣的:(java反射机制(1)- 知识点总结Java Reflection API操作)