这篇Java教程基于JDK1.8。教程中的示例和实践不会使用未来发行版中的优化建议。
教程:反射之成员
成员 该教程展示了如何使用反射API来获取类的字段、方法和构造器。提供了这些示例:设置和获取字段,方法调用,使用反射获取的构造器创建对象实例。
字段有一个类型和一个值。java.lang.reflect.Field 类提供了能够访问一个对象的类型信息以及获取、设置值的方法。
一个字段要么是原始类型,要么是引用类型。一共有8种原始类型:boolean、byte、char、short、int、long、float和double。引用类型是指java.lang.Object的直接或间接子类,包括接口、数组和枚举类型。
FieldSpy 示例展示了打印字段的类型。
以下为示例输出:
$ java reflection/FieldSpy reflection.FieldSpy b
Type: class [[Z
GenericType: class [[Z
$ java reflection/FieldSpy reflection.FieldSpy name
Type: class java.lang.String
GenericType: class java.lang.String
$ java reflection/FieldSpy reflection.FieldSpy list
Type: interface java.util.List
GenericType: java.util.List<java.lang.Integer>
$ java reflection/FieldSpy reflection.FieldSpy val
Type: class java.lang.Object
GenericType: T
- 能够修饰字段的修饰符有:
- 访问修饰符:public、protected和private
- 控制运行时行为的特定于字段的修饰符:transient 和 volatile
- 单例修饰符:static
- 禁止修改值修饰符:final
- 注解
FieldModifierSpy 展示了通过给定的修饰符来查找字段,它也展示了通过调用Field.isSynthetic() 判断字段是否是编译器生成的、调用Field.isEnumCostant()判断字段是否是枚举常量。
示例输出如下所示:
$ java reflection/FieldModifierSpy reflection.FieldModifierSpy volatile
Fields in Class 'reflection.FieldModifierSpy' containing modifiers: volatile
share [ synthetic=false enum_constant=false ]
$ java reflection/FieldModifierSpy reflection.Spy public
Fields in Class 'reflection.Spy' containing modifiers: public
BLACK [ synthetic=false enum_constant=true ]
WHITE [ synthetic=false enum_constant=true ]
$ java reflection/FieldModifierSpy reflection.FieldModifierSpy\$Inner final
Fields in Class 'reflection.FieldModifierSpy$Inner' containing modifiers: final
this$0 [ synthetic=true enum_constant=false ]
$ java reflection/FieldModifierSpy reflection.Spy private static final
Fields in Class 'reflection.Spy' containing modifiers: private static final
$VALUES [ synthetic=true enum_constant=false ]
给定一个类的实例,是可以使用反射来设置字段的值 。这只适用于通过一般的方法行不通时才可以使用。因为这样的访问通常违反类的设计意图,所以应该谨慎使用。
Book 类演示了如何设置long、array和枚举类型。
$ java reflection/Book
BEFORE: chapters = 0
AFTER: chapters = 12
BEFORE: characters = [Alice, White Rabbit]
AFTER: characters = [Queen, King]
BEFORE: twin = DEE
AFTER: twin = DUM
类型不匹配报IllegalArgumentException
在使用反射时,只有在运行期会发生类型检查,在编译期不会进行装箱。FieldTrouble展示了当对引用类型调用Field.setInt() 来设置值的时候会抛出IllegalArgumentException异常。
$ java reflection/FieldTrouble
Exception in thread "main" java.lang.IllegalArgumentException: Can not set java.lang.Integer field reflection.FieldTrouble.val to (int)42
at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:167)
at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:191)
at sun.reflect.UnsafeObjectFieldAccessorImpl.setInt(UnsafeObjectFieldAccessorImpl.java:114)
at java.lang.reflect.Field.setInt(Field.java:949)
at reflection.FieldTrouble.main(FieldTrouble.java:19)
为了解决这个问题,可以将有问题的这一行代码改为
valField.set(ft,43);
非公有字段报NoSuchFieldException
$ java reflection/FieldSpy java.lang.String count
java.lang.NoSuchFieldException: count
at java.lang.Class.getField(Class.java:1703)
at reflection.FieldSpy.main(FieldSpy.java:16)
当修改final字段时报IllegalAccessException
当试图访问private修饰的或者final修饰的字段时,可能会抛出IllegalAccessException异常。
FieldTroubleToo演示了当对一个final类型的变量设置值时运行的异常堆栈。
$ java reflection/FieldTroubleToo
Exception in thread "main" java.lang.IllegalAccessException: Can not set final boolean field reflection.FieldTroubleToo.b to (boolean)false
at sun.reflect.UnsafeFieldAccessorImpl.throwFinalFieldIllegalAccessException(UnsafeFieldAccessorImpl.java:76)
at sun.reflect.UnsafeFieldAccessorImpl.throwFinalFieldIllegalAccessException(UnsafeFieldAccessorImpl.java:84)
at sun.reflect.UnsafeQualifiedBooleanFieldAccessorImpl.setBoolean(UnsafeQualifiedBooleanFieldAccessorImpl.java:96)
at java.lang.reflect.Field.setBoolean(Field.java:801)
at reflection.FieldTroubleToo.main(FieldTroubleToo.java:19)
当通过反射方法修改了它的可访问性属性后,则不再报错。
//通过反射修改其可访问性
bField.setAccessible(true);
方法有返回值、参数,也可能会抛出异常。 java.lang.reflect.Method提供了一些方法用来获取参数和返回值的类型信息,它也可以用来在指定的对象上调用方法。
一个方法的声明包括方法名、修饰符、返回值、参数列表和一系列抛出的异常。java.lang.reflect.Method提供的方法可以获取一个方法的所有类型信息。
MethodSpy 展示了如何枚举类中的方法,并获取指定方法名称的返回值、参数列表和异常信息。
$ java reflection/MethodSpy java.lang.Class getConstructor
public java.lang.reflect.Constructor<T> java.lang.Class.getConstructor(java.lang.Class<?>...) throws java.lang.NoSuchMethodException,java.lang.SecurityException
ReturnType: class java.lang.reflect.Constructor
GenericReturnType: java.lang.reflect.Constructor<T>
ParameterType: class [Ljava.lang.Class;
GenericParameterType: java.lang.Class<?>[]
ExceptionType: class java.lang.NoSuchMethodException
GenericExceptionType: class java.lang.NoSuchMethodException
ExceptionType: class java.lang.SecurityException
GenericExceptionType: class java.lang.SecurityException
$ java reflection/MethodSpy java.lang.Class cast
public T java.lang.Class.cast(java.lang.Object)
ReturnType: class java.lang.Object
GenericReturnType: T
ParameterType: class java.lang.Object
GenericParameterType: class java.lang.Object
$ java reflection/MethodSpy java.io.PrintStream format
public java.io.PrintStream java.io.PrintStream.format(java.util.Locale,java.lang.String,java.lang.Object...)
ReturnType: class java.io.PrintStream
GenericReturnType: class java.io.PrintStream
ParameterType: class java.util.Locale
GenericParameterType: class java.util.Locale
ParameterType: class java.lang.String
GenericParameterType: class java.lang.String
ParameterType: class [Ljava.lang.Object;
GenericParameterType: class [Ljava.lang.Object;
public java.io.PrintStream java.io.PrintStream.format(java.lang.String,java.lang.Object...)
ReturnType: class java.io.PrintStream
GenericReturnType: class java.io.PrintStream
ParameterType: class java.lang.String
GenericParameterType: class java.lang.String
ParameterType: class [Ljava.lang.Object;
GenericParameterType: class [Ljava.lang.Object;
你可以通过java.lang.reflect.Executable.getParameters这个方法得到任何一个方法或者构造器的所有参数名称(由于Method和Constructor都继承于Executable,所以也继承了Executable.getParameters这个方法)。一般来说,.class结尾的文件不会存储格式化的参数名称。这是因为许多生成和使用类文件的工具不希望包含参数名的.class文件占用更大的静态和动态空间。这样以来,这些工具不得不处理更大的.class文件,JVM也会使用更多的内存。再加上,有些对安全敏感的参数名比如秘钥或者密码会暴露参数信息。
为了能够在.class文件中存储格式化参数,并让反射API能够获取到这些参数,可以在javac命令编译源码的时候加上 -parameters 选项。
MethodParameterSpy 演示了如何获取指定类的所有构造器和方法的格式化参数名,同时也打印出了每个参数的具体信息。
以下命令将打印出 ExampleMethods所有构造器和方法的格式化参数名称。
注意:编译ExampleMethods的时候记得要加上 -parameters 选项。
$ java reflection/MethodParameterSpy reflection.ExampleMethods
public class reflection.ExampleMethods<T>
constructors count: 1
public reflection.ExampleMethods()
parameters count : 0
declare constructors count: 1
public reflection.ExampleMethods()
parameters count : 0
methods count: 4
public boolean reflection.ExampleMethods.simpleMethod(java.lang.String,java.lang.Integer)
method return type: boolean
method return generic type: boolean
parameter type: public final class java.lang.String
parameter name: stringParam
parameter modifiers: 0
parameter isImplicit?: false
parameter isNamePresent?: true
parameter isSynthetic?: false
parameter type: public final class java.lang.Integer
parameter name: intParam
parameter modifiers: 0
parameter isImplicit?: false
parameter isNamePresent?: true
parameter isSynthetic?: false
public int reflection.ExampleMethods.varArgsMethod(java.lang.String...)
method return type: int
method return generic type: int
parameter type: public abstract final class [Ljava.lang.String;
parameter name: manyStrings
parameter modifiers: 0
parameter isImplicit?: false
parameter isNamePresent?: true
parameter isSynthetic?: false
public boolean reflection.ExampleMethods.methodWithList(java.util.List<java.lang.String>)
method return type: boolean
method return generic type: boolean
parameter type: public abstract interface java.util.List<E>
parameter name: list
parameter modifiers: 0
parameter isImplicit?: false
parameter isNamePresent?: true
parameter isSynthetic?: false
public <T> void reflection.ExampleMethods.genericMethod(T[],java.util.Collection<T>)
method return type: void
method return generic type: void
parameter type: public abstract final class [Ljava.lang.Object;
parameter name: t
parameter modifiers: 0
parameter isImplicit?: false
parameter isNamePresent?: true
parameter isSynthetic?: false
parameter type: public abstract interface java.util.Collection<E>
parameter name: c
parameter modifiers: 0
parameter isImplicit?: false
parameter isNamePresent?: true
parameter isSynthetic?: false
隐式和合成参数
考虑 MethodParameterExamples 这个类中的内部类:
public class MethodParameterExamples {
public class InnerClass { }
}
InnerClass 是一个 非静态的内嵌类。内部类的构造器也是隐式声明的。但是它的构造器会包含一个参数,当Java编译器编译了InnerClass,它将生成一个.class 文件内容如下所示:
public class MethodParameterExamples {
public class InnerClass {
final MethodParameterExamples parent;
InnerClass(final MethodParameterExamples this$0) {
parent = this$0;
}
}
}
InnerClass内部类的构造器参数的类型是它的外部类,该例中是指MethodParameterExamples。它将输出:
public reflection.MethodParameterExamples$InnerClass(reflection.MethodParameterExamples)
parameters count : 1
parameter type: public class reflection.MethodParameterExamples
parameter name: this$0
parameter modifiers: 32784
parameter isImplicit?: true
parameter isNamePresent?: true
parameter isSynthetic?: false
如果Java编译器生成的构造器与源代码中显式或隐式声明的构造器都不对应并且它们不是类初始化方法,则将其标记为合成的。
考虑下面的场景:
public class MethodParameterExamples {
enum Colors {
RED, WHITE;
}
}
当Java编译器遇到enum构造时,它会创建几个与.class文件结构兼容的方法,并提供enum的预期功能。比如Java编译器将为Colors 枚举创建一个.class文件如下所示:
final class Colors extends java.lang.Enum<Colors> {
public final static Colors RED = new Colors("RED", 0);
public final static Colors WHITE = new Colors("WHITE", 1);
private final static values = new Colors[]{ RED, WHITE };
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);
}
}
Java编译器为enum创建了3个构造器和方法:Colors(String name, int ordinal), Colors[] values()和Colors valueOf(String name)。values和valueOf 方法是被隐式声明的,那么它们的参数也是隐式声明的了。
Colors(String name, int ordinal) 这个构造器是默认的并且是隐式声明的。但是构造器的参数(name和ordinal)不是隐式声明的。这些参数既不是显示声明也不是隐式声明,它是合成的。
MethodParameterExamples 完整的打印结果如下所示:
$ java reflection/MethodParameterExamples
Inner class :
public class reflection.MethodParameterExamples$InnerClass
constructors count: 1
public reflection.MethodParameterExamples$InnerClass(reflection.MethodParameterExamples)
parameters count : 1
parameter type: public class reflection.MethodParameterExamples
parameter name: this$0
parameter modifiers: 32784
parameter isImplicit?: true
parameter isNamePresent?: true
parameter isSynthetic?: false
declare constructors count: 1
public reflection.MethodParameterExamples$InnerClass(reflection.MethodParameterExamples)
parameters count : 1
parameter type: public class reflection.MethodParameterExamples
parameter name: this$0
parameter modifiers: 32784
parameter isImplicit?: true
parameter isNamePresent?: true
parameter isSynthetic?: false
enum class Colors:
static final enum reflection.MethodParameterExamples$Colors
constructors count: 0
declare constructors count: 1
private reflection.MethodParameterExamples$Colors()
parameters count : 2
parameter type: public final class java.lang.String
parameter name: $enum$name
parameter modifiers: 4096
parameter isImplicit?: false
parameter isNamePresent?: true
parameter isSynthetic?: true
parameter type: int
parameter name: $enum$ordinal
parameter modifiers: 4096
parameter isImplicit?: false
parameter isNamePresent?: true
parameter isSynthetic?: true
methods count: 2
public static reflection.MethodParameterExamples$Colors[] reflection.MethodParameterExamples$Colors.values()
method return type: abstract final class [Lreflection.MethodParameterExamples$Colors;
method return generic type: reflection.MethodParameterExamples$Colors[]
public static reflection.MethodParameterExamples$Colors reflection.MethodParameterExamples$Colors.valueOf(java.lang.String)
method return type: static final enum reflection.MethodParameterExamples$Colors
method return generic type: reflection.MethodParameterExamples$Colors
parameter type: public final class java.lang.String
parameter name: name
parameter modifiers: 32768
parameter isImplicit?: true
parameter isNamePresent?: true
parameter isSynthetic?: false
方法的声明中有许多修饰符:
- 访问修饰符:public, protected 和 private
- 单实例限制修饰符:static
- 禁止方法被重写修饰符:final
- 方法重写修饰符:override
- 防止可重入性修饰符:synchronized
- 原生方法修饰符:native
- 强制严格浮点行为的修饰符:strictfp
- 注解
MethodModifierSpy 展示了如何列出指定名称方法的修饰符。它还显示了方法是否是合成的(编译器生成的)、是否是可变参数,或是否是桥接方法(编译器生成的,以支持通用接口)。
$ java reflection/MethodModifierSpy java.lang.Object wait
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 ]
public final void java.lang.Object.wait() throws java.lang.InterruptedException
Modifiers: public final
[ synthetic=false var_args=false bridge=false ]
3 matching overloads method
$ java reflection/MethodModifierSpy java.lang.StrictMath toRadians
public static strictfp double java.lang.StrictMath.toRadians(double)
Modifiers: public static strictfp
[ synthetic=false var_args=false bridge=false ]
1 matching overload method
$ java reflection/MethodModifierSpy reflection.MethodModifierSpy incr
private static synchronized void reflection.MethodModifierSpy.incr()
Modifiers: private static synchronized
[ synthetic=false var_args=false bridge=false ]
1 matching overload method
$ java reflection/MethodModifierSpy java.lang.Class getConstructor
public java.lang.reflect.Constructor<T> java.lang.Class.getConstructor(java.lang.Class<?>...) throws java.lang.NoSuchMethodException,java.lang.SecurityException
Modifiers: public transient
[ synthetic=false var_args=true bridge=false ]
1 matching overload method
$ java reflection/MethodModifierSpy java.lang.String compareTo
public int java.lang.String.compareTo(java.lang.Object)
Modifiers: public volatile
[ synthetic=true var_args=false bridge=true ]
public int java.lang.String.compareTo(java.lang.String)
Modifiers: public
[ synthetic=false var_args=false bridge=false ]
2 matching overloads method
查找并调用具有特定声明的方法
考虑一个测试套件采用反射来调用一个类中的私有方法。Deet示例演示了查找所有以字符串test开头的,有一个boolean类型的返回值,以及一个单 Locale 参数的公有方法。然后调用每个匹配的方法。
$ java reflection/Deet reflection.Deet ja JP JP
invoke method testDeet
local = 日文 (日本,JP),ISO language code = jpn
invoking of testDeet return true
invoke method testFoo
invoking of testFoo return false
$ java reflection/Deet reflection.Deet xx XX XX
invoke method testDeet
java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at reflection.Deet.main(Deet.java:60)
Caused by: java.util.MissingResourceException: Couldn't find 3-letter language code for xx
at java.util.Locale.getISO3Language(Locale.java:1603)
at reflection.Deet.testDeet(Deet.java:19)
... 5 more
invoking of testDeet throw java.lang.reflect.InvocationTargetException
invoking of testDeet return null
invoke method testFoo
invoking of testFoo return false
使用可变数量的参数调用方法
InvokeMain 示例展示了如何根据传递的参数集合调用任意一个类中的入口函数main()。
$ java reflection/InvokeMain reflection.Deet reflection.Deet ja JP JP
invoking reflection.Deet.main()
invoke method testDeet
local = 日文 (日本,JP),ISO language code = jpn
invoking of testDeet return true
invoke method testFoo
invoking of testFoo return false
类型擦除导致的 NoSuchMethodException
MethodTrouble 示例说明了当在类中搜索特定方法时不考虑类型擦除时会发生什么。
$ java reflection/MethodTrouble lookup java.lang.Integer
java.lang.NoSuchMethodException: reflection.MethodTrouble.lookup(java.lang.Integer)
at java.lang.Class.getMethod(Class.java:1786)
at reflection.MethodTrouble.main(MethodTrouble.java:22)
$ java reflection/MethodTrouble find java.lang.Integer
found
public void reflection.MethodTrouble.find(java.lang.Integer)
$ java reflection/MethodTrouble find java.lang.Object
java.lang.NoSuchMethodException: reflection.MethodTrouble.find(java.lang.Object)
at java.lang.Class.getMethod(Class.java:1786)
at reflection.MethodTrouble.main(MethodTrouble.java:22)
调用方法时报 IllegalAccessException
当试图调用一个私有的或者不可访问的方法时,将抛出IllegalAccessException异常。
MethodTroubleAgain 示例演示了当试图访问一个类中的私有方法时将导致的典型异常。
$ java reflection/MethodTroubleAgain
java.lang.IllegalAccessException: Class reflection.MethodTroubleAgain can not access a member of class reflection.AnotherClass with modifiers "private"
at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:102)
at java.lang.reflect.AccessibleObject.slowCheckMemberAccess(AccessibleObject.java:296)
at java.lang.reflect.AccessibleObject.checkAccess(AccessibleObject.java:288)
at java.lang.reflect.Method.invoke(Method.java:491)
at reflection.MethodTroubleAgain.main(MethodTroubleAgain.java:24)
方法调用时报 IllegalArgumentException
MethodTroubleToo 示例演示了许多方法导致的非预期结果。
$ java reflection/MethodTroubleToo 0
PONG!
$ java reflection/MethodTroubleToo 1
PONG!
$ java reflection/MethodTroubleToo 2
Exception in thread "main" java.lang.IllegalArgumentException: wrong number of arguments
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at reflection.MethodTroubleToo.main(MethodTroubleToo.java:33)
$ java reflection/MethodTroubleToo 3
PONG!
$ java reflection/MethodTroubleToo 4
Exception in thread "main" java.lang.IllegalArgumentException: wrong number of arguments
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at reflection.MethodTroubleToo.main(MethodTroubleToo.java:40)
当调用方法失败时报InvocationTargetException
当方法调用抛出InvocationTargetException时,该异常会封装所有的异常(检查型异常和非检查型异常)。MethodTroubleReturns示例演示如何获取方法调用时的原始异常。
$ java reflection/MethodTroubleReturns
drinkMe failed:I can't drink a negative amount of liquid
构造器的声明包括名称、修饰符、参数和异常声明。java.lang.reflect.Constructor提供了获取这些信息的方式。
ConstructorSift 示例演示了如何搜索一个类声明的构造器。
$ java reflection/ConstructorSift java.util.Formatter java.util.Locale
public java.util.Formatter(java.lang.String,java.lang.String,java.util.Locale) throws java.io.FileNotFoundException,java.io.UnsupportedEncodingException
GenericParameterType[0]: class java.lang.String
GenericParameterType[1]: class java.lang.String
*GenericParameterType[2]: class java.util.Locale
public java.util.Formatter(java.io.File,java.lang.String,java.util.Locale) throws java.io.FileNotFoundException,java.io.UnsupportedEncodingException
GenericParameterType[0]: class java.io.File
GenericParameterType[1]: class java.lang.String
*GenericParameterType[2]: class java.util.Locale
private java.util.Formatter(java.nio.charset.Charset,java.util.Locale,java.io.File) throws java.io.FileNotFoundException
GenericParameterType[0]: class java.nio.charset.Charset
*GenericParameterType[1]: class java.util.Locale
GenericParameterType[2]: class java.io.File
private java.util.Formatter(java.util.Locale,java.lang.Appendable)
*GenericParameterType[0]: class java.util.Locale
GenericParameterType[1]: interface java.lang.Appendable
public java.util.Formatter(java.io.OutputStream,java.lang.String,java.util.Locale) throws java.io.UnsupportedEncodingException
GenericParameterType[0]: class java.io.OutputStream
GenericParameterType[1]: class java.lang.String
*GenericParameterType[2]: class java.util.Locale
public java.util.Formatter(java.lang.Appendable,java.util.Locale)
GenericParameterType[0]: interface java.lang.Appendable
*GenericParameterType[1]: class java.util.Locale
public java.util.Formatter(java.util.Locale)
*GenericParameterType[0]: class java.util.Locale
$ java reflection/ConstructorSift java.lang.String "[C"
java.lang.String(char[],boolean)
*GenericParameterType[0]: class [C
GenericParameterType[1]: boolean
public java.lang.String(char[])
*GenericParameterType[0]: class [C
public java.lang.String(char[],int,int)
*GenericParameterType[0]: class [C
GenericParameterType[1]: int
GenericParameterType[2]: int
$ java reflection/ConstructorSift java.lang.ProcessBuilder "[Ljava.lang.String;"
public java.lang.ProcessBuilder(java.lang.String...)
*GenericParameterType[0]: class [Ljava.lang.String;
$ java reflection/ConstructorSift java.util.HashMap java.util.Map
public java.util.HashMap(java.util.Map<? extends K, ? extends V>)
*GenericParameterType[0]: java.util.Map<? extends K, ? extends V>
构造器的修饰符相比较少
- 访问修饰符:public、protected和private
- 注解
ConstructorAccess 示例演示了如何通过给定类的访问修饰符来查找构造器。
$ java reflection/ConstructorAccess java.io.File private
private java.io.File(java.lang.String,java.io.File)
[isSynthetic = false , var_args = false]
private java.io.File(java.lang.String,int)
[isSynthetic = false , var_args = false]
合成构造器很少见。但是SyntheticConstructor这个示例演示了一个典型的合成构造器应用场景。
$ java reflection/ConstructorAccess reflection.SyntheticConstructor package-private
reflection.SyntheticConstructor(reflection.SyntheticConstructor$1)
[isSynthetic = true , var_args = false]
有两种方法可以创建一个类的实例:java.lang.reflect.Constructor.newInstance()和Class.newInstance()
- Class.newInstance()只能调用无参的构造器,java.lang.reflect.Constructor.newInstance()可以调用任意构造器,而不论它有几个参数。
- Class.newInstance()可以抛出任意异常,无论检查性异常还是非检查性异常;java.lang.reflect.Constructor.newInstance()抛出的所有异常都由InvocationTargetException进行包装。
- Class.newInstance()要求构造器必须可见;java.lang.reflect.Constructor.newInstance()在有些情况下可以调用私有构造器。
参考 ConsoleCharset 示例。
$ java reflection/ConsoleCharset
console.charset : UTF-8
Charset defaultCharset : UTF-8
另外一种常见的应用是反射调用有参数的构造器。RestoreAliases示例演示了如何找到一个单参数的构造器并调用它。
$ java reflection/RestoreAliases
emailAliases :
Fang
Duke
下面这些是开发者在通过反射调用构造器过程中遇到的常见错误。
因缺少无参构造器导致的InstantiationException
ConstructorTrouble 示例演示了代码尝试使用Class.newInstance() 来创建实例,但是没有提供一个可见的无参构造器。
$ java reflection/ConstructorTrouble
java.lang.InstantiationException: reflection.ConstructorTrouble
at java.lang.Class.newInstance(Class.java:427)
at reflection.ConstructorTrouble.main(ConstructorTrouble.java:16)
Caused by: java.lang.NoSuchMethodException: reflection.ConstructorTrouble.<init>()
at java.lang.Class.getConstructor0(Class.java:3082)
at java.lang.Class.newInstance(Class.java:412)
... 1 more
Class.newInstance()抛出非预期异常
ConstructorTroubleToo 示例显示了使用Class.newInstance()中的一个无法解决的问题。
$ java reflection/ConstructorTroubleToo class
Exception in thread "main" java.lang.RuntimeException: throw a runtimeException
at reflection.ConstructorTroubleToo.<init>(ConstructorTroubleToo.java:13)
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
at java.lang.Class.newInstance(Class.java:442)
at reflection.ConstructorTroubleToo.main(ConstructorTroubleToo.java:21)
$ java reflection/ConstructorTroubleToo
java.lang.reflect.InvocationTargetException
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
at reflection.ConstructorTroubleToo.main(ConstructorTroubleToo.java:23)
Caused by: java.lang.RuntimeException: throw a runtimeException
at reflection.ConstructorTroubleToo.<init>(ConstructorTroubleToo.java:13)
... 5 more
定位或调用正确的构造器时遇到的问题
ConstructorTroubleAgain 演示了在使用错误代码定位或调用构造器时将会失败的情形。
$ java reflection/ConstructorTroubleAgain
Exception in thread "main" java.lang.IllegalArgumentException: wrong number of arguments
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
at reflection.ConstructorTroubleAgain.main(ConstructorTroubleAgain.java:32)
$ java reflection/ConstructorTroubleAgain int
java.lang.NoSuchMethodException: reflection.ConstructorTroubleAgain.<init>(int)
at java.lang.Class.getConstructor0(Class.java:3082)
at java.lang.Class.getConstructor(Class.java:1825)
at reflection.ConstructorTroubleAgain.main(ConstructorTroubleAgain.java:34)
$ java reflection/ConstructorTroubleAgain Object
passed object parameter
注意: new 和 Constructor.newInstance() 最大的区别是 new 会完成构造器参数类型检查、封箱和方法解析。但在反射方法中这些都需要显示指定。
尝试调用不可见构造器时报的IllegalAccessException
当尝试调用一个私有或者其他不可见的构造器时将会抛出IllegalAccessException异常。ConstructorTroubleAccess 演示了这种情况下的异常情形。
$ java reflection/ConstructorTroubleAccess
java.lang.IllegalAccessException: Class reflection.ConstructorTroubleAccess can not access a member of class reflection.Deny with modifiers "private"
at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:102)
at java.lang.Class.newInstance(Class.java:436)
at reflection.ConstructorTroubleAccess.main(ConstructorTroubleAccess.java:23)