反射常被用于在程序运行时检查或修改其执行行为。
为什么要使用反射 反射可以在下面几点帮助我们
扩展功能扩展已有类的功能,例如有些字段不支持访问或修改,可以使用反射扩展。 类浏览器和可视化开发类浏览器需要枚举类的成员,广泛应用于可视化开发环境 调试和测试工具调试器需要能够检查类的私有成员,测试工具会使用反射调用API。 反射的弊端 反射很强大,但是不要滥用,如果可以避免使用,尽量避免。当使用反射时,下面的问题需要注意。
性能开销反射会动态地处理对象,导致JVM的优化不能进行,因此,要避免在性能敏感地地方使用反射。 安全限制 内部资源暴露反射可以访问private方法,这可能会带来意料之外的副作用。 反射与类 java中的对象不是基本值类型就是引用类型,而所有的引用类型都继承自java.lang.Object
。
Java虚拟机为每个类生成一个不可变的java.lang.Class
的类实例,该类实例提供了对象属性的运行期检查,包括了对象的成员和类型信息。java.lang.Class
是所有反射操作的入口。
如何获取类实例 java.lang.Class
是所有反射操作的入口。需要调用java.lang.Class
的对应方法,以获取类实例。
Object.getClass()
如果可以获取到一个对象的实例,最简单的获取类实例的方法是调用该对象的getClass()
方法。注意,这只是用于引用类型(getClass()
方法继承自Object
)。下面是几个例子:
1 Class c = "foo" .getClass();
1 2 enum E {A,B}Class c= A.getClass();
1 2 Set<String> s = new HashSet <String>(); Class c = s.getClass();
1 2 byte [] bytes = new byte [1024 ];Class c = bytes.getClass();
.class
语法如果我们可以获得类型但是无法获取对应的对象,那么可以使用.class
语法来获得类实例。这同时解决了基本值类型的类实例获取方法。
1 2 boolean b;Class c = boolean .class;
1 Class c = java.io.PrintStream.class;
1 Class c = int [][][].class;
Class.forName()
如果可以获得一个类的fully-qualified name(包名+类名),那么可以通过static方法Class.forName()
来获得对应的类实例。注意,基本值类型不能使用这个方法。
1 Class c = Class.forName("java.lang.String" );
数组类型的Class.forName()
1 Class cDoubleArray = Class.forName("[D" );
对应double[].class.getName()
1 Class cStringArray = Class.forName("[[Ljava.lang.String;" );
对应String[][].class.getName()
基本值类型通过包装类获取类实例 每个基本值类型和void都有自己对应的包装类(java.lang包中),因此可以通过包装类的TYPE
属性来获取对应的封装类的类实例。
1 2 Class c = Double.TYPE;Class c = Void.TYPE;
Class
的其它方法Class.getSuperclass()
返回当前类实例的父类。
Class.getClasses()
返回Class<?>[]
,包含了当前类实例的所有public成员类,接口,枚举(包括继承的成员)的类实例。
1 2 3 4 5 6 7 8 Class<?>[] c = Character.class.getDeclaredClasses();
Class.getDeclaredClasses()
返回Class<?>[]
,包含了当前类实例的所有(public, protected, friendlly, private )成员类,接口,枚举(包括继承的成员)的类实例。
1 2 3 4 5 6 7 8 9 Class<?>[] c = Character.class.getDeclaredClasses();
getDeclaringClass()
java.lang.Class.getDeclaringClass()
java.lang.reflect.Field.getDeclaringClass()
java.lang.reflect.Method.getDeclaringClass()
java.lang.reflect.Constructor.getDeclaringClass()
获取Class,Field,Method,Constructor声明所在类,注意,匿名内部类没有声明所在类。
1 2 3 4 5 6 7 8 9 10 11 Field f = System.class.getField("out" );Class c = f.getDeclaringClass();public class MyClass { static Object o1 = new Object () { public void m () {} }; static Class o = o1.getClass().getEnclosingClass(); }
Class.getEnclosingClass()
返回匿名内部类的封装类。
1 2 3 4 5 6 7 public class MyClass { static Object o1 = new Object () { public void m () {} }; static Class o = o1.getClass().getEnclosingClass(); }
类的标识符和类型检查 一个类有一个或多个修饰符,这些修饰符会影响运行期行为。
访问权限修饰符public protected friendly(默认包权限,也被称作default) private 重载修饰符 静态修饰符 不可修改修饰符 强制严格浮点操作修饰符 注解 java.lang.reflect.Modifer
包含了所有可能的修饰符的声明,也包含了解析Class.getModifiers()
返回值的方法。
获取修饰符的例子 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 import java.lang.annotation.Annotation;import java.lang.reflect.Modifier;import java.lang.reflect.Type;import java.lang.reflect.TypeVariable;import java.util.ArrayList;import java.util.List;import static java.lang.System.out;public class ClassDeclarator { public static void main (String... args) { try { Class<?> c = Class.forName(args[0 ]); out.format("Class:%n %s%n%n" , c.getCanonicalName()); out.format("Modifiers:%n %s%n%n" ,Modifier.toString(c.getModifiers())); out.format("Type Parameters:%n" ); TypeVariable[] tv = c.getTypeParameters(); if (tv.length != 0 ) { out.format(" " ); for (TypeVariable t : tv) out.format("%s " , t.getName()); out.format("%n%n" ); } else { out.format(" -- No Type Parameters --%n%n" ); } out.format("Implemented Interfaces:%n" ); Type[] intfs = c.getGenericInterfaces(); if (intfs.length != 0 ) { for (Type intf : intfs) out.format(" %s%n" , intf.toString()); out.format("%n" ); } else { out.format(" -- No Implemented Interfaces --%n%n" ); } out.format("Inheritance Path:%n" ); List<Class> l = new ArrayList <Class>(); printAncestor(c, l); if (l.size() != 0 ) { for (Class<?> cl : l) out.format(" %s%n" , cl.getCanonicalName()); out.format("%n" ); } else { out.format(" -- No Super Classes --%n%n" ); } out.format("Annotations:%n" ); Annotation[] ann = c.getAnnotations(); if (ann.length != 0 ) { for (Annotation a : ann) out.format(" %s%n" , a.toString()); out.format("%n" ); } else { out.format(" -- No Annotations --%n%n" ); } } catch (ClassNotFoundException x) { x.printStackTrace(); } } private static void printAncestor (Class<?> c, List<Class> l) { Class<?> ancestor = c.getSuperclass(); if (ancestor != null ) { l.add(ancestor); printAncestor(ancestor, l); } } }
探索类的成员 java.lang.Class
中获取fields,methods和constructors的方法分为两种,一种是获取全部成员,将结果放入List;一种是获取名称对应的成员。 另外,根据成员的声明位置,获取成员的方法也分为两种,一种是直接获取类中所有声明的成员,一种是获取类的public成员(包括继承的成员)。
JAVA 使用 java.lang.reflect.Member
接口代表反射使用的类成员。Member成员包含以下几种:
java.lang.reflect.Field
java.lang.reflect.Method
java.lang.reflect.Constructor
field
Class API List of members ? Inherited members ? Private members ? getDeclaredField()
no no yes getField()
no yes no getDeclaredFields()
yes no yes getFields()
yes yes no
method
Class API List of members ? Inherited members ? Private members ? getDeclaredMethod()
no no yes getMethod()
no yes no getDeclaredMethods()
yes no yes getMethods()
yes yes no
constructor
Class API List of members ? Private members ? getDeclaredConstructor()
no yes getConstructor()
no no getDeclaredContructors()
yes yes getContructors()
yes no
获取类的成员的代码示例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 import java.lang.reflect.Constructor;import java.lang.reflect.Field;import java.lang.reflect.Method;import java.lang.reflect.Member;import static java.lang.System.out;enum ClassMember { CONSTRUCTOR, FIELD, METHOD, CLASS, ALL }public class ClassSpy { public static void main (String... args) { try { Class<?> c = Class.forName(args[0 ]); out.format("Class:%n %s%n%n" , c.getCanonicalName()); Package p = c.getPackage(); out.format("Package:%n %s%n%n" , (p != null ? p.getName() : "-- No Package --" )); for (int i = 1 ; i < args.length; i++) { switch (ClassMember.valueOf(args[i])) { case CONSTRUCTOR: printMembers(c.getConstructors(), "Constructor" ); break ; case FIELD: printMembers(c.getFields(), "Fields" ); break ; case METHOD: printMembers(c.getMethods(), "Methods" ); break ; case CLASS: printClasses(c); break ; case ALL: printMembers(c.getConstructors(), "Constuctors" ); printMembers(c.getFields(), "Fields" ); printMembers(c.getMethods(), "Methods" ); printClasses(c); break ; default : assert false ; } } } catch (ClassNotFoundException x) { x.printStackTrace(); } } private static void printMembers (Member[] mbrs, String s) { out.format("%s:%n" , s); for (Member mbr : mbrs) { if (mbr instanceof Field) out.format(" %s%n" , ((Field)mbr).toGenericString()); else if (mbr instanceof Constructor) out.format(" %s%n" , ((Constructor)mbr).toGenericString()); else if (mbr instanceof Method) out.format(" %s%n" , ((Method)mbr).toGenericString()); } if (mbrs.length == 0 ) out.format(" -- No %s --%n" , s); out.format("%n" ); } private static void printClasses (Class<?> c) { out.format("Classes:%n" ); Class<?>[] clss = c.getClasses(); for (Class<?> cls : clss) out.format(" %s%n" , cls.getCanonicalName()); if (clss.length == 0 ) out.format(" -- No member interfaces, classes, or enums --%n" ); out.format("%n" ); } }
反射中的类成员–Field java.lang.reflect.Filed
提供了对特定对象Field的操作,包括获取类型信息,get和set值。
获取Field的类型 返回field声明类型的类实例。
返回field对应的泛型类型。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 import java.lang.reflect.Field;import java.util.List;public class FieldSpy <T> { public boolean [][] b = {{ false , false }, { true , true } }; public String name = "Alice" ; public List<Integer> list; public T val; public static void main (String... args) { try { Class<?> c = Class.forName(args[0 ]); Field f = c.getField(args[1 ]); System.out.format("Type: %s%n" , f.getType()); System.out.format("GenericType: %s%n" , f.getGenericType()); } catch (ClassNotFoundException x) { x.printStackTrace(); } catch (NoSuchFieldException x) { x.printStackTrace(); } } }
获取Field的修饰符 field的声明中会包含部分修饰符:
权限访问修饰符 Field专属运行期修饰符该字段不加入序列化transient
并发写可见 volatile
静态修饰符 static
值不可变修饰符 final
注解 Field.getModifiers()
方法返回不同修饰符的Int值,Modifier
类可以用于解析int值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 import java.lang.reflect.Field;import java.lang.reflect.Modifier;import static java.lang.System.out;enum Spy { BLACK , WHITE }public class FieldModifierSpy { volatile int share; int instance; class Inner {} public static void main (String... args) { try { Class<?> c = Class.forName(args[0 ]); int searchMods = 0x0 ; for (int i = 1 ; i < args.length; i++) { searchMods |= modifierFromString(args[i]); } Field[] flds = c.getDeclaredFields(); out.format("Fields in Class '%s' containing modifiers: %s%n" , c.getName(), Modifier.toString(searchMods)); boolean found = false ; for (Field f : flds) { int foundMods = f.getModifiers(); if ((foundMods & searchMods) == searchMods) { out.format("%-8s [ synthetic=%-5b enum_constant=%-5b ]%n" , f.getName(), f.isSynthetic(), f.isEnumConstant()); found = true ; } } if (!found) { out.format("No matching fields%n" ); } } catch (ClassNotFoundException x) { x.printStackTrace(); } } private static int modifierFromString (String s) { int m = 0x0 ; if ("public" .equals(s)) m |= Modifier.PUBLIC; else if ("protected" .equals(s)) m |= Modifier.PROTECTED; else if ("private" .equals(s)) m |= Modifier.PRIVATE; else if ("static" .equals(s)) m |= Modifier.STATIC; else if ("final" .equals(s)) m |= Modifier.FINAL; else if ("transient" .equals(s)) m |= Modifier.TRANSIENT; else if ("volatile" .equals(s)) m |= Modifier.VOLATILE; return m; } }
Field.isSynthetic()
isSynthetic()
方法可以用来判断field是否是编译器生成的用于内部使用的属性.例如,内部类(不包括静态内部类)中会使用字段this$0
指向外部类的对象引用;enums会使用字段$VALUES
来实现static方法values()
。这些synthetic field
被包含在Class.getDeclaredFields()
的返回结果中,但不会被包含在Class.getField()
中,因为这些synthetic field
通常来说不是public的。
内部类this$0
1 2 3 4 5 public class OutClass { public class InnerClass { } }
执行javac
,javap -verbose
命令
生成OutClass.class
,OutClass$InnerClass.class
文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 { final OutClass this$0; //此处是对外部类对象的引用 descriptor: LOutClass; flags: ACC_FINAL, ACC_SYNTHETIC // isSynthetic public OutClass$InnerClass(OutClass); descriptor: (LOutClass;)V flags: ACC_PUBLIC Code: stack=2, locals=2, args_size=2 0: aload_0 1: aload_1 2: putfield #1 // Field this$0:LOutClass; 5: aload_0 6: invokespecial #2 // Method java/lang/Object."<init>":()V 9: return LineNumberTable: line 2: 0 }
Field值的get和set 对于某个给定的对象,可以使用反射来给对象的属性赋值,这通常发生在无法通过正常方法赋值的场景下。注意,通过反射赋值违反了类的设计初衷,所以使用是应当尽可能的慎重。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 import java.lang.reflect.Field;import java.util.Arrays;import static java.lang.System.out;enum Tweedle { DEE, DUM }public class Book { public long chapters = 0 ; public String[] characters = { "Alice" , "White Rabbit" }; public Tweedle twin = Tweedle.DEE; public static void main (String... args) { Book book = new Book (); String fmt = "%6S: %-12s = %s%n" ; try { Class<?> c = book.getClass(); Field chap = c.getDeclaredField("chapters" ); out.format(fmt, "before" , "chapters" , book.chapters); chap.setLong(book, 12 ); out.format(fmt, "after" , "chapters" , chap.getLong(book)); Field chars = c.getDeclaredField("characters" ); out.format(fmt, "before" , "characters" , Arrays.asList(book.characters)); String[] newChars = { "Queen" , "King" }; chars.set(book, newChars); out.format(fmt, "after" , "characters" , Arrays.asList(book.characters)); Field t = c.getDeclaredField("twin" ); out.format(fmt, "before" , "twin" , book.twin); t.set(book, Tweedle.DUM); out.format(fmt, "after" , "twin" , t.get(book)); } catch (NoSuchFieldException x) { x.printStackTrace(); } catch (IllegalAccessException x) { x.printStackTrace(); } } }
NOTE: 通过反射进行赋值需要一定数量性能资源,用于前置操作,例如验证访问权限。从运行期的角度来看,结果是相同的,并且操作和在代码中修改的原子性相同。 但是使用反射会失去虚拟机对代码的优化,下面就是个例子,虚拟机会优化,反射却不会。
1 2 3 int x = 1 ;x = 2 ; x = 3 ;
需要注意的Exception 不可转型异常 IllegalArgumentException
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 import java.lang.reflect.Field;public class FieldTrouble { public Integer val; public static void main (String... args) { FieldTrouble ft = new FieldTrouble (); try { Class<?> c = ft.getClass(); Field f = c.getDeclaredField("val" ); f.setInt(ft, 42 ); } catch (NoSuchFieldException x) { x.printStackTrace(); } catch (IllegalAccessException x) { x.printStackTrace(); } } }
对于非反射的表达式Integer val = 42;
编译器会将值类型42装箱成包装类new Integer(42)
。 当使用反射时,编译器不会进行装箱。 要解决上面的问题可以将f.setInt(ft, 42);
改为调用Field.set(Object obj, Object value)
方法。
无权限修改异常 常见于修改private或final属性 IllegalAccessException
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 import java.lang.reflect.Field;public class FieldTroubleToo { public final boolean b = true ; public static void main (String... args) { FieldTroubleToo ft = new FieldTroubleToo (); try { Class<?> c = ft.getClass(); Field f = c.getDeclaredField("b" ); f.setBoolean(ft, Boolean.FALSE); } catch (NoSuchFieldException x) { x.printStackTrace(); } catch (IllegalArgumentException x) { x.printStackTrace(); } catch (IllegalAccessException x) { x.printStackTrace(); } } }
解决方案: 调用f.setAccessible(true);
方法,提供访问权限.
反射中的类成员–Method java.lang.reflect.Method
类提供了用于获取方法修饰符,返回类型,参数,注解和异常的API。同时,还可以通过API来调用方法。
获取方法声明信息 一个方法的声明包含了方法名,修饰符,参数,返回类型和可抛出的异常。下面是一个例子,展示了怎样列举出某个特定类的所有声明方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 import java.lang.reflect.Method;import java.lang.reflect.Type;import static java.lang.System.out;public class MethodSpy { private static final String fmt = "%24s: %s%n" ; <E extends RuntimeException > void genericThrow () throws E {} public static void main (String... args) { try { Class<?> c = Class.forName(args[0 ]); Method[] allMethods = c.getDeclaredMethods(); for (Method m : allMethods) { if (!m.getName().equals(args[1 ])) { continue ; } out.format("%s%n" , m.toGenericString()); out.format(fmt, "ReturnType" , m.getReturnType()); out.format(fmt, "GenericReturnType" , m.getGenericReturnType()); Class<?>[] pType = m.getParameterTypes(); Type[] gpType = m.getGenericParameterTypes(); for (int i = 0 ; i < pType.length; i++) { out.format(fmt,"ParameterType" , pType[i]); out.format(fmt,"GenericParameterType" , gpType[i]); } Class<?>[] xType = m.getExceptionTypes(); Type[] gxType = m.getGenericExceptionTypes(); for (int i = 0 ; i < xType.length; i++) { out.format(fmt,"ExceptionType" , xType[i]); out.format(fmt,"GenericExceptionType" , gxType[i]); } } } catch (ClassNotFoundException x) { x.printStackTrace(); } } }
NOTE: Method.getGenericExceptionTypes()
存在的原因是有可能使用泛型异常类型去声明一个方法,但是该方法很少被使用,因为捕获一个泛型异常是不可能的。
官方说明
获取方法参数 java.lang.reflect.Executable.getParameters
方法可以获取方法或构造器的参数(类Method
和Constructor
都继承了类Executable
的Executable.getParameters
方法)。但是,class文件默认情况下不会存储形参的名称。
要将正式参数名称存储在class文件中,从而使Reflection API能够检索形参名称,请使用编译器选项 -parameters
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 import java.lang.reflect.*;import java.util.function.*;import static java.lang.System.out;public class MethodParameterSpy { private static final String fmt = "%24s: %s%n" ; <E extends RuntimeException > void genericThrow () throws E {} public static void printClassConstructors (Class c) { Constructor[] allConstructors = c.getConstructors(); out.format(fmt, "Number of constructors" , allConstructors.length); for (Constructor currentConstructor : allConstructors) { printConstructor(currentConstructor); } Constructor[] allDeclConst = c.getDeclaredConstructors(); out.format(fmt, "Number of declared constructors" , allDeclConst.length); for (Constructor currentDeclConst : allDeclConst) { printConstructor(currentDeclConst); } } public static void printClassMethods (Class c) { Method[] allMethods = c.getDeclaredMethods(); out.format(fmt, "Number of methods" , allMethods.length); for (Method m : allMethods) { printMethod(m); } } public static void printConstructor (Constructor c) { out.format("%s%n" , c.toGenericString()); Parameter[] params = c.getParameters(); out.format(fmt, "Number of parameters" , params.length); for (int i = 0 ; i < params.length; i++) { printParameter(params[i]); } } public static void printMethod (Method m) { out.format("%s%n" , m.toGenericString()); out.format(fmt, "Return type" , m.getReturnType()); out.format(fmt, "Generic return type" , m.getGenericReturnType()); Parameter[] params = m.getParameters(); for (int i = 0 ; i < params.length; i++) { printParameter(params[i]); } } public static void printParameter (Parameter p) { out.format(fmt, "Parameter class" , p.getType()); out.format(fmt, "Parameter name" , p.getName()); out.format(fmt, "Modifiers" , p.getModifiers()); out.format(fmt, "Is implicit?" , p.isImplicit()); out.format(fmt, "Is name present?" , p.isNamePresent()); out.format(fmt, "Is synthetic?" , p.isSynthetic()); } public static void main (String... args) { try { printClassConstructors(Class.forName(args[0 ])); printClassMethods(Class.forName(args[0 ])); } catch (ClassNotFoundException x) { x.printStackTrace(); } } } import java.util.*; public class ExampleMethods <T> { public boolean simpleMethod (String stringParam, int intParam) { System.out.println("String: " + stringParam + ", integer: " + intParam); return true ; } public int varArgsMethod (String... manyStrings) { return manyStrings.length; } public boolean methodWithList (List<String> listParam) { return listParam.isEmpty(); } public <T> void genericMethod (T[] a, Collection<T> c) { System.out.println("Length of array: " + a.length); System.out.println("Size of collection: " + c.size()); } }
执行 MethodParameterSpy 的输出:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 $ java MethodParameterSpy ExampleMethods Number of constructors: 1 Constructor public ExampleMethods() Number of declared constructors: 1 Declared constructor public ExampleMethods() Number of methods: 4 Method public boolean ExampleMethods.simpleMethod(java.lang.String,int) Return type : boolean Generic return type : boolean Parameter class: class java.lang.String Parameter name: stringParam Modifiers: 0 Is implicit?: false Is name present?: true Is synthetic?: false Parameter class: int Parameter name: intParam Modifiers: 0 Is implicit?: false Is name present?: true Is synthetic?: false Method public int ExampleMethods.varArgsMethod(java.lang.String...) Return type : int Generic return type : int Parameter class: class [Ljava.lang.String; Parameter name: manyStrings Modifiers: 0 Is implicit?: false Is name present?: true Is synthetic?: false Method public boolean ExampleMethods.methodWithList(java.util.List<java.lang.String>) Return type : boolean Generic return type : boolean Parameter class: interface java.util.List Parameter name: listParam Modifiers: 0 Is implicit?: false Is name present?: true Is synthetic?: false Method public <T> void ExampleMethods.genericMethod(T[],java.util.Collection<T>) Return type : void Generic return type : void Parameter class: class [Ljava.lang.Object; Parameter name: a Modifiers: 0 Is implicit?: false Is name present?: true Is synthetic?: false Parameter class: interface java.util.Collection Parameter name: c Modifiers: 0 Is implicit?: false Is name present?: true Is synthetic?: false
Parameter类中的方法 getType:返回参数的声明类型的Class对象。 getName:返回参数的名称。如果参数的名称存在,则此方法返回.class文件提供的名称。否则,以argN的形式返回,其中N是声明参数的方法中的参数索引。例如,假设您编译了ExampleMethods而未指定-parameters编译器选项。MethodParameterSpy将为方法ExampleMethods.simpleMethod打印以下内容: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public boolean ExampleMethods.simpleMethod(java.lang.String,int) Return type: boolean Generic return type: boolean Parameter class: class java.lang.String Parameter name: arg0 Modifiers: 0 Is implicit?: false Is name present?: false Is synthetic?: false Parameter class: int Parameter name: arg1 Modifiers: 0 Is implicit?: false Is name present?: false Is synthetic?: false
getModifiers :返回一个整数,表示形式参数拥有的各种特征。该值是以下值的总和: 值(十进制) 值(十六进制) 描述 16 0×0010 声明形式参数 final 4096 为0x1000 形式参数是合成的。或者,可以调用该方法isSynthetic。 32768 为0x8000 该参数在源代码中隐式声明。或者,您可以调用该方法isImplicit
isImplicit:如果在源代码中隐式声明此参数,则返回true。 isNamePresent: 如果参数来自.class文件,则返回true。 isSynthetic:如果在源代码中既未隐式声明也未显式声明此参数,则返回true。 隐式和合成参数 我们知道,在类未定义构造器时,JVM会自动生成默认构造器。在上面的输出中,可以看到:
1 2 Number of declared constructors: 1 public ExampleMethods()
考虑下面这种情况:
1 2 3 public class MethodParameterExamples { public class InnerClass { } }
InnerClass是非静态内部类。它的构造函数也是隐式声明的。但是,这个构造函数将包含一个参数。当Java编译器编译时InnerClass,它会创建一个类似于以下代码的.class文件:
1 2 3 4 5 6 7 8 public class MethodParameterExamples { public class InnerClass { final MethodParameterExamples parent; InnerClass(final MethodParameterExamples this$0){ parent = this$0; } } }
这个内部类的构造器包含一个隐式参数,类型是封装类的类型,MethodParameterSpy将为方法打印:
1 2 3 4 5 6 7 public MethodParameterExamples$InnerClass(MethodParameterExamples) Parameter class: class MethodParameterExamples Parameter name: this$0 Modifiers: 32784 Is implicit?: true Is name present?: true Is synthetic?: false
java 编译器实现的构造器,如果在源代码中没有对应的声明方法(无论是显示声明还是隐式声明),类初始化方法<clinit>
除外。合成构造器是由java 编译器实现的,并且因为不同的java编译器实现,合成构造器也互相不同。以下面的类举例:
1 2 3 4 5 public class MethodParameterExamples { enum Colors { RED, WHITE; } }
当Java编译器遇到enum 的构造器时,它会生成一个.class
文件和几个方法,以实现enum的功能。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 final class Colors extends java .lang.Enum<Colors> { public final static Colors RED = new Colors ("RED" , 0 ); public final static Colors BLUE = new Colors ("WHITE" , 1 ); private final static values = new Colors []{ RED, BLUE }; private Colors (String name, int ordinal) { super (name, ordinal); } public static Colors[] values(){ return values; } public static Colors valueOf (String name) { return (Colors)java.lang.Enum.valueOf(Colors.class, name); } }
其中 values 和valueOf 是隐式方法,因此,它们的参数名称也是隐式声明的,Colors(String name, int ordinal)
是默认构造函数,它是隐式声明的。但是其参数(name, ordinal)
并不是隐式声明(默认构造器是无参的),所以它们是合成参数。
enum 类 默认构造函数的形式参数不是隐式声明的,因为不同的编译器不需要就此构造函数的形式达成一致;另一个 Java 编译器可能为其指定不同的形式参数。
MethodParameterSpy将为方法打印:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 enum Colors: Number of constructors: 0 Number of declared constructors: 1 Declared constructor #1 private MethodParameterExamples$Colors() Parameter class: class java.lang.String Parameter name: $enum$name Modifiers: 4096 Is implicit?: false Is name present?: true Is synthetic?: true Parameter class: int Parameter name: $enum$ordinal Modifiers: 4096 Is implicit?: false Is name present?: true Is synthetic?: true Number of methods: 2 Method #1 public static MethodParameterExamples$Colors[] MethodParameterExamples$Colors.values() Return type: class [LMethodParameterExamples$Colors; Generic return type: class [LMethodParameterExamples$Colors; Method #2 public static MethodParameterExamples$Colors MethodParameterExamples$Colors.valueOf(java.lang.String) Return type: class MethodParameterExamples$Colors Generic return type: class MethodParameterExamples$Colors Parameter class: class java.lang.String Parameter name: name Modifiers: 32768 Is implicit?: true Is name present?: true Is synthetic?: false
分析方法修饰符 方法声明的几个修饰符:
访问修饰符:public, protected,private 静态修饰符:static 禁止修改值的修饰符:final 需要覆盖的修饰符:abstract 防止重入的修饰符:synchronized 指示在另一种编程语言中实现的修饰符:native 修改器强制严格的浮点行为:strictfp 注解:Annotations 下面例子列出了具有给定名称的方法的修饰符。它还显示该方法是合成的(编译器生成的)、可变参数 还是桥接方法(编译器生成的以支持泛型接口)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 import java.lang.reflect.Method;import java.lang.reflect.Modifier;import static java.lang.System.out;public class MethodModifierSpy { private static int count; private static synchronized void inc () { count++; } private static synchronized int cnt () { return count; } public static void main (String... args) { try { Class<?> c = Class.forName(args[0 ]); Method[] allMethods = c.getDeclaredMethods(); for (Method m : allMethods) { if (!m.getName().equals(args[1 ])) { continue ; } out.format("%s%n" , m.toGenericString()); out.format(" Modifiers: %s%n" , Modifier.toString(m.getModifiers())); out.format(" [ synthetic=%-5b var_args=%-5b bridge=%-5b ]%n" , m.isSynthetic(), m.isVarArgs(), m.isBridge()); inc(); } out.format("%d matching overload%s found%n" , cnt(), (cnt() == 1 ? "" : "s" )); } catch (ClassNotFoundException x) { x.printStackTrace(); } } }
下面的方法为例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 $ java MethodModifierSpy java.lang.Object wait public final void java.lang.Object.wait() throws java.lang.InterruptedException Modifiers: public final [ synthetic=false var_args=false bridge=false ] public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException Modifiers: public final [ synthetic=false var_args=false bridge=false ] public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException Modifiers: public final native [ synthetic=false var_args=false bridge=false ] 3 matching overloads found $ java MethodModifierSpy java.lang.StrictMath toRadians public static double java.lang.StrictMath.toRadians(double) Modifiers: public static strictfp [ synthetic=false var_args=false bridge=false ] 1 matching overload found $ java MethodModifierSpy MethodModifierSpy inc private synchronized void MethodModifierSpy.inc() Modifiers: private synchronized [ synthetic=false var_args=false bridge=false ] 1 matching overload found $ java MethodModifierSpy java.lang.Class getConstructor public java.lang.reflect.Constructor<T> java.lang.Class.getConstructor (java.lang.Class<T>[]) throws java.lang.NoSuchMethodException, java.lang.SecurityException Modifiers: public transient [ synthetic=false var_args=true bridge=false ] 1 matching overload found $ java MethodModifierSpy java.lang.String compareTo public int java.lang.String.compareTo(java.lang.String) Modifiers: public [ synthetic=false var_args=false bridge=false ] public int java.lang.String.compareTo(java.lang.Object) Modifiers: public volatile [ synthetic=true var_args=false bridge=true ] 2 matching overloads found
注意,Method.isVarArgs() 返回true是指如下结构:
1 public Constructor<T> getConstructor(Class<?>... parameterTypes)
而不是
1 public Constructor<T> getConstructor(Class<?> [] parameterTypes)
可以看到 String.compareTo() 包含了两个方法,在 String.java中声明的方法是:
1 public int compareTo (String anotherString) ;
第二个合成方法是用于桥接的,发生这种情况是因为 String 实现了参数化接口 Comparable. 在类型擦除期间,继承的方法 Comparable.compareTo()的参数类型从 java.lang.Object更改为 java.lang.String 。由于在擦除后方法的参数类型不再匹配,因此无法进行重写。这将产生编译时错误,添加桥接方法可避免此问题.
Method实现了java.lang.reflect.AnnotatedElement。因此,任何带有java.lang.annotation.RetentionPolicy.RUNTIME的运行时注释都可以被检索。
调用方法 反射提供了一种在类上调用方法的方法。通常,仅当无法在非反射代码中将类的实例强制转换为所需类型时,才需要执行此操作。使用 java.lang.reflect.Method.invoke() 调用,第一个参数是要在其上调用此特定方法的对象实例(如果方法是 static,则第一个参数应为null).后续参数是方法的参数。如果方法调用引发异常,它将抛出 java.lang.reflect.InvocationTargetException 。可以使用 InvocationTargetException.getCause() 方法检索该方法的原始异常。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;import java.lang.reflect.Type;import java.util.Locale;import static java.lang.System.out;import static java.lang.System.err;public class Deet <T> { private boolean testDeet (Locale l) { out.format("Locale = %s, ISO Language Code = %s%n" , l.getDisplayName(), l.getISO3Language()); return true ; } private int testFoo (Locale l) { return 0 ; } private boolean testBar () { return true ; } public static void main (String... args) { if (args.length != 4 ) { err.format("Usage: java Deet <classname> <langauge> <country> <variant>%n" ); return ; } try { Class<?> c = Class.forName(args[0 ]); Object t = c.newInstance(); Method[] allMethods = c.getDeclaredMethods(); for (Method m : allMethods) { String mname = m.getName(); if (!mname.startsWith("test" ) || (m.getGenericReturnType() != boolean .class)) { continue ; } Type[] pType = m.getGenericParameterTypes(); if ((pType.length != 1 ) || Locale.class.isAssignableFrom(pType[0 ].getClass())) { continue ; } out.format("invoking %s()%n" , mname); try { m.setAccessible(true ); Object o = m.invoke(t, new Locale (args[1 ], args[2 ], args[3 ])); out.format("%s() returned %b%n" , mname, (Boolean) o); } catch (InvocationTargetException x) { Throwable cause = x.getCause(); err.format("invocation of %s failed: %s%n" , mname, cause.getMessage()); } } } catch (ClassNotFoundException x) { x.printStackTrace(); } catch (InstantiationException x) { x.printStackTrace(); } catch (IllegalAccessException x) { x.printStackTrace(); } } }
使用例子
1 2 3 4 5 6 7 8 9 10 $ java Deet Deet ja JP JP invoking testDeet() Locale = Japanese (Japan,JP), ISO Language Code = jpn testDeet() returned true $ java Deet Deet xx XX XX invoking testDeet() invocation of testDeet failed: Couldn't find 3-letter language code for xx
Method.invoke()可用于将可变数量的参数传递给方法。可变变量的方法的实现就像变量参数打包在数组中一样。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;import java.util.Arrays;public class InvokeMain { public static void main (String... args) { try { Class<?> c = Class.forName(args[0 ]); Class[] argTypes = new Class [] { String[].class }; Method main = c.getDeclaredMethod("main" , argTypes); String[] mainArgs = Arrays.copyOfRange(args, 1 , args.length); System.out.format("invoking %s.main()%n" , c.getName()); main.invoke(null , (Object)mainArgs); } catch (ClassNotFoundException x) { x.printStackTrace(); } catch (NoSuchMethodException x) { x.printStackTrace(); } catch (IllegalAccessException x) { x.printStackTrace(); } catch (InvocationTargetException x) { x.printStackTrace(); } } }
Exception 由于类型擦除引起的异常,NoSuchMethodException。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 import java.lang.reflect.Method;public class MethodTrouble <T> { public void lookup (T t) {} public void find (Integer i) {} public static void main (String... args) { try { String mName = args[0 ]; Class cArg = Class.forName(args[1 ]); Class<?> c = (new MethodTrouble <Integer>()).getClass(); Method m = c.getMethod(mName, cArg); System.out.format("Found:%n %s%n" , m.toGenericString()); } catch (NoSuchMethodException x) { x.printStackTrace(); } catch (ClassNotFoundException x) { x.printStackTrace(); } } } $ java MethodTrouble lookup java.lang.Integer java.lang.NoSuchMethodException: MethodTrouble.lookup(java.lang.Integer) at java.lang.Class.getMethod(Class.java:1605 ) at MethodTrouble.main(MethodTrouble.java:12 ) $ java MethodTrouble lookup java.lang.Object Found: public void MethodTrouble.lookup(T)
当使用泛型参数类型声明方法时,编译器会将泛型类型替换为其上限,在本例中,上限为 Object。因此,当代码搜索 时,找不到任何lookup方法.
非法访问产生的IllegalAccessException 如果尝试调用私有或以其他方式无法访问的方法,会产生异常IllegalAccessException
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;class AnotherClass { private void m () {} } public class MethodTroubleAgain { public static void main (String... args) { AnotherClass ac = new AnotherClass (); try { Class<?> c = ac.getClass(); Method m = c.getDeclaredMethod("m" ); Object o = m.invoke(ac); } catch (NoSuchMethodException x) { x.printStackTrace(); } catch (InvocationTargetException x) { x.printStackTrace(); } catch (IllegalAccessException x) { x.printStackTrace(); } } } $ java MethodTroubleAgain java.lang.IllegalAccessException: Class MethodTroubleAgain can not access a member of class AnotherClass with modifiers "private" at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:65 ) at java.lang.reflect.Method.invoke(Method.java:588 ) at MethodTroubleAgain.main(MethodTroubleAgain.java:15 )
IllegalArgumentException 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 import java.lang.reflect.Method;public class MethodTroubleToo { public void ping () { System.out.format("PONG!%n" ); } public static void main (String... args) { try { MethodTroubleToo mtt = new MethodTroubleToo (); Method m = MethodTroubleToo.class.getMethod("ping" ); switch (Integer.parseInt(args[0 ])) { case 0 : m.invoke(mtt); break ; case 1 : m.invoke(mtt, null ); break ; case 2 : Object arg2 = null ; m.invoke(mtt, arg2); break ; case 3 : m.invoke(mtt, new Object [0 ]); break ; case 4 : Object arg4 = new Object [0 ]; m.invoke(mtt, arg4); break ; default : System.out.format("Test not found%n" ); } } catch (Exception x) { x.printStackTrace(); } } } $ java MethodTroubleToo 0 PONG!
由于 Method.invoke() 的所有参数(第一个参数除外)都是可选的,因此当要调用的方法没有参数时,可以省略它们。
1 2 $ java MethodTroubleToo 1 PONG!
在这种情况下,代码会生成此编译器警告,因为null是不明确的。
1 2 3 4 5 6 7 8 $ javac MethodTroubleToo.java MethodTroubleToo.java:16: warning: non-varargs call of varargs method with inexact argument type for last parameter; m.invoke(mtt, null); // works (expect compiler warning) ^ cast to Object for a varargs call cast to Object[] for a non-varargs call and to suppress this warning 1 warning
无法确定null是表示空参数数组还是 第一个参数.
1 2 3 4 5 6 7 8 9 $ java MethodTroubleToo 2 java.lang.IllegalArgumentException: wrong number of arguments at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke (NativeMethodAccessorImpl.java:39 ) at sun.reflect.DelegatingMethodAccessorImpl.invoke (DelegatingMethodAccessorImpl.java:25 ) at java.lang.reflect.Method.invoke(Method.java:597 ) at MethodTroubleToo.main(MethodTroubleToo.java:21 )
尽管参数是null ,但这个还是失败,是因为该类型是一个 Object,并且ping()根本不需要任何参数。
1 2 $ java MethodTroubleToo 3 PONG!
这之所以有效,是因为创建一个空数组 new Object[0]
,这等效于不传递任何可选参数。
1 2 3 4 5 6 7 8 9 10 $ java MethodTroubleToo 4 java.lang.IllegalArgumentException: wrong number of arguments at sun.reflect.NativeMethodAccessorImpl.invoke0 (Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke (NativeMethodAccessorImpl.java:39 ) at sun.reflect.DelegatingMethodAccessorImpl.invoke (DelegatingMethodAccessorImpl.java:25 ) at java.lang.reflect.Method.invoke(Method.java:597 ) at MethodTroubleToo.main(MethodTroubleToo.java:28 )
与前面的示例不同,如果空数组存储在 Object 中,则将其视为 Object。这与案例 2 失败的原因相同,ping()不需要参数。
InvocationTargetException 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;public class MethodTroubleReturns { private void drinkMe (int liters) { if (liters < 0 ) throw new IllegalArgumentException ("I can't drink a negative amount of liquid" ); } public static void main (String... args) { try { MethodTroubleReturns mtr = new MethodTroubleReturns (); Class<?> c = mtr.getClass(); Method m = c.getDeclaredMethod("drinkMe" , int .class); m.invoke(mtr, -1 ); } catch (InvocationTargetException x) { Throwable cause = x.getCause(); System.err.format("drinkMe() failed: %s%n" , cause.getMessage()); } catch (Exception x) { x.printStackTrace(); } } } $ java MethodTroubleReturns drinkMe () failed: I can't drink a negative amount of liquid
反射中的类成员–Constructor java.lang.reflect.Constructor
表示类的构造器。
查找构造函数 示例说明了如何在类的声明构造函数中搜索具有给定类型参数的构造函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 import java.lang.reflect.Constructor;import java.lang.reflect.Type;import static java.lang.System.out;public class ConstructorSift { public static void main (String... args) { try { Class<?> cArg = Class.forName(args[1 ]); Class<?> c = Class.forName(args[0 ]); Constructor[] allConstructors = c.getDeclaredConstructors(); for (Constructor ctor : allConstructors) { Class<?>[] pType = ctor.getParameterTypes(); for (int i = 0 ; i < pType.length; i++) { if (pType[i].equals(cArg)) { out.format("%s%n" , ctor.toGenericString()); Type[] gpType = ctor.getGenericParameterTypes(); for (int j = 0 ; j < gpType.length; j++) { char ch = (pType[j].equals(cArg) ? '*' : ' ' ); out.format("%7c%s[%d]: %s%n" , ch, "GenericParameterType" , j, gpType[j]); } break ; } } } } catch (ClassNotFoundException x) { x.printStackTrace(); } } }
Method.getGenericParameterTypes()将查阅类文件中的签名属性(如果存在)。如果该属性不可用,它将回退到 Method.getParameterType(). 该类型未因泛型的引入而更改.
分析构造函数修饰符 构造函数的修饰符较少:
访问修饰符:public,protected,private 注解:Annotations 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 import java.lang.reflect.Constructor;import java.lang.reflect.Modifier;import java.lang.reflect.Type;import static java.lang.System.out;public class ConstructorAccess { public static void main (String... args) { try { Class<?> c = Class.forName(args[0 ]); Constructor[] allConstructors = c.getDeclaredConstructors(); for (Constructor ctor : allConstructors) { int searchMod = modifierFromString(args[1 ]); int mods = accessModifiers(ctor.getModifiers()); if (searchMod == mods) { out.format("%s%n" , ctor.toGenericString()); out.format(" [ synthetic=%-5b var_args=%-5b ]%n" , ctor.isSynthetic(), ctor.isVarArgs()); } } } catch (ClassNotFoundException x) { x.printStackTrace(); } } private static int accessModifiers (int m) { return m & (Modifier.PUBLIC | Modifier.PRIVATE | Modifier.PROTECTED); } private static int modifierFromString (String s) { if ("public" .equals(s)) return Modifier.PUBLIC; else if ("protected" .equals(s)) return Modifier.PROTECTED; else if ("private" .equals(s)) return Modifier.PRIVATE; else if ("package-private" .equals(s)) return 0 ; else return -1 ; } } $ java ConstructorAccess java.io.File private private java.io.File(java.lang.String,int ) [ synthetic=false var_args=false ] private java.io.File(java.lang.String,java.io.File) [ synthetic=false var_args=false ]
创建新的类实例 有两种反射方法可用于创建类的实例:java.lang.reflect.Constructor.newInstance() 和 Class.newInstance() 。前者是优选的,因为:
Class.newInstance() 只能调用零参数构造函数,而 Constructor.newInstance()可以调用任何构造函数,而不管参数的数量如何。 Class.newInstance() 会引发构造函数引发的任何异常,无论它是检查还是未检查。Constructor.newInstance()总是用 InvocationTargetException 包装抛出的异常。 Class.newInstance() 要求构造函数是可见的;Constructor.newInstance()在某些情况下可以调用private构造函数。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 import java.io.Console;import java.nio.charset.Charset;import java.lang.reflect.Constructor;import java.lang.reflect.Field;import java.lang.reflect.InvocationTargetException;import static java.lang.System.out;public class ConsoleCharset { public static void main (String... args) { Constructor[] ctors = Console.class.getDeclaredConstructors(); Constructor ctor = null ; for (int i = 0 ; i < ctors.length; i++) { ctor = ctors[i]; if (ctor.getGenericParameterTypes().length == 0 ) break ; } try { ctor.setAccessible(true ); Console c = (Console)ctor.newInstance(); Field f = c.getClass().getDeclaredField("cs" ); f.setAccessible(true ); out.format("Console charset : %s%n" , f.get(c)); out.format("Charset.defaultCharset(): %s%n" , Charset.defaultCharset()); } catch (InstantiationException x) { x.printStackTrace(); } catch (InvocationTargetException x) { x.printStackTrace(); } catch (IllegalAccessException x) { x.printStackTrace(); } catch (NoSuchFieldException x) { x.printStackTrace(); } } }
UNIX 系统的示例输出:
1 2 3 $ java ConsoleCharset Console charset : ISO-8859-1 Charset.defaultCharset() : ISO-8859-1
Constructor.newInstance() 的另一个常见应用是调用采用参数的构造函数.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 import java.lang.reflect.Constructor;import java.lang.reflect.Field;import java.lang.reflect.InvocationTargetException;import java.util.HashMap;import java.util.Map;import java.util.Set;import static java.lang.System.out;class EmailAliases { private Set<String> aliases; private EmailAliases (HashMap<String, String> h) { aliases = h.keySet(); } public void printKeys () { out.format("Mail keys:%n" ); for (String k : aliases) out.format(" %s%n" , k); } } public class RestoreAliases { private static Map<String, String> defaultAliases = new HashMap <String, String>(); static { defaultAliases.put("Duke" , "duke@i-love-java" ); defaultAliases.put("Fang" , "fang@evil-jealous-twin" ); } public static void main (String... args) { try { Constructor ctor = EmailAliases.class.getDeclaredConstructor(HashMap.class); ctor.setAccessible(true ); EmailAliases email = (EmailAliases)ctor.newInstance(defaultAliases); email.printKeys(); } catch (InstantiationException x) { x.printStackTrace(); } catch (IllegalAccessException x) { x.printStackTrace(); } catch (InvocationTargetException x) { x.printStackTrace(); } catch (NoSuchMethodException x) { x.printStackTrace(); } } } $ java RestoreAliases Mail keys: Duke Fang
Array 数组类型可以通过调用 Class.isArray()来标识。反射支持通过java.lang.reflect.Array.newInstance()动态创建任意类型和维度的数组的能力.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 import java.lang.reflect.Array;import java.lang.reflect.Constructor;import java.lang.reflect.InvocationTargetException;import java.util.regex.Pattern;import java.util.regex.Matcher;import java.util.Arrays;import static java.lang.System.out;public class ArrayCreator { private static String s = "java.math.BigInteger bi[] = { 123, 234, 345 }" ; private static Pattern p = Pattern.compile("^\\s*(\\S+)\\s*\\w+\\[\\].*\\{\\s*([^}]+)\\s*\\}" ); public static void main (String... args) { Matcher m = p.matcher(s); if (m.find()) { String cName = m.group(1 ); String[] cVals = m.group(2 ).split("[\\s,]+" ); int n = cVals.length; try { Class<?> c = Class.forName(cName); Object o = Array.newInstance(c, n); for (int i = 0 ; i < n; i++) { String v = cVals[i]; Constructor ctor = c.getConstructor(String.class); Object val = ctor.newInstance(v); Array.set(o, i, val); } Object[] oo = (Object[])o; out.format("%s[] = %s%n" , cName, Arrays.toString(oo)); } catch (ClassNotFoundException x) { x.printStackTrace(); } catch (NoSuchMethodException x) { x.printStackTrace(); } catch (IllegalAccessException x) { x.printStackTrace(); } catch (InstantiationException x) { x.printStackTrace(); } catch (InvocationTargetException x) { x.printStackTrace(); } } } } $ java ArrayCreator java.math.BigInteger [] = [123 , 234 , 345 ]
数组的操作 java.lang.reflect.Field.set(Object obj,Object value) : 设置数组的值 java.lang.reflect.Field.get(Object): 获取数组的值 java.lang.reflect.Array 提供了对数组内部操作的方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 import java.lang.reflect.Array;import static java.lang.System.out;public class CreateMatrix { public static void main (String... args) { Object matrix = Array.newInstance(int .class, 2 , 2 ); Object row0 = Array.get(matrix, 0 ); Object row1 = Array.get(matrix, 1 ); Array.setInt(row0, 0 , 1 ); Array.setInt(row0, 1 , 2 ); Array.setInt(row1, 0 , 3 ); Array.setInt(row1, 1 , 4 ); for (int i = 0 ; i < 2 ; i++) for (int j = 0 ; j < 2 ; j++) out.format("matrix[%d][%d] = %d%n" , i, j, ((int [][])matrix)[i][j]); } } $ java CreateMatrix matrix[0 ][0 ] = 1 matrix[0 ][1 ] = 2 matrix[1 ][0 ] = 3 matrix[1 ][1 ] = 4
Array.newInstance(Class<?> componentType, int… dimensions) 提供了一种创建多维数组的便捷方法
enum Class.isEnum() 指示此类是否表示枚举类型 Class.getEnumConstants() 检索枚举常量的列表,这些枚举常量按声明的顺序进行定义,返回的值与调用枚举类型values()返回的值相同。 java.lang.reflect.Field.isEnumConstant() 指示此字段是否表示枚举类型的元素 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 import java.util.Arrays;import static java.lang.System.out;enum Eon { HADEAN, ARCHAEAN, PROTEROZOIC, PHANEROZOIC }public class EnumConstants { public static void main (String... args) { try { Class<?> c = (args.length == 0 ? Eon.class : Class.forName(args[0 ])); out.format("Enum name: %s%nEnum constants: %s%n" , c.getName(), Arrays.asList(c.getEnumConstants())); if (c == Eon.class) out.format(" Eon.values(): %s%n" , Arrays.asList(Eon.values())); } catch (ClassNotFoundException x) { x.printStackTrace(); } } } $ java EnumConstants java.lang.annotation.RetentionPolicy Enum name: java.lang.annotation.RetentionPolicy Enum constants: [SOURCE, CLASS, RUNTIME] $ java EnumConstants java.util.concurrent.TimeUnit Enum name: java.util.concurrent.TimeUnit Enum constants: [NANOSECONDS, MICROSECONDS, MILLISECONDS, SECONDS, MINUTES, HOURS, DAYS]
由于enum实际上是类,可以用上面的反射方法获取 字段、方法和构造函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 import java.lang.reflect.Constructor;import java.lang.reflect.Field;import java.lang.reflect.Method;import java.lang.reflect.Member;import java.util.List;import java.util.ArrayList;import static java.lang.System.out;public class EnumSpy { private static final String fmt = " %11s: %s %s%n" ; public static void main (String... args) { try { Class<?> c = Class.forName(args[0 ]); if (!c.isEnum()) { out.format("%s is not an enum type%n" , c); return ; } out.format("Class: %s%n" , c); Field[] flds = c.getDeclaredFields(); List<Field> cst = new ArrayList <Field>(); List<Field> mbr = new ArrayList <Field>(); for (Field f : flds) { if (f.isEnumConstant()) cst.add(f); else mbr.add(f); } if (!cst.isEmpty()) print(cst, "Constant" ); if (!mbr.isEmpty()) print(mbr, "Field" ); Constructor[] ctors = c.getDeclaredConstructors(); for (Constructor ctor : ctors) { out.format(fmt, "Constructor" , ctor.toGenericString(), synthetic(ctor)); } Method[] mths = c.getDeclaredMethods(); for (Method m : mths) { out.format(fmt, "Method" , m.toGenericString(), synthetic(m)); } } catch (ClassNotFoundException x) { x.printStackTrace(); } } private static void print (List<Field> lst, String s) { for (Field f : lst) { out.format(fmt, s, f.toGenericString(), synthetic(f)); } } private static String synthetic (Member m) { return (m.isSynthetic() ? "[ synthetic ]" : "" ); } } $ java EnumSpy java.lang.annotation.RetentionPolicy Class: class java .lang.annotation.RetentionPolicy Constant: public static final java.lang.annotation.RetentionPolicy java.lang.annotation.RetentionPolicy.SOURCE Constant: public static final java.lang.annotation.RetentionPolicy java.lang.annotation.RetentionPolicy.CLASS Constant: public static final java.lang.annotation.RetentionPolicy java.lang.annotation.RetentionPolicy.RUNTIME Field: private static final java.lang.annotation.RetentionPolicy[] java.lang.annotation.RetentionPolicy. [ synthetic ] Constructor: private java.lang.annotation.RetentionPolicy() Method: public static java.lang.annotation.RetentionPolicy[] java.lang.annotation.RetentionPolicy.values() Method: public static java.lang.annotation.RetentionPolicy java.lang.annotation.RetentionPolicy.valueOf(java.lang.String)
Class.getFields()和 Class.getDeclaredFields() 不保证返回值的顺序与声明源代码中的顺序匹配。如果应用程序需要排序,请使用 Class.getEnumConstants()
java.lang.reflect.Field.set(Object obj,Object value) : 设置enum的值 java.lang.reflect.Field.get(Object): 获取enum的值 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 import java.lang.reflect.Field;import static java.lang.System.out;enum TraceLevel { OFF, LOW, MEDIUM, HIGH, DEBUG }class MyServer { private TraceLevel level = TraceLevel.OFF; } public class SetTrace { public static void main (String... args) { TraceLevel newLevel = TraceLevel.valueOf(args[0 ]); try { MyServer svr = new MyServer (); Class<?> c = svr.getClass(); Field f = c.getDeclaredField("level" ); f.setAccessible(true ); TraceLevel oldLevel = (TraceLevel)f.get(svr); out.format("Original trace level: %s%n" , oldLevel); if (oldLevel != newLevel) { f.set(svr, newLevel); out.format(" New trace level: %s%n" , f.get(svr)); } } catch (IllegalArgumentException x) { x.printStackTrace(); } catch (IllegalAccessException x) { x.printStackTrace(); } catch (NoSuchFieldException x) { x.printStackTrace(); } } }