滴水穿石-11 反射
1 反射中涉及的几个概念
1.1 类的加载
当程序要使用某个类的时候,如果这个类尚未加载到内存中,系统会通过加载,连接,初始化三步实现对该类的初始化.
1.2 类的初始化时机
创建类的实例;
访问类的静态成员;(为静态变量赋值;调用静态方法)
使用反射强制创年某个类或接口对应的java.lang.Class对象
初始化某个类的子类
直接使用java.exe命令运行某个主类
1.3 反射
在运行状态中,获取类的成员的机制.(类的成员:所有的属性和方法)
首先获取Class类(字节码文件)
Class类:
成员变量 Field
构造方法 Constructor
成员方法 Method
package d11; //获取Class文件对象的三种方式 //A:Object类的getClass()方法 //B:数据类型的静态属性Class //C:Class类中的静态方法(className 注意:是类的全路径:d11.Person) // public static Class forName(String className) public class ReflectDemo1 { public static void main(String[] args) throws ClassNotFoundException { Person p = new Person(); //A:Object类的getClass()方法 Class c1 = p.getClass(); //B:数据类型的静态属性Class Class c2 = Person.class; //C:Class类中的静态方法(className 注意:是类的全路径:d11.Person) Class c3 =Class.forName("d11.Person") ; System.out.println(c1+" "+c2+" "+c3); //输出结果 class d11.Person class d11.Person class d11.Person } }
2 反射练习
2.1 获取构造方法
package d11; public class Person { // 定义三种不同访问类型的构造方法 public Person() { } Person(String name) { this.name = name; } private Person(String name, int age, String address) { this.name = name; this.age = age; this.address = address; } // 定义三种不同访问类型的成员变量 public String name; int age; private String address; // 定义三种不同访问类型的成员方法 public void show() { System.out.println("show"); } void show(String msg) { System.out.println("show: "+msg); } private String show(String name,int age,String address) { return "姓名 : "+name+" 年龄: "+age+" 地址: "+address; } @Override public String toString() { return "Person [name=" + name + ", age=" + age + ", address=" + address + "]"; } }
package d11; import java.lang.reflect.Constructor; //通过Class对象获取所有的构造方法,得到一个数组集合 public class ReflectDemo2_GetConstructor { public static void main(String[] args) throws ClassNotFoundException { // 创建Class对象 Class c1 = Class.forName("d11.Person"); // 获取所有的public构造方法 Constructor[] cs = c1.getConstructors(); for (Constructor constructor : cs) { System.out.println(constructor); } /* * 输出结果 public d11.Person() 分析:只有Public权限的输出 */ // 获取所有的构造方法 Constructor[] cs2 = c1.getDeclaredConstructors(); for (Constructor constructor : cs2) { System.out.println(constructor); } /* * private d11.Person(java.lang.String,int,java.lang.String) d11.Person(java.lang.String) public d11.Person() */ // 获取所有的构造方法(貌似还有一个排序) } }
2.1.1 public权限构造方法的获取和使用
package d11; import java.lang.reflect.Constructor; //通过Class对象获取所有的构造方法,得到一个数组集合 public class ReflectDemo3_GetConstructor_public { public static void main(String[] args) throws Exception { //01-创建Class对象 Class c1 = Class.forName("d11.Person"); //02-获取所有的public构造方法 Constructor cpub = c1.getConstructor(); //03-通过构造方法创建新对象 Object obj = cpub.newInstance(); System.out.println(obj.toString()); /*输出结果 * Person [name=null, age=0, address=null] * */ } }
2.1.2 protect权限构造方法的获取和使用
package d11; import java.lang.reflect.Constructor; //通过Class对象获取所有的构造方法,得到一个数组集合 public class ReflectDemo3_GetConstructor_protect { public static void main(String[] args) throws Exception { //01-创建Class对象 Class c1 = Class.forName("d11.Person"); //02-获取Protect构造方法 (含有参数) Constructor cpub = c1.getDeclaredConstructor(String.class); //03-通过构造方法创建新对象 Object obj = cpub.newInstance("张三"); System.out.println(obj.toString()); /*输出结果 * Person [name=张三, age=0, address=null] * */ } }
2.1.3 private权限构造方法的获取和使用
package d11; import java.lang.reflect.Constructor; //通过Class对象获取所有的构造方法,得到一个数组集合 public class ReflectDemo3_GetConstructor_private { public static void main(String[] args) throws Exception { //01-创建Class对象 Class c1 = Class.forName("d11.Person"); //02-获取Protect构造方法 (含有参数) Constructor cpub = c1.getDeclaredConstructor(String.class,int.class,String.class); //03-设置访问权限 cpub.setAccessible(true); //04-通过构造方法创建新对象 Object obj = cpub.newInstance("张三",10,"中国"); System.out.println(obj.toString()); /*输出结果 * Person [name=张三, age=10, address=中国] * */ } }
2.2 获取成员变量
package d11; import java.lang.reflect.Field; import java.lang.reflect.Field; //通过Class对象获取所有的成员变量,得到一个数组集合 public class ReflectDemo2_GetFields { public static void main(String[] args) throws ClassNotFoundException { // 创建Class对象 Class c1 = Class.forName("d11.Person"); // 获取所有的public成员变量 Field[] cs = c1.getFields(); for (Field field : cs) { System.out.println(field); } /* * 输出结果 public java.lang.String d11.Person.name 分析:只有Public权限的输出 */ // 获取所有的成员变量 Field[] cs2 = c1.getDeclaredFields(); for (Field field : cs2) { System.out.println(field); } /* * public java.lang.String d11.Person.name int d11.Person.age private java.lang.String d11.Person.address */ // 获取所有的成员变量(貌似还有一个排序) } }
2.2.1 public权限成员变量的获取和使用
package d11; import java.lang.reflect.Constructor; import java.lang.reflect.Field; //通过Class对象获取所有的构造方法,得到一个数组集合 public class ReflectDemo3_GetField_public { public static void main(String[] args) throws Exception { //01-创建Class对象 Class c1 = Class.forName("d11.Person"); Constructor cpub = c1.getConstructor(); //03-通过构造方法创建新对象 Object obj = cpub.newInstance(); //04-获取成员变量 Field fName = c1.getField("name"); //05-设置 fName.set(obj, "李四"); System.out.println(obj.toString()); } }
2.2.2 protect权限成员变量的获取和使用
package d11; import java.lang.reflect.Constructor; import java.lang.reflect.Field; //通过Class对象获取所有的构造方法,得到一个数组集合 public class ReflectDemo3_GetField_protect { public static void main(String[] args) throws Exception { //01-创建Class对象 Class c1 = Class.forName("d11.Person"); //02-获取Protect构造方法 (含有参数) Constructor cpub = c1.getConstructor(); //03-通过构造方法创建新对象 Object obj = cpub.newInstance(); //04 获取age Field fAge = c1.getDeclaredField("age"); fAge.set(obj, 20); System.out.println(obj.toString()); /*输出结果 * Person [name=null, age=20, address=null] * */ } }
2.2.3 private权限成员变量的获取和使用
package d11; import java.lang.reflect.Constructor; import java.lang.reflect.Field; //通过Class对象获取所有的构造方法,得到一个数组集合 public class ReflectDemo3_GetField_private { public static void main(String[] args) throws Exception { //01-创建Class对象 Class c1 = Class.forName("d11.Person"); //02-获取Protect构造方法 (含有参数) Constructor cpub = c1.getConstructor(); //03-通过构造方法创建新对象 Object obj = cpub.newInstance(); //04-获取Field Field fAddress = c1.getDeclaredField("address"); fAddress.setAccessible(true); //03-设置访问权限 fAddress.set(obj, "中国"); System.out.println(obj.toString()); /*输出结果 * Person [name=null, age=0, address=中国] * */ } }
2.3 获取成员方法
package d11; import java.lang.reflect.Method; import java.lang.reflect.Method; //通过Class对象获取所有的成员变量,得到一个数组集合 public class ReflectDemo2_GetMethods { public static void main(String[] args) throws ClassNotFoundException { // 创建Class对象 Class c1 = Class.forName("d11.Person"); // 获取所有的public成员变量 Method[] cs = c1.getMethods(); for (Method method : cs) { //System.out.println(method); } /* * 输出结果 public java.lang.String d11.Person.toString() public void d11.Person.show() public final void java.lang.Object.wait() throws java.lang.InterruptedException public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException public boolean java.lang.Object.equals(java.lang.Object) public native int java.lang.Object.hashCode() public final native java.lang.Class java.lang.Object.getClass() public final native void java.lang.Object.notify() public final native void java.lang.Object.notifyAll() */ // 获取所有的成员变量 Method[] cs2 = c1.getDeclaredMethods(); for (Method method : cs2) { System.out.println(method); } /* public java.lang.String d11.Person.toString() void d11.Person.show(java.lang.String) private java.lang.String d11.Person.show(java.lang.String,int,java.lang.String) public void d11.Person.show() */ // 获取所有的成员变量(貌似还有一个排序) } }
2.3.1 public权限成员方法的获取和使用
package d11; import java.lang.reflect.Constructor; import java.lang.reflect.Method; //通过Class对象获取所有的构造方法,得到一个数组集合 public class ReflectDemo3_GetMethod_public { public static void main(String[] args) throws Exception { //01-创建Class对象 Class c1 = Class.forName("d11.Person"); Constructor cpub = c1.getConstructor(); //03-通过构造方法创建新对象 Object obj = cpub.newInstance(); //04-获取成员变量 Method mshow = c1.getMethod("show"); //05-设置 mshow.invoke(obj); /*输出结果 * show * */ } }
2.3.2 protect权限成员方法的获取和使用
package d11; import java.lang.reflect.Constructor; import java.lang.reflect.Method; //通过Class对象获取所有的构造方法,得到一个数组集合 public class ReflectDemo3_GetMethod_protect { public static void main(String[] args) throws Exception { //01-创建Class对象 Class c1 = Class.forName("d11.Person"); //02-获取Protect构造方法 (含有参数) Constructor cpub = c1.getConstructor(); //03-通过构造方法创建新对象 Object obj = cpub.newInstance(); //04 获取age Method mShow = c1.getDeclaredMethod("show",String.class); mShow.invoke(obj, "woshi Show"); /*输出结果 * show: woshi Show * */ } }
2.3.3 private权限成员方法的获取和使用
package d11; import java.lang.reflect.Constructor; import java.lang.reflect.Method; //通过Class对象获取所有的构造方法,得到一个数组集合 public class ReflectDemo3_GetMethod_private { public static void main(String[] args) throws Exception { //01-创建Class对象 Class c1 = Class.forName("d11.Person"); //02-获取Protect构造方法 (含有参数) Constructor cpub = c1.getConstructor(); //03-通过构造方法创建新对象 Object obj = cpub.newInstance(); //04-获取Method Method mShow = c1.getDeclaredMethod("show",String.class,int.class,String.class); mShow.setAccessible(true); Object msg = mShow.invoke(obj, "zhang san",20,"China"); String msgStr = (String)mShow.invoke(obj, "zhang san",20,"China"); System.out.println(msg); System.out.println(msgStr); /*输出结果 * 姓名 : zhang san 年龄: 20 地址: China 姓名 : zhang san 年龄: 20 地址: China * */ } }
2.4 模拟配置文件完成方法调用
2.4.1 假设有一个简单的配置文件
2.4.2 有多个实体类文件
package d11; public class Student { // 定义三种不同访问类型的构造方法 public Student() { } Student(String name) { this.name = name; } // 定义三种不同访问类型的成员变量 public String name; // 定义三种不同访问类型的成员方法 public void show() { System.out.println("我是学生show方法!!"); } }
package d11; public class Person { // 定义三种不同访问类型的构造方法 public Person() { } Person(String name) { this.name = name; } private Person(String name, int age, String address) { this.name = name; this.age = age; this.address = address; } // 定义三种不同访问类型的成员变量 public String name; int age; private String address; // 定义三种不同访问类型的成员方法 public void show() { System.out.println("show"); } void show(String msg) { System.out.println("show: "+msg); } private String show(String name,int age,String address) { return "姓名 : "+name+" 年龄: "+age+" 地址: "+address; } @Override public String toString() { return "Person [name=" + name + ", age=" + age + ", address=" + address + "]"; } }
2.4.3 创建一个测试类,完成多个类文件之间的调用
package d11; import java.io.FileNotFoundException; import java.io.FileReader; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.util.Properties; public class Test { public static void main(String[] args) throws Exception { // 加载键值对数据 Properties prop = new Properties(); FileReader fr = new FileReader("D:\\bbb\\class.txt"); prop.load(fr); fr.close(); //获取数据 String className = prop.getProperty("className"); String methodName = prop.getProperty("methodName"); //反射 Class c = Class.forName(className); //调用构造方法 Constructor con = c.getConstructor(); Object obj = con.newInstance(); //调用方法 Method m = c.getMethod(methodName); m.invoke(obj); } }
3 练习:
3.1 向ArrayList<Integer>中添加一个字符串"hello"
package d11; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; public class ArrayListDemo { public static void main(String[] args) throws Exception { //向ArrayList<Integer>中添加一个字符串"hello" ArrayList<Integer> array = new ArrayList<Integer>(); Class c = array.getClass(); Method m = c.getMethod("add", Object.class); m.invoke(array, "hello"); m.invoke(array, "word"); System.out.println(array); /* * [hello, word] * */ } }
3.2 添加一个工具类,给某个类中的某个属性复制
package d11; import java.lang.reflect.Field; public class Tool { public void setProperty(Object obj,String PropertyName,Object value) throws Exception{ //根据对象获取对象字节码文件 Class c = obj.getClass(); //根据c获取字段 Field f = c.getDeclaredField(PropertyName); //给字段复制 f.setAccessible(true); //给对象的成员变量赋值 f.set(obj, value); } }
package d11; public class ToolDemo { public static void main(String[] args) throws Exception { //给Person的Name赋值 Person p = new Person(); //注意此时不能给address 私有属性赋值 Tool t = new Tool(); t.setProperty(p, "address", "北京"); System.out.println(p.toString()); /*输出结果 * Person [name=null, age=0, address=北京] * */ } }
4 动态代理
现在有一个StudentDao接口和UserDao接口以及两个接口的实现类
package d11; public interface StudentDao { public abstract void login(); public abstract void register(); }
package d11; public interface UserDao { public abstract void add(); public abstract void modify(); public abstract void find(); public abstract void delete(); }
package d11; public class StudentDaoImp implements StudentDao { @Override public void login() { System.out.println("登录事件"); } @Override public void register() { System.out.println("注册事件"); } }
package d11; public class UserDaoImp implements UserDao { @Override public void add() { System.out.println("添加事件"); } @Override public void modify() { System.out.println("修改事件"); } @Override public void find() { System.out.println("查询事件"); } @Override public void delete() { System.out.println("删除事件"); } }
一般的使用方式
package d11; import java.io.FileNotFoundException; import java.io.FileReader; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.util.Properties; public class TestDemo { public static void main(String[] args) throws Exception { StudentDaoImp s = new StudentDaoImp(); s.login(); s.register(); /*输出结果 登录事件 注册事件 */ //通过动态代理,需要创建一个动态代理对象 } }
现在如果想在方法调用前加一个权限判断,以及在方法调用后做一个日志记录.而且相对User等类也添加同样的功能,可以通过动态代理实现.
若实现动态代理,可借助Proxy类中的newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)方法
若借助Proxy类中的newProxyInstance 需要一个参数InvocationHandler.所以县创建一个子类对象实现该接口
package d11; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; public class MyInvocationHandler implements InvocationHandler { private Object target; public MyInvocationHandler(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //重写invoke方法,需要一个目标 (添加一个构造方法) System.out.println("验证权限"); Object result = method.invoke(target, args); System.out.println("日志记录"); return result; } }
package d11; import java.io.FileNotFoundException; import java.io.FileReader; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.Properties; public class TestDemo { public static void main(String[] args) throws Exception { StudentDaoImp s = new StudentDaoImp(); s.login(); s.register(); /*输出结果 登录事件 注册事件 */ System.out.println("---------"); //若借助Proxy类中的newProxyInstance 需要一个参数InvocationHandler MyInvocationHandler handler = new MyInvocationHandler(s); StudentDao sp = (StudentDao)Proxy.newProxyInstance(s.getClass().getClassLoader(), s.getClass().getInterfaces(), handler); sp.login(); sp.register(); System.out.println("---------"); //同理,调用其他的类 UserDaoImp u = new UserDaoImp(); MyInvocationHandler handler2 = new MyInvocationHandler(u); UserDao up = (UserDao)Proxy.newProxyInstance(u.getClass().getClassLoader(), u.getClass().getInterfaces(), handler2); up.add(); /*输出结果 登录事件 注册事件 --------- 验证权限 登录事件 日志记录 验证权限 注册事件 日志记录 --------- 验证权限 添加事件 日志记录 */ } }