反射是指对于任何一个类,在”运行的时候”都可以直接得到这个类全部成分。
- 在运行时,可以直接得到这个类的 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 类型的数据而正常运行。```javapublic 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);}}
