建議30: 不要隨便設置隨機種子
隨機數在太多的地方使用了,比如加密、混淆數據等,我們使用隨機數是期望獲得一個唯一的、不可仿造的數字,以避免產生相同的業務數據造成混亂。在Java項目中通常是通過Math.random方法和Random類來獲得隨機數的,我們來看一段代碼:
public class Client { public static void main(String[] args) { Random r = new Random(); for(int i=1;i<4;i++){ System.out.println("第"+i+"次:"+r.nextInt()); } } }
代碼很簡單,我們一般都是這樣獲得隨機數的,運行此程序可知:三次打印的隨機數都不相同,即使多次運行結果也不同,這也正是我們想要隨機數的原因。我們再來看下面的程序:
1 public class Client { 2 public static void main(String[] args) { 3 Random r = new Random(1000); 4 for (int i = 1; i < 4; i++) { 5 System.out.println("第" + i + "次:" + r.nextInt()); 6 } 7 } 8 }
上面使用了Random的有參構造,運行結果如下:
第1次:-1244746321 第2次:1060493871 第3次:-1826063944
計算機不同輸出的隨機數也不同,但是有一點是相同的:在同一台機器上,甭管運行多少次,所打印的隨機數都是相同的,也就是說第一次運行,會打印出這三個隨機數,第二次運行還是打印出這三個隨機數,只要是在同一台硬件機器上,就永遠都會打印出相同的隨機數,似乎隨機數不隨機了,問題何在?
那是因為產生隨機數的種子被固定了,在Java中,隨機數的產生取決於種子,隨機數和種子之間的關系遵從以下兩個規則:
種子不同,產生不同的隨機數。
種子相同,即使實例不同也產生相同的隨機數。
看完上面兩個規則,我們再來看這個例子,會發現問題就出在有參構造上,Random類的默認種子(無參構造)是System.nanoTime()的返回值(JDK 1.5版本以前默認種子是System. currentTimeMillis()的返回值),注意這個值是距離某一個固定時間點的納秒數,不同的操作系統和硬件有不同的固定時間點,也就是說不同的操作系統其納秒值是不同的,而同一個操作系統納秒值也會不同,隨機數自然也就不同了。(順便說下,System.nanoTime不能用於計算日期,那是因為“固定”的時間點是不確定的,納秒值甚至可能是負值,這點與System. currentTimeMillis不同。)
new Random(1000)顯式地設置了隨機種子為1000,運行多次,雖然實例不同,但都會獲得相同的三個隨機數。所以,除非必要,否則不要設置隨機種子。
順便提一下,在Java中有兩種方法可以獲得不同的隨機數:通過java.util.Random類獲得隨機數的原理和Math.random方法相同,Math.random()方法也是通過生成一個Random類的實例,然后委托nextDouble()方法的,兩者是殊途同歸,沒有差別。
注意 若非必要,不要設置隨機數種子。
