反射是一种可以间接操作目标对象的机制。当使用反射时,JVM 在运行的时候才动态加载类,对于任意类,知道其属性和方法,并不需要提前在编译期知道运行的对象是谁,允许运行时的 Java 程序获取类的信息并对其进行操作。
对象的类型在编译期就可以确定,但程序运行时可能需要动态加载一些类(之前没有用到,故没有加载进 jvm),使用反射可以在运行期动态生成对象实例并对其进行操作。
在获取到 Class 对象之后,反向获取和操作对象的各种信息。
我们先建一个类
public class People {
private int age;
private String name;
private People() {
age = 18;
name = "Tony";
}
public People(int age,String name) {
this.age = age;
this.name = name;
}
private void print() {
System.out.println(this.toString());
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
获取 Class 对象有三种方法:
在运行期间,一个类只能有一个 Class 对象产生
通过 getDeclaredConstructors 方法我们可以得到类的所有构造方法。
public class Test {
public static void main(String[] args) {
Class c = People.class;
//得到类的所有构造方法
Constructor[] constructors = c.getDeclaredConstructors();
for(int i = 0; i < constructors.length; i++) {
//获得构造方法的类型
System.out.println("构造方法的类型:" + Modifier.toString(constructors[i].getModifiers()));
//获得构造方法的所有参数
Class[] parametertypes = constructors[i].getParameterTypes();
for (int j = 0; j < parametertypes.length; j++) {
System.out.print(parametertypes[j].getName() + " ");
}
System.out.println("");
}
}
}
返回结果如下:
通过该方法,我们可以获取类中所有构造方法和构造方法中的参数。
在 getDeclaredConstructor 方法中,我们未传入参数,表示希望得到类的特定构造方法。同时在代码中要进行异常捕获,因为可能不存在对应的构造方法。
public class Test {
public static void main(String[] args){
Class c = People.class;
Constructor constructor;
try {
//得到类的特定构造方法,无参构造方法不传参数
constructor = c.getDeclaredConstructor();
//获得构造方法的类型
System.out.println("构造方法的类型:" + Modifier.toString(constructor.getModifiers()));
//获得构造方法的所有参数
System.out.println("构造方法的参数:");
Class[] parametertypes = constructor.getParameterTypes();
for (int j = 0; j < parametertypes.length; j++) {
System.out.print(parametertypes[j].getName() + " ");
}
} catch (Exception e) {}
}
}
public People(int age,String name) {
this.age = age;
this.name = name;
}
在 getDeclaredConstructor 方法中,我们可以传入一个 Class 数组,里面包含 int 和 java.lang.String 的 Class 对象。
public class Test {
public static void main(String[] args){
Class c = People.class;
Class[] p = {int.class,String.class};
Constructor constructor;
try {
//得到类的特定构造方法,这次传入int和java.lang.String两个参数
constructor = c.getDeclaredConstructor(p);
//获得构造方法的类型
System.out.println("构造方法的类型:" + Modifier.toString(constructor.getModifiers()));
//获得构造方法的所有参数
System.out.println("构造方法的参数:");
Class[] parametertypes = constructor.getParameterTypes();
for (int j = 0; j < parametertypes.length; j++) {
System.out.print(parametertypes[j].getName() + " ");
}
} catch (Exception e) {}
}
}
在上面。我们已经学习了如何获取类中特定的构造方法,在这里,我们不仅要获取,还要对类的构造方法进行调用。
我们先修改类的两个构造函数,分别加上一打印语句,代码如下:
private People() {
age = 18;
name = "Tony";
System.out.println("private People()调用成功");
}
public People(int age,String name) {
this.age = age;
this.name = name;
System.out.println("public People(int age,String name)调用成功");
}
我们先使用这个 public 访问类型的构造函数
public class Test {
public static void main(String[] args){
Class c = People.class;
Class[] p = {int.class,String.class};
Constructor constructor;
try {
constructor = c.getDeclaredConstructor(p);
//创建实例
constructor.newInstance(10,"HaWei");
} catch (Exception e) {}
}
}
结果如下
那么我们如何通过反射调用类的 private 访问类型的构造函数呢?其实大体与上面一样,只是我们需要设置constructors.setAccessible(true);
罢了。
public class Test {
public static void main(String[] args){
Class c = People.class;
Constructor constructor;
try {
//获取类的无参构造函数
constructor = c.getDeclaredConstructor();
constructor.setAccessible(true);
//创建实例
constructor.newInstance();
} catch (Exception e) {}
}
}
我们尝试调用一下类的这个私有方法
private void print() {
System.out.println(this.toString());
}
关于调用方法,我们可以通过 getDeclaredMethod 来获取该方法,然后通过调用 invoke 执行。
public class Test {
public static void main(String[] args){
Class c = People.class;
Constructor constructor;
try {
//获取类的无参构造函数
constructor = c.getDeclaredConstructor();
constructor.setAccessible(true);
//创建实例
People obj = (People) constructor.newInstance();
//获取需要调用的方法,需要两个参数,第一个参数是方法名,第二个参数是参数类型(本例不需要传入参数)
Method method = c.getDeclaredMethod("print");
method.setAccessible(true);
//调用方法,需要两个参数,第一个参数是类的实例,第二个参数是方法参数
method.invoke(obj);
} catch (Exception e) {}
}
}
public class Test {
public static void main(String[] args){
Class c = People.class;
Constructor constructor;
try {
//获取类的无参构造函数
constructor = c.getDeclaredConstructor();
constructor.setAccessible(true);
//创建实例
People obj = (People) constructor.newInstance();
//修改之前的name字段值
System.out.println("修改之前:" + obj.getName());
//获取类的name字段
Field field = c.getDeclaredField("name");
field.setAccessible(true);
//修改类的私有字段
field.set(obj,"HaWei");
//修改之后的name字段值
System.out.println("修改之后:" + obj.getName());
} catch (Exception e) {}
}
}
可以在运行时获得类的内容,对于 Java 这种先编译再运行的语言,能够让我们很方便的写出灵活的代码,这些代码可以在运行时装配,无需在组件之间进行源代码的链接,更加容易实现面向对象。
参考:Java反射技术详解
Java基础篇:反射机制详解