JDK1.7新特性(4):java语言动态性之反射API
直接通过一个代码示例来熟悉java中通过反射来对构造函数/域以及方法处理的相关API:
1 package com.rampage.jdk7.chapter2; 2 3 import java.lang.reflect.Array; 4 import java.lang.reflect.Constructor; 5 import java.lang.reflect.Field; 6 import java.lang.reflect.InvocationTargetException; 7 import java.lang.reflect.Method; 8 9 import com.rampage.jdk7.chapter2.NonStaticNestedClass.NestedClass; 10 11 /** 12 * 这是java反射API的程序代码示例,其中包括java7以及java7之前的一些API 13 * @author zyq 14 * 15 */ 16 public class ReflectionAPI { 17 public static void main(String[] args) throws Exception { 18 ReflectionAPI api = new ReflectionAPI(); 19 api.testConstructors(); 20 api.testFields(); 21 api.testMethods(); 22 api.testArrays(); 23 } 24 25 /** 26 * 使用java.lang.reflect.Array来操作和创建数组 27 */ 28 private void testArrays() { 29 // 这个包下的Array类提供了一个newInstance方法来创建新的数组,第一个参数是数组中的元素类型,后面的参数是数组的维度信息 30 // 创建一个String类型的长度为10的数组 31 String[] names = (String[]) Array.newInstance(String.class, 10); 32 names[0] = "KiDe"; 33 // 设置数组中下标为1的元素的值 34 Array.set(names, 1, "Kid"); 35 36 // 创建两个三维数组 37 int[][][] matrix1 = (int[][][]) Array.newInstance(int.class, 3, 3, 3); 38 matrix1[0][0][0] = 12; 39 int[][][] matrix2 = (int[][][]) Array.newInstance(int[].class, 3, 4); // 这个其实也是三维数组,因为数组的类型是int[] 40 matrix2[0][0][0] = 11; 41 matrix2[0][1] = new int[3]; 42 43 44 } 45 46 /** 47 * 反射获取构造方法的测试例子 48 */ 49 @SuppressWarnings("unchecked") 50 private void testConstructors() { 51 try { 52 // Part1: 测试getConstructors方法,直接调用该方法,不会返回父类的构造方法。只会返回当前类的public类型的构造函数。 53 Constructor<Child>[] childConstructors = (Constructor<Child>[]) Child.class.getConstructors(); 54 /** 55 * 输出为: 56 com.rampage.jdk7.chapter2.Child 57 [Ljava.lang.reflect.Parameter;@15db9742 58 1 59 */ 60 for (Constructor<Child> constructor : childConstructors) { 61 System.out.println(constructor.getName()); 62 System.out.println(constructor.getParameters()); 63 System.out.println(constructor.getParameterCount()); 64 } 65 66 Constructor<Parent>[] parentConstructors = (Constructor<Parent>[]) Parent.class.getConstructors(); 67 /** 68 * 输出为: 69 com.rampage.jdk7.chapter2.Parent 70 [Ljava.lang.Class;@6d06d69c 71 1 72 com.rampage.jdk7.chapter2.Parent 73 [Ljava.lang.Class;@7852e922 74 0 75 发现其实不管该构造函数是否有参数getParameterTypes或者getParameters都会返回值 76 */ 77 for (Constructor<Parent> constructor : parentConstructors) { 78 System.out.println(constructor.getName()); 79 System.out.println(constructor.getParameterTypes()); 80 System.out.println(constructor.getParameterCount()); 81 } 82 Parent.class.getConstructor(); 83 Parent.class.getConstructor(double.class); 84 // Parent.class.getConstructor(void.class); 会抛出异常,当构造函数没有参数的时候直接不传即可,而不能传void.class 85 // Parent.class.getConstructor(int.class); 会抛出异常,因为以int为参数的构造函数是不存在的 86 87 // Part2: 测试getDeclaredConstruct,该方法能够得到当前类的所有构造函数,不管是public/protected还是private。 88 childConstructors = (Constructor<Child>[]) Child.class.getDeclaredConstructors(); 89 /** 90 * 输出为: 91 com.rampage.jdk7.chapter2.Child 92 [Ljava.lang.reflect.Parameter;@4e25154f 93 1 94 com.rampage.jdk7.chapter2.Child 95 [Ljava.lang.reflect.Parameter;@70dea4e 96 1 97 */ 98 for (Constructor<Child> constructor : childConstructors) { 99 System.out.println(constructor.getName()); 100 System.out.println(constructor.getParameters()); 101 System.out.println(constructor.getParameterCount()); 102 // 私有的构造函数实例化对象的时候需要先调用 setAccessible(true),否则会报权限错误。 103 } 104 105 // Part3: 参数长度可变的构造函数 106 // 获取构造函数的时候指定构造函数的参数应当当作数组来指定 107 Constructor<VaragsConstructor> constructor = VaragsConstructor.class.getDeclaredConstructor(int[].class); 108 System.out.println(constructor.getName()); // com.rampage.jdk7.chapter2.VaragsConstructor 109 // 利用得到的Constructor创建对象的实例时,可以把传入的参数转换成Object类型,也可以使用参数类型的数组 110 VaragsConstructor obj = constructor.newInstance((Object) (new int[] {1, 2, 3, 4})); 111 obj = constructor.newInstance(new int[] {1, 2, 3, 4}); 112 113 // Part4: 嵌套类的构造方法 114 // 嵌套类需要区分静态嵌套类和非静态嵌套类,对于静态嵌套类,获得其构造函数的方法和其他的非嵌套类没有区别,对于非静态的嵌套类, 115 // 因为必须先初始化外部类才能使用非静态的嵌套类, 因此在获取构造函数以及使用得到的构造函数实例化对象的时候第一个参数必须是外部类class和外部类实例 116 Constructor<NestedClass> constructor2 = NestedClass.class.getDeclaredConstructor(NonStaticNestedClass.class, int.class); 117 NonStaticNestedClass object = new NonStaticNestedClass(); 118 constructor2.newInstance(object, 12); 119 } catch (ReflectiveOperationException e) { // 在java6中各种反射的异常需要逐个捕获。而java7提供了一个新的异常类: ReflectiveOperationException,该类可以捕获所有反射操作的异常(因为他是所有反射操作异常的父类) 120 System.out.println(e.getCause()); // 得到异常的具体信息 121 } 122 123 } 124 125 /** 126 * 反射进行域处理的例子 127 */ 128 private void testFields() { 129 // 同样测试getField和getDeclareField的区别 130 Field[] fields = Child.class.getFields(); 131 System.out.println(); 132 /** 133 * 输出为: 134 field3 135 field3 136 可以看出这时候获得了父和子中所有的public的field 137 */ 138 for (Field field : fields) { 139 System.out.println(field.getName()); 140 } 141 142 fields = Child.class.getDeclaredFields(); 143 System.out.println(); 144 /** 145 * 输出为: 146 field0 147 field1 148 field2 149 field3 150 可以看出此时输出了自己本身这个类中的所有域而不仅仅是public的 151 */ 152 for (Field field : fields) { 153 System.out.println(field.getName()); 154 // 给私有的域设置值需要先调用 setAccessible(true),否则会报权限错误。 155 } 156 } 157 158 private void testMethods() throws IllegalAccessException, IllegalArgumentException, InvocationTargetException { 159 Method[] methods = Child.class.getMethods(); 160 System.out.println(); 161 /** 162 * 输出为: 163 parentMethod 164 wait 165 wait 166 wait 167 equals 168 toString 169 hashCode 170 getClass 171 notify 172 notifyAll 173 可以看出把所有继承层次中(包括Object)的public方法都或得到了 174 */ 175 for (Method method : methods) { 176 System.out.println(method.getName()); 177 } 178 179 methods = Child.class.getDeclaredMethods(); 180 System.out.println(); 181 /** 182 * 输出为: 183 ChildMethod 184 只是获得了当前类中的所有方法 185 */ 186 for (Method method : methods) { 187 System.out.println(method.getName()); 188 // 对于私有的method如果直接调用会抛出异常但是通过 method.setAccessible(true)就可以解决这一问题。 189 } 190 } 191 192 /** 193 * 用反射实现的设置某个属性值的方法 194 * 通过反射API,java也可以应用在灵活性很高的场景中,最长见的Servlet中http请求参数值的填充。 195 * 虽然随着java虚拟机性能的改进,反射API的性能有所提升。但是反射方法和非反射方法的性能差距还是客观存在的。 196 * 因此在一些性能要求很高的场景中要慎用反射API或者将常调用的反射获得的方法先缓存起来。 197 */ 198 void invokeSetter(Object obj, String field, Object value) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { 199 String methodName = "set" + field.substring(0, 1).toUpperCase() + field.substring(1); 200 Class<?> clazz = obj.getClass(); 201 Method setMethod = clazz.getMethod( methodName, value.getClass()); 202 setMethod.invoke(obj, value); 203 } 204 } 205 206 class Parent { 207 String field0; 208 private String field1; 209 protected String field2; 210 public String field3; 211 212 public Parent(double a) {} 213 public Parent() {} 214 public void parentMethod() {} 215 } 216 217 class Child extends Parent { 218 String field0; 219 private String field1; 220 protected String field2; 221 public String field3; 222 223 // 如果父类没有同样的构造函数,则默认会调用父类的无参构造函数,如果父类没有无参构造函数,则编译会报错,会要求显示调用父类的某一个构造函数,以确保父类会在子类被实例化之前被实例化。 224 public Child(int i) {} 225 private Child(long l) {} 226 private void ChildMethod() {} 227 } 228 229 class VaragsConstructor { 230 public VaragsConstructor(int... varags) {}; 231 } 232 233 class NonStaticNestedClass { 234 class NestedClass { 235 NestedClass(int i) {} 236 } 237 }
黎明前最黑暗,成功前最绝望!