Skip to content

1 什么是反射机制

JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取对象信息以及动态调用对象方法的功能称为java语言的反射机制。

说白了,就是从内存中的字节码文件中获取相关信息。

2 反射API

涉及的类:

Class类—描述类的信息,java.lang包下

Field类—描述类的属性的信息

Method类—描述类的方法的信息

Constructor类—描述类的构造方法的信息

2.1 Class

JVM通过.class文件加载类,.class文件是对类的信息的描述,每当JVM加载一个类,就产生对应的Class对象

它的构造函数是私有的,因此我们无法通过new创建Class类型的对象

Java.lang.Class类的常用方法

方法
public String getName()获得该类的名字
public static Class forName(String className)加载类
public Object newInstance()创建某类的一个新实例
public Field getDeclaredField(String name)返回一个 Field 对象
public Field getField(String name)返回一个 Field 对象(public)
public Field[] getDeclaredFields()返回 Field类型的一个数组,类的所有属性
public Field[] getFields()获得Field类型的数组,类的所有公有属性
public Method[] getDeclaredMethods()获得Method类型的数组,类的所有方法
public Method[] getMethods()获得Method类型的数组,类的所有公有方法
public Method getMethod(String name,Class ... paramTypes )获得某类中的某个方法
Constructor<?>[] getDeclaredConstructors()返回此Class对象表示的类声明的所有构造方法
Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)返回此Class对象所表示的类或接口的指定构造方法

2.1.1 获取Class对象

java
//方法1:对象.getClass()
Student stu = new Student();
Class clazz = stu.getClass();	
//方法2:类.class
clazz = Student.class;
clazz = String.class;
//方法3:Class.forName()
clazz = Class.forName("com.glls.reflect.Student");
clazz = Class.forName("java.lang.String");
clazz = Class.forName("java.util.Date");

2.1.2 获取Constructor

java
Class cla = Class.forName("com.glls.reflect.Student");
//得到类中的所有public的构造函数
Constructor[] cons = cla.getConstructors();
for (Constructor c : cons) {
    System.out.println(c);
}
System.out.println("--------------------------");
//得到类中所有的构造函数(包括私有的)
cons = cla.getDeclaredConstructors();
for (Constructor c : cons) {
    System.out.println(c);
}

//得到默认的构造方法(无参的构造方法)
Constructor c = cla.getConstructor(null);

// 得到带参的构造方法
c = cla.getConstructor(String.class, int.class);

//可以得到私有的(非公有的)构造方法
c = cla.getDeclaredConstructor(int.class);
//设置可以访问
c.setAccessible(true);

2.1.2 通过反射创建对象

java
Class clazz = Class.forName("com.glls.reflect.Student");
Object obj = clazz.newInstance();
java
// 获取带参的构造方法
Constructor con = cla.getConstructor(new Class[ ]{String.class, int.class});
Object obj = con.newInstance(new Object[ ]{"fy", 18});
Object obj2 = con.newInstance("zhangsan", 18);

2.2 Field

Java.lang.reflect.Field类的常用方法:

方法
public String getName()获得属性名
public Class getType()获得属性的类型
Object get(Object obj)返回指定对象上此Field对象字段的值
void set(Object obj, Object value)将指定对象上此Field对象表示的字段设置为指定的值
void setAccessible(boolean flag)设置指定对象上此Field对象表示的字段的可见性

2.2.1 获取Field对象

java
Class cla = Class.forName("com.glls.reflect.Student");
//得到public的成员变量
Field[] fields = cla.getFields();
for (Field f : fields) {
    System.out.println(f);
}
System.out.println("---------------");
//得到所有的成员变量
fields = cla.getDeclaredFields();
for (Field f : fields) {
    System.out.println(f);
}

//获得指定名称的成员变量的Field对象
Field f = cla.getField("age");

// 获得私有的变量
f = cla.getDeclaredField("weight");
f.setAccessible(true);

2.2.2 属性的调用

java
Class clazz = Class.forName("com.glls.reflect.Student");
Object obj = clazz.newInstance();

Field field = cla.getDeclaredField("name");
field.setAccessible(true);	
field.set(obj, "lisi");
field.get(obj);			// stu.getName();

2.3 Method

Java.lang.reflect.Method类的常用方法:

方法
public String getName()获得方法名
public Class[] getParameterTypes()获得一个存储方法的参数类型的数组
public Class getReturnType()获得方法的返回值类型
public Object invoke(Object obj, Object... args)调用方法,第一个参数表示某个对象,后面的参数表示方法的参数值

2.3.1 获取Method对象

java
Class cls = Class.forName("com.glls.reflect.Student");

//获取类中所有public的方法,包括父类中的方法
Method[] methods = cls.getMethods();
for (Method m : methods) {
    System.out.println(m);
}
System.out.println("-----------------");

//获得本类中的所有方法
methods = cls.getDeclaredMethods();
for (Method m : methods) {
    System.out.println(m);
}

//根据方法名和参数类型,获取Method对象
Method m = cls.getMethod("drink", null);
m = cls.getMethod("eat", int.class);


//获得任意访问权限的方法(包括私有的)
m = cls.getDeclaredMethod("sleep", null);
m.setAccessible(true);

2.3.2 调用方法

java
Obejct stu = cla.newInstance();
Method method1 = cla.getDeclaredMethod("getName", null);
// 调用方法,第一个参数表示方法所在的对象
// 调用无参的方法
method1.invoke(stu, null);

Method method2 = cla.getDeclaredMethod("setName", new Class[]{String.class});
// 调用带参的方法
method2.invoke(stu,  new Object[]{"setName is running"});

附录

简单工厂模式的优化

java
public class PayFactory {
	private PayFactory(){}
    public static IPay getInstance(String className){
        try{
            return (IPay) Class.forName(className).newInstance(); 
        }catch(Exception e){
            e.printStackTrace();
        }
		return null;
        
    }
}