一,反射
1.Class类
源文件(T.java)经过编译后生成类文件,实例化该类生成对象运行;java运行时系统为会所有的对象维护一个被称为“运行时”的类型标记,跟踪着每一个对象所属的类,保存着对象所属类的信息,而用于保存这些信息的类被称为Class。
每一个Class对象都是一个保存对象所属的类信息的类文件,每一个类文件的类型是Class。
- 例:类型为T的对象的类文件是T.class,T.class是一个对象,其类型为Class。(体现了Java“万物皆对象,物(对象)以类聚”的思想:T.class类文件也是一个对象,其类型为Class)
获取一个对象T的类文件T.class的三种方法:
//方式一:对象的getClass()方法 Class cl = new Date().getClass(); //方式二:Class的静态方法forName(String)根据类名加载,若String不是类名或接口名,出现异常 Class cl = Class.forName("java.util.Date"); //方式三:类名.class Class cl = Date.class;
Class对象的newInstance()方法可以创建Class对象所指类的实例
//无参对象 Class.forName("java.util.Date").newInstance();
注意:使用newInstance()要求Class对象所表示的类必须有空参数的构造函数,若没有空参构造,则可以获取构造函数的Constructor对象创建带参对象。
public class UserRef { public static void main(String[] args) throws Exception { //使用forName,传入类型名用.分隔包名 Class<User> cl = (Class<User>) Class.forName("com.common.User"); createInstance(cl); } //测试利用Class对象创建带参的实例 public static void createInstance(Class<User> clazz) throws Exception{ //获取构造函数,注意参数类型可以使用int.class==Integer.TYPE,但不能使用Integer.class Constructor<User> cons = clazz.getConstructor(Integer.TYPE, String.class); Object[] initargs = new Object[]{20, "hi"}; //初始化参数 User u = cons.newInstance(initargs); System.out.println(u); } } //User对象 package com.common; public class User { private int id; private String name; public User(int id, String name) { super(); this.id = id; this.name = name; } //setters and getters }
基础数据类型与其包装类的关系:
基础类型.class == 包装类.TYPE,但不等于包装类.classpublic void testBasicType(){ sop(Integer.TYPE == int.class); //true sop(Integer.class == int.class); //false sop(Double.TYPE == double.class); //true sop(Double.class == double.class); //false sop(Character.TYPE == char.class); //true sop(Character.class == char.class); //false sop(Boolean.TYPE == boolean.class); //true sop(Boolean.class == boolean.class); //false }
2.利用反射分析类的能力
利用反射可以获取一个运行对象的所有属性:
- 类修饰符
- 属性的修饰符、名称等
- 构造方法的修饰符、名称、参数类型、返回类型等
- 通用方法的修饰符、名称、参数类型、返回类型等
实例:
//打印该类的所有构造函数 public static void printConstructors(Class cl){ //返回cl.class的所有构造函数 Constructor[] constructors = cl.getDeclaredConstructors(); for(Constructor c : constructors){ //获得构造函数类型 String name = c.getName(); //获得构造函数修饰符 String modifiers = Modifier.toString(c.getModifiers()); System.out.print(" " + modifiers + " " + name + " ("); //获得构造函数所有形参,也是class对象 Class[] types = c.getParameterTypes(); for(int j = 0; j < types.length; j++){ //列出所有形参 if(j > 0) System.out.print(" ,"); String tname = types[j].getName(); System.out.print(tname); } System.out.println(")"); } } //打印所有方法 public static void printMethods(Class cl) { Method[] methods = cl.getDeclaredMethods(); for(Method m : methods){ //打印方法的修饰符、返回类型、方法名、参数 String mname = m.getName(); Class returntype = m.getReturnType(); String modifiers = Modifier.toString(m.getModifiers()); System.out.print(" "+modifiers+" "+ returntype.getName() + " " + mname + " ("); //参数 Class[] paramTypes = m.getParameterTypes(); for(int j = 0; j < paramTypes.length; j++){ //列出所有形参 if(j > 0) System.out.print(" ,"); String tname = paramTypes[j].getName(); System.out.print(tname); } System.out.println(")"); } } //打印所有域 public static void printFields(Class cl){ Field[] fields = cl.getDeclaredFields(); for(Field f : fields){ //打印域的修饰符、名称 String name = f.getName(); String modifiers = Modifier.toString(cl.getModifiers()); System.out.println("\t"+ modifiers + " "+ name); } }
3. 运行时利用反射分析对象
通过反射可以获取类中的域和方法等属性,而且利用反射也可以查看该类对象的域值。
查看对象域值的关键方法是Field类的get方法;
Employee harry = new Employee("harry potter", 3500, 10, 1, 1988); Class cl = harry.getClass(); Field f = cl.getDeclaredField("name"); f.setAccessible(true); Object v = f.get(harry); //harry potter f.set(harry, "john wick"); System.out.println(v);
- f是一个Field类型的对象,obj是某个包含f域的类的对象,f.get(obj)将返回obj对象中的f域的值,其类型为Object。
- 调用f.set(obj, newvalue)可以将obj对象中f域值设为新值newValue。
4. 反射编写泛型数组
Arrays.copyOf(数组变量,新长度)可以动态地扩展已经填满的数组。
Employee[] e = new Employee[100]; ... e = Arrays.copyOf(e, 200);
利用反射可以编写一个类似的扩展数组操作
//传入数组对象和长度 public static Object goodCopyOf(Object a, int newLength){ Class cl = a.getClass(); if(!cl.isArray()) return null; //非数组返回空 //获取数组元素类型 Class componentType = cl.getComponentType(); //根据长度扩展数组 int length = Array.getLength(a); //原长度 Object newArray = Array.newInstance(componentType, newLength); //将原来数组数据复制到新数组数据 System.arraycopy(a, 0, newArray, 0, Math.min(newLength, length)); return newArray; } //使用 e = (Employee)goodCopyOf(e, 200);
注意使用了Array.newInstance(componentType, newLength)创建一个长度为newLength,数组元素类型为componentType的新数组。
4. 调用任意方法
Field对象的get方法可以查看域所在类的对象中的域值,Method也有一个invoke方法,允许调用封装在当前Method所在对象的该方法。签名:
Object invoke(Object obj, Object… args);
第一个参数obj需传入method所在对象,若方法是静态的,可以设为null
第二个参数args需要传入调用该方法所需的参数值,若方法为无参,可以不传。实例:
public class MethodPointerTest { public static void main(String[] args) throws Exception { Class cl = MethodPointerTest.class; //当前类的类文件对象 Method square = cl.getMethod("square", double.class); //获取cl类中名称为square,参数类型为double的Method对象 //获取Math类中名称为sqrt,参数类型为double的Method对象 Method sqrt = Math.class.getMethod("sqrt", double.class); printTableMethod(1, 10, 10, square); printTableMethod(1, 10, 10, sqrt); } public static double square(double x){ return x * x; } public static void printTableMethod(double f, double t, int n, Method m) throws Exception{ //调用m方法,从m(f)到m(t)打印n个函数数据 double s = (t - f) / (n - 1); for(double x = f; x <= t; x += s){ //invoke调用m方法,计算x double y = (Double)m.invoke(null, x); System.out.printf("%10.4f | %10.4f%n", x, y); } } }
二,动态代理
当在程序运行时,需要创建实现了某些接口的对象,但不清楚这些接口具体是什么(真正需要调用时才能确定这些接口),此时即可使用动态代理的方式生成实现具体接口的对象。方式:
Object proxy = Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
第一个参数:类加载器,使用null作为默认的类加载器
第二个参数:需要实现的具体接口的Class对象集合
第三个参数:调用处理器,实现了InvocationHandler接口的对象。调用处理器:不仅包装了需要代理的对象(构造方法传入),而且还说明了对接口中的方法的调用方式:一切对接口中的方法的调用都由调用处理器中的invoke方法完成。
具体流程:一个对象obj需要实现某些接口,但不确定,可以将obj传入调用处理器InvocationHandler,再由Proxy.newProxyInstance生成obj对象的代理对象proxy,对proxy中接口方法的调用都由invoke方法实现。常用于:对已实现了接口的对象完成接口方法的覆盖。
示例:动态代理生成一个实现了Comparable接口的Integer对象,并对Comparable接口的CompareTo方法覆盖,改写。
public class ProxyTest { public static void main(String[] args) { Object[] elements = new Object[1000]; for(int i = 0; i < elements.length; i++){ //需代理的对象 Integer value = i + 1; //调用处理器 TraceHandler handler = new TraceHandler(value); //生成代理对象 Object proxy = Proxy.newProxyInstance(null, new Class[]{Comparable.class}, handler); //数组存储代理对象 elements[i] = proxy; } //随机取一个整数 Integer key = new Random().nextInt(elements.length) + 1; //二分查找 int result = Arrays.binarySearch(elements, key); if(result > 0) System.out.println(elements[result]); } } public class TraceHandler implements InvocationHandler{ private Object target; //需代理的对象 public TraceHandler(Object obj) { //存储包装对象 this.target = obj; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 对接口中方法的具体调用都由invoke实现 System.out.print(target); System.out.print("."+method.getName()+" ("); //打印参数 if(args != null){ for(int i = 0; i < args.length; i++){ System.out.print(args[i]); if(i < args.length - 1) System.out.print(", "); } } System.out.println(")"); //方法正常调用,注意具体实现是由需要代理的对象target完成 return method.invoke(target, args); } }