解讀王垠博客“一道 Java 面試題”


       偶然拜讀IT界知名大佬王垠老師的博客,發現一個有意思的題目:

1 // 這段代碼里面到底哪一行錯了?為什么?
2 // 原文:http://www.yinwang.org/blog-cn/2020/02/13/java-type-system
3 public static void f() {
4     String[] a = new String[2];
5     Object[] b = a;
6     a[0] = "hi";
7     b[1] = Integer.valueOf(42);
8 }

       雖然小菜才疏學淺,但本着學習交流的態度,寫下此篇文章來分析一下這個問題。

 

       首先我們要讀懂每一行代碼在做什么:

      

       String[] a = new String[2]; 定義一個字符串類型的數組a,並初始化。

       Object[] b = a; 定義一個對象類型的數組b,並將字符串類型數組a賦值給b。

       a[0] = "hi"; 使用變量a訪問數組中的第一個元素,賦值。

       b[1] = Integer.valueOf(42); 使用變量b訪問數組中的第二個元素,賦值。

 

       只有簡單的四行代碼,相信讀者都可以看的懂。

       先不考慮太多,直接執行一下代碼,編譯通過,運行報錯:java.lang.ArrayStoreException: java.lang.Integer。

       錯誤提示我們第四行代碼有問題,不可以將整型數據存儲到數組b中,而b是一個Object類型的數組,編譯通過,卻無法賦值。

       分析一下原因,數組b的引用指向數組a,我們操作數組b,實際在內存中,訪問的應該是數組a,而數組a是一個字符串類型數組,整個過程中,並不存在Object類型的數組,僅有一個字符串類型的數組在內存中被創建,如圖:

       變量a和變量b只不過是門面,通過這兩道門,到達的是同一個房間。只不過a門只允許String類型通過,而b門沒有任何限制。

       因此,假如我們寫下a[0] = Integer.valueOf(42);,編譯器立刻會發現錯誤,提示類型錯誤,而b[1] = Integer.valueOf(42);的寫法是符合規則的,但由於實際數據結構是String數組,所以運行肯定無法通過。

       為什么會這樣?出現這種問題的根本原因,在於Object[] b = a;,嚴格來說,這種語法是錯誤的,但是在JDK規范中卻被認可。

       為什么說是錯誤的?面向對象中的繼承我們再熟悉不過了,子類完全具有父類的能力,子類可以退化成為父類。

       單說String的確是Object的子類,完全符合規則,但數組是另一回事,本例中String數組僅僅能容納String類型的元素,而Object數組可以容納任意類型的元素,String數組並非完全具有Object數組的能力。

       從另一個角度看,無論是String[] a還是Object[] b,這兩種寫法中的變量a和變量b,僅僅能決定指針的指向(引用哪個具體的數組),而無法控制數組內的元素,只能整體操作,而數組必然要涉及某個元素的部分操作,這就造成數組內部數據結構的“逸出”,必然會出現問題。

綜上,數組之間的抽象是錯誤的,數組之間沒有直接的繼承的能力,不屬於面向對象繼承的討論范疇。

       實際編寫代碼時,不必過分糾結這個問題,盡量不使用這種危險的操作,而是用更加優雅的方式去實現:

1 // 這樣就能很好的發現錯誤,避免給自己挖坑
2 public static void f() {
3     String[] a = new String[2];
4     Object b = a;  //數組本身也是對象
5     a[0] = "hi";
6     ((String[]) b)[1] = Integer.valueOf(42);
7 }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM