Blog

It's a Wonderful Life

java的反射机制

2017-04-26 23:38 Posted in Learn with Java

反射(reflection)通俗来讲就是根据给出的类名(字符串方式)来动态地生成对象。这种编程方式可以让对象在生成时才决定到底是哪一种对象。

下面给出一个详细的实例代码,注意参看注释。

package com.leo.test;

import java.lang.reflect.AnnotatedType;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Type;

public class ReflectionTest
{
    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, IllegalArgumentException, IllegalArgumentException, InvocationTargetException
    {
        //get class's package name and class name
        demo1();
        
        System.out.println("-------------------------------");
        //verify every class is a subclass of Class object
        demo2();
        
        System.out.println("-------------------------------");
        //create a new object through reflection
        demo3();
        
        System.out.println("-------------------------------");
        //create a new object through reflection (with a construction parameter)
        demo4();
        
        System.out.println("-------------------------------");
        //get private fields of new created object
        demo5();
        
        System.out.println("-------------------------------");
        //get object's specific information through reflection
        demo6();
        
        System.out.println("-------------------------------");
        //invoke new created object's methods through reflection
        demo7();
        
        System.out.println("-------------------------------");
        //get object's ClassLoader through reflection
        demo8();
    }
    
    //get class's package name and class name
    public static void demo1() throws ClassNotFoundException
    {
        Class<?> c = Class.forName("com.leo.test.Person");
        System.out.println("Package name: " + c.getPackage().getName());
        System.out.println("Class name: " + c.getName());
    }
    
    //verify every class is a subclass of Class object
    public static void demo2() throws ClassNotFoundException
    {
        Class<?> c = null;
        
        c = Class.forName("com.leo.test.Person");
        System.out.println("Package name: " + c.getPackage().getName());
        System.out.println("Class name: " + c.getName());
    }
    
    //create a new object through reflection
    public static void demo3() throws ClassNotFoundException, InstantiationException, IllegalAccessException
    {
        Class<?> c = Class.forName("com.leo.test.Person");
        //Cus c.newInstance() can't carry on a parameter, therefore, Person class must have a no-parameter's constructor
        Person p = (Person) c.newInstance();
        p.setName("Leo");
        p.setAge(22);
        System.out.println(p.toString());
    }
    
    //create a new object through reflection (with a construction parameter)
    public static void demo4() throws ClassNotFoundException, NoSuchMethodException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException
    {
        Class<?> c = Class.forName("com.leo.test.Person");
        Constructor<?> constructor = c.getConstructor(String.class, int.class);
        Person p = (Person) constructor.newInstance("Leo", 22);
        System.out.println(p.toString());
    }
    
    //get private fields of new created object
    public static void demo5() throws ClassNotFoundException, IllegalArgumentException, IllegalAccessException, InstantiationException
    {
        Class<?> c = Class.forName("com.leo.test.Person");
        Person p = (Person) c.newInstance();
        Field[] declaredFields = c.getDeclaredFields();
        for (Field f : declaredFields)
        {
            f.setAccessible(true);
            if (f.getAnnotatedType().getType().equals(int.class))
            {
                f.set(p, 22);
            }
            else if (f.getAnnotatedType().getType().equals(String.class))
            {
                f.set(p, "Leo");
            }
        }
        
        System.out.println(p.toString());
    }
    
    //get object's specific information through reflection
    public static void demo6() throws ClassNotFoundException
    {
        Class<?> c = Class.forName("com.leo.test.Policeman");
        Class<?> sc = c.getSuperclass();
        System.out.println("SuperClass: " + sc.getName());
        
        System.out.println();
        
        Field[] fields = c.getSuperclass().getDeclaredFields();
        for (Field f : fields)
        {
            System.out.println(f);
        }
        
        System.out.println();
        
        Method[] methods = c.getMethods();
        for (Method m : methods)
        {
            System.out.println(m);
        }
    }
    
    //invoke new created object's methods through reflection
    public static void demo7() throws ClassNotFoundException, NoSuchMethodException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException
    {
        Class<?> c = Class.forName("com.leo.test.Policeman");
        Method m = c.getMethod("patrol");
        m.invoke(c.newInstance());
        
        Method m1 = c.getMethod("patrol", int.class);
        m1.invoke(c.newInstance(), 30);
    }
    
    //get object's ClassLoader through reflection
    public static void demo8() throws ClassNotFoundException
    {
        Class<?> c = Class.forName("com.leo.test.Person");
        System.out.println(c.getClassLoader().getClass().getName());
    }
}

Person:

package com.leo.test;

public class Person
{
    protected String name;
    protected int age;

    public Person(){}

    public Person(String name, int age)
    {
        this.name = name;
        this.age = age;
    }

    public String getName()
    {
        return name;
    }

    public void setName(String name)
    {
        this.name = name;
    }

    public int getAge()
    {
        return age;
    }

    public void setAge(int age)
    {
        this.age = age;
    }

    @Override
    public String toString()
    {
        return "Person{" + "name=" + name + ", age=" + age + '}';
    }
}

Policeman:

package com.leo.test;

public class Policeman extends Person
{
    public Policeman() {}

    public Policeman(String name, int age)
    {
        this.name = name;
        this.age = age;
    }
    
    public void patrol()
    {
        System.out.println("Police patroling...");
    }
    
    public void patrol(int minutes)
    {
        System.out.println("Police patrol for " + minutes + " minutes...");
    }
}

为了方便起见,我把实例代码中的Checked Exceptionthrow掉了,在实际开发环境不建议这么做。

同时,在编写这段代码的时候,我学习到了getFields()getDeclaredFields()这两个方法的不同点就是前者只返回一个包含了类内部定义的public域,以及继承而来的public域的Field对象数组,而后者返回一个只包含类内部定义的全部Fields对象的数组(包括public protected default private),而不包括继承而来的域。所以如果我们想拿到继承来的域对象,可以使用以下语句:

Class<?> c = Class.forName("foo");
Field[] fields = c.getSuperClass().getDeclaredFields();

getMethods()getDeclaredMethods()方法同上所述。