反射-成员

这篇Java教程基于JDK1.8。教程中的示例和实践不会使用未来发行版中的优化建议。
教程:反射之成员

成员 该教程展示了如何使用反射API来获取类的字段、方法和构造器。提供了这些示例:设置和获取字段,方法调用,使用反射获取的构造器创建对象实例。

  • 字段

字段有一个类型和一个值。java.lang.reflect.Field 类提供了能够访问一个对象的类型信息以及获取、设置值的方法。

  1. 获取字段类型信息

一个字段要么是原始类型,要么是引用类型。一共有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
  1. 获取并修改字段的修饰符
  • 能够修饰字段的修饰符有:
  • 访问修饰符: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 ]
  1. 获取并设置字段值

给定一个类的实例,是可以使用反射来设置字段的值 。这只适用于通过一般的方法行不通时才可以使用。因为这样的访问通常违反类的设计意图,所以应该谨慎使用。

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
  1. 故障排查

类型不匹配报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提供了一些方法用来获取参数和返回值的类型信息,它也可以用来在指定的对象上调用方法。

  1. 获取方法类型信息

一个方法的声明包括方法名、修饰符、返回值、参数列表和一系列抛出的异常。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;
  1. 获取方法参数的名字

你可以通过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
  1. 获取并修改方法的修饰符

方法的声明中有许多修饰符:

  • 访问修饰符: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
  1. 方法调用

查找并调用具有特定声明的方法

考虑一个测试套件采用反射来调用一个类中的私有方法。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
  1. 故障排查

类型擦除导致的 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
  • 构造器
  1. 查找构造器

构造器的声明包括名称、修饰符、参数和异常声明。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>
  1. 改变构造器修饰符

构造器的修饰符相比较少

  • 访问修饰符: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]
  1. 创建类的实例

有两种方法可以创建一个类的实例:java.lang.reflect.Constructor.newInstance()Class.newInstance()

参考 ConsoleCharset 示例。

$ java reflection/ConsoleCharset
console.charset : UTF-8
Charset defaultCharset : UTF-8

另外一种常见的应用是反射调用有参数的构造器。RestoreAliases示例演示了如何找到一个单参数的构造器并调用它。

$ java reflection/RestoreAliases
emailAliases : 
  Fang
  Duke
  1. 故障排查

下面这些是开发者在通过反射调用构造器过程中遇到的常见错误。

因缺少无参构造器导致的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)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值