StackOverflow 週報 - 高質量問題的問答(Java、Python)
這是 Stack Overflow 第三週週報,本週加入了 Python 的內容,原計劃兩篇 Java、兩篇 Python。但明天過節所以今天就先把週報發了,兩篇 Java、一篇 Python。公眾號「渡碼」為日更,歡迎關注。
DAY1. 使用隨機數列印"hello world"
今天我們看一個有意思的例子,看看下面的程式碼為什麼每次執行都能輸出 "hello world"。
public static String randomString(int i) { Random ran = new Random(i); StringBuilder sb = new StringBuilder(); while (true) { int k = ran.nextInt(27); System.out.println(k); if (k == 0) { break; } sb.append((char)('`' + k)); } return sb.toString(); }
我們不禁會想,生成隨機數不是隨機嗎,為什麼每次執行結果都一樣。
要解釋這個問題,我們需要了解 “偽隨機” 的概念。只要隨機數是由確定的演算法生成的,那就是偽隨機,也就是說它的生成看似隨機但是有一定規律的。而“真隨機”需要真實的隨機事件取得,所以計算機只能生成偽隨機數。
知道了“偽隨機”概念,今天的例子就好解釋了。在之前的文章中我們看過生成隨機數的程式碼,是根據當前種子(seed)通過特定的規則(演算法)生成隨機數並更新種子,因此 Random 生成的隨機數是“偽隨機”數。此外我們都知道演算法有個特性叫確定性,也就是說相同的輸入只能得出相同的輸出。對於生成隨機數這個演算法來說,每次呼叫只要初始種子(seed)不變,那麼一定會生成相同的隨機數。這就解釋了,為什麼每次執行生成的隨機數序列都是一樣的。
最後,生成的 int 型別隨機數 k,通過 (char)('`' + k) 這行程式碼轉成字元,這裡用到的是 ASCII 碼相關的知識,不再贅述。
今天通過這個簡單的例子瞭解了偽隨機的概念。歡迎交流,關注公眾號每天分享一個知識點。
原文地址
DAY2. Java 巢狀類的兩種形式
Java 中巢狀類有兩種形式,官方定義為:如果巢狀類為靜態的,則稱為靜態巢狀類,如果是非靜態的則稱為內部類。設計巢狀類有以下的好處:
- 程式碼組織:如果我們定義的某個類只服務於當前類,而不會在其他名稱空間中使用,那麼將它定義在當前類的名稱空間中是明智的。就像成員變數,在面向物件程式設計思想下,並不是所有的成員都要定義為全域性的
- 訪問許可權:巢狀類可以直接訪問外部類的成員,即便是 private 修飾的
- 便利性:沒必要為每一個類建立一個檔案
下面看看這兩種類的寫法以及優勢。先看看靜態巢狀類:
class OuterClass { private static int a =10; class StaticNestedClass { } } // 用法 OuterClass.StaticNestedClass nestedObject = new OuterClass.StaticNestedClass();
靜態類初始化時除了類前面加“外部類."外,其他的跟使用一個普通類相同。並且在 nestedObject 物件上使用跟普通物件一樣。靜態類最大的優勢在於可以直接訪問外部類私有的靜態成員。
再看看內部類:
class OuterClass { private int a = 10; class InnerClass { } } // 用法 OuterClass outerObject = new outerObject(); OuterClass.InnerClass innerObject = outerObject.new InnerClass();
與非靜態成員類似,InnerClass 都繫結在一個外部物件上。因此初始化 InnerClass 時,需要先建立 OuterClass 物件。內部類除了可以訪問外部類的靜態私有成員外,還可以直接訪問外部類的非靜態私有成員。當然,內部類使用時有個限制,不能宣告 static 成員。
原文地址
DAY3. 是 Python 的設計缺陷嗎
先看一個問題,猜猜下面程式碼的輸出結果是什麼?
def a(): print("a executed") return [] def b(x=a()): x.append(5) print(x) b() b() b()
輸出結果如下:
a executed [5] [5, 5] [5, 5, 5]
可以看到,每次呼叫 b() 時程式碼的輸出都不一樣。並且,a 函式只執行了一次。因此,我們可以確定,b 函式的預設引數是在函式定義時僅被計算了一次,而不是函式每次呼叫都會計算。這個問題可以這樣看:Python 中函式也是一個物件而預設引數是物件的成員,所以,它會被儲存、更新。但你可能會想,這是不是 Python 的設計缺陷。答案是否定的,如今的 Python 如此流行,如果僅僅是設計缺陷這麼簡單的問題,那麼會很快被修復。那麼,你可能還會想是不是可以讓函式執行時再確定預設引數值,這樣就可以避免上面的問題了。然而這樣同樣會有問題,看下面的例子:
fruits = ("apples", "bananas", "loganberries") def eat(food=fruits): print(food) def some_random_function(): global fruits fruits = ("blueberries", "mangos")
假設,全域性變數 fruits 被修改了, 然後我們呼叫 eat 時,她的預設引數值就跟之前的呼叫不一致了, 同樣會令我們疑惑。而 Python 的設計者認為這種情況給語言使用者造成的疑惑更大,因此他們採用第一種的設計方式,在函式定義時確定預設值。
其實這樣的問題不光 Python 存在,任何語言都存在, 我們下面看一個 Java的例子。
StringBuffer s = new StringBuffer("Hello World"); Map<StringBuffer,Integer> counts = new HashMap<StringBuffer,Integer>(); counts.put(s, 5); s.append("!!!!"); for (Map.Entry<StringBuffer, Integer> entry : counts.entrySet()) { System.out.println(entry.getKey() + "\t" + entry.getValue()); }
我們寫入 counts 的字串是 "Hello World",而輸出是變成了 "Hello World!!!!"。這跟我們剛剛討論的 Python 的問題類似,這裡沒有對錯,無論使用什麼方式,都會有人提出不同意見。到底使用哪種方式是語言的設計者考慮的問題,而每種方式有什麼坑,我們作為語言的使用者應該提前瞭解且避免。
以上便是 Stack Overflow 的第二週週報,希望對你有用,後續會繼續更新,如果想看日更內容歡迎關注公眾號。
公眾號「渡碼」,分享更多高質量內容