1

问题是:在构造函数中初始化 VTDGen、VTDNav 或 AutoPilot 后,在其他方法中它们引用 null 并引发相应的异常。

public class Configuration {
public Configuration(String dir, String filename, ResourceBundle resourceBundle) throws IOException, IndexWriteException, IndexReadException {
    String XMLFilename = filename + ".xml";
    String indexFilename = filename + ".vxl";
    vtdGen = new VTDGen();

    vtdGen.parseFile("D:/Librarian/config/configuration.xml", true);
    vtdNav = vtdGen.getNav();
    autoPilot = new AutoPilot(vtdNav);
    boolean gen = vtdGen!=null;
    boolean nav =  vtdNav!=null;
    boolean pilot = autoPilot!= null;
    System.out.println("VTDGEN - " + gen + "\n" + "VTDNAV - " + nav + "\n" + "AUTOPILOT - " + pilot + "\n");
}

public Configuration(ResourceBundle resourceBundle) {
    try {
        new Configuration(defaultDir, defaultFileName, resourceBundle);
    } catch (IOException | IndexWriteException | IndexReadException e) {
        e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
    }
}

public ArrayList<String> stringValue(String xPathExpression) {
    try {
        boolean pilot = autoPilot != null;
        System.out.println("AUTOPILOT - " + pilot + "\n" + "String.length = " + xPathExpression.length() + "\n" + "String = " + xPathExpression);
        autoPilot.selectXPath(xPathExpression);
        int i;
        while ((i = autoPilot.evalXPath())!=-1) {
            stringResult.add(vtdNav.toString(i));
        }
    } catch (XPathEvalException | NavException | XPathParseException e) {
        e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
    }
    return stringResult;
}

public void writeIndex(File indexFile) {
    try (FileOutputStream fos = new FileOutputStream(indexFile)){
        if (parsed) {
            vtdGen.writeIndex(fos);
        }
    } catch (IndexWriteException | IOException e) {
        e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
    }
}

public File createIndexFile(String dir, String filename){
    indexFile = new File(dir + filename + ".vxl");
    writeIndex(indexFile);
    return indexFile;
}

private final String defaultDir = System.getProperty("user.dir") + "/config/";
private final String defaultFileName = "configuration";
private final String defaultXMLFile = defaultFileName + ".xml";
private final String defaultIndexFile = defaultFileName + ".vxl";

private boolean parsed;
private VTDGen vtdGen;
private VTDNav vtdNav;
private AutoPilot autoPilot;
private File indexFile;
private ArrayList<String> stringResult;
}

结果是:

VTDGEN - true
VTDNAV - true
AUTOPILOT - true
AUTOPILOT - false
String.length = 30
String = /configuration/lastMode/text()

例外:

Exception in thread "JavaFX Application Thread" java.lang.RuntimeException: java.lang.reflect.InvocationTargetException
...
Caused by: java.lang.reflect.InvocationTargetException
...
Caused by: java.lang.NullPointerException
at core.config.Configuration.stringValue(Configuration.java:45)
at UI.PrimaryStageController.test(PrimaryStageController.java:77)
... 57 more

为什么 autoPilot 引用 null?

4

2 回答 2

2

我的猜测是您正在调用此构造函数:

public Configuration(ResourceBundle resourceBundle) {
    try {
        new Configuration(defaultDir, defaultFileName, resourceBundle);
    } catch (IOException | IndexWriteException | IndexReadException e) {
        e.printStackTrace(); 
    }
}

这并不像你认为的那样。它试图创建一个新Configuration对象,但实际上并没有对它做任何事情。无论该构造函数是否成功完成,实例中的所有实例变量都将具有其默认值。

可以链接到另一个构造函数,而不是使用this- 但随后您必须声明此构造函数可以抛出相同的检查异常:

public Configuration(ResourceBundle resourceBundle)
    throws IOException, IndexWriteException, IndexReadException {
    this(defaultDir, defaultFileName, resourceBundle);
}

当您链接到另一个构造函数时,您无法捕获异常。

如果你真的(真的,真的)想要抑制这样的异常,你应该把它变成一个静态方法,并确保你返回一个对新构造的带有实际数据的对象的引用。

public static Configuration fromResourceBundle(ResourceBundle resourceBundle) {
    try {
        return new Configuration(defaultDir, defaultFileName, resourceBundle);
    } catch (IOException | IndexWriteException | IndexReadException e) {
        // Swallowing exceptions? Really?
        e.printStackTrace();
        // At least the caller will get a null reference back instead of
        // a broken object...
        return null;
    }
}
于 2013-08-01T10:56:00.727 回答
0

使用this

public Configuration(ResourceBundle resourceBundle) throws Exception {
    this(Configuration(defaultDir, defaultFileName, resourceBundle));
}

在构造函数中:

public Configuration(ResourceBundle resourceBundle) {
    try {
        new Configuration(defaultDir, defaultFileName, resourceBundle);
    } catch (IOException | IndexWriteException | IndexReadException e) {
        e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
    }
}

您创建了另一个配置实例,它为您提供了输出

VTDGEN - true
VTDNAV - true
AUTOPILOT - true

您正在另一个实例上调用方法,该实例使用字段的默认值初始化

编辑

正如 Jon Skeet 所说,您需要抛出异常。在构造函数中吞入异常是一种不好的做法,因为您成功从构造函数返回并且对象未正确初始化

于 2013-08-01T10:57:29.730 回答