1 反射概述
基本概述
反射是指对于任何一个Class类,在”运行的时候”都可以直接得到这个类全部成分。
在运行时,可以直接得到这个类的构造器对象:Constructor
在运行时,可以直接得到这个类的成员变量对象:Field
在运行时,可以直接得到这个类的成员方法对象:Method
这种运行时动态获取类信息以及动态调用类中成分的能力称为Java语言的反射机制。
反射的关键
反射的第一步都是先得到编译后的Class类对象,然后就可以得到Class的全部成分
反射的作用
- 可以在运行时得到一个类的全部成分然后操作。
- 可以破坏封装性。(很突出)
- 也可以破坏泛型的约束性。(很突出)
- 更重要的用途是适合:做Java高级框架
- 基本上主流框架都会基于反射设计一些通用技术功能。
2. 反射获取类对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
|
public class Test { public static void main(String[] args) throws Exception { Class c = Class.forName("cn.jyw.demo.Student");
Class c1 = Student.class;
Student s = new Student(); Class c2 = s.getClass(); } }
|
3. 反射获取构造器对象
3.1 使用反射技术获取构造器对象并使用
- 获得class对象
- 获得Constructor对象
- 创建对象
3.2 Class类中用于获取构造器的方法
| 方法 |
说明 |
| Constructor<?>[] getConstructors() |
返回所有构造器对象的数组(只能拿public的) |
Constructor<?>[] getDeclaredConstructors() |
返回所有构造器对象的数组,存在就能拿到 |
| Constructor<T> getConstructor(Class<?>… parameterTypes) |
返回单个构造器对象(只能拿public的) |
Constructor\<T> getDeclaredConstructor(Class<?>... parameterTypes) |
返回单个构造器对象,存在就能拿到 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
|
@Test public void getConstructors(){ Class c = Student.class; Constructor[] constructors = c.getConstructors(); for (Constructor constructor : constructors) { System.out.println(constructor.getName() + "===>" + constructor.getParameterCount()); } }
@Test public void getDeclaredConstructors(){ Class c = Student.class; Constructor[] constructors = c.getDeclaredConstructors(); for (Constructor constructor : constructors) { System.out.println(constructor.getName() + "===>" + constructor.getParameterCount()); } }
@Test public void getConstructor() throws Exception { Class c = Student.class; Constructor cons = c.getConstructor(); System.out.println(cons.getName() + "===>" + cons.getParameterCount()); }
@Test public void getDeclaredConstructor() throws Exception { Class c = Student.class; Constructor cons = c.getDeclaredConstructor(); System.out.println(cons.getName() + "===>" + cons.getParameterCount());
Constructor cons1 = c.getDeclaredConstructor(String.class, int.class); System.out.println(cons1.getName() + "===>" + cons1.getParameterCount());
}
|
3.3 Constructor类中用于创建对象的方法
- 可以通过定位类的构造器对象。
- 如果构造器对象没有访问权限可以通过:void setAccessible(true)打开权限
- 构造器可以通过T newInstance(Object… initargs)调用自己,传入参数!
| 符号 |
说明 |
| T newInstance(Object… initargs) |
根据指定的构造器创建对象 |
| public void setAccessible(boolean flag) |
设置为true,表示取消访问检查,进行暴力反射 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| @Test public void getDeclaredConstructor() throws Exception { Class c = Student.class; Constructor cons = c.getDeclaredConstructor(); System.out.println(cons.getName() + "===>" + cons.getParameterCount());
cons.setAccessible(true); Student s = (Student) cons.newInstance(); System.out.println(s);
Constructor cons1 = c.getDeclaredConstructor(String.class, int.class); System.out.println(cons1.getName() + "===>" + cons1.getParameterCount()); Student s1 = (Student) cons1.newInstance("孙悟空", 1000); System.out.println(s1); }
|
3.4 总结
- 利用反射技术获取构造器对象的方式
- getDeclaredConstructors()
- getDeclaredConstructor (Class<?>…parameterTypes)
- 反射得到的构造器可以做什么?
4. 反射获取成员变量对象
4.1 使用反射技术获取成员变量对象并使用
- 获得class对象
- 获得Field对象
- 赋值或者获取值
4.2 Class类中用于获取成员变量的方法
| 方法 |
说明 |
| Field[] getFields() |
返回所有成员变量对象的数组(只能拿public的) |
| Field[] getDeclaredFields() |
返回所有成员变量对象的数组,存在就能拿到 |
| Field getField(String name) |
返回单个成员变量对象(只能拿public的) |
| Field getDeclaredField(String name) |
返回单个成员变量对象,存在就能拿到 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
|
@Test public void getDeclaredFields(){ Class c = Student.class; Field[] fields = c.getDeclaredFields(); for (Field field : fields) { System.out.println(field.getName() + "==>" + field.getType()); } }
@Test public void getDeclaredField() throws Exception { Class c = Student.class; Field f = c.getDeclaredField("age"); System.out.println(f.getName() +"===>" + f.getType()); }
|
4.3 Field类中用于取值、赋值的方法
| 符号 |
说明 |
| void set(Object obj, Object value): |
赋值 |
| Object get(Object obj) |
获取值。 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| @Test public void setField() throws Exception { Class c = Student.class; Field ageF = c.getDeclaredField("age");
ageF.setAccessible(true);
Student s = new Student(); ageF.set(s , 18); System.out.println(s);
int age = (int) ageF.get(s); System.out.println(age);
}
|
4.4 总结
- 利用反射技术获取成员变量的方式
- getDeclaredFields()
- getDeclaredField(String name)
2.反射得到成员变量可以做什么?
5. 反射获取方法对象
5.1 使用反射技术获取方法对象并使用
- 获得class对象
- 获得Method对象
- 运行方法
5.2 Class类中用于获取成员方法的方法
| 方法 |
说明 |
| Method[] getMethods() |
返回所有成员方法对象的数组(只能拿public的) |
| Method[] getDeclaredMethods() |
返回所有成员方法对象的数组,存在就能拿到 |
| Method getMethod(String name, Class<?>… parameterTypes) |
返回单个成员方法对象(只能拿public的) |
| Method getDeclaredMethod(String name, Class<?>… parameterTypes) |
返回单个成员方法对象,存在就能拿到 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14
|
@Test public void getDeclaredMethods(){ Class c = Dog.class; Method[] methods = c.getDeclaredMethods(); for (Method method : methods) { System.out.println(method.getName() +" 返回值类型:" + method.getReturnType() + " 参数个数:" + method.getParameterCount()); } }
|
5.3 Method类中用于触发执行的方法
Object invoke(Object obj, Object… args)
运行方法
参数一:用obj对象调用该方法
参数二:调用方法的传递的参数(如果没有就不写)
返回值:方法的返回值(如果没有就不写)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
|
@Test public void getDeclardMethod() throws Exception { Class c = Dog.class; Method m = c.getDeclaredMethod("eat"); Method m2 = c.getDeclaredMethod("eat", String.class);
m.setAccessible(true); m2.setAccessible(true);
Dog d = new Dog(); Object result = m.invoke(d); System.out.println(result);
Object result2 = m2.invoke(d, "骨头"); System.out.println(result2); }
|
5.4 总结
- 利用反射技术获取成员方法对象的方式
获取类中成员方法对象
getDeclaredMethods()
getDeclaredMethod (String name, Class<?>… parameterTypes)
- 反射得到成员方法可以做什么?
依然是在某个对象中触发该方法执行。
Object invoke(Object obj, Object… args)
如果某成员方法是非public的,需要打开权限(暴力反射),然后再触发执行
setAccessible(boolean)
6. 反射的作用-绕过编译阶段为集合添加数据
反射是作用在运行时的技术,此时集合的泛型将不能产生约束了,此时是可以为集合存入其他任意类型的元素
泛型只是在编译阶段可以约束集合只能操作某种数据类型,在编译成Class文件进入运行阶段的时候,其真实类型都是ArrayList了,泛型相当于被擦除了
编译成Class文件进入运行阶段的时候,泛型会自动擦除
- 反射是作用在运行时的技术,此时不存在泛型了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| ArrayList<String> lists1 = new ArrayList<>(); ArrayList<Integer> lists2 = new ArrayList<>();
System.out.println(lists1.getClass()); System.out.println(lists2.getClass());
System.out.println(lists1.getClass() == lists2.getClass());
System.out.println("---------------------------"); ArrayList<Integer> lists3 = new ArrayList<>(); lists3.add(23); lists3.add(22);
Class c = lists3.getClass(); Method add = c.getDeclaredMethod("add", Object.class); boolean rs = (boolean) add.invoke(lists3, "黑马"); System.out.println(rs);
System.out.println(lists3);
ArrayList list4 = lists3; list4.add("白马"); list4.add(false); System.out.println(lists3); }
|
7. 反射的作用-通用框架的底层原理
需求
给你任意一个对象,在不清楚对象字段的情况可以,可以把对象的字段名称和对应值存储到文件中去
分析
- 定义一个方法,可以接收任意类的对象。
- 每次收到一个对象后,需要解析这个对象的全部成员变量名称。
- 这个对象可能是任意的,那么怎么样才可以知道这个对象的全部成员变量名称呢?
- 使用反射获取对象的Class类对象,然后获取全部成员变量信息。
- 遍历成员变量信息,然后提取本成员变量在对象中的具体值
- 存入成员变量名称和值到文件中去即可