Java基础知识(20)- Java 反射机制 (二) | 执行方法(访问方法)、访问数组、操作泛型(Generics)
1. 通过反射执行方法(访问方法)
要动态获取一个对象方法的信息,首先需要通过下列方法之一创建一个 Method 类型的对象或者数组。
getMethods()
getMethods(String name,Class<?> …parameterTypes)
getDeclaredMethods()
getDeclaredMethods(String name,Class<?>...parameterTypes)
如果是访问指定的构造方法,需要根据该方法的入口参数的类型来访问。例如,访问一个名称为 max,入口参数类型依次为 int 和 String 类型的方法。
下面的两种方式均可以实现:
objectClass.getDeclaredConstructor("max",int.class,String.class);
objectClass.getDeclaredConstructor("max",new Class[]{int.class,String.class});
Method 类的常用方法如表所示。
| 静态方法名称 | 说明 |
| getName() | 获取该方法的名称 |
| getParameterType() | 按照声明顺序以 Class 数组的形式返回该方法各个参数的类型 |
| getReturnType() | 以 Class 对象的形式获得该方法的返回值类型 |
| getExceptionTypes() | 以 Class 数组的形式获得该方法可能抛出的异常类型 |
| invoke(Object obj,Object...args) | 利用 args 参数执行指定对象 obj 中的该方法,返回值为 Object 类型 |
| isVarArgs() | 查看该方法是否允许带有可变数量的参数,如果允许返回 true,否则返回 false |
| getModifiers() | 获得可以解析出该方法所采用修饰符的整数 |
实例:
1 import java.lang.reflect.Method; 2 3 public class App { 4 public static void main( String[] args ) { 5 6 // 通过反射执行方法(访问方法) 7 BookMethod bookMethod = new BookMethod(); 8 Class bookMethodClass = bookMethod.getClass(); 9 10 Method[] declaredMethods = bookMethodClass.getDeclaredMethods(); 11 12 for (int i = 0; i < declaredMethods.length; i++) { 13 14 Method method = declaredMethods[i]; 15 16 System.out.println("方法名称 -> " + method.getName()); 17 18 System.out.print("参数类型 -> ( "); 19 Class[] parameterTypes = method.getParameterTypes(); 20 for (int j = 0; j < parameterTypes.length; j++) { 21 System.out.print(parameterTypes[j]); 22 if (j < parameterTypes.length-1) 23 System.out.print(", "); 24 } 25 System.out.print(" )\n"); 26 27 System.out.println("是否带可变数量参数 -> " + method.isVarArgs()); 28 System.out.println("返回值类型 -> " + method.getReturnType()); 29 30 System.out.print("可能抛出的异常类型 -> ( "); 31 32 Class[] methodExceptions = method.getExceptionTypes(); 33 for (int j = 0; j < methodExceptions.length; j++) { 34 System.out.print(methodExceptions[j]); 35 if (j < methodExceptions.length-1) 36 System.out.print(", "); 37 } 38 System.out.print(" )\n"); 39 40 // 方法的访问权限为 private,会拋出异常; 41 // 或调用 setAccessible(true) 设置允许访问 private 成员 42 //method.setAccessible(true); 43 44 try { 45 if (method.getName().equals("publicMethod")) { 46 System.out.println("publicMethod(1) = " + method.invoke(bookMethod, 1)); 47 } else if (method.getName().equals("protectedMethod")) { 48 System.out.println("protectedMethod(\"3\", 8) = " + method.invoke(bookMethod, "3", 8)); 49 } else if (method.getName().equals("privateMethod")) { 50 Object[] parameters = new Object[] { new String[] { "T", "E", "S", "T" } }; 51 System.out.println("privateMethod(...) = " + method.invoke(bookMethod, parameters)); 52 } else { 53 method.invoke(bookMethod); 54 } 55 } catch (Exception e) { 56 //e.printStackTrace(); 57 System.out.println("调用该方法失败:error -> " + e.getMessage()); 58 } 59 60 System.out.println("----------------------------------------------"); 61 } 62 } 63 } 64 65 class BookMethod { 66 67 // 默认访问权限方法 68 void defaultMethod() { 69 System.out.println("执行 defaultMethod() 方法"); 70 } 71 72 // 默认访问权限 static 方法 73 static void defaultStaticMethod() { 74 System.out.println("执行 defaultStaticMethod() 方法"); 75 } 76 77 // public 方法 78 public int publicMethod(int i) { 79 System.out.println("执行 publicMethod() 方法"); 80 return 100 + i; 81 } 82 83 // protected 方法 84 protected int protectedMethod(String s, int i) throws NumberFormatException { 85 System.out.println("执行 protectedMethod() 方法"); 86 return Integer.valueOf(s) + i; 87 } 88 89 // private 方法 90 private String privateMethod(String... args) { 91 System.out.println("执行 privateMethod() 方法"); 92 StringBuffer sb = new StringBuffer(); 93 for (int i = 0; i < args.length; i++) { 94 sb.append(args[i]); 95 } 96 return sb.toString(); 97 } 98 99 }
输出:
方法名称 -> privateMethod
参数类型 -> ( class [Ljava.lang.String; )
是否带可变数量参数 -> true
返回值类型 -> class java.lang.String
可能抛出的异常类型 -> ( )
调用该方法失败:error -> Class com.example.ReflectionApp can not access a member of class com.example.BookMethod with modifiers "private transient"
----------------------------------------------
方法名称 -> protectedMethod
参数类型 -> ( class java.lang.String, int )
是否带可变数量参数 -> false
返回值类型 -> int
可能抛出的异常类型 -> ( class java.lang.NumberFormatException )
执行 protectedMethod() 方法
protectedMethod("3", 8) = 11
----------------------------------------------
方法名称 -> publicMethod
参数类型 -> ( int )
是否带可变数量参数 -> false
返回值类型 -> int
可能抛出的异常类型 -> ( )
执行 publicMethod() 方法
publicMethod(1) = 101
----------------------------------------------
方法名称 -> defaultMethod
参数类型 -> ( )
是否带可变数量参数 -> false
返回值类型 -> void
可能抛出的异常类型 -> ( )
执行 defaultMethod() 方法
----------------------------------------------
方法名称 -> defaultStaticMethod
参数类型 -> ( )
是否带可变数量参数 -> false
返回值类型 -> void
可能抛出的异常类型 -> ( )
执行 defaultStaticMethod() 方法
2. 通过反射访问数组
java.lang.reflect.Array 类提供静态方法来动态创建和访问Java数组。
实例:
1 import java.lang.reflect.Field; 2 3 public class App { 4 public static void main( String[] args ) { 5 6 // 通过反射访问数组 7 Class<?> classType = null; 8 Object newArray = null; 9 try { 10 classType = Class.forName("java.lang.String"); 11 newArray = Array.newInstance(classType, 3); 12 } catch (Exception e) { 13 //e.printStackTrace(); 14 System.out.println("Create array instance failed: " + e.getMessage()); 15 return; 16 } 17 Array.set(newArray, 0, "Country"); 18 Array.set(newArray, 1, "Province"); 19 Array.set(newArray, 2, "City"); 20 21 printArray(newArray); 22 printArray("It is string"); 23 24 } 25 26 private static void printArray(Object obj) { 27 Class cls = obj.getClass(); 28 // 判断是否是数组 29 if (cls.isArray()) { 30 int arrLen = Array.getLength(obj); 31 for (int i = 0; i < arrLen; i++){ 32 System.out.println("Array(" + cls.getComponentType().getName() + ") : " + Array.get(obj, i)); 33 } 34 }else { 35 // 不是数组,直接输出 36 System.out.println(obj); 37 } 38 } 39 }
输出:
Array(java.lang.String) : Country
Array(java.lang.String) : Province
Array(java.lang.String) : City
It is string
3. 通过反射操作泛型(Generics)
Java 5 开始增加了 ParameterizedType, GenericArrayType, TypeVariable 和 WildcardType 几种类型,在 java.lang.reflect 包。
ParameterizedType: 表示一种参数化的类型,比如Collection< String >
GenericArrayType: 表示一种元素类型是参数化类型或者类型变量的数组类型
TypeVariable: 是各种类型变量的公共父接口
WildcardType: 代表一种通配符类型表达式,比如?、? extends Number、? super Integer。(wildcard是一个单词:就是”通配符“)
实例:
1 import java.lang.reflect.Method; 2 import java.lang.reflect.ParameterizedType; 3 import java.lang.reflect.Type; 4 import java.util.List; 5 import java.util.Map; 6 7 public class App { 8 public static void main(String[] args) { 9 10 try { 11 // 获得指定方法参数泛型信息 12 Method method = Demo.class.getMethod("testMethod01", Map.class, List.class); 13 Type[] paramTypes = method.getGenericParameterTypes(); 14 15 System.out.println("testMethod01() 的参数类型"); 16 for (Type t : paramTypes) { 17 if(t instanceof ParameterizedType){ 18 Type[] genericTypes = ((ParameterizedType) t).getActualTypeArguments(); 19 for (Type genericType : genericTypes) { 20 System.out.println("参数类型 -> " + genericType); 21 } 22 } 23 } 24 25 System.out.println("testMethod02() 的返回值类型"); 26 Method method2 = Demo.class.getMethod("testMethod02", null); 27 Type returnType = method2.getGenericReturnType(); 28 if(returnType instanceof ParameterizedType){ 29 Type[] genericTypes = ((ParameterizedType) returnType).getActualTypeArguments(); 30 31 for (Type genericType : genericTypes) { 32 System.out.println("返回值类型 -> "+genericType); 33 } 34 } 35 36 } catch (Exception e) { 37 e.printStackTrace(); 38 } 39 } 40 } 41 42 class Demo { 43 44 public void testMethod01(Map<String, Demo> map, List<Demo> list){ 45 System.out.println("Demo.testMethod01()"); 46 } 47 48 public Map<Integer,Demo> testMethod02(){ 49 System.out.println("Demo.testMethod02()"); 50 return null; 51 } 52 }
输出:
testMethod01() 的参数类型
参数类型 -> class java.lang.String
参数类型 -> class com.example.Demo
参数类型 -> class com.example.Demo
testMethod02() 的返回值类型
返回值类型 -> class java.lang.Integer
返回值类型 -> class com.example.Demo
浙公网安备 33010602011771号