文档

Java™ 教程
隐藏目录
故障排除
指南: 反射 API
课程:

故障排除

以下示例展示了在反射类时可能遇到的典型错误。

编译器警告:"注意:...使用了未经检查或不安全的操作"

当调用一个方法时,参数值的类型会被检查和可能转换。 ClassWarning 调用 getMethod() 会导致典型的未经检查的转换警告:

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注解来注释有问题的代码行。

构造函数不可访问时的InstantiationException

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。唯一的解决方案是修改代码,使用扩展了AccessibleObjectConstructor.newInstance()方法。


提示: 一般来说,最好使用Constructor.newInstance(),原因在创建新类实例部分中的成员课程中有描述。

成员课程的构造函数故障排除部分中,还可以找到使用Constructor.newInstance()可能遇到的其他问题的示例。


上一页: 发现类成员
下一页: 成员