从上一篇注解的学习中发现,要想自定义的注解生效,必须添加元注解@Retention(RetentionPolicy.RUNTIME),注解会在class字节码文件中存在,jvm虚拟机在加载class文件后仍然存在,在运行时可以通过反射获取到,反射又是如何获取到想要的数据的呢?通过这个问题,就引出了下一个重要的知识点,java的高级特性:反射。在我前期泛泛而学的情况,现在越发觉得,接触到的java各种技术,各个知识点,都是环环相扣,在学习的时候循序渐进更能体会到其中的奥妙之处。
Java的反射(reflection)机制是指在程序的运行状态中,可以构造任意一个类的对象,可以了解任意一个对象所属的类,可以了解任意一个类的成员变量和方法,可以调用任意一个对象的属性和方法。这种动态获取程序信息以及动态调用对象的功能称为Java语言的反射机制。反射被视为动态语言的关键。
这里参考知乎:bravo1988对如何理解反射的回答,部分摘抄和总结。
要想使用反射,首先要明白几个概念。
字段、方法、构造器对象等
注解
泛型
Class类准备了很多字段用来表示一个.class文件的信息,对于字段、方法、构造器等,为了更详细地描述这些重要信息,还写了三个类,每个类里面都有很详细的对应。我们要搞清楚Class类与Method类、Field类等的关系。
获得类对象则有以下三种方式:
public class Clazz {
/**
* class获得方式
*
* @param args
*/
public static void main(String[] args) {
//1 通过类型获得
// 语法:类名.class
// 应用场景:确定类型 等
Class clazz1 = Clazz.class;
System.out.println("语法:类名.class|" + clazz1);
//2 通过实例对象获得
// 语法:变量.getClass()
// 应用场景:在方法内部通过参数获得类型 等
Clazz c = new Clazz();
Class<? extends Clazz> aClass = c.getClass();
System.out.println("语法:变量.getClass()|" + aClass);
//3 通过字符串获得
// 语法:Class.forName("全限定类名")
// 应用场景:通过配置获得字符串 等
try {
Class<?> aClass1 = Class.forName("com.apang.clazz.Clazz");
System.out.println("Class.forName(\"全限定类名\")|"+aClass1);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
>>>语法:类名.class|class com.apang.clazz.Clazz
>>>语法:变量.getClass()|class com.apang.clazz.Clazz
>>>Class.forName("全限定类名")|class com.apang.clazz.Clazz
在使用Class.forName创建对象的过程中,由于Class类的构造器是私有的,我们无法手动new一个Class对象,只能由JVM创建。JVM在构造Class对象时,需要传入一个类加载器,然后才有我们上面分析的一连串加载、创建过程。总而言之一句话就是要通过类加载器去完成。
而用newInstance()方式创建对象时,需要注意newInstance()底层就是调用无参构造对象的newInstance()。所以,本质上Class对象要想创建实例,其实都是通过构造器对象。如果没有空参构造对象,就无法使用clazz.newInstance(),必须要获取其他有参的构造对象然后调用构造对象的newInstance()。
3. 反射API
由于本人没具体使用过反射,对一些应用级别的东西还不太了解,不过根据大佬所述,在日常开发中,反射的目的主要有两个:一是对象的实例化,二是反射调用方法。其中有两个点需要注意:
通过以上,发现了解反射还必须清楚类加载机制和jvm内存模型,好像下一个需要学习的方向又确定了。
原文:https://www.cnblogs.com/apangne/p/13370079.html