为啥 InputStreamReader 从 jar 读取时会抛出 NPE?

Posted

技术标签:

【中文标题】为啥 InputStreamReader 从 jar 读取时会抛出 NPE?【英文标题】:Why does InputStreamReader throw a NPE when reading from a jar?为什么 InputStreamReader 从 jar 读取时会抛出 NPE? 【发布时间】:2011-08-26 17:33:55 【问题描述】:

我正在尝试使用流迭代已知目录中的类文件。最终目标是获取特定包中存在的所有类的类名,然后在运行时加载类并使用反射来获取所有静态常量的名称和值。这在我从机器上的源代码运行程序时有效,但是当我将它作为 jar 运行时,BufferedReader 会从ready()readLine() 中抛出一个 NPE。这是代码(为简洁起见,省略了错误处理和最佳实践):

private void printClassNamesInPackage(final String strPackage) throws Exception 
    // The returned implementation of InputStream seems to be at fault
    final InputStream       packageStream = getClass().getClassLoader().getResourceAsStream( strPackage );
    final InputStreamReader streamReader  = new InputStreamReader( packageStream );
    final BufferedReader    reader        = new BufferedReader( streamReader );
    // Throws NPE from inside ready() - SEE STACKTRACE BELOW
    // reader.ready()
    String strLine;
    // Throws NPE from inside readLine() -  SEE STACKTRACE BELOW
    while ( null != (strLine = reader.readLine()) ) 
        System.out.println( strLine );
    

来自reader.ready()的堆栈跟踪

java.lang.NullPointerException
    at java.io.FilterInputStream.available(FilterInputStream.java:142)
    at sun.nio.cs.StreamDecoder.inReady(StreamDecoder.java:343)
    at sun.nio.cs.StreamDecoder.implReady(StreamDecoder.java:351)
    at sun.nio.cs.StreamDecoder.ready(StreamDecoder.java:165)
    at java.io.InputStreamReader.ready(InputStreamReader.java:178)
    at java.io.BufferedReader.ready(BufferedReader.java:436)

来自reader.readLine()的堆栈跟踪

java.lang.NullPointerException
    at java.io.FilterInputStream.read(FilterInputStream.java:116)
    at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:264)
    at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:306)
    at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:158)
    at java.io.InputStreamReader.read(InputStreamReader.java:167)
    at java.io.BufferedReader.fill(BufferedReader.java:136)
    at java.io.BufferedReader.readLine(BufferedReader.java:299)
    at java.io.BufferedReader.readLine(BufferedReader.java:362)

单步执行会发现以下InputStream 实现:

来源:java.io.ByteArrayInputStream 来自罐子:sun.net.www.protocol.jar.JarURLConnection$JarURLInputStream

进一步查看JarURLInputStream,我发现继承的(来自FilterInputStream)字段InputStream innull,这导致了生成的NPE。

不幸的是,这是我在调试器中所能达到的深度。

关于如何正确执行此操作的任何想法?我错过了什么或做错了什么?谢谢!

【问题讨论】:

回顾一下,您是在尝试获取文件夹而不是文件的InputStream,对吧? 是否可以“getClass().getClassLoader().getResourceAsStream(strPackage);”是否返回 null? @BalusC:是的。最初的实现只是为strPackage 目录创建了一个new File,然后使用listFiles() 来获取其中的类。我正在重构它以使用流而不是 File 对象,这样我们就可以支持从 jar 执行。 @JustinKSU:不,你可以看到我提供了getResourceAsStream返回的运行时实现。 文件夹不会返回 InputStream 以及所有文件或其他内容的列表。使用 JarInputStream 以编程方式提取 JAR。 rgagnon.com/javadetails/java-0513.html 【参考方案1】:

文件夹不会返回 InputStream 以及所有文件或其他内容的列表。使用 JarInputStream 以编程方式提取 JAR。你可以找到一个例子here。作为参考,这里有一个稍微修改的相关性摘录:

public static List<String> getClassNamesInPackage(String jarName, String packageName) throws IOException 
    JarInputStream jarFile = new JarInputStream(new FileInputStream(jarName));
    packageName = packageName.replace(".", "/");
    List<String> classes = new ArrayList<String>();

    try 
        for (JarEntry jarEntry; (jarEntry = jarFile.getNextJarEntry()) != null;) 
            if ((jarEntry.getName().startsWith(packageName)) && (jarEntry.getName().endsWith(".class"))) 
                classes.add(jarEntry.getName().replace("/", "."));
            
        
     finally 
        jarFile.close();
    

    return classes;

【讨论】:

感谢您的回复,我不知道JarInputStream 类。这可能很好用,但我担心需要 jar 名称 - 为什么没有解决方案可以保持我从 jar 加载而不是本地文件系统透明的事实?我还没有尝试过,但我会告诉你我发现了什么。 它确实存在,但不是您想要的方式。如果您知道它们的名称,则只能引用类路径上的项目。然后类加载器透明地处理加载:它可能从文件、JAR、网络套接字等加载。没有列出这些项目的通用方法。

以上是关于为啥 InputStreamReader 从 jar 读取时会抛出 NPE?的主要内容,如果未能解决你的问题,请参考以下文章

为啥 InputStreamReader 无法读取 websocket 包的内容?

为啥我的 ja.lproj 不显示日语?

为啥布尔 DBHelper.updateStudents(java.lang.String,java.lang.String,java.lang.String,java.lang.String, ja

inputstream,inputstreamreader,与reader的关系

使用json时为啥转不了net.sf.ezmorph.bean.morphdynabean cannot be cast to ja

InputStream,InputStreamReader和Reader的关系