抛出不可能的NullPointerException

在几年前写的一个网络服务器项目中,我偶尔会得到一个非常奇怪的 – 在我看来 – 不可能是NullPointerException 。 它发生在用于在控制台上记录输出的实用程序方法中。

这是该方法的错误摘录:

 try { Encoder encoder = Base64.getEncoder(); if(logWriter != null) { logWriter.write(String.valueOf(System.currentTimeMillis())); logWriter.write(" "); logWriter.write(String.valueOf(level)); logWriter.write(" "); logWriter.write(encoder.encodeToString(Thread.currentThread().getName().getBytes()).replaceAll("(?:\\r\\n|\\n\\r|\\n|\\r)", "")); logWriter.write(" "); logWriter.write(encoder.encodeToString(log.getBytes()).replaceAll("(?:\\r\\n|\\n\\r|\\n|\\r)", "")); logWriter.write("\r\n"); logWriter.flush(); } lastWriterActivity = System.currentTimeMillis(); } catch (IOException e) { println("Failed to write log to file: " + e, Color.RED); try { logWriter.close(); } catch (IOException e1) { println("### Possible resource leak; unable to close log writer", Color.RED); } logWriter = null; } 

其中logWriterBufferedWriter 。 NPE抛出在调用logWriter.close()的第一个catch块中。

但是:如何在我的try块中抛出IOException ,而我的logWriter是!= nullBase64.getEncoder()不能抛出IOException ,也没有其他代码可以执行。

这是我的堆栈跟踪:

 Exception in thread "connection_0:0:0:0:0:0:0:1@1544725509" java.lang.NullPointerException at org.jpuzzle.main.Logger.write(Logger.java:347) at org.jpuzzle.main.Logger.verbose(Logger.java:187) at org.jpuzzle.protocol.http.HttpRequest.onRequest(HttpRequest.java:1090) at org.jpuzzle.network.ConnectionListener$Connection.proceed(ConnectionListener.java:438) at org.jpuzzle.network.ConnectionListener$Connection.run(ConnectionListener.java:408) 

我的方法是synchronized ,所以互斥不应该没有困难,我不明白为什么会发生这种情况。

简短回答:

重写catch块以避免NullPointerException

 try { if(logWriter != null) { logWriter.close(); } } catch (IOException e1) { println("### Possible resource leak; unable to close log writer", Color.RED); } 

答案很长:

是的,logWriter是静态的….不同步阻止方法的parralel方法执行?

没有。

需要certificate吗?

运行此代码:

 public class NowImFeelingZombified { static Object logWriter = new Object(); public static void main(String[] args) { final NowImFeelingZombified zombie1 = new NowImFeelingZombified(); final NowImFeelingZombified zombie2 = new NowImFeelingZombified(); Thread t1 = new Thread("zombie1 ") { @Override public void run() { zombie1.syncedMethod(); } }; Thread t2 = new Thread("zombie2 ") { @Override public void run() { zombie2.syncedMethod(); } }; t1.start(); t2.start(); } private synchronized void syncedMethod() { try { System.out.println(Thread.currentThread().getName() + logWriter.toString()); Thread.sleep(2000L); System.out.println(Thread.currentThread().getName() + logWriter.toString()); logWriter = null; } catch (InterruptedException e) { } } } 

输出是这样的:

 zombie1 java.lang.Object@1c34796b zombie2 java.lang.Object@1c34796b zombie1 java.lang.Object@1c34796b Exception in thread "zombie2 " java.lang.NullPointerException at NowImFeelingZombified.syncedMethod(NowImFeelingZombified.java:44) 

Howsa?

同步方法会获取方法调用方的锁定。 含义t1不关心t2 (当然也不关心logWriter )。

而已。 请阅读此处了解完整故事 。

另外,很久以前我为我继续使用的Util类写了一个这样的5-liner。 它使我免于编写用于关闭Closeables catch

 public static void close(Closeable closeable) { if (closeable != null) { try { closeable.close(); } catch (IOException e) { //logging } } } 

无论如何,Java 1.7引入了您可能想要探索的AutoCloseable (由BufferedWriter实现)。