反射是指对于任何一个类,在”运行的时候”都可以直接得到这个类全部成分。
- 在运行时,可以直接得到这个类的 Class 文件对象(Class)
- 在运行时,可以直接得到这个类的构造器对象。(Constructor)
- 在运行时,可以直接得到这个类的成员变量对象。(Field)
在运行时,可以直接得到这个类的成员方法对象。(Method)
1. Class 对象
1.1 获取 Class 对象的三种方式
类名.class;
Class clazz = Student.class;
类实例对象.getClass():Object 类.
public final native Class<?> getClass();
Class clazz = new Student().getClass();
Class.forName(“类的全限名”):Class 类.
public static Class<?> forName(String className)
Class clazz = Class.forName("com.zhangshuaiyin.Student");
1.2 Class 对象常用方法
public String getSimpleName()
:获得类名字符串:类名public String getName()
:获得类的全限定类名:包名+类名 ```java clazz.getSimpleName(); // Student
clazz.getName(); // com.zhangshuaiyin.Student
<a name="fwLXk"></a>
# 2. Constructor 构造器对象
反射的第一步是先得到Class类对象
<a name="BGAcL"></a>
## 2.1 获取构造器对象
1. `Constructor getConstructor(Class... parameterTypes)`:根据参数匹配获取某个构造器,只能拿public 修饰的构造器,几乎不用
1. `Constructor getDeclaredConstructor(Class... parameterTypes)`:根据参数匹配获取某个构造器,只要声明就可以定位,不关心权限修饰符,建议使用
1. `Constructor[] getConstructors()`:获取所有的构造器,只能拿 public 修饰的构造器。几乎不用
1. `Constructor[] getDeclaredConstructors()`:获取所有声明的构造器,只要你写我就能拿到,无所谓权限。建议使用
<a name="L7FWI"></a>
## 2.2 Constructor 常用方法
1. `T newInstance(Object... initargs)`:创建对象,注入构造器需要的数据。
1. `public void setAccessible(boolean flag)`:修改访问权限,true 代表暴力攻破权限(强制访问私有等修饰权限),false 表示保留不可访问权限(暴力反射)
<a name="pr8HI"></a>
# 3. Field 成员变量对象
Field提供有关类或接口的单个字段的信息和动态访问。 反射字段可以是**类(静态)字段**或**实例字段**。
Field允许在 get 或 set 访问操作期间发生扩大转换,但如果发生缩小转换,则会引发IllegalArgumentException 。
<a name="LFRPs"></a>
## 3.1 获取成员变量对象
1. `Field getField(String name)`:根据成员变量名获得对应 Field 对象,只能获得 public 修饰的
1. `Field getDeclaredField(String name)`:根据成员变量名获得对应 Field 对象,只要声明了就可以得到
1. `Field[] getFields()`:获得所有的成员变量对应的 Field 对象,只能获得 public 修饰的
1. `Field[] getDeclaredFields()`:获得所有的成员变量对应的 Field 对象,只要声明了就可以得到
<a name="vvNM8"></a>
## 3.2 Field 常用方法
1. `public void set(Object obj, Object value)`:给 obj 对象的字段设置 value 值;
1. obj:成员变量所属对象;
1. value:要给该成员变量赋的值;
2. `public Object get(Object obj)`:获取 obj 对象的成员变量的值。
2. `public Class<?> getType()`:获取属性的类型,返回 Class 对象。
2. `public String getName()`:获取属性的名称。
2. `public void setAccessible(boolean flag)`:暴力反射,设置为 true 可以直接访问私有类型的属性。
<a name="GmWrs"></a>
# 4. Method 方法对象
Method 提供有关类或接口上的单个方法的信息和对其的访问。 反映的方法可以是 **类方法 **或 **实例方法(包括抽象方法)**。
当将要调用的实际参数与基础方法的形式参数进行匹配时, Method 允许发生扩展转换,但如果发生缩小转换,它会抛出 IllegalArgumentException 。
<a name="d5PEm"></a>
## 4.1 获取方法对象
1. `public Method getMethod(String name, Class<?>... parameterTypes)`:根据方法名和参数类型获得对应的方法对象,只能获得 public 的
1. `public Method getDeclaredMethod(String name, Class<?>... parameterTypes)`:根据方法名和参数类型获得对应的方法对象,包括 private 的;
1. `public Method[] getMethods()`:获得类中的所有成员方法对象,返回数组,只能获得 public 修饰的且包含父类的;
1. `public Method[] getDeclaredMethods()`:获得类中的所有成员方法对象,返回数组,只获得本类申明的方法。
<a name="i2Cvh"></a>
## 4.2 Method 常用方法
1. `public Object invoke(Object obj, Object... args)`
1. obj:该方法所属对象;
1. args:方法参数列表;
2. `public void setAccessible(boolean flag)`
<a name="g1CSJ"></a>
# 5. 反射破坏面向对象的封装性和泛型约束性
1. 反射可以破坏面向对象的封装性(暴力反射)。
1. 同时可以破坏泛型的约束性。
对于破坏泛型约束性的解释:首先要了解一个概念,泛型是工作在编译阶段的,在运行阶段根据类型擦除,标注的泛型会不起作用,而反射作用于运行阶段,这时通过反射获取泛型类对象操作数据是不受泛型约束的。
比如以下示例:Double 类型的 List 通过反射插入了一个 String 类型的数据而正常运行。
```java
public class ReflectDemo {
public static void main(String[] args) throws Exception {
// 泛型只能工作在编译阶段,运行阶段泛型就消失了,
// 反射工作在运行时阶段。
List<Double> scores = new ArrayList<>();
scores.add(99.3);
scores.add(199.3);
scores.add(89.5);
// 拓展:通过反射暴力的注入一个其他类型的数据进去。
// a.先得到集合对象的Class文件对象
Class<?> c = scores.getClass();
// b.从ArrayList的Class对象中定位add方法
Method add = c.getDeclaredMethod("add", Object.class);
// c.触发scores集合对象中的add执行(运行阶段,泛型不能约束了)
add.invoke(scores, "波仔");
System.out.println(scores);
}
}