反射和动态代理
- 反射
- 前言
- 获取class对象的方式
- 反射获取构造方法
- 反射获取成员变量
- 反射获取成员方法
- 实例
- 动态代理
反射
前言
什么是反射?
反射允许对成员变量,成员方法和构造方法的信息进行编程访问。
为什么用反射 / 反射的作用?
可以轻易地获取成员变量、构造方法和成员方法的所有信息。
①获取一个类里面所有的信息,获取到了之后,再执行其他的业务逻辑。
②结合配置文件,动态的创建对象并调用方法。
获取class对象的方式
三种方式
①Class.forName("全类名");
②类名.class
③对象.getClass()
使用时机
这三种方式的使用时机实际上和Java文件的编译过程息息相关:
源代码阶段没有把代码加载到内存当中,只是在硬盘中进行的操作,这一阶段使用第一种方式获得class字节码文件的对象;运行Java代码时,需要将类的字节码文件加载到内存中,这个阶段是加载阶段,该阶段使用第二种方式;在内存中创建某个类的对象时为运行阶段,该阶段使用第三种方式。
具体实现
//1. Class.forName("全类名") 全类名就是:包名+类名
//clazz_one就是Student类的字节码文件
//第一种方式最为常用
Class clazz_one = Class.forName("com.wmy.myreflect1.Student");
System.out.println(clazz_one);//2. 类名.class
//第二种方式更多的是当做参数来传递
Class clazz_two = Student.class;
System.out.println(clazz_two);//3. 对象.getClass()
//当我们已经有了这个类的对象时 才可以使用方式三
Class clazz_three = new Student().getClass();
System.out.println(clazz_three);
System.out.println(clazz_one == clazz_two);
System.out.println(clazz_two == clazz_three);
System.out.println(clazz_one == clazz_three);
运行结果
反射获取构造方法
在Java中,存在“万物皆对象”的思想,比如说class字节码文件是Class类的对象,那么构造方法可以看作是Constructor类的对象、成员变量(字段)可以看作是Field类的对象、成员方法可以看作是Method类的对象。
//1. 获取class字节码文件对象
Class clazz = Class.forName("com.wmy.myreflect1.demo02.Student02");
//2. 获取构造方法
System.out.println("获取公共构造方法:");
Constructor[] cons1 = clazz.getConstructors();
for (Constructor con : cons1){System.out.println(con);
}
System.out.println("分割线-----------------------------------"+"\n");
System.out.println("获取所有构造方法(含私有):");
Constructor[] cons2 = clazz.getDeclaredConstructors();
for (Constructor con : cons2){System.out.println(con);
}
System.out.println("注:虽然该方法可以获得私有构造方法,但是无法通过这种方式完成私有构造方法new一个对象出来");
System.out.println("分割线-----------------------------------"+"\n");
System.out.println("根据构造方法中参数类型和参数数量的不同 向getDeclaredConstructor中传入参数类型的字节码文件即可获得对应的构造方法:");
Constructor con1 = clazz.getDeclaredConstructor();
Constructor con2 = clazz.getDeclaredConstructor(String.class);
Constructor con3 = clazz.getDeclaredConstructor(int.class);
Constructor con4 = clazz.getDeclaredConstructor(String.class, int.class);
System.out.println(con1);
System.out.println(con2);
System.out.println(con3);
System.out.println(con4);
System.out.println("分割线-----------------------------------"+"\n");
System.out.println("获取权限修饰符");
int modifiers = con4.getModifiers();
System.out.println("1表示公有public 2表示私有private");
System.out.println(modifiers);
System.out.println("分割线-----------------------------------"+"\n");
System.out.println("暴力反射:表示临时取消权限校验");
con4.setAccessible(true);
Student02 stu = (Student02)con4.newInstance("张三", 23);
System.out.println(stu);
反射获取成员变量
//1. 获取class字节码文件的对象
Class clazz = Class.forName("com.wmy.myreflect1.demo03.Student03");
//2. 获取成员变量
System.out.println("获取所有公共成员变量:");
Field[] fields1 = clazz.getFields();
for (Field field : fields1){System.out.println(field);
}
System.out.println("分割线-----------------"+"\n");
System.out.println("获取所有成员变量(含私有):");
Field[] fields2 = clazz.getDeclaredFields();
for (Field field : fields2){System.out.println(field);
}
System.out.println("分割线-----------------"+"\n");
System.out.println("获取单个成员变量:");
Field gender = clazz.getField("gender");
System.out.println(gender);
Field name = clazz.getDeclaredField("name");
System.out.println(name);
Field age = clazz.getDeclaredField("age");
System.out.println(age);
System.out.println("分割线-----------------"+"\n");
System.out.println("获取权限修饰符:");
int modifiers = name.getModifiers();
System.out.println(modifiers);
System.out.println("获取成员变量名称:");
String n = name.getName();
System.out.println(n);
System.out.println("获取成员变量类型:");
Class t = name.getType();
System.out.println(t);
System.out.println("获取成员变量记录的值:");
Student03 s = new Student03("张三",23,"男");
name.setAccessible(true);
String value = (String) name.get(s);
System.out.println(value);
System.out.println("修改对象里面记录的值:");
name.set(s,"李四");
System.out.println(s);
反射获取成员方法
//1. 获取class字节码文件对象
Class clazz = Class.forName("com.wmy.myreflect1.demo04.Student04");
//2. 获取成员方法
System.out.println("获取所有方法对象(包含父类中所有的公共方法):");
Method[] methods1 = clazz.getMethods();
for (Method method : methods1){System.out.println(method);
}
System.out.println("分割线-----------------"+"\n");
System.out.println("获取所有方法对象(不能获取父类的,但是可以获取本类中私有的方法):");
Method[] methods2 = clazz.getDeclaredMethods();
for (Method method : methods2){System.out.println(method);
}
System.out.println("分割线-----------------"+"\n");
System.out.println("获取指定的单一方法:");
Method m = clazz.getDeclaredMethod("eat", String.class, int.class);
System.out.println(m);
System.out.println("获取方法的修饰符:");
int modifiers = m.getModifiers();
System.out.println(modifiers);
System.out.println("获取方法的名字:");
String name = m.getName();
System.out.println(name);
System.out.println("获取方法的形参:");
Parameter[] parameters = m.getParameters();
for (Parameter parameter : parameters) {System.out.println(parameter);
}
System.out.println("获取方法抛出的异常:");
Class[] exceptionTypes = m.getExceptionTypes();
for (Class exceptionType : exceptionTypes) {System.out.println(exceptionType);
}
System.out.println("获取方法的返回值:");
Student04 s = new Student04();
m.setAccessible(true);
String result = (String)m.invoke(s,"石乐志",996);
System.out.println(result);
实例
需求1
保存信息:对于任意一个对象,都可以把对象所有的字段名和值,保存到文件中去。
实现
public class MyReflectDemo {public static void main(String[] args) throws IllegalAccessException, IOException {/** 对于任意一个对象 都可以把对象所有的字段名和值 保存到文件中去* */Student s = new Student("小A" , 23, '女' , 167.5 , "睡觉");Teacher t = new Teacher("gls" , 1000);saveObject(t);}//把对象的所有成员变量名和值保存到本地文件中private static void saveObject(Object obj) throws IllegalAccessException, IOException {//1. 获取字节码文件的对象Class clazz = obj.getClass();//创建IO流BufferedWriter bw = new BufferedWriter(new FileWriter("G:\\JavaWorks_IntelliJIDEA\\myreflect\\src\\main\\java\\com\\wmy\\myreflect1\\record.txt"));//2. 获取所有的成员变量Field[] fields = clazz.getDeclaredFields();for (Field field : fields) {field.setAccessible(true);//获取成员变量的名字String name = field.getName();//获取成员变量的值Object value = field.get(obj);//写出数据bw.write(name+"="+value);bw.newLine();}bw.close();}
}
结果
需求2
跟配置文件结合动态创建:反射可以和配置文件结合的方式,动态的创建对象,并调用方法。
实现
prop.properties
classname=com.wmy.myreflect1.case2.Student
method=study
com/wmy/myreflect1/case2/MyReflectDemo.java
public class MyReflectDemo {public static void main(String[] args) throws IllegalAccessException, IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException {/** 反射可以和配置文件结合的方式 动态地创建对象 并调用方法* *///1.读取配置文件中的信息Properties prop = new Properties();FileInputStream fis = new FileInputStream("G:\\JavaWorks_IntelliJIDEA\\myreflect\\prop.properties");prop.load(fis);fis.close();System.out.println(prop);//2.获取全类名和方法名String className = (String)prop.get("classname");String methodName = (String)prop.get("method");System.out.println(className);System.out.println(methodName);//3.利用反射去创建对象并运行方法Class clazz = Class.forName(className);//获取构造方法Constructor con = clazz.getDeclaredConstructor();Object o = con.newInstance();System.out.println(o);//获取成员方法并运行Method method = clazz.getDeclaredMethod(methodName);method.setAccessible(true);method.invoke(o);}
}
动态代理
1.什么是动态代理?
动态代理可以无侵入式的给代码增加额外的功能。
2.程序为什么需要代理?
对象如果嫌身上干的事太多的话,可以通过代理来转移部分职责。
对象有什么方法想被代理,代理就一定要有对应的方法。
3.代理长什么样?
代理里面就是对象要被代理的方法。
4.Java通过什么来保证代理的样子?
通过接口保证,后面的对象和代理需要实现同一个接口,接口中就是被代理的所有方法。
5.如何为Java对象创建一个代理对象?
java.lang.reflect.Proxy类
:提供了为对象产生代理对象的方法:
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interface,InvocationHandler h)
参数一:用于指定用哪个类加载器 去加载生成的代理类
参数二:指定接口 这些接口用于指定生成的代理长什么样 也就是有哪些方法
参数三:用来指定生成的代理对象要干什么事情
需求
实现BigStar类中成员方法的动态代理。
步骤
1.创建BigStar类(记得实现步骤2中的接口)
com/wmy/mydynamicproxy/BigStar.java
public class BigStar implements Star{private String name;public BigStar(){}public BigStar(String name){this.name = name;}//唱歌@Overridepublic String sing(String name){System.out.println(this.name+"正在唱"+name);return "谢谢大家";}//跳舞@Overridepublic void dance(){System.out.println(this.name+"正在让你蠢蠢欲动");}public String getName() {return name;}public void setName(String name) {this.name = name;}@Overridepublic String toString() {return "BigStar{" +"name='" + name + '\'' +'}';}
}
2.封装要代理的方法为接口
com/wmy/mydynamicproxy/Star.java
public interface Star {//我们可以把所有想要被代理的方法定义在接口当中//唱歌public abstract String sing(String name);//跳舞public abstract void dance();
}
3.创建ProxyUtil类用于创建代理
com/wmy/mydynamicproxy/ProxyUtil.java
/*
* 类的作用:
* 创建一个代理
* */
public class ProxyUtil {/** 方法的作用:* 给一个明星的对象创建一个代理* 形参:* 被代理的明星对象* 返回值:* 给明星创建的代理* 需求:* 外面的人想要大明星唱一首歌* 1.获取代理的对象* 代理对象 = ProxyUtil.createProxy(大明星的对象);* 2.再调用代理的唱歌的方法* 代理对象.唱歌的方法();* */public static Star createProxy(BigStar bigStar){/** public static Object newProxyInstance(ClassLoader loader, Class<?>[] interface,InvocationHandler h)* 参数一:用于指定用哪个类加载器(将字节码文件加载到内存中的工具) 去加载生成的代理类* 参数二:指定接口 这些接口用于指定生成的代理长什么样 也就是有哪些方法* 参数三:用来指定生成的代理对象要干什么事情* */Star star = (Star)Proxy.newProxyInstance(ProxyUtil.class.getClassLoader(),new Class[]{Star.class},new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {/** 参数一:代理的对象* 参数二:要运行的方法 sing* 参数三:调用sing方法时,传递的实参* */if("sing".equals(method.getName())){System.out.println("准备话筒 收钱!");}else if("dance".equals(method.getName())){System.out.println("准备场地 收钱!");}//去找大明星开始唱歌或者跳舞//代码的表现形式:调用大明星里面唱歌或者跳舞的方法return method.invoke(bigStar,args);}});return star;}
}
4.创建测试类测试动态代理的过程
com/wmy/mydynamicproxy/Test.java
/** 需求:* 外面的人想要大明星唱一首歌* 1.获取代理的对象* 代理对象 = ProxyUtil.createProxy(大明星的对象);* 2.再调用代理的唱歌的方法* 代理对象.唱歌的方法();* */
public class Test {public static void main(String[] args) {//1.获取代理的对象BigStar bigStar = new BigStar("鸡哥");Star proxy = ProxyUtil.createProxy(bigStar);//2.调用唱歌的方法String result = proxy.sing("姬霓太美");System.out.println(result);//2.调用跳舞的方法proxy.dance();}
}
5.效果