本教程是针对 JDK 8 编写的。本页面中描述的示例和实践不利用后续版本中引入的改进,并且可能使用不再可用的技术。
请参阅Java 语言更改以获取 Java SE 9 及后续版本中更新的语言特性的摘要。
请参阅JDK 发布说明以获取有关所有 JDK 发布的新功能、增强功能和已删除或弃用选项的信息。
以下示例展示了在反射类时可能遇到的典型错误。
当调用一个方法时,参数值的类型会被检查和可能转换。 调用 ClassWarninggetMethod() 会导致典型的未经检查的转换警告:
import java.lang.reflect.Method;
public class ClassWarning {
void m() {
try {
Class c = ClassWarning.class;
Method m = c.getMethod("m"); // 警告
// 实际代码应该更优雅地处理这个异常
} catch (NoSuchMethodException x) {
x.printStackTrace();
}
}
}
$ javac ClassWarning.java
注意:ClassWarning.java 使用了未经检查或不安全的操作。
注意:重新编译时使用 -Xlint:unchecked 查看详细信息。
$ javac -Xlint:unchecked ClassWarning.java
ClassWarning.java:6: 警告: [unchecked] 对使用了原始类型 Class 的 getMethod
(String,Class<?>...) 的未经检查的调用
Method m = c.getMethod("m"); // 警告
^
1 个警告
许多库方法已经通过泛型声明进行了改进,包括 Class 中的几个方法。由于 c 被声明为一个原始类型(没有类型参数),而 getMethod() 对应的参数是一个参数化类型,因此会发生未经检查的转换。编译器需要生成警告。(参见 Java语言规范,Java SE 7版,第 5.1.9节 未经检查的转换 和 5.3节 方法调用转换。)
有两种可能的解决方案。更可取的是修改 c 的声明,包含一个适当的泛型类型。在这种情况下,声明应为:
Class<?> c = warn.getClass();
或者,可以在有问题的语句之前使用预定义的注解 @SuppressWarnings 显式地抑制警告。
Class c = ClassWarning.class;
@SuppressWarnings("unchecked")
Method m = c.getMethod("m");
// 警告消失
@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
// 产品代码应更优雅地处理这些异常
} 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关键字非常相似,会因为相同的原因而失败。在反射中,通常的解决方案是利用java.lang.reflect.AccessibleObject类来抑制访问控制检查;然而,这种方法不起作用,因为java.lang.Class没有继承AccessibleObject。唯一的解决方案是修改代码,使用扩展了AccessibleObject的Constructor.newInstance()方法。
在成员课程的构造函数故障排除部分中,还可以找到使用Constructor.newInstance()可能遇到的其他问题的示例。