眾所周知,在Java中final String中的值是一成不變的。大家都知道String的+(拼接)運算會丟棄記憶體引用並在記憶體中重新開拓地址,事實上也確實如此。但final的變數真的是一成不變的嗎?謹以此文開啟程式設計師思路,跳出定式思維,希望本文會給你的程式設計師生涯帶來新的思考。
一個簡單的例子
這個例子很久遠,早有前輩做過,但並不是所有的程式設計師都接觸過。通常喜歡“獵奇”的程式設計師對此不會陌生。
import java.lang.reflect.Field;
public class ChangeFinalString {
public static void main(String[] args) throws Exception {
final String s = "12345: caiyongji";
System.out.println(s);
System.out.println("hashcode: " + s.hashCode());
Field f = String.class.getDeclaredField("value");
f.setAccessible(true);
char[] value = (char[]) f.get(s);
value[0] = (char) 20851;
value[1] = (char) 27880;
value[2] = (char) 20844;
value[3] = (char) 20247;
value[4] = (char) 21495;
System.out.println(s);
System.out.println("hashcode: " + s.hashCode());
}
}
複製程式碼
例子中,通過final初始化一個String,然後反射獲取String類中名為value的Field,並重新賦值value。執行後,你會發現String的hashcode值沒有變,String的值卻變了。另外,你還會發現這幾行簡單程式碼的彩蛋。
得出結論
看!是不是覺得常規可以被打破,只要有足夠的技巧就可以在程式設計中為所欲為?也許有些嚴謹的程式設計師會指出hashcode方法的實現依賴於JVM,並不能直接反映記憶體的情況。說實話,你甚至不需要記住本文的例子,我只是想打破你固有的思維模式。
然而,上面的並不是我想說的真正結論。
下面,才是“聖誕版”真正的意義。
也許有些人會覺得收穫頗豐,我又掌握了一個面試問題的答案。沒錯,甚至像阿里、百度這樣的國內巨頭企業中,有一些面試官(注意是有一些),確實會問一些”final變數是不可變”這樣的silly question. 他們鑽研一門語言甚至知道所有具體實現的細節,甚至語言的bug。 但換個角度,在如此鑽研的同時,你是否考慮了你的時間成本、女朋友以及後代?!
介面論
作為一個程式設計師,你是業務邏輯和程式碼實現的介面。沒人在乎你怎麼實現的業務,只在乎你的程式碼是否高效、準確、易用、易擴充(具體講,比如多少ms返回結果,引數結構是否簡單,是否容易新增新功能)。同理,你使用Java作為你實現業務邏輯的工具,你更可以選擇Python、Node.js、Kotlin甚至Linux shell指令碼,那麼你是否要理解Java語言的所有實現?正如我在《如何成為10倍速的程式設計師》中所說的不要記憶。
我是在號召大家不求甚解嗎?
是的。
反設計
如果把程式設計手段當成一種工具,無外乎語言、文件、框架,這些都可以當成是工具的一部分。不要試圖用錘子開啟螺絲釘,雖然你確實可以做到。但這是反設計的。就像這個例子一樣,你可以通過reflect改變final,但說白了,這種技巧並沒有什麼用,它會給你帶來很多麻煩,比如在擴充時、在java版本升級時考慮相容性。
所以,在使用一種“工具”時,要儘量按照工具製造者的思維模式進行使用,所有這些所謂的“技巧”都是在反設計。
最後,專注、嚴謹、邏輯清晰是一個程式設計師的品質,但在技術迭代如此迅速的大背景下,在程式碼中追求極致是一個人類無法做到的(如果用掌握技巧的多來衡量的話,你永遠無法掌握所有技巧,並且,在技術迭代過程中,你原本掌握的技巧也在逐步淘汰。),別忘了你生活中的朋友、家人和你的愛好。
過完聖誕就是新的一年,祝所有的程式設計師朋友聖誕快樂(本文寫於平安夜),同時在新的一年裡開啟程式設計師新的、不一樣的人生。