工学1号馆

home

java核心系列11-反射

By Wu Yudong on June 12, 2015

原创文章,转载请注明: 转载自工学1号馆

在Java运行时环境中,对于任意一个类,能否知道这个类有哪些属性和方法?对于任意一个对象,能否调用它的任意一个方法?答案是肯定的。这种动态获取类的信息以及动态调用对象的方法的功能来自于Java 语言的反射机制。

Java的反射机制

Java 反射机制主要提供了以下功能:

在运行时判断任意一个对象所属的类。

在运行时构造任意一个类的对象。

在运行时判断任意一个类所具有的成员变量和方法。

在运行时调用任意一个对象的方法

Reflection 是Java被视为动态(或准动态)语言的一个关键性质。这个机制允许程序在运行时透过Reflection API取得任何一个已知名称的class的内部信息,包括其modifiers(诸如public, static 等等)、superclass(例如Object)、实现之interfaces(例如Serializable),也包括fields和methods的所有信息,并可于运行时改变fields内容或调用methods

在JDK中,主要由以下类来实现Java反射机制,这些类都位于java.lang.reflect包中:

Class类:代表一个类。

Field 类:代表类的成员变量(成员变量也称为类的属性)。

Method类:代表类的方法。

Constructor 类:代表类的构造方法。

Array类:提供了动态创建数组,以及访问数组的元素的静态方法

Proxy类以及InvocationHandler接口:提供了动态生成代理类以及实例的方法

其中,Class类是Reflection API 中的核心类,它有以下方法:

  • getName():获得类的完整名字
  • getFields():获得类的public类型的属性
  • getDeclaredFields():获得类的所有属性
  • getMethods():获得类的public类型的方法
  • getDeclaredMethods():获得类的所有方法
  • getMethod(String name, Class[] parameterTypes):获得类的特定方法,name参数指定方法的名字,parameterTypes 参数指定方法的参数类型
  • getConstructors():获得类的public类型的构造方法
  • getConstructor(Class[] parameterTypes):获得类的特定构造方法,parameterTypes 指定构造方法的参数类型
  • newInstance():通过类的不带参数的构造方法创建这个类的一个对象

每当一個类被载入时,JVM就自动为其生成一个Class对象,通过操作class对象,我们可以得到该对象的所有成员并操作它们,举个例子:

package javatest;
import java.util.*;
class Student {
    private String name;
    private int age;
    private int ID;
    public Student() {
        
    }
    public Student(String name, int age, int ID) {
        this.name = name;
        this.age = age;
        this.ID = ID;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}
public class javatest {
    public static void main(String[] args) {
        Student s1 = new Student("java", 20, 123);
        Class ss = s1.getClass();
        System.out.println("getName: " + ss.getName());
        System.out.println("getFields: " + ss.getFields());
        System.out.println("getDeclaredFields: " + ss.getDeclaredFields());
        System.out.println("getMethods: " + ss.getMethods());
        System.out.println("isInterface: " + ss.isInterface());
        System.out.println("isPrimitive: " + ss.isPrimitive());
        System.out.println("isArray: " + ss.isArray());
        System.out.println("SuperClass: " + ss.getSuperclass().getName());
    }
}

运行结果如下:

getName: javatest.Student
getFields: [Ljava.lang.reflect.Field;@4e25154f
getDeclaredFields: [Ljava.lang.reflect.Field;@70dea4e
getMethods: [Ljava.lang.reflect.Method;@5c647e05
isInterface: false
isPrimitive: false
isArray: false
SuperClass: java.lang.Object

通过反射得到类对象:

获取方式 说明 示例
object.getClass()每个对象都有此方法 获取指定实例对象的Class List list = new ArrayList();Class listClass = list.getClass();
class. getSuperclass() 获取当前Class的继承类Class List list = new ArrayList();Class listClass = list.getClass();Class superClass = listClass. getSuperclass();
Object.class .class直接获取 Class listClass = ArrayList.class;
Class.forName(类名) 用Class的静态方法,传入类的全称即可 try {Class c = Class.forName("java.util.ArrayList");} catch (ClassNotFoundException e) {e.printStackTrace();}
Primitive.TYPE 基本数据类型的封装类获取Class的方式 Class longClass = Long.TYPE;Class integerClass = Integer.TYPE;Class voidClass = Void.TYPE;

平常情况我们通过new Object来生成一个类的实例,但有时候我们没法直接new,只能通过反射动态生成。

通过反射实例化对象:

实例化无参构造函数的对象,两种方式:

① Class. newInstance();

② Class. getConstructor (new Class[]{}).newInstance(new Object[]{})

实例化带参构造函数的对象:

class.getConstructor(Class<?>... parameterTypes) . newInstance(Object... initargs)

接下来举个例子实战一下:

package javatest;
import java.util.*;

class BaseUser {
    public int baseId;
    public int getBaseId() {
        return baseId;
    }
    public void setBaseId(int baseId) {
        this.baseId = baseId;
    }
}

class User extends BaseUser {
    private int id;
    public String name;
    public User(){}
    public User(String name) {
        this.name = name;
    }
    private int getId() {
        return id;
    }
    private void serId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}

public class javatest {
    public static void main(String[] args) {
        Class<?> userClass = User.class;
        try {
            User user = (User)userClass.newInstance();
            System.out.println("1.反射实例化(无参): " + user);
            User user2 = (User)userClass.getConstructor(new Class[]{}).newInstance(new Object[]{});
            System.out.println("2.反射实例化(无参): " + user2);
            User user3 = (User)userClass.getConstructor(new Class[]{String.class}).newInstance(new Object[]{"test"});
            System.out.println("反射实例化(带参): " + user3 + " 属性Name的值: " + user3.getName());
            User user4 = new User();
            System.out.println("正常实例化: " + user4);
        } catch(Exception e) {
            e.printStackTrace();
        }
    }
}

运行结果如下:

1.反射实例化(无参): javatest.User@7852e922
2.反射实例化(无参): javatest.User@4e25154f
反射实例化(带参): javatest.User@70dea4e 属性Name的值: test
正常实例化: javatest.User@5c647e05

通过反射调用Method(方法):

获得当前类以及超类的public Method:

Method[] arrMethods = classType.getMethods();

获得当前类申明的所有Method:

Method[] arrMethods = classType.getDeclaredMethods();

获得当前类以及超类指定的public Method:

Method method = classType.getMethod(String name, Class<?>... parameterTypes);

获得当前类申明的指定的Method:

Method method = classType.getDeclaredMethod(String name, Class<?>... parameterTypes)

通过反射动态运行指定Method:

Object obj = method.invoke(Object obj, Object... args)

例:动态操纵Method,改变一下上面的例子的主函数:

public class reflectMethodDemo {
    public static void main(String[] args) {
        User user = new User();
        Class<?> userClass = User.class;
        
        Method[] publicMethod = userClass.getMethods();
        for(Method method : publicMethod) {
            System.out.println("获得当前类以及超类的所有publicMethod: " + method);
        }
        
        Method[] currentMethod = userClass.getDeclaredMethods();
        for(Method method : currentMethod) {
            System.out.println("获得当前类自己声明的所有的Method: " + method);
        }
        try {
            Method setBaseIdMethod = userClass.getMethod("setBaseId", new Class[]{int.class});
            System.out.println("获得当前类或超类的public Method setBaseId: " + setBaseIdMethod);
            Method setIdMethod = userClass.getDeclaredMethod("setId", new Class[]{int.class});
            System.out.println("获得当前类的Method setId: " + setIdMethod);
            setIdMethod.setAccessible(true);
            setIdMethod.invoke(user, new Object[]{110});
            Method getIdMethod = userClass.getDeclaredMethod("getId", new Class[]{});
            getIdMethod.setAccessible(true);
            Integer getId = (Integer)getIdMethod.invoke(user, new Object[]{});
            System.out.println("调用getId方法获得: " + getId);
        } catch(Exception e) {
            e.printStackTrace();
        }
    }
}

通过反射调用Field(变量):

获得当前类以及超类的public Field:

Field[] arrFields = classType.getFields();

获得当前类申明的所有Field:

Field[] arrFields = classType.getDeclaredFields();

获得当前类以及超类指定的public Field:

Field field = classType.getField(String name);

获得当前类申明的指定的Field:

Field field = classType.getDeclaredField(String name);

通过反射动态设定Field的值:

fieldType.set(Object obj, Object value);

通过反射动态获取Field的值:

Object obj = fieldType.get(Object obj) ;

例:动态操纵Field,改变一下上面的例子的主函数:

public class reflectFieldDemo {
    public static void main(String[] args) {
        User1 user = new User1();
        Class<?> userClass = user.getClass();
        Field[] publicField = userClass.getFields();
        for(Field field : publicField) {
            System.out.println("获得该类及超类所有public Field: " + field);
        }
        
        Field[] currentField = userClass.getDeclaredFields();
        for(Field field : currentField) {
            System.out.println("获得该类自己声明的所有Field: " + field);
        }
        try {
            Field baseIdField = userClass.getField("baseId");
            System.out.println("获得该类或超类名为baseId的public Field: " + baseIdField);
            Field idField = userClass.getDeclaredField("id");
            System.out.println("获得该类自己声明的名为id的Field: " + idField);
            idField.setAccessible(true);
            idField.set(user, 110);
            Integer id = (Integer)idField.get(user);
            System.out.println("id的值为: " + id);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Java反射总结:

1、只要用到反射,先获得Class Object

2、没有方法能获得当前类的超类的private方法和属性,你必须通过getSuperclass()找到超类以后再去尝试获得

3、通常情况即使是当前类,private属性或方法也是不能访问的,你需要设置压制权限setAccessible(true)来取得private的访问权。但说实话,这已经破坏了面向对象的规则,所以除非万不得已,请尽量少用。

如果文章对您有帮助,欢迎点击下方按钮打赏作者

Comments

No comments yet.
To verify that you are human, please fill in "七"(required)