Java 17 Reflection 反射 Class 篇
对于 Java 中的 Reflection 反射知识点,算是 Java 进阶的知识点之一了。 太多的框架基于该知识点进行开发和实现。
这里我们先了解一个概念, 在编程开发中,或者在计算机技术中, 反射式编程,或者叫做反射(reflection)。一般情况下是指,在程序运行中,可以访问、检测、以及修改它自身状态和行为的一种机制或能力。另一方面说,反射可以在程序运行的时候, 能够 “观察” 并修改自己的状态和行为。
说到反射, 就避免不了说说 Class 文件的格式。 已经 Class 包含什么,class(类对象)、Constructor(类的构造器对象)、Field(类的属性对象)、Method(类的方法对象)。
除了 Class 其他的三项都来自:java.base 模块的 java.lang.reflect 包。
需要注意的是 Class 是一个 class 对象。而非 class。
根据面向对象的设计原则:单一职责原则来说,对应的上面说的分类都有对应的封装和处理。
分别是:
java.lang.Classjava.lang.reflect.Constructorjava.lang.reflect.Fieldjava.lang.reflect.Method根据语法规则, 我们知道,想要有构造器之前要有类。方法和属性只能定义在类中。
根据反射机制的规则,可以拆分成如下步骤:
获得对应的 Class 对象接着可以获得类对象中的构造器对象类的属性对象类的方法对象。接着就可以做调用属性,方法做一些我们想做的事情。Class
首先既然是类为主, 我们就使用 Class 对象进行引申讲解。先看一下该对象的定义:
public final class Class<T> implements java.io.Serializable, GenericDeclaration, Type, AnnotatedElement, TypeDescriptor.OfField<Class<?>>, Constable
对于该类, 使用 final 进行修饰,表示该类无法再被继承。 并且没有对应的构造方法。
该类中包含了很多方法,先看 API 其中包含了三个静态方法。
实际的定义:
public static Class<?> forName(Module module, String name)public static Class<?> forName(String className) throws ClassNotFoundExceptionpublic static Class<?> forName(String name, boolean initialize, ClassLoader loader) throws ClassNotFoundException何为二进制名字:
作为 String 参数提供给 ClassLoader 中的方法的任何类名称必须是 Java 语言规范定义的二进制名称。
有效的二进制名字示例:
java.lang.Stringjava.util.Listjava.net.URLClassLoader$3$1该字符串必须是包含包名加类名, 如果没有包名就不写。
三个方法演示:
String className = "ClassTest";Class clazz1 = Class.forName(className);System.out.println(clazz1);Class clazz2 = Class.forName(ClassTest.class.getModule(), className);System.out.println(clazz2);Class clazz3 = Class.forName(className, true, ClassTest.class.getClassLoader());System.out.println(clazz3);这里的 clazz2 对象,其中 Module 的传入参数, 根据 API 和 jls 的定义, 就找到类似的这一种方式。如果您有什么其他的方式,希望评论区告诉我一下。 感谢。共同学习!
完整代码以及运行效果如下:
加载了 Class 对象之后,怎么拿到对应的这个对象呢?这里分两种情况, 在 Java 9 之前使用 clazz.newInstance()。但是该方法从 Java 9 开始就标注为过期了。
@Deprecated(since="9")public T newInstance() throws InstantiationException, IllegalAccessException演示代码如下:
对于 getDeclaredConstructor() 方法, 在反射的 Constructor 篇继续详细讲解, 这里只要知道用来创建一个新对象的就好了。
感觉返回值类型进行说明一下。 接下来看看返回类型为 boolean 的方法。
除了这些之外,可以根据模块、包、类、接口、构造器、属性、方法来区分对应的方法。
模块(Module)
System.out.println( clazz.getModule() );
包(package)
代码如下:
System.out.println( clazz.getPackage());System.out.println( clazz.getPackageName());
类(class)、接口 (interface)、数组
把类方法理解透, 基本上对于 Java 的 JVM 加载机制能够理解又上一层楼。
演示代码:
运行效果:
构造器(Constructor)
首先对于构造器方法,这里只是演示如何获取, 具体获取之后怎么操作单独出一篇进行讲解。
对于构造函数有对应的 5 个方法。 2 个公共方法相关的。 2 个获得所有的方法, 以及一个获得内部类相关的构造方法。
属性(Field)
对于属性而言, 也有叫做变量,或者叫做领域。 或者叫做字段。
方法(Method)
代码演示效果。
这里可以看到不管是方法也好, 属性也好,构造器操作也好, 都有对应的公共类型的获得方式。以及全部的对应的列表数据。
注解(Annotation)
对于注解的使用, 可以参考上一篇发表的文章,用来注解相关的知识点, 使用篇。 有详细的描述。 不再把之前的内容 copy 到这。
反射的 Class 篇使用就是这样。演示了如何针对 Class 的操作。下一篇会详细的讲解属性和方法相关的操作。
最近的输出上思考了一些东西, 之前为什么写特别基础的内容。 就像现在输出的 Java 17 基础。 主要和自身的精力有关系。避免重复的知识点记忆。首先必须要详细对于人的记忆来说, 不使用就会遗忘,所以学过的内容, 整理成笔记以及知识点输出,对于自己的复看就十分的有意义。 但为什么要用基础的开篇。因为觉得基础很重要,另外是在写到框架的时候, 可以很方便的引用自己的写的基础知识。
第二点,就是强制自己去更加系统的对于基础的体系的学习。 因为想要系统的,成体系的输出内容, 首先你的知识点要全面,否则没有办法做到输出的内容的质量。
第三点,有了基础知识框架之后, 在深入学习知识点的时候, 就可以进行改进知识内容。比方说, 现在是 Java 使用篇。在你学习到 JVM 的时候,或者相关深入知识点的时候, 就可以在这个基础上进行迭代。只要你还在从事该行业, 使用该编程语言。 你的这个“知识大脑”,也就是系列的输出内容就成为你的 API 文档库。任何时候都可以进行复看,引用。
希望2022,各个方面都有一个好的收获!加油!