抛出不可能的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; }
其中logWriter
是BufferedWriter
。 NPE抛出在调用logWriter.close()
的第一个catch
块中。
但是:如何在我的try
块中抛出IOException
,而我的logWriter是!= null
? Base64.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
实现)。