为什么 getResource() 方法 return 在 JDK 11 中为空?
Why does the getResource() method return null in JDK 11?
简单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();
}
getResource
和 getResourceAsStream
方法在 JDK 8 (java-8-openjdk-amd64) 中工作正常,但它们总是 return null
在 JDK 11.
问题:为什么?我该如何解决这个问题?
- 操作系统:LinuxMint 19 Tara x64
- IDE:日食 2018-12 (4.10.0)
我已经在 MacOS 上用 openjdk 8 和 11 试过你的应用程序,但它不能同时工作。我认为您需要查看 [1] 和 [2] 才能了解 getResourceAsStream
的工作原理。
TLDR:
如果路径是绝对路径(即以斜杠开头 - /
),则 class.getResourceAsStream()
在提供的路径中搜索
如果路径不是绝对路径(即不以斜杠开头),则 class.getResourceAsStream()
在与包名称相对应的构造路径中搜索,其中点被替换为斜线
所以它是否有效取决于两件事:
- 你的路径是绝对路径还是非绝对路径?
file
是否与 class 位于同一个包中?
基本上在您提供的示例中,如果路径不是绝对路径,它将永远无法工作,因为 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 起,在命名模块中的 class 上调用 getResourceXXX 只会定位该模块中的资源,它不会像以前那样搜索 class 路径在以前的版本中。因此,当您使用 Class.class.getResourceAsStream()
时,它将尝试在包含 java.lang.Class
的模块中定位资源,即 java.base
模块。显然你的资源不在那个模块中,所以它 returns 为空。
您必须 java 9+ 在您的模块中搜索文件,这很可能是 "unnamed module"。您可以通过将 Class
更改为模块中定义的任何 class 来做到这一点,以使 java 使用正确的 class 加载器。
还有另一种解决方案,使用 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");
简单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();
}
getResource
和 getResourceAsStream
方法在 JDK 8 (java-8-openjdk-amd64) 中工作正常,但它们总是 return null
在 JDK 11.
问题:为什么?我该如何解决这个问题?
- 操作系统:LinuxMint 19 Tara x64
- IDE:日食 2018-12 (4.10.0)
我已经在 MacOS 上用 openjdk 8 和 11 试过你的应用程序,但它不能同时工作。我认为您需要查看 [1] 和 [2] 才能了解 getResourceAsStream
的工作原理。
TLDR:
如果路径是绝对路径(即以斜杠开头 -
/
),则class.getResourceAsStream()
在提供的路径中搜索如果路径不是绝对路径(即不以斜杠开头),则
class.getResourceAsStream()
在与包名称相对应的构造路径中搜索,其中点被替换为斜线
所以它是否有效取决于两件事:
- 你的路径是绝对路径还是非绝对路径?
file
是否与 class 位于同一个包中?
基本上在您提供的示例中,如果路径不是绝对路径,它将永远无法工作,因为 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 起,在命名模块中的 class 上调用 getResourceXXX 只会定位该模块中的资源,它不会像以前那样搜索 class 路径在以前的版本中。因此,当您使用 Class.class.getResourceAsStream()
时,它将尝试在包含 java.lang.Class
的模块中定位资源,即 java.base
模块。显然你的资源不在那个模块中,所以它 returns 为空。
您必须 java 9+ 在您的模块中搜索文件,这很可能是 "unnamed module"。您可以通过将 Class
更改为模块中定义的任何 class 来做到这一点,以使 java 使用正确的 class 加载器。
还有另一种解决方案,使用 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");