[Java5新特性]反射

什么是反射机制

反射机制是Java 5版本提供的高级新特性,这种机制允许Java程序在运行状态中,对任意一个类都能知道该类的所有属性和方法;对任意一个对象,都能调用该对象的属性和方法。这种动态获取信息及动态调用对象的属性和方法的功能称之为Java的反射机制。

在我们编写Java类文件时,真正运行的是编译之后的“.class”文件。而“.class”文件在运行时,被加载到内存后,都是一个Class类的对象。我们可以通过Java 5提供的反射机制获取到该类的构造器、方法及成员变量等。

简单来说,Java的反射机制中提供了Class类、Constructor、Method及Field等,Class类就是类的元神,Constructor就是构造器的元神,Method就是方法的元神,Field就是成员变量的元神。

反射中的Class类

Class类是Java 5提供一个新类型,就叫做类类型。如何可以获取一个Class类型的对象呢?传统方式就是利用new Class()方式,但Java 5提供不同的方式。参看以下代码:

public class Demo {
    @Test
    public void demo() throws ClassNotFoundException {

        // 第一种方式:类名.class
        Class c1 = int.class;
        Class c2 = int[].class;

        // 第二种方式:对象.getClass()
        Class c3 = "hello".getClass();

        // 第三种方式:Class.forName("类全名")
        Class c4 = Class.forName("app.java.reflect.Demo");
    }
}

通过以上代码我们可以了解,获取Class实例的方式有三种:

  • 类名.class,例如:Class c1 = Object.class;
  • 对象.getClass(),例如:Class c2 = “Hello”.getClass();
  • Class.forName(“类全名”),例如:Class c1 = Class.forName(“app.java.reflect.Demo”);

获取到Class类实例之后,有什么具体用途呢?第一,我们可以通过Class类实例的getName()和getSimpleName()方法,获取到对应类的全类名或类名。

public class Demo {
    @Test
    public void demo() throws ClassNotFoundException {
        Class c1 = "hello".getClass();
        // java.lang.String
        System.out.println(c1.getName());

        Class c2 = int[].class;
        // [I:"["表示数组,"I"表示int类型
        System.out.println(c2.getName());
        // int[]
        System.out.println(c2.getSimpleName());
    }
}

第二,我们可以通过Class类实例的getSuperclass()方法,获取到对应类的完整继承链关系。

public class Demo {
    @Test
    public void demo() throws ClassNotFoundException {
        Class c = java.awt.Frame.class;
        while (c.getSuperclass() != null) {
            System.out.println(c.getSuperclass().getName());
            c = c.getSuperclass();
        }
    }
}

第三,我们可以通过反射机制来创建对象。

public class Demo {
    @Test
    public void demo() throws ClassNotFoundException, InstantiationException, IllegalAccessException {
        String className = "app.java.reflect.User";
        Class c = Class.forName(className);
        User user = (User) c.newInstance();
        user.setUsername("king");
        System.out.println(user.getUsername());
    }
}
class User{
    private String username;
    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    private String password;
}

反射中的Constructor

传统Java方式获取类的实例,基本都是通过构造器来实现,反射机制中的Class类同样具有构造器。那反射机制中Class类的构造器如何获得呢?我们来看以下代码:

public class Demo {

    @Test
    public void demo() throws Exception{
        Class<User> c = User.class;

        // 获取c类型中所有public构造器
        Constructor[] constructors1 = c.getConstructors();
        // 获取c类型中所有构造器
        Constructor[] constructors2 = c.getDeclaredConstructors();

        System.out.println(constructors1.length);
        System.out.println(constructors2.length);

        // 获取没有参数的public构造器
        Constructor con1 = c.getConstructor();
        // 获取参数类型依次为String.class,String.class的public构造器
        Constructor con2 = c.getConstructor(String.class,String.class);
        // 获取参数类型为String.class的构造器
        Constructor con3 = c.getDeclaredConstructor(String.class);
    }
}

通过上面的代码,我们可以知道:在获取到Class类实例后,有四种方式可以获取到对应的构造器。

  • getConstructors()方法:获取Class类所有的public构造器,其中包含没有参数和有参数的。
  • getDeclaredConstructors()方法:获取Class类所有的构造器,其中包含由public、private和protected修饰符的。
  • getConstructor()方法:获取具体的public修饰构造器,参数表示是否具有参数的构造器。
  • getDeclaredConstructor()方法:获取具体的构造器,其中包含由public、private和protected修饰符的。

我们得到Class类型的构造器之后,又会有什么样的用途呢?第一,可以通过构造器来创建实例对象。

public class Demo {
    @Test
    public void demo() throws Exception{
        Class<User> c = User.class;
        Constructor<User> constructor = c.getConstructor(String.class,String.class);
        User user = constructor.newInstance("king","123");
        System.out.println(user);
    }
}

第二,我们可以构造器来打印指定类型的所有构造器,以及参数类型。

public class Demo {
    @Test
    public void demo() throws Exception{
        Class<String> c = String.class;
        Constructor[] constructors = c.getDeclaredConstructors();
        for (Constructor constructor : constructors) {
            System.out.print(constructor.getDeclaringClass().getSimpleName() + "(");
            Class[] classes = constructor.getParameterTypes();
            for (int i = 0; i < classes.length; i++) {
                System.out.print(classes[i].getSimpleName());
                if (i < classes.length - 1) {
                    System.out.print(", ");
                }
            }
            System.out.println(")");
        }
    }
}

反射中的Method

与Class类的构造器类似的就是方法,我们首先讨论如何获取方法。

public class Demo {
    @Test
    public void demo() throws Exception{
        Class<User> c = User.class;

        // 获取本类和父类中的所有public方法
        Method[] ms1 = c.getMethods();
        System.out.println(ms1.length);

        // 获取本类中所有方法
        Method[] ms2 = c.getDeclaredMethods();
        System.out.println(ms2.length);

        // 获取本类或父类中名称为setUsername,参数类型为String的public方法
        Method m1 = c.getMethod("setUsername", String.class);

        // 获取本类中声明的名称为toString的,没有参数的方法,它可以是任何访问级别,但它不能是父类中的方法
        Method m2 = c.getDeclaredMethod("toString");
    }
}

通过上面的代码,我们可以知道:在获取到Class类实例后,有四种方式可以获取到对应的方法。

  • getMethods()方法:获取Class类及父类的所有public方法。
  • getDeclaredMethods()方法:获取Class类的所有方法,其中包含由public、private修饰符的。
  • getMethod()方法:获取Class类及父类中具体的public方法。
  • getDeclaredMethod()方法:获取Class类中具体的方法,其中包含由public、private修饰符的。但不能是父类中的方法。

下面我们来看一个利用Class类实例方法的练习。

public class Demo {
    @Test
    public void demo() throws Exception{
        Class<User> c = User.class;

        Method method = c.getMethod("setUsername", String.class);

        Object object = c.newInstance();

        method.invoke(object, "king");

        System.out.println(object);
    }
}

反射中的Field

与Class类的构造器和方法类似的就是成员变量,我们首先讨论如何获取成员变量。

public class Demo {
    @Test
    public void demo() throws Exception{
        Class<User> c = User.class;

        // 获取本类和父类中的所有public成员变量
        Field[] fields1 = c.getFields();
        System.out.println(fields1.length);

        // 获取本类中所有成员变量
        Field[] fields2 = c.getDeclaredFields();
        System.out.println(fields2.length);

        // 获取本类或父类中名称为password,参数类型为String的public成员变量
        Field field1 = c.getField("password");

        // 获取本类中名称为password的成员变量,但它不能是父类中的成员变量
        Field field2 = c.getDeclaredField("password");
    }
}

通过上面的代码,我们可以知道:在获取到Class类实例后,有四种方式可以获取到对应的成员变量。

  • getFields()方法:获取Class类及父类的所有public成员变量。
  • getDeclaredFields()方法:获取Class类的所有成员变量,其中包含由public、private修饰符的。
  • getField()方法:获取Class类及父类的具体成员变量。
  • getDeclaredField()方法:获取Class类的具体成员变量,其中包含由public、private修饰符的。但不能是父类中的方法。

获取到Class类的成员变量之后,我们可以进行设置和获取操作。

public class Demo {
    @Test
    public void demo() throws Exception{
        Class<User> c = User.class;

        Field field = c.getField("password");

        Object object = c.newInstance();

        field.set(object, "123");
        System.out.println(field.get(object));
    }
}

需要注意的是这里只能设置和获取修饰符为public的成员变量,不能操作修饰符为private的成员变量。

AccessibleObject

AccessibleObject类是Constructor、Method和Field三个类的父类。AccessibleObject类的常用方法有以下几种:

  • isAccessible()方法:判断当前成员是否可访问。
  • setAccessible()方法:设置当前成员是否可访问。

当Class类中的构造器、方法和成员变量是私有的时候,如果我们想反射操作的话,就必须先调用setAccessible(true)方法。

public class Demo {
    @Test
    public void demo() throws Exception{
        Class<User> c = User.class;

        Field field = c.getDeclaredField("username");

        Object object = c.newInstance();

        field.setAccessible(true);

        field.set(object, "king");

        System.out.println(field.get(object));
    }
}

转载说明:请注明作者及原文链接,谢谢!

posted @ 2015-05-31 22:55  原来我就是麦兜  阅读(301)  评论(0编辑  收藏  举报