j**a 介面作為儲存常量的最佳位置的問題。
由於在 j**a 介面中宣告的字段在編譯時會自動新增靜態 final 修飾符,因此它們被宣告為常量。 因此,介面通常是儲存常量的最佳位置。 但是,當涉及到j**a的實際應用時,存在一些問題。
這個問題有兩個原因,首先是我們使用的常量不是靜態的,而是相對於變數不能改變的。 例如,我們在專案開始時定義常數 314,雖然由於計算準確性的提高,我們可能會重新定義 314159,此時應更改整個專案對此常量的引用。 其次,j**a 是一種動態語言。 與 C++ 等靜態語言不同,j**a 對某些欄位的引用可以在執行時動態進行,這種靈活性是 j**a 等動態語言的一大優勢。 因此,有時我們在更改 J**a 專案中的某些部分內容時不需要重新編譯整個專案,而只需要編譯更改的部分並重新發布即可更改整個應用程式。
說了這麼多,你還不知道我要說什麼?好的,讓我們看乙個簡單的例子:
有乙個介面 a,乙個類 b,如下所示:
//file a.j**apublic interface a//file b.j**apublic class b}
很簡單,好吧,編譯乙個J**a 和 Bj**a。
執行,輸入 j**a b,顯然結果如下:
class a's name = bright
我們現在將修改乙個J**A 如下:
//file a.j**apublic interface a
編譯乙個j**a,重新執行b類,輸入j**a b,注意:結果如下。
class a's name = bright
為什麼不是“A類的名字=光明的大海”?我們使用 JDK 提供的反編譯工具 j**ap 來反編譯 B類檢視它是什麼,鍵入:j**ap -c b,結果如下:
compiled from b.j**apublic class b extends j**a.lang.object method b()0 aload_01 invokespecial #1 4 returnmethod void main(j**a.lang.string)0 getstatic #2 3 ldc #3 5 invokevirtual #4 8 return
注意到標籤 3 的 ** 了嗎?通過引用靜態 final 字段,編譯器已將介面 A 中名稱的內容編譯為類 B,而不是對介面 A 中名稱的引用。 因此,除非我們重新編譯 B 類,否則介面 A 中 Name 的更改不會反映在 B 類中。 如果你這樣做,那麼 J**A 的動態優勢就消失了。
解決方案,有兩種解決方法。
第一種方法是停止使用常量,將必填字段放入類宣告中,並刪除 final 修飾符。 但是,該方法不再是常量存在一定的風險,因此在系統執行時可能會被其他類修改並出現錯誤,這與將其設定為常量的初衷相悖,因此不推薦使用。
第二種方法是將乙個常量放入類宣告中,並使用類方法獲取該常量的值。 為了保持對這個常量的引用簡單,我們可以使用靜態方法。 我們將J**a 和 Bj**a 修改如下:
//file a.j**apublic class a}//file b.j**apublic class b}
同樣,我們編譯乙個J**a 和 Bj**a。執行類 b,鍵入 j**a b,顯然結果是這樣的:
class a’s name = bright
現在讓我們修改乙個J**A 如下:
//file a.j**apublic class a}
讓我們再編譯乙個j**a,重新執行b類,輸入j**a b:結果如下。
class a's name = bright sea
終於得到了我們想要的結果,我們可以再次反編譯 b類 要檢視 B 類更改了什麼,請鍵入:
j**ap -c b,結果如下:
compiled from b.j**apublic class b extends j**a.lang.object method b()0 aload_01 invokespecial #1 4 returnmethod void main(j**a.lang.string)0 getstatic #2 3 new #3 6 dup7 invokespecial #4 10 ldc #5 12 invokevirtual #6 15 invokestatic #7 18 invokevirtual #6 21 invokevirtual #8 24 invokevirtual #9 27 return
注意 10-15 行標記的 **,類 b 已經成為類 a 的 getname() 方法的引用,當常量名稱的值發生變化時,我們只需要修改並重新編譯類 a 中的常量,我們就可以將整個應用程式對這個常量的引用更改,而無需編譯整個專案專案, 既保持了J**A的動態優勢,又保持了使用常量的初衷,所以方法2是最佳解。