如果需要修改Test类对象的private testName属性,我们会怎么做呢?
class Test {
private String testName = "hello";
public void setTestName(String name) {
testName = name;
}
}
通常我们可以提供一个public setTestName方法去修改testName值。
当然,还可以使用本文的主角达到修改testName值。
反射机制常用方法之一:修改对象中private属性
创建Test类对象test,test对象中有私有属性testName,testName初始值为hello。通过反射,将testName值修改成world。
class Test {
private String testName = "hello";
public String getTestName() {
return testName;
}
public static void main(String[] args) {
try {
Test test = new Test();
System.out.println("testName:" + test.getTestName());//testName:hello
Class<?> clazz = test.getClass();
Field testNameField = clazz.getDeclaredField("testName");
testNameField.setAccessible(true);
testNameField.set(test, "world");
System.out.println("testName:" + test.getTestName());//testName:world
} catch (Exception e) {
}
}
}
通过例子我们感受到反射机制的作用,到底什么是反射,为什么需要反射,反射常用方法还有哪些。
-
什么是反射?
在计算机科学中,反射是指计算机程序在运行时(Run time)可以访问、检测和修改它本身状态或行为的一种能力。
用比喻来说,反射就是程序在运行的时候能够“观察”并且“修改”自己的行为。
在Java中,
反射能“观察”自己的具体表现就是:对于任意一个类,都能够知道这个类的所有方法和属性。
反射能“修改”自己的具体表现就是:对于任意一个对象,都能够调用它的所有方法和改变所有的属性。
-
为什么需要反射?
1.反射使得设计更优雅易懂,例如日常使用的Spring框架,注解的形式就相当优雅易懂而且实用。
关于“注解”详细介绍请点击//TODO
@Controller
class Test {
public static void main(String[] args) {
Class clazz = Test.class;
Annotation[] annotations = clazz.getAnnotations();
for (Annotation annotation : annotations) {
System.out.println(annotation);
}
}
}
2.Java中的动态代理、拦截器等,都是使用反射的“观察”和“修改”能力。
通过反射的“观察”和“修改”能力,可以设计出更优雅易读而且实用的应用程序。为什么需要反射?因为在设计程序时,需要“观察”和“修改”的能力,而拥有这些能力的我们定义为反射。
-
反射的常用方法
获取Class对象的三种方式
1.通过对象获取
class Test {
public static void main(String[] args) {
Test test = new Test();
Class clazz = test.getClass();
}
}
2.通过类获取
class Test {
public static void main(String[] args) {
Class clazz = Test.class;
}
}
3.通过Class.forName获取
class Test {
public static void main(String[] args) {
try {
Class clazz = Class.forName("Test");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
获取类字段
class Test {
private String nickname;
public static void main(String[] args) {
Class clazz = Test.class;
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
System.out.println(field);
}
}
}
获取类方法
class Test {
private String nickname;
public String getNickname() {
return nickname;
}
public static void main(String[] args) {
Class clazz = Test.class;
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
System.out.println(method);
}
}
}
获取类构造器
class Test {
private String nickname;
public Test(String nickname) {
this.nickname = nickname;
}
public static void main(String[] args) {
try {
Class clazz = Test.class;
Constructor constructor = clazz.getConstructor(String.class);
Test test = (Test) constructor.newInstance("tom");
} catch (Exception e) {
e.printStackTrace();
}
}
}
更多反射的方法可以查看Class类的源码。查看源码时,例如Class.forName,里面调用forName0方法,会发现forName0方法是一个native方法,如何查看native方法的源码,JDK native源码查看。
@CallerSensitive
public static Class<?> forName(String className)
throws ClassNotFoundException {
Class<?> caller = Reflection.getCallerClass();
return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
}
/** Called after security check for system loader access checks have been made. */
private static native Class<?> forName0(String name, boolean initialize,
ClassLoader loader,
Class<?> caller)
throws ClassNotFoundException;
我们会发现,Java中,反射基本围绕着Class类展开,去实现反射的“观察”和“修改”能力,关于Class类文件的结构,参考Class类文件的结构。了解Class类文件的结构后,需要再去了解虚拟机类加载机制以及类加载器//TODO。
而本文主要介绍反射机制基础概念,反射的意义及反射常用方法。
想深入理解反射在JVM中的实现原理,除了需要阅读源码(包括阅读native方法源码),还需要理解Class类文件的结构,以及虚拟机类加载机制,做完这些工作,才算比较明白反射机制。
深入理解反射机制//TODO