【问题标题】:Why does the getResource() method return null in JDK 11?为什么 JDK 11 中的 getResource() 方法返回 null?
【发布时间】:2019-01-03 11:52:12
【问题描述】:

简单的Java程序:

public static String loadText(String file) {
    StringBuilder finalString = new StringBuilder();

    InputStream in = null;
    BufferedReader reader = null;
    InputStreamReader isr = null;

    try{
        System.out.println("Text File: " + file);

        // Version 1
        //URL url = Thread.currentThread().getClass().getResource(file); 
        //in = url.openStream();

        // Version 2
        in = Class.class.getResourceAsStream(file);

        isr = new InputStreamReader(in);
        reader = new BufferedReader(isr);
        String line;
        while((line = reader.readLine()) != null) {
            finalString.append(line).append("//\n");
        }
    }
    catch(IOException e) {
        e.printStackTrace();
        System.exit(-1);
    }
    finally {
        try {
            if (isr != null) { isr.close(); }
            if (reader != null) { reader.close(); }
            if (in != null) { in.close(); }
            } catch (IOException e) { e.printStackTrace(); }
        }
    return finalString.toString();
}

getResourcegetResourceAsStream 方法在 JDK 8 (java-8-openjdk-amd64) 中运行良好,但在 JDK 11 中它们总是返回 null

问题:为什么?我该如何解决这个问题?

  • 操作系统:Linux Mint 19 Tara x64
  • IDE:Eclipse 2018-12 (4.10.0)

【问题讨论】:

  • 您使用的“文件”参数的典型值是多少?
  • 请检查this问题
  • Class.class.getResourceAsStream(...) 将尝试在 java.base 模块中定位资源。将“Class.class”替换为您的类的名称,以便它在您的模块(或类路径)中找到它。
  • 进一步,值得学习try-with-resources statement...
  • @Alexey a) try-with-resource 已成为该语言的一部分已有十年之久,Java 开发人员无需进一步解释即可理解。它与任何其他高级语言结构没有什么不同,例如for-each 循环或字符串连接,或使用 OOP 概念。冗长并不意味着“不言自明”。 b) 向后兼容什么?您问的是关于 Java 11 的问题,但坚持要兼容 Java 6 甚至更早版本? c) 使用 try-with-resource 在哪些方面会妨碍调试?

标签: java java-11


【解决方案1】:

我已经在 MacOS 上使用 openjdk 8 和 11 尝试过您的应用程序,但两者都不能使用。我认为您需要查看 [1] 和 [2] 以了解 getResourceAsStream 的工作原理。

TLDR:

  1. 如果路径是绝对路径(即以斜杠开头 - /),则 class.getResourceAsStream() 在提供的路径中搜索

  2. 如果路径不是绝对路径(即不以斜杠开头),则 class.getResourceAsStream() 在与包名对应的构造路径中搜索,其中的点被斜杠替换

所以它是否有效取决于两件事:

  1. 你的路径是绝对的吗?
  2. file 是否与类位于同一包中?

基本上在您提供的示例中,如果路径不是绝对路径,它永远无法工作,因为Class.class.getResourceAsStream() 将始终将路径解析为java/lang/<file>,因此您的文件必须在系统包中。因此,您必须使用 <MyClass>.class.getResourceAsStream() 或使用绝对路径

[1]https://docs.oracle.com/javase/7/docs/api/java/lang/ClassLoader.html#getResource(java.lang.String)

[2]https://docs.oracle.com/javase/7/docs/api/java/lang/Class.html#getResource%28java.lang.String%29


更新

从 Java SE 9 开始,对命名模块中的类调用 getResourceXXX 只会定位该模块中的资源,它不会像在以前的版本中那样搜索类路径。因此,当您使用Class.class.getResourceAsStream() 时,它将尝试在包含java.lang.Class 的模块中定位资源,即java.base 模块。显然您的资源不在该模块中,因此它返回 null。

你必须让 java 9+ 搜索你的模块中的文件,这很可能是一个“未命名的模块”。您可以通过将Class 更改为模块中定义的任何类来做到这一点,以使java 使用正确的类加载器。

【讨论】:

  • 很抱歉让您感到困惑。是的,路径是绝对的。包文件夹中的 TXT 文件,其中包含 Java 类文件并包含在构建路径中。文件路径:/configuration/options.txt
  • 好的,那是因为 java 模块。我会更新我的答案
  • 感谢您的解释。从 Class.class.getResourceAsStream(file) 更改为 TextTools.class.getResourceAsStream(file) 解决错误。
  • 呃,这对于 Java 9 及更高版本中的模块来说是一种奇怪的行为。更改为 getClass().getResourceXXX 对我有用。
  • @markthegrea 都是正确的,这取决于您使用的 java 版本
【解决方案2】:

还有一个使用ClassLoader的解决方案

ClassLoader.getSystemResourceAsStream(file)

从所有位置加载。我正在使用它从 JAR 文件中加载资源,该文件仅包含资源而没有可执行代码,因此前面的技巧无法工作,因为 JAR 中没有 SomeClass 可以执行 SomeClass.class.getResourceAsStream()

我只是不了解内部结构,我还必须更改文件的路径。

我在 JAR 存档的根目录中有带有文件“my-file.txt”的 JAR 文件。

对于 java 8,这是可行的

Class.class.getResourceAsStream("/my-file.txt");

对于 java 11,即使文件位于根路径中,我也必须删除前导 /

ClassLoader.getSystemResourceAsStream("my-file.txt");

【讨论】:

    猜你喜欢
    • 2015-07-04
    • 1970-01-01
    • 2017-04-09
    • 2010-10-12
    • 2012-06-13
    • 2015-10-10
    • 1970-01-01
    • 2014-07-14
    • 1970-01-01
    相关资源
    最近更新 更多