0x00、前言
学习java基础知识记录,方便查阅。
0x01、反射基础
一、反射概念
java执行分为编译期和运行期
编译期是指把源码交给编译器编译成计算机可以执行的文件的过程。在 Java 中也就是把 Java 代码编成 class 文件的过程。编译期只是做了一些翻译功能,并没有把代码放在内存中运行起来,而只是把代码当成文本进行操作,比如检查错误。运行期是把编译后的文件交给计算机执行,直到程序运行结束。所谓运行期就把在磁盘中的代码放到内存中执行起来。
Java 反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为 Java 语言的反射机制。简单来说,反射机制指的是程序在运行时能够获取自身的信息。在 Java 中,只要给定类的名字,就可以通过反射机制来获得类的所有信息。
Java 反射机制主要提供了以下功能,这些功能都位于java.lang.reflect
包。
- 在运行时判断任意一个对象所属的类。
- 在运行时构造任意一个类的对象。
- 在运行时判断任意一个类所具有的成员变量和方法。
- 在运行时调用任意一个对象的方法。
- 生成动态代理。
反射与常用引用类对象区别
正常方式:引入对应的包类名称——>通过new实例化——>获取实例化对象
反射方式:实例化类对象——>Class获取方法——>得到完整的包类名称
Java反射机制的优缺点
优点:
- 能够运行时动态获取类的实例,大大提高系统的灵活性和扩展性。
- 与Java动态编译相结合,可以实现无比强大的功能。
- 降低代码程序之间的依赖性。
- 对于Java这种先编译再运行的语言,能够让我们很方便的创建灵活的代码,这些代码可以在运行时装配,无需在组件之间进行源代码的链接,更加容易实现面向对象。
缺点:
- 反射会消耗一定的系统资源,因此,如果不需要动态地创建一个对象,那么就不需要用反射;
- 冗余了很多代码量。
- 反射调用方法时可以忽略权限检查,获取这个类的私有方法和属性,因此可能会破坏类的封装性而导致安全问题。
二、反射实现
1、总结简述
1、通过Class类获取类。
2、通过newInstance()对类进行实例化。
3、通过Field访问成员变量|通过Method访问成员方法|通过Constructor访问成员构造方法
注:如果实例类存在构造方法,newInstance()实例化必须保证实例类存在无参构造方法,如只有有参构造方法,newInstance()会报错。Java9以后推荐用clazz.getDeclaredConstructor().newInstance()方式即获取构造方法后再实例化,而非直接newInstance()。
反射机制重要的类
类 | 含义 |
---|---|
java.lang.Class | 代表整个字节码。代表一个类型,代表整个类。 |
java.lang.reflect.Method | 代表字节码中的方法字节码。代表类中的方法。 |
java.lang.reflect.Constructor | 代表字节码中的构造方法字节码。代表类中的构造方法(方法名同类名相同且无参的方法)。 |
java.lang.reflect.Field | 代表字节码中的属性字节码。代表类中的成员变量(静态变量+实例变量)。 |
必须通过Class获取类过后才能获取Method、Constructor、Field
也就是说Class是反射实现的前提。且Class并不是new出来的,而是java内置的。
2、Class访问类
Class获取方式
方式 | 示例注解 |
---|---|
Class.forName(“完整类名带包名”) | Class A=Class.forName(“com.java.reflect.people”); |
对象.getClass() | people peo=new people();Class A=peo.getClass(); |
任何类型.class | Class A = String.class; |
反射类可访问的常用方法
代码实现
people类:
package com.javaweb.reflect; |
实现类:
package com.javaweb.reflect; |
三种获取类的实现截图:
2、Field访问成员变量
通过下列任意一个方法访问成员变量时将返回 Field 类型的对象或数组。
Field声明使用的方法
Field声明时的方法 | 注解 |
---|---|
getFields() | 获取所有权限为public的成员变量 |
getField(String name) | 获取变量名为name的成员变量 |
getDeclaredFields() | 获取当前对象的所有成员变量 |
getDeclaredField(String name) | 获取变量名为name的成员变量 |
注:针对private私有的变量,需要使用setAccessible(true)
方法打破封装,访问私有变量
Field常用方法
方法名称 | 说明 |
---|---|
getName() | 获得该成员变量的名称 |
getType() | 获取表示该成员变量的 Class 对象 |
get(Object obj) | 获得指定对象 obj 中成员变量的值,返回值为 Object 类型 |
set(Object obj, Object value) | 将指定对象 obj 中成员变量的值设置为 value |
getlnt(0bject obj) | 获得指定对象 obj 中成员类型为 int 的成员变量的值 |
setlnt(0bject obj, int i) | 将指定对象 obj 中成员变量的值设置为 i |
setFloat(Object obj, float f) | 将指定对象 obj 中成员变量的值设置为 f |
getBoolean(Object obj) | 获得指定对象 obj 中成员类型为 boolean 的成员变量的值 |
setBoolean(Object obj, boolean b) | 将指定对象 obj 中成员变量的值设置为 b |
getFloat(Object obj) | 获得指定对象 obj 中成员类型为 float 的成员变量的值 |
setAccessible(boolean flag) | 此方法可以设置是否忽略权限直接访问 private 等私有权限的成员变量 |
getModifiers() | 获得可以解析出该方法所采用修饰符的整数 |
代码实现
people类:
package com.javaweb.reflect; |
实现类:
package com.javaweb.reflect; |
Field实现截图:
3、Method访问成员方法
要动态获取一个对象方法的信息,首先需要通过下列方法之一创建一个Method
类型的对象或者数组。
Method声明使用的方法
Method声明使用的方法 | 注解 |
---|---|
getMethods() | 获取所有权限为public的成员方法 |
getMethods(String name,Class<?> …parameterTypes) | 获取方法名为name的成员方法,参数类型在方法名逗号后面,没有形参就不传 |
getDeclaredMethods() | 获取的成员所有的方法 |
getDeclaredMethods(String name,Class<?>…parameterTypes) | 获取方法名为name的成员方法,参数类型在方法名逗号后面,没有形参就不传 |
注:针对private私有的方法,需要使用setAccessible(true)
方法打破封装,访问私有方法
Method常用方法
静态方法名称 | 说明 |
---|---|
getName() | 获取该方法的名称 |
getParameterType() | 按照声明顺序以 Class 数组的形式返回该方法各个参数的类型 |
getReturnType() | 以 Class 对象的形式获得该方法的返回值类型 |
getExceptionTypes() | 以 Class 数组的形式获得该方法可能抛出的异常类型 |
invoke(Object obj,Object…args) | 利用 args 参数执行指定对象 obj 中的该方法,返回值为 Object 类型 |
isVarArgs() | 查看该方法是否允许带有可变数量的参数,如果允许返回 true,否则返回 false |
getModifiers() | 获得可以解析出该方法所采用修饰符的整数 |
代码实现
people类:
package com.javaweb.reflect; |
实现类:
package com.javaweb.reflect; |
Method实现截图:
4、Constructor访问成员构造方法
为了能够动态获取对象构造方法的信息,首先需要通过下列方法之一创建一个 Constructor 类型的对象或者数组。
Constructor声明使用的方法
Constructor声明使用的方法 | 注解 |
---|---|
getConstructor(Class<?>…parameterTypes) | 获取所有权限为public的构造方法 |
getDeclaredConstructors() | 获取当前对象的所有构造方法 |
getDeclaredConstructor(Class<?>…parameterTypes) | 获取当前对象所有带参数类型的构造方法 |
Constructor常用的方法
方法名称 | 说明 |
---|---|
public String getName() | 返回构造方法名 |
isVarArgs() | 查看该构造方法是否允许带可变数量的参数,如果允许,返回 true,否则返回false |
getParameterTypes() | 按照声明顺序以 Class 数组的形式获取该构造方法各个参数的类型 |
getExceptionTypes() | 以 Class 数组的形式获取该构造方法可能抛出的异常类型 |
newInstance(Object … initargs) | 通过该构造方法利用指定参数创建一个该类型的对象,如果未设置参数则表示采用默认无参的构造方法 |
setAccessiable(boolean flag) | 如果该构造方法的权限为 private,默认为不允许通过反射利用 netlnstance()方法创建对象。如果先执行该方法,并将入口参数设置为 true,则允许创建对象 |
getModifiers() | 获得可以解析出该构造方法所采用修饰符的整数 |
代码实现
people类:
package com.javaweb.reflect; |
实现类:
package com.javaweb.reflect; |
Constructor实现截图:
0x02、思考
Java反射是真的累啊看下来,为了实现动态对类的操作,绕了很大一圈,多出来很多代码去实现这个功能,但确实使用反射很大程度降低了代码之间的依赖性,实现动态加载。
java反射核心想法就是:突破常规访问限制,就是为了动态访问类。
导致的安全问题也是因为突破常规访问限制,利用反射去访问对象以及篡改变量值包括一些私有属性的变量。
0x03、参考链接
http://c.biancheng.net/view/6907.html
https://blog.csdn.net/qq_44715943/article/details/120587716