Java 教程是为 JDK 8 编写的。本页中描述的示例和实践未利用在后续版本中引入的改进。
以下示例显示了在反映类时可能遇到的典型错误。
当一个方法被调用时,参数值的类型被检查并且可能被转换。 调用 ClassWarninggetMethod() 以产生典型的未经检查的转换警告:
import java.lang.reflect.Method;
public class ClassWarning {
void m() {
try {
Class c = ClassWarning.class;
Method m = c.getMethod("m"); // warning
// production code should handle this exception more gracefully
} catch (NoSuchMethodException x) {
x.printStackTrace();
}
}
}
$ javac ClassWarning.java
Note: ClassWarning.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
$ javac -Xlint:unchecked ClassWarning.java
ClassWarning.java:6: warning: [unchecked] unchecked call to getMethod
(String,Class<?>...) as a member of the raw type Class
Method m = c.getMethod("m"); // warning
^
1 warning
许多库方法已经使用泛型声明进行了改进,包括 Class 中的几个。由于 c 被声明为 raw (原始) 类型(没有类型形参)且 getMethod() 的相应的参数是参数化类型,所以发生未经检查的转换。编译器需要生成警告。(参见 The Java Language Specification, Java SE 7 Edition 中的 Unchecked Conversion 和 Method Invocation Conversion 部分。)
有两种可能的解决方案。比较好的方案是修改 c 的声明以包含适当的泛型类型。在这种情况下,声明应该是:
Class<?> c = warn.getClass();
或者,可以在有问题的语句之前的使用预定义注解 @SuppressWarnings 明确抑制警告。
Class c = ClassWarning.class;
@SuppressWarnings("unchecked")
Method m = c.getMethod("m");
// warning gone
@SuppressWarnings 注解违规的行。
Class.newInstance()如果尝试创建类的新实例,并且类的无参构造函数不可见时,将抛出 InstantiationException 。 示例说明了生成的堆栈跟踪。ClassTrouble
class Cls {
private Cls() {}
}
public class ClassTrouble {
public static void main(String... args) {
try {
Class<?> c = Class.forName("Cls");
c.newInstance(); // InstantiationException
// production code should handle these exceptions more gracefully
} catch (InstantiationException x) {
x.printStackTrace();
} catch (IllegalAccessException x) {
x.printStackTrace();
} catch (ClassNotFoundException x) {
x.printStackTrace();
}
}
}
$ java ClassTrouble
java.lang.IllegalAccessException: Class ClassTrouble can not access a member of
class Cls with modifiers "private"
at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:65)
at java.lang.Class.newInstance0(Class.java:349)
at java.lang.Class.newInstance(Class.java:308)
at ClassTrouble.main(ClassTrouble.java:9)
Class.newInstance()与 new 关键字的行为非常相似,并且会因 new 失败的相同原因而失败。反射中的典型解决方案是利用 java.lang.reflect.AccessibleObject 类,它提供了抑制访问控制检查的能力;但是,这种方案不起作用,因为 java.lang.Class 没有继承 AccessibleObject。唯一的解决方案是修改代码以使用 Constructor.newInstance(),它确实继承了 AccessibleObject。
使用 Constructor.newInstance() 的潜在问题的其他示例可以在 Members 课程的 Constructor Troubleshooting 部分中找到。