更好的默认NullPointerException消息是否会传入Java?
我最近對OpenJDK core-libs-dev郵件列表上的2019年 2月至2019年 3月的討論感興趣,該討論涉及解決缺少與NullPointerException相關聯的詳細消息的問題,該消息在用其無參數構造函數實例化后拋出。 這是我在使用Java時經常遇到的一個問題,甚至導致我在某些情況下更改代碼以更好地解決該問題。
在許多情況下,如果語句中僅存在一個NullPointerException可能來源,并且堆棧跟蹤中有行號(不是),則NullPointerException (NPE)可以是更容易解決的異常之一(或至少診斷為null )。用-g:none 編譯 )。
盡管對于Java NullPointerException來說尤其具有挑戰性,但是即使在某些情況下,即使對于有經驗的Java開發人員,沒有消息的NullPointerException也會令人失望。 沒有與NullPointerException關聯的消息時,最明顯的情況是給定語句中有多個候選者可能拋出NullPointerException 。 這種情況的一個示例是以這種方式在每個先前方法的返回對象上調用方法: getA().getB().getC()... ,其中每個方法都可能返回null 。 另一個示例是,如果調用者將null傳遞給被取消引用為原始類型的方法,則方法(或構造函數)的原始數據類型的多個參數可能導致NullPointerException 。
增強功能JDK-8218628 (“向NullPointerException添加詳細消息,描述什么為null?!?#xff09;解決了其中一些情況。 此增強功能的描述指出:“獲取NPE時,通常很難確定表達式中的哪個引用為空。 這項更改會添加一條消息告訴您?!?此增強功能還提供了一些Java語句示例,這些示例通常會導致NullPointerException并可能令人沮喪地缺乏細節。 我已經在GitHub托管的類NpeDemo中捕獲了與這些示例類似的情況(請參閱此版本以匹配下面輸出中的行號)。 當執行這些演示示例(它們全部有意拋出NPE)時,使用默認設置進行編譯時,輸出顯示如下,如下所示(仍提供完整的堆棧信息):
========================================= | # | # 1 : Element [ null boolean array | ] on : Element [ 0 ] on ========================================= java.lang.NullPointerException at dustin.examples.npe.NpeDemo.demonstrateFirstExampleIndexAccessOnNullBooleanArray(NpeDemo.java: 37 ) at dustin.examples.npe.NpeDemo.demonstrateJdk8218628Examples(NpeDemo.java: 179 ) at dustin.examples.npe.NpeDemo.main(NpeDemo.java: 310 ) ================================= | # | # 2 : .length on null boolean [] | ================================= java.lang.NullPointerException at dustin.examples.npe.NpeDemo.demonstrateSecondExampleLengthOnNullBooleanArray(NpeDemo.java: 59 ) at dustin.examples.npe.NpeDemo.demonstrateJdk8218628Examples(NpeDemo.java: 180 ) at dustin.examples.npe.NpeDemo.main(NpeDemo.java: 310 ) ======================================= | # | # 3 : Assigning float : Assigning to null float [] | ======================================= java.lang.NullPointerException at dustin.examples.npe.NpeDemo.demonstrateThirdExampleAssigningValueToElementOfNullFloatArray(NpeDemo.java: 80 ) at dustin.examples.npe.NpeDemo.demonstrateJdk8218628Examples(NpeDemo.java: 181 ) at dustin.examples.npe.NpeDemo.main(NpeDemo.java: 310 ) ====================================== | # | # 4 : Accessing field on null object | : Accessing field on object | ====================================== java.lang.NullPointerException at dustin.examples.npe.NpeDemo.demonstrateFourthExampleAccessInstanceFieldOfNullObject(NpeDemo.java: 101 ) at dustin.examples.npe.NpeDemo.demonstrateJdk8218628Examples(NpeDemo.java: 182 ) at dustin.examples.npe.NpeDemo.main(NpeDemo.java: 310 ) =================== | # | # 5 : throw null ; | ; | =================== java.lang.NullPointerException at dustin.examples.npe.NpeDemo.demonstrateFifthExampleThrowingConstantNull(NpeDemo.java: 121 ) at dustin.examples.npe.NpeDemo.demonstrateJdk8218628Examples(NpeDemo.java: 183 ) at dustin.examples.npe.NpeDemo.main(NpeDemo.java: 310 ) ================================================ | # | # 6 : Method invocation on null instance field | : Method invocation on ================================================ java.lang.NullPointerException at dustin.examples.npe.NpeDemo.demonstrateSixthExampleMethodInvocationOnNullInstanceField(NpeDemo.java: 141 ) at dustin.examples.npe.NpeDemo.demonstrateJdk8218628Examples(NpeDemo.java: 184 ) at dustin.examples.npe.NpeDemo.main(NpeDemo.java: 310 ) ============================================= | # | # 7 : () on null instance field | () on synchronized () on instance field | ============================================= java.lang.NullPointerException at dustin.examples.npe.NpeDemo.demonstrateSeventhExampleSynchronizedNullInstanceField(NpeDemo.java: 161 ) at dustin.examples.npe.NpeDemo.demonstrateJdk8218628Examples(NpeDemo.java: 185 ) at dustin.examples.npe.NpeDemo.main(NpeDemo.java: 310 ) ========================================================================== | <<< Null Lost in Long Series of Method Invocations in Single Statement | ========================================================================== java.lang.NullPointerException at dustin.examples.npe.NpeDemo.demonstrateNullLostInSeriesOfMethodInvocationsInSingleStatement(NpeDemo.java: 198 ) at dustin.examples.npe.NpeDemo.main(NpeDemo.java: 311 ) ======================================================= | <<< Null Lost in Dereferenced Constructor Arguments | ======================================================= java.lang.NullPointerException at dustin.examples.npe.NpeDemo.demonstrateNullLostInConstructorAcceptingMultiplePotentiallyNullArgumentsDereferenced(NpeDemo.java: 226 ) at dustin.examples.npe.NpeDemo.main(NpeDemo.java: 312 ) ================================================== | <<< Null Lost in Dereferenced Method Arguments | ================================================== java.lang.NullPointerException at dustin.examples.npe.NpeDemo.demonstrateNullLostInMethodAcceptingMultiplePotentiallyNullArgumentsDereferenced(NpeDemo.java: 254 ) at dustin.examples.npe.NpeDemo.main(NpeDemo.java: 313 )上面示例中顯示的NullPointerException沒有提供任何消息。 但是,在這些情況下,罪魁禍首相對容易識別,因為它們發生的方法很小,并且行號直接指向拋出NPE的位置。 如果沒有行號(使用-g:none編譯的源代碼)并且方法很長(可能會拋出NPE的多行),或者方法的重載版本具有相同的名稱,則將很難識別這些代碼。
如果使用-g:none編譯了代碼,則堆棧跟蹤中將不會顯示類名或行號[僅列出(Unknown Source)而不是(文件名:行號)],檢測起來可能會比較棘手。拋出NPE的位置,特別是如果是從冗長的方法中拋出很多NPE的方法拋出的,或者是從同一類中多次重載的方法拋出的,尤其是僅使用方法名稱就沒有幫助。
上面演示的一些示例都具有NPE,即使人們知道行號也很難識別NPE,因為該行上有很多潛在的NPE投擲者。 在這種情況下,最受JDK-8218628建議的更改。
盡管已為JDK-8218628實現了一個解決方案,但此后已確定要考慮足夠的考慮因素以證明JDK增強提案 (JEP)合理,以制定出更多的設計和實現細節。 該JEP是JDK-8220715 (“向NullPointerException添加詳細消息,描述什么為null”)及其“摘要”狀態,“開發或維護Java應用程序時經常遇到NullPointerExceptions。 NullPointerExceptions通常不包含消息。 這使查找異常原因變得復雜。 該JEP建議增強異常文本,以告知什么為空以及哪個操作失敗。”
JEP JDK-8220715還提供了未明確提供NPE消息時所建議的基本算法的詳細描述。 文字指出,在示例中拋出NullPointerException時,“原始Java代碼不可用”,但該信息仍“存儲在異常對象的'backtrace'字段中”,該字段是“ jvm實施。”
JEP JDK-8220715強調“計算此處提出的NullPointerException消息是相當大的開銷”,但是通過提出“延遲計算消息直到實際訪問消息”來解決該問題。 換句話說,僅當實例化NullPointerException時未提供顯式消息時,才計算“默認” NPE消息。
JEP JDK-8220715的“替代方案”部分指出:“當前的提議是在Java運行時中以C ++的方式實現這一點,直接訪問元空間中的可用數據結構?!?本節考慮了該方法的一些替代方法(例如,通過諸如StackWalker之類的JDK庫來實現),并說明了為什么建議的方法可能比該方法更可取。
有關與建議的NullPointerException消息增強功能相關的更多背景詳細信息,請參見OpenJDK core-libs-dev郵件列表 。 以下是該討論中的一些帖子,每篇帖子都摘錄了一些有趣的內容:
- Goetz Lindenmaier :“…從Java 5開始,我們的內部VM報告詳細的空指針異常消息。 我想將此功能貢獻給OpenJDK。 …消息是通過解析字節碼生成的。 為了在分配NPE時沒有任何開銷,僅在通過getMessage()或序列化訪問消息時才生成該消息。 為此,我在NPE上添加了一個字段,以指示仍需要延遲計算消息。”
- Christoph Langer :“……感謝您最終將其引入OpenJDK。 我知道有人會對這個功能很滿意。”
- 彼得·勒瓦特(Peter Levart) :“確保將NPE_MESSAGE_PENDING初始化為新的String(“ something”),否則您可能會通過字符串interning與其他人共享此常量引用……”
- 安德魯·迪恩(Andrew Dinn) :“此外,如果您希望消息反映出發生異常時實際正在使用的字節碼,那么您確實需要通過從方法元數據中提取字節碼來做到這一點。 JvmtiClassFileReconstitutor返回的字節碼將不包括由ClassFileTransformer安裝的任何字節碼更改。 但是,這可能是蠕蟲病毒的潛在威脅,因為方法的舊版本和新版本以及關聯的字節碼可以同時存在。 您需要確定方法的哪個版本,以及由此產生異常的字節碼。 如果您試圖通過調用JVM從Java做到這一點,那么我認為您將會遇到問題。”
- Goetz Lindenmaier :“最初的實現是C ++,并在發生異常的位置使用給定方法*和BCI遍歷元空間。 因此,它僅使用內存中已經存在的數據。 請參閱jvm.cpp中的JVM_GetExtendedNPEMessage()。 想法是使用StackWalker和ASM在Java中實現這一點。 如果我有正確的字節碼和正確的起點,ASM將有助于實現我認為的分析。”
- Mandy Chung :“我們都認為改善NPE消息是對該平臺的有用增強,并有助于開發人員確定NPE的產生原因。 ……這將引發有關提案功能的討論,然后再討論在VM,庫或組合中實現該功能的最佳方法?!?
- Maurizio Cimadamore :“……此增強功能將是對我們平臺的重要補充……我也認為,這種增強功能的設計空間并不平凡,最好在一種非媒介的介質中進行探索(并捕獲!)。補丁?!?
- Goetz Lindenmaier :“…消息的措辭更好……尤其是對前幾條消息,他們指出了此更改的有用性。 他們恰好說了一連串的取消引用是無效的。”
- Maurizio Cimadamore :“…請找到隨附的基于ASM的補丁。 它只是一個PoC,因此它不能提供與RFE / JEP中討論的消息一樣細的消息,但是可以進行增強以涵蓋自定義調試屬性……”
主題中還有許多其他帖子,上面的帖子是討論的示例。
具有與NPE相關的更好的“默認”信息將是一個受歡迎的補充。 JDK-8218628當前與JDK 13相關聯,但是由于現在存在JDK-8220715 ,因此可能不確定與JDK 13相關聯。為此已編寫了JEP草案 。
翻譯自: https://www.javacodegeeks.com/2019/03/nullpointerexception-messages-coming-java.html
總結
以上是生活随笔為你收集整理的更好的默认NullPointerException消息是否会传入Java?的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 安卓用什么语言开发(安卓什么语言)
- 下一篇: Spring Boot CommandL