我们可以通过编程方式调用 Class 的各项功能,与通过构造函数的方法直接调用类功能的效果是一致的,只不过前者是间接调用,后者是直接调用罢了。
先上一段简单代码
public class Person {
private int id;
private String name;
public void setId(int id) {
this.id = id;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return name + "\t" + id;
}
}
我们不能使用 new Person 的方式来实例化以及使用方法,所以使用 java 的反射机制也可以使用同样的功能
public class ReflectTest {
public static Person initByDefaultConst() throws ClassNotFoundException, NoSuchMethodException
, IllegalAccessException, InvocationTargetException, InstantiationException {
//通过类装载器获取 Person 对象,但这个对象并没有什么卵用
ClassLoader loader = Thread.currentThread().getContextClassLoader();
Class clazz = loader.loadClass("com.draper.Person");
//获取默认构造器对象
Constructor cons = clazz.getDeclaredConstructor(null);
Person person = (Person) cons.newInstance();
//在这里直接 return 出去将只是个对象,他的属性都是默认值,例如 null 或 0...
return person;
}
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException
, InvocationTargetException, InstantiationException, IllegalAccessException {
Person person = initByDefaultConst();
System.out.println(person.toString());
}
}
结果如下
所以我们要想办法调用 Person 的方法 setId() 和 setName() 来赋值,但是我在这里讲的不是
person.setId(111);
person.setName("小明");
这样不符合我们学习反射的目的,虽然也可以
所以我们使用反射的方法完成如下
public class ReflectTest {
public static Person initByDefaultConst() throws ClassNotFoundException
, NoSuchMethodException, IllegalAccessException
, InvocationTargetException, InstantiationException {
//通过类装载器获取 Person 对象,但这个对象并没有什么卵用
ClassLoader loader = Thread.currentThread().getContextClassLoader();
Class clazz = loader.loadClass("com.draper.Person");
//获取默认的构造器对象并实例化,请连起来读,构造器对象~
Constructor cons = clazz.getDeclaredConstructor(null);
Person person = (Person) cons.newInstance();
//通过反射方法来设置属性
Method setId = clazz.getMethod("setId",int.class);
setId.invoke(person, 123);
Method setName = clazz.getMethod("setName", String.class);
setName.invoke(person, "小明");
return person;
}
public static void main(String[] args) throws ClassNotFoundException
, NoSuchMethodException, InvocationTargetException
, InstantiationException, IllegalAccessException {
Person person = initByDefaultConst();
System.out.println(person.toString());
}
}
根据上述简单的例子,我们来分析里面使用的类
ClassLoader
在 Java 中,ClassLoader 是一个抽象类,位于 java.lang 包。里面有很多重要接口方法
- Class loadClass(String name)
name 参数指定类装载器需要装载类的名字,必须使用全限定名,如 com.draper.beans.Person
- Class defineClass(String name,byte[] b, int off, int len)
将类文件的字节数组转化成 JVM 内部的 java.lang.Class 对象。字符数组可以从本地文件系统、远程网络获取。参数 name 为字节数组对应的全限定类名。
- Class findSystemClass(String name)
从本地文件系统 Class 文件系统载入 Class 文件,如果本地文件系统没有 Class 文件,则将抛出 ClassNotFoundException 异常。该方法是 JVM 默认使用的装载机制。
- Class findLoadedClass(String name)
调用该方法来查看 ClassLoader 是否已装入某个类。如果已装入,那么返回 java.lang.Class 对象;否则返回 null。如果强行装载已存在的类,那么将会抛出链接错误。
- ClaasLoader getParent()
获取类装载器的父装载器。除根装载器外,所有的类装载器都有且仅有一个父装载器。ExtClassLoader 的父装载器是根装载器,因为根装载器非 Java 语言编写,所以无法获得,将返回 null。
Constructor
类的构造函数反射类,通过Class#getConstructors() 方法可以获取类的所有构造函数反射对象数组。
Method
类方法的反射类,通过 Class#getDeclardMethods() 方法可以获取类的所有方法反射类对象数组 Method[]。
Field
类成员变量的反射类,通过 Class#getDeclaredFields() 方法可以获取类的成员变量反射对象数组,通过 Class#getDeclaredField(String name) 则可以获取某个特定成员变量反射对象。
未完待续,还差一点 Field 的使用,太晚了,得睡觉了