偶然拜讀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 }
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/206048.html
標籤:Java
上一篇:Java 只有值傳遞!為什么?
