目录
#0.总览
#1. 类的反射
①介绍
②获取
③作用
获取构造函数:
创建实例:
字段操作:
方法操作:
获取修饰符:
#2.总结
#0.总览
反射,官方是这样介绍它的:
Reflection is a feature in the Java programming language. It allows an executing Java program to examine or "introspect" upon itself, and manipulate internal properties of the program.
翻译过来就是:反射是 Java 编程语言中的一项功能。它允许正在执行的 Java 程序检查或“内省”自身,并作程序的内部属性。
再翻译成人话就是:反射可以让程序可以把本身的各种方法,参数当作变量来使用。
为什么翻译成“反射”?因为这个过程就像是照镜子:程序通过照镜子知道自己有什么方法和参数。
详细的说,有:
- 获取类的信息(类名、方法、字段、构造方法等)。
- 创建类的实例。
- 调用类的方法、修改字段的值。
- 动态地加载类和进行方法调用。
- 与代理息息相关
#1. 类的反射
①介绍
首先得介绍一下Class类
Class类是反射的核心部分
Class类和真实的类对象不是一个东西!Class类只是包含了对应类关于其涉及反射的一系列操作的类。每个类都会有属于自己的Class对象:
列如 String.class Integer.class等
尝试输出:
System.out.println(String.class);
会得到 class java.lang.String
这是类的全限定类名,可以看成是类的标识。
②获取
通过一些方法,我们能获得一个类的Class对象:
Class ex1 = Class.forName("java.lang.String");
Class ex2 = String.class;
String string = new String();
Class ex3 = string.getClass();
那获取到Class类有什么用呢?
③作用
获取构造函数:
拿这个类来举例:
public class Temp {public int a;public int b;private int c = 10;public Temp() {}public Temp(int a) {this.a = a;}private Temp(int a, int b) {this.a = a;this.b = b;}public void sing(){System.out.println("ahhhhhh!");}private void dance(){System.out.println("haaaaaa!");}
}
Class ex = Temp.class;ex.getConstructor(); ex.getConstructor(int.class);
这会获得其一个指定的公共构造方法,注意!括号中的参数要这样写上对应的Class类才能获取到!
ex.getDeclaredConstructor();则可以获取到所有构造方法
相对的,Constructor<?>[] constructors = ex.getConstructors();则会获取所有公用构造方法
getDeclaredConstructors同理
创建实例:
Constructor constructor = ex.getConstructor(int.class);
Temp obj = (Temp) constructor.newInstance( 30);这样就根据指定的构造方法创建出了一个指定的实例
字段操作:
Temp obj = (Temp) constructor.newInstance( 30);
Field field = ex.getField("a");
System.out.println(field);
int value = field.getInt(obj);
System.out.println(value);这里会输出 30
注意!除了getInt还有getchar等对应基本数据类型,其他的统一为get()
但其实get()也能处理基本数据类型
格式为 Field.get(已实例化的对象)
对于私有变量,无独有偶:getDeclaredField(“c”)即可
但是只能System.out.println(field);,即获取全限定字段名,无法获得具体值!
解决方法在下:
但是对于字段修改:
如果是公共字段:
field.set(obj, 666);
就可以完成修改
私有字段是无法修改的!
必须先取消限制:
field.setAccessible(true);
这样才可以让私有字段像公共字段一样可以被查看,修改。
方法操作:
对于方法,有了前面的铺垫则简单得多:
对于公共方法:
Class ex = Temp.class;
Constructor constructor = ex.getConstructor();
Temp obj = (Temp) constructor.newInstance();Method greetMethod = ex.getMethod("sing");
greetMethod.invoke(obj);这样就成功执行了sing方法
如果想要获取所有方法
Method[] methods = ex.getMethods();即可
但是必须要有一个已经实例化的对象obj才能正常的通过invoke操作来执行方法!
实际上,执行的方法实际上是已经实例化的obj里面对应的方法!
对于一些函数,可能名字相同但是参数不同,可以仿照构造函数那样加以区分:
Method greetMethod = ex.getMethod("sing",int.class);
Method greetMethod = ex.getMethod("sing",String.class);类似这样来区分
对于私有方法:
和字段一模一样
在方法名上加上Declared
如getDeclaredMethod,getDeclaredMethods
然后得加上greetMethod.setAccessible(true);才能正常操作
获取修饰符:
可以通过类似于:
int x = greetMethod.getModifiers();
来获取任何方法/字段的修饰符,也许你会疑惑为什么是int类型,原因是JAVA中的确是用数字来存储修饰符的,比如说:
public
=1,
private
=2
,protected
=3,
default
=4
获取接口:
获取接口相对来说非常简单:
Class<?>[] interfaces = ex.getInterfaces();
这样便可获取所有ex类所实现的接口
获取注释:
Annotation[] annotations = ex.getAnnotations();
这样就获取了ex类所有的注释内容!
如果想获取方法的注释也非常简单:
Annotation[] annotations = ex.getMethod("xxx").getAnnotations();
(提一句,对于每个注释都是一个单独的类型,假如你需要@CY注释,改成
CY annotation = ex.getMethod("xxx").getAnnotation();也行)接下来想要提取注释变量什么的就随你了
这种方法可以用来实现公共字段补全
#2.总结
归功于反射机制,程序得以对本身进行修改而不依赖于人工修改,也提供了更多的灵活性,
更是为实现动态代理提供了可能。
(我也写了一篇介绍代理的文章,欢迎捧场!)
你也许会发现,这种获取参数,接口,方法是不是很熟悉?是不是我们用的IDE就有点用了这种方式的意思?不然为什么我们写了一个函数就能提示要放什么参数进去?
这的确很像,但实际上IDE是会实时构造一个抽象语法树(AST),它是代码结构的表示。这使得 IDE 能够理解类、方法、字段和参数的存在及其类型。
尽管如此,反射非常重要,java,spring框架里面反射无处不在,
但是,反射的性能开销很大,因为它要在运行中实时解析类,方法,字段,这些原本是在编译时完成的,而且反射无法被编译器进行优化!
所以谨慎使用!