1. 写在前面
“反射” 这个概念一直不了解,不过其实也不是什么高深的东西。本着代码能有什么高深的呢
这样的出发点,我们来看一下所谓的reflection
是什么东西。
2. 反射是干什么的
Reflection
是指 通过 Class
实例 来获取 Class
的相关信息
3. Class
要明白反射,我们首先要知道,java
中所有的类都是 Class
。
jvm 在运行时遇到一个类,会将其读入内存,动态加载。而不是一次性加载所有类。
而 jvm 加载一个类的时候,就会创建一个 Class
,就是一个 类名为 Class
的 class
实例。
Class class = new Class(String);
而每一个类的实例都包含其所有信息。
获取一个 Class 实例
String s = "test";
Class cls = s.getClass();
Class cls = String.class();
看下面一段代码
// reflection
public class Main {
public static void main(String[] args) {
printClassInfo("".getClass());
printClassInfo(Runnable.class);
printClassInfo(java.time.Month.class);
printClassInfo(String[].class);
printClassInfo(int.class);
}
static void printClassInfo(Class cls) {
System.out.println("Class name: " + cls.getName());
System.out.println("Simple name: " + cls.getSimpleName());
if (cls.getPackage() != null) {
System.out.println("Package name: " + cls.getPackage().getName());
}
System.out.println("is interface: " + cls.isInterface());
System.out.println("is enum: " + cls.isEnum());
System.out.println("is array: " + cls.isArray());
System.out.println("is primitive: " + cls.isPrimitive());
}
}
可以看到 ,JVM 给 int
也创建了 class
实例。
class 实例可以直接用来比较
Integer n = new Integer(123);
boolean b1 = n instanceof Integer; // true,因为n是Integer类型
boolean b2 = n instanceof Number; // true,因为n是Number类型的子类
boolean b3 = n.getClass() == Integer.class; // true,因为n.getClass()返回Integer.class
boolean b4 = n.getClass() == Number.class; // false,因为Integer.class!=Number.class
instanceof 会匹配类型和子类
4. 获取类的字段
这里提供了两种方法用来获取字段:
getField()
获取包括父类的public
字段
getDeclaredField()
获取当前类的所有字段
public class Main {
public static void main(String[] args) throws Exception {
Class stdClass = Student.class;
// 获取public字段"score":
System.out.println(stdClass.getField("score"));
// 获取继承的public字段"name":
System.out.println(stdClass.getField("name"));
// 获取所有字段"grade": 这里当然包括了 private & public
System.out.println(stdClass.getDeclaredField("grade"));
}
}
class Student extends Person {
public int score;
private int grade;
}
class Person {
public String name;
}
获取类的字段 的 值
看下面的代码,也可以获取类的字段的值
public class Main {
public static void main(String[] args) throws Exception {
Object p = new Person("Xiao Ming");
Class c = p.getClass();
Field f = c.getDeclaredField("name");
Object value = f.get(p);
System.out.println(value); // "Xiao Ming"
}
}
修改类的字段的值
public static void main(String[] args) throws Exception {
Person p = new Person("Xiao Ming");
System.out.println(p.getName()); // "Xiao Ming"
Class c = p.getClass();
Field f = c.getDeclaredField("name");
f.setAccessible(true);
f.set(p, "Xiao Hong");
System.out.println(p.getName()); // "Xiao Hong"
}
}
这里如果要修改 private 的字段的值,需要先
setAccessible()
小结
通过Class实例的方法可以获取Field实例:getField()
,getFields()
,getDeclaredField()
,getDeclaredFields()
;
通过Field实例可以获取字段信息:getName()
,getType()
,getModifiers()
;
通过Field实例可以读取或设置某个对象的字段,如果存在访问限制,要首先调用setAccessible(true)
来访问非public
字段。
通过反射读写字段是一种非常规方法,它会破坏对象的封装。
5. 获取类的方法
基本上和获取类的字段是一样的
通过Class实例的方法可以获取Method实例:getMethod()
,getMethods()
,getDeclaredMethod()
,getDeclaredMethods()
;
通过Method实例可以获取方法信息:getName()
,getReturnType()
,getParameterTypes()
,getModifiers()
;
通过Method实例可以调用某个对象的方法:Object invoke(Object instance, Object... parameters)
;
通过设置setAccessible(true)
来访问非public
方法;
6. 获取类的构造方法
-
getConstructor(Class...)
:获取某个public
的Constructor
-
getDeclaredConstructor(Class...)
:获取某个Constructor
getConstructors(Class...)
getDeclaredConstructors(Class...)
调用非public
的Constructor
时,必须首先通过setAccessible(true)
设置允许访问。setAccessible(true)可能会失败
这里获取的构造方法都是自己的,不会是父类
网友评论