目录
一、反射概述
1、反射概述
反射是指对于任何一个Class类,在"运行的时候"都可以直接得到这个类全部成分。
在运行时,可以直接得到这个类的构造器对象:Constructor
在运行时,可以直接得到这个类的成员变量对象:Field
在运行时,可以直接得到这个类的成员方法对象:Method
这种运行时动态获取类信息以及动态调用类中成分的能力称为Java语言的反射机制。
2、反射的核心思想
是得到编译以后的class文件对象。
二、反射获取类对象的三种方式
1、反射的第一步是什么?
获取Class类对象,这样才可以解析类的全部成分
2、获取Class类的对象的三种方式
方式一:Class c1 = Class.forName(“全类名”);
方式二:Class c2 = 类名.class
方式三:Class c3 = 对象.getClass();
3、演示代码
public static void main(String[] args) throws Exception {
//1、Class类中的一个静态方法:forname(权限名:包名+类名)
Class c = Class.forName("com.wfl.d2_reflect_class.Student");
System.out.println(c); //Student.class
//2、类名.class
Class c2 = Student.class;
System.out.println(c2);
//3、对象.getClass() 获取对象对应的Class对象
Student s = new Student();
Class c3 = s.getClass();
System.out.println(c3);
}
3.1、结果展示
三、反射获取构造器对象
1.使用反射技术获取构造器对象
1.1.反射的第一步是先得到类对象,然后从类对象中获取类的成分对象。
1.2.Class类中用于获取构造器的方法
方法 | 说明 |
Constructor<?>[] getConstructors() | 返回所有构造器对象的数组(只能拿public的) |
Constructor<?>[] getDeclaredConstructors() | 返回所有构造器对象的数组,存在就能拿到 |
Constructor<T> getConstructor(Class<?>... parameterTypes) | 返回单个构造器对象(只能拿public的) |
Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) | 返回单个构造器对象,存在就能拿到 |
2.演示代码
//1、getConstructors:
//获取全部构造器:只能获取pubilc修饰的构造器。
//Constructor[] getConstructors()
@Test
public void getConstructors() {
//a.第一步获取类对象
Class c = Student.class;
//b.获取全部构造器(只能获取pubilc修饰的构造器)
Constructor[] constructors = c.getConstructors();
//c.遍历构造器
for (Constructor constructor : constructors) {
System.out.println(constructor.getName() + "==>" + constructor.getParameterCount());
}
}
//2、getDeclaredConstructors():
//获取全部构造器:只能只要你敢写,这里就都可以获取到
@Test
public void getDeclaredConstructors() {
//a.第一步获取类对象
Class c = Student.class;
//b.获取全部构造器
Constructor[] constructors = c.getDeclaredConstructors();
//c.遍历构造器
for (Constructor constructor : constructors) {
System.out.println(constructor.getName() + "==>" + constructor.getParameterCount());
}
}
//3、getConstructor(Class...parameterTypes)
//获取某个构造器:只能获取pubilc修饰的某个构造器。
@Test
public void getConstructor() throws Exception {
//a.第一步获取类对象
Class c = Student.class;
//b.获取某个构造器(只能获取pubilc修饰的某个构造器。获取无参构造器)
Constructor constructor = c.getConstructor();
System.out.println(constructor.getName() + "==>" + constructor.getParameterCount());
}
//4、getDeclaredConstructor(Class...parameterTypes)
//获取某个构造器:只能只要你敢写,这里就能拿到
@Test
public void getDeclaredConstructor () throws Exception {
//a.第一步获取类对象
Class c1 = Student.class;
//b.获取某个构造器(无参构造器)
Constructor cons = c1.getDeclaredConstructor();
System.out.println(cons.getName() + "==>" + cons.getParameterCount());
//c.定义某个有参构造器
Constructor cons1 = c1.getDeclaredConstructor(String.class,int.class);
System.out.println(cons1.getName() + "==>" + cons1.getParameterCount());
}
2.1.结果展示
3. Constructor类中用于创建对象的方法
符号 | 说明 |
T newInstance(Object... initargs) | 根据指定的构造器创建对象 |
public void setAccessible(boolean flag) | 设置为true,表示取消访问检查,进行暴力反射 |
4.演示代码
public void getDeclaredConstructor () throws Exception {
//a.第一步获取类对象
Class c1 = Student.class;
//b.获取某个构造器(无参构造器)
Constructor cons = c1.getDeclaredConstructor();
System.out.println(cons.getName() + "==>" + cons.getParameterCount());
//如果遇到了私有的构造器,可以暴力反射
cons.setAccessible(true); //权限被打开了
Student s = (Student) cons.newInstance();
System.out.println(s);
System.out.println("------------------------------");
//c.定义某个有参构造器
Constructor cons1 = c1.getDeclaredConstructor(String.class,int.class);
System.out.println(cons1.getName() + "==>" + cons1.getParameterCount());
Student s1 = (Student) cons1.newInstance("商务卡", 1000);
System.out.println(s1);
}
4.1、结果展示
5、注意
反射可以破坏封装性,私有的方法也可以被执行。
四、反射获取成员变量对象
1、反射技术获取成员变量对象
1.1、反射的第一步是先得到类对象,然后从类对象中获取类的成分对象。
1.2、Class类中用于获取成员变量的方法
方法 | 说明 |
Field[] getFields() | 返回所有成员变量对象的数组(只能拿public的) |
Field[] getDeclaredFields() | 返回所有成员变量对象的数组,存在就能拿到 |
Field getField(String name) | 返回单个成员变量对象(只能拿public的) |
Field getDeclaredField(String name) | 返回单个成员变量对象,存在就能拿到 |
2、代码演示
/**
1、获取全部成员变量
Field[] getDeclaredFields();
获得所有的成员变量对应的Field对象,只要声明了就可以得到
*/
@Test
public void getDeclaredFields(){
//a.定位Class对象
Class c = Student.class;
//b.定位全部成员变量
Field[] f = c.getDeclaredFields();
//c.遍历一下
for (Field field : f) {
System.out.println(field.getName()+"==>"+field.getType());
}
}
/**
2、获取某个成员变量
Field getDeclaredField();
获得所有的成员变量对应的Field对象,只要声明了就可以得到
*/
@Test
public void getDeclaredField() throws Exception {
//a.定位Class对象
Class c = Student.class;
//b.根据名称定位某个成员变量
Field f = c.getDeclaredField("age");
System.out.println(f.getName()+"==>"+f.getType());
}
2.1、结果展示
3、Field类中用于取值、赋值的方法
符号 | 说明 |
void set(Object obj, Object value): | 赋值 |
Object get(Object obj) | 获取值。 |
4、代码展示
@Test
public void getDeclaredField() throws Exception {
//a.定位Class对象
Class c = Student.class;
//b.根据名称定位某个成员变量
Field f = c.getDeclaredField("age");
f.setAccessible(true); //暴力打开权限
//c.赋值
Student s = new Student();
f.set(s,18); //s.setAge(18);
System.out.println(s);
//d取值
int age = (int) f.get(s);
System.out.println(age);
}
4.1、结果展示
5、注意
如果某成员变量是非public的,需要打开权限(暴力反射),然后再取值、赋值 setAccessible(boolean)
五、反射获取方法对象
1、反射技术获取方法对象
1.1、反射的第一步是先得到类对象,然后从类对象中获取类的成分对象。
1.2、Class类中用于获取成员方法的方法
方法 | 说明 |
Method[] getMethods() | 返回所有成员方法对象的数组(只能拿public的) |
Method[] getDeclaredMethods() | 返回所有成员方法对象的数组,存在就能拿到 |
Method getMethod(String name, Class<?>... parameterTypes) | 返回单个成员方法对象(只能拿public的) |
Method getDeclaredMethod(String name, Class<?>... parameterTypes) | 返回单个成员方法对象,存在就能拿到 |
2.、Method类中用于触发执行的方法
符号 | 说明 |
Object invoke(Object obj, Object... args) | 运行方法 参数一:用obj对象调用该方法 参数二:调用方法的传递的参数(如果没有就不写) 返回值:方法的返回值(如果没有就不写) |
3、代码展示
/**
1、提取全部方法:包括私有的
*/
@Test
public void getDeclardMethods() {
//a.获取类对象
Class c = Dog.class;
//b.提取全部方法:包括私有的
Method[] methods = c.getDeclaredMethods();
//c.遍历全部方法
for (Method method : methods) {
System.out.println(method.getName()+"返回值类型"+method.getReturnType()+"参数个数:"+method.getParameterCount());
}
}
/**
1、提取某个方法
*/
@Test
public void getDeclardMethod() throws Exception {
//a.获取类对象
Class c = Dog.class;
//b.提取某个方法
Method m1 = c.getDeclaredMethod("eat");
Method m2 = c.getDeclaredMethod("eat",String.class);
//暴力打开权限
m1.setAccessible(true);
m2.setAccessible(true);
//c.触发方法的执行
Dog d = new Dog();
//注意没有返回结果,就返回null
Object result = m1.invoke(d);
System.out.println(result);
Object result2 = m2.invoke(d,"骨头");
System.out.println(result2);
} /**
1、提取全部方法:包括私有的
*/
@Test
public void getDeclardMethods() {
//a.获取类对象
Class c = Dog.class;
//b.提取全部方法:包括私有的
Method[] methods = c.getDeclaredMethods();
//c.遍历全部方法
for (Method method : methods) {
System.out.println(method.getName()+"返回值类型"+method.getReturnType()+"参数个数:"+method.getParameterCount());
}
}
/**
1、提取某个方法
*/
@Test
public void getDeclardMethod() throws Exception {
//a.获取类对象
Class c = Dog.class;
//b.提取某个方法
Method m1 = c.getDeclaredMethod("eat");
Method m2 = c.getDeclaredMethod("eat",String.class);
//暴力打开权限
m1.setAccessible(true);
m2.setAccessible(true);
//c.触发方法的执行
Dog d = new Dog();
//注意没有返回结果,就返回null
Object result = m1.invoke(d);
System.out.println(result);
Object result2 = m2.invoke(d,"骨头");
System.out.println(result2);
}
3.1、结果展示
4、注意
如果某成员方法是非public的,需要打开权限(暴力反射),然后再触发执行 setAccessible(boolean)
六、反射的作用-绕过编译阶段为集合添加数据
1、反射是作用在运行时的技术,此时集合的泛型将不能产生约束了,此时是可以为集合存入其他任意类型的元素
2、泛型只是在编译阶段可以约束集合只能操作某种数据类型,在编译成Class文件进入运行阶段的时候,其真实类型都是ArrayList了,泛型相当于被擦除了。
3、演示代码
3.1介绍:一开始定义一个Integer的泛型,后经过反射可以为其添加String类型的数据。但是有一招简单的方法可以实现,就是将一个集合的数据赋值给另一个集合,给另一个添加,就可以添加任意类型的数据了。
public static void main(String[] args) throws Exception {
//需求:反射现实泛型擦除后,加入其他类型的元素
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()); //ArrayList.class
System.out.println("-------------------");
ArrayList<Integer> lists3 = new ArrayList<>();
lists3.add(22);
lists3.add(23);
// lists3.add("小王");
Class c = lists3.getClass();
Method add = c.getMethod("add", Object.class);
Boolean b = (Boolean) add.invoke(lists3, "小王");
System.out.println(b);
System.out.println(lists3);
//简化实现 没有用反射
ArrayList lists4 = lists3;
lists4.add("白马");
lists4.add("白马");
lists4.add("黑马");
System.out.println(lists4);
System.out.println(lists3);
}
七、反射的作用-通用框架的底层原理
需求:给你任意一个对象,在不清楚对象字段的情况可以,可以把对象的字段名称和对应值存储到文件中去。
1.主要代码
package com.wfl.d7_reflect_framework;
import java.io.FileOutputStream;
import java.io.PrintStream;
import java.lang.reflect.Field;
public class MybatisUtil {
/**
保存对象
* @param obj
*/
public static void save(Object obj){
try (
PrintStream ps = new PrintStream(new FileOutputStream("D:\\code\\javasepromax\\junit-reflect-annotation-proxy-app\\src\\data.txt",true));
){
//1、提起这个类的全部成员变量:只有反射可以解决
Class c = obj.getClass(); // c.getSimpleName()获取当前类名 c.getName获取当前权限名:包名+类名
ps.println("========================"+c.getSimpleName()+"=====================");
//2、提取它的全部成员变量
Field[] fields = c.getDeclaredFields();
//3、获取成员变量的信息
for (Field field : fields) {
String name = field.getName();
//提取本成员变量在obj对象的取值
field.setAccessible(true);
String value = field.get(obj)+"";
ps.println(name+"="+value);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
1.1结果:打印到指定的TXT文件中
八、反射的作用
1、可以在运行时得到一个类的全部成分然后操作。
2、可以破坏封装性。(很突出)
3、也可以破坏泛型的约束性。(很突出)
4、更重要的用途是适合:做Java高级框架
5、基本上主流框架都会基于反射设计一些通用技术功能。