官方文档的描述,反射通常用于需要检查或修改 Java
虚拟机中运行的应用程序的运行时行为的程序。反射是一种功能强大的技术,可以使应用程序执行不可能执行的操作。
1. 获取类对象的几种方式
所有反射操作的入口都是 java.lang.Class
。所以,我们首先要知道怎么去获取到 Class
,即类对象。根据代码是否有权访问对象、类的名称、类型或现有类,有几种方法可以获得 Class
。
1.1 Object.getClass()
如果有类的实例,则可以直接 getClass()
方法获取对应的 Class
。getClass
是 Obejct
类中的方法,所以只要是继承自 Obejct
的类都可以调用。
例如:
public class Box {
public void method() {
}
}
public static void main(String[] args) {
Box box = new Box();
Class clazz = box.getClass();
}
1.2 ".class"
如果没有类的实例,但是类是可以访问到的,那么可以通过追加 .class
来获得这个类。这也是获取基本类型的最简单方法。
例如:
Class clazzBox = Box.class;
Class clazzBoolean = boolean.class; //不能通过 getClass() 获取
1.3 Class.forName()
如果类的包名和类名是已知的,那么可以通过静态方法 Class.forName()
获取类对象。此方法可能会抛出 ClassNotFoundException
异常。
例如:
try {
Class box = Class.forName("testjava.Box");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
1.4 TYPT 属性
对于基本类型,最方便快捷地获取 Class
的方式是通过追加 .class
。但是,我们还可以通过另外一种方式获取基本类型的类对象。
我们知道,每一种基本类型都会对应的有一个包装类,比如,boolean
的 包装类为 Boolean
、int
的包装类为 Integer
。每一个包装类中,都一个静态的 TYPE
变量,它就是用来存放包装类对应的基本类型的类对象的。
所以,我们可以这样获取基本类型的类对象:
Class clazzInt = Integer.TYPE;
if (clazzInt == int.class) { //int.class 和 Integer.TYPE 是相同的
System.out.println("true");
}
2. 获取构造器实例化对象和属性信息
2.1 构造函数
构造函数用于创建类的实例,完成一些初始化操作,构造方法不能被继承。
构造函数可能包含名称、修饰符、参数和一系列的异常等。可以通过 java.lang.reflect.Constructor
提供的方法获取到相应的信息。
public class Box {
private Box() {
System.out.println("***** Box()");
}
public Box(String name) {
System.out.println("***** Box(name) name:" + name);
}
}
public static void main(String[] args) {
Class boxClass = Box.class;
//获取 Box 类的所有构造方法
Constructor[] allConstructor = boxClass.getDeclaredConstructors();
for (Constructor constructor : allConstructor) {
System.out.println("***** " + constructor.toGenericString());
//获取构造方法的参数
Type[] gpTypes = constructor.getGenericParameterTypes();
for (int j = 0; j < gpTypes.length; j++) {
System.out.println("***** GenericParameterType " + j + " :" + gpTypes[j]);
}
System.out.println("***** --------------------------");
}
}
打印
***** private testjava.Box()
***** --------------------------
***** public testjava.Box(java.lang.String)
***** GenericParameterType 0 :class java.lang.String
***** --------------------------
通过反射实例化对象,有两种方式:
- 直接使用
Class.newInstance()
实例化对象。 - 先获取到类的构造方法
Constructor
,然后通过Constructor.newInstance()
实例化对象。
两者相比,通常建议使用后者,因为前者调用的是无参数的构造函数实例化对象,并且需要无参构造函数是可访问的。而后者对参数没有要求,并且就算是 private
的也是可以调用的。
例如:
public class Box {
private String name;
private Box() {
name = "Box";
System.out.println("***** Box()");
}
public Box(String name) {
this.name = name;
System.out.println("***** Box(name) name:" + name);
}
public String getName() {
return name;
}
}
public static void main(String[] args) {
Class boxClass = Box.class;
//获取 Box 类的所有构造方法
Constructor[] allConstructor = boxClass.getDeclaredConstructors();
Constructor constorZeroParams = null; //无参构造方法
Constructor constorParams = null; //有参构造方法
for (Constructor constructor : allConstructor) {
if (constructor.getGenericParameterTypes().length == 0) {
constorZeroParams = constructor;
} else {
constorParams = constructor;
}
}
if (constorZeroParams != null) {
constorZeroParams.setAccessible(true);
try {
Box box = (Box) constorZeroParams.newInstance();
//Box box = Box.class.newInstance(); //会报异常 java.lang.IllegalAccessException: Class testjava.TestJava can not access a member of class testjava.Box with modifiers "private"
System.out.println("name:" + box.getName());
} catch (Exception e) {
e.printStackTrace();
}
}
if (constorParams != null) {
constorParams.setAccessible(true);
try {
Box box = (Box) constorParams.newInstance("haha");
System.out.println("name:" + box.getName());
} catch (Exception e) {
e.printStackTrace();
}
}
}
打印结果:
***** Box()
name:Box
***** Box(name) name:haha
name:haha
2.2 方法
java.lang.reflect.Method
类提供了 API
去获取方法的信息:修饰符、返回值、参数、注解信息和异常等。
public class Box {
private String name;
private Box() {
name = "Box";
System.out.println("***** Box()");
}
public Box(String name) {
this.name = name;
System.out.println("***** Box(name) name:" + name);
}
public void setName(String name) {
this.name = name;
System.out.println("***** setName(name) name:" + name);
}
public String getName() {
return name;
}
private void method1() {
System.out.println("private 方法");
}
public void method2() {
System.out.println("public 方法");
}
public int method3() {
System.out.println("带返回值 方法");
return 0;
}
public <E extends RuntimeException> void method4() throws E {
System.out.println("异常 方法");
}
public static void staticMethod() {
System.out.println("静态 方法");
}
}
public static void main(String[] args) {
Class boxClass = Box.class;
Method[] allMethods = boxClass.getMethods();
for (Method method : allMethods) {
System.out.println("---------------------");
//获取所有的方法
System.out.println("method: " + method.toGenericString());
//参数类型
Type[] gpTypes = method.getGenericParameterTypes();
Class[] pTypes = method.getParameterTypes();
for (int i = 0; i < gpTypes.length; i++) {
System.out.println("pType: " + pTypes[i]);
System.out.println("gpType: " + gpTypes[i]);
}
//异常类型
Class<?>[] xType = method.getExceptionTypes();
Type[] gxType = method.getGenericExceptionTypes();
for (int i = 0; i < xType.length; i++) {
System.out.println("ExceptionType: "+ xType[i]);
System.out.println("GenericExceptionType: " + gxType[i]);
}
//返回值
Class<?> returnClazz = method.getReturnType();
System.out.println("return class: " + returnClazz);
}
System.out.println("---------------------");
//获取指定的方法并使用
try {
Method method = boxClass.getDeclaredMethod("setName", String.class);
method.setAccessible(true);
//成员方法,需要先实例化一个对象
Constructor constructor = null;
Constructor[] constructors = boxClass.getDeclaredConstructors();
for (Constructor cons : constructors) {
if (cons.getGenericParameterTypes().length == 0) {
constructor = cons;
break;
}
}
constructor.setAccessible(true);
Box box = (Box) constructor.newInstance();
method.invoke(box, "method");
System.out.println("name: " + box.getName());
//调用静态函数,不需要实例化对象
Method staticMethod = boxClass.getDeclaredMethod("staticMethod");
staticMethod.invoke(null, null);
} catch (Exception e) {
e.printStackTrace();
}
}
打印结果:
---------------------
method: public java.lang.String testjava.Box.getName()
return class: class java.lang.String
---------------------
method: public void testjava.Box.setName(java.lang.String)
pType: class java.lang.String
gpType: class java.lang.String
return class: void
---------------------
method: public static void testjava.Box.staticMethod()
return class: void
---------------------
method: public int testjava.Box.method3()
return class: int
---------------------
method: public <E> void testjava.Box.method4() throws E
ExceptionType: class java.lang.RuntimeException
GenericExceptionType: E
return class: void
---------------------
method: public void testjava.Box.method2()
return class: void
---------------------
method: public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
pType: long
gpType: long
pType: int
gpType: int
ExceptionType: class java.lang.InterruptedException
GenericExceptionType: class java.lang.InterruptedException
return class: void
---------------------
method: public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
pType: long
gpType: long
ExceptionType: class java.lang.InterruptedException
GenericExceptionType: class java.lang.InterruptedException
return class: void
---------------------
method: public final void java.lang.Object.wait() throws java.lang.InterruptedException
ExceptionType: class java.lang.InterruptedException
GenericExceptionType: class java.lang.InterruptedException
return class: void
---------------------
method: public boolean java.lang.Object.equals(java.lang.Object)
pType: class java.lang.Object
gpType: class java.lang.Object
return class: boolean
---------------------
method: public java.lang.String java.lang.Object.toString()
return class: class java.lang.String
---------------------
method: public native int java.lang.Object.hashCode()
return class: int
---------------------
method: public final native java.lang.Class<?> java.lang.Object.getClass()
return class: class java.lang.Class
---------------------
method: public final native void java.lang.Object.notify()
return class: void
---------------------
method: public final native void java.lang.Object.notifyAll()
return class: void
---------------------
***** Box()
***** setName(name) name:method
name: method
静态 方法
属性
同样的,可以通过 java.lang.reflect.Field
获取到类的所有属性相关的信息。
public class Box {
public static final String TAG = "Box";
public String name = "box";
private int age = 18;
public String getName() {
return name;
}
public int getAge() {
return age;
}
}
public static void main(String[] args) {
Class boxClass = Box.class;
//获取所有属性
Field[] fields = boxClass.getFields();
for (Field field : fields) {
System.out.println("field: " + field.toGenericString());
}
try {
//修改 public 的 name 变量
Field nameField = boxClass.getField("name");
//实例化一个 Box 对象
Object box = boxClass.getConstructor().newInstance();
//获取 name 值
String name = (String) nameField.get(box);
System.out.println("name 修改前:" + name);
//修改 name 值
nameField.set(box, "filedName");
name = (String) nameField.get(box);
System.out.println("name 修改后:" + name);
//修改 private 的 age 属性
Field ageField = boxClass.getDeclaredField("age");//getField 和 getDeclaredField 的区别是 前者只能获取public的,后者是获取所有的
ageField.setAccessible(true); //设置访问权限
//获取 age 的值
int age = (int) ageField.get(box);
System.out.println("age 修改前:" + age);
//修改 age 的值
ageField.set(box, 24);
age = (int) ageField.get(box);
System.out.println("age 修改后:" + age);
//获取静态的变量 TAG 的值
Field tagField = boxClass.getDeclaredField("TAG");
String tag = (String) tagField.get(null); //不需要具体对象
System.out.println("tag:" + tag);
} catch (Exception e) {
e.printStackTrace();
}
}
field: public static final java.lang.String testjava.Box.TAG
field: public java.lang.String testjava.Box.name
name 修改前:box
name 修改后:filedName
age 修改前:18
age 修改后:24
tag:Box
参考: