你好呀,我是歪歪。
我最近在 stackoverflow 上看到一段代码,怎么说呢。
就是初看一脸懵逼,看懂直接跪下!
我先带你看看 stackoverflow 上的这个问题是啥,然后引出这段代码:
问题特别简单,就一句话:
谁能给我解释一下:为什么这段代码使用随机字符串打印出了 hello world?
代码也很简单,我把它拿出来给你跑一下:
public class MainTest { public static void main(String[] args) { System.out.println(randomString(-229985452) + " " + randomString(-147909649)); } public static String randomString(int i) { Random ran = new Random(i); StringBuilder sb = new StringBuilder(); while (true) { int k = ran.nextInt(27); if (k == 0) break; sb.append((char) ('`' + k)); } return sb.toString(); } }
上面的代码你也可以直接粘贴到你的运行环境中跑一下,看看是不是也输出的 hello world:
高赞回答
高赞回答也特别简单,就这么两句话。
我给你翻译一下,这个哥们说:
当我们调用 Random 的构造方法时,给定了一个“种子”(seed)参数。比如本例子中的:-229985452 或 -147909649。
那么 Random 将从指定的种子值开始生成随机数。
而每个用相同的种子构造的 Random 对象,都会按照产生相同的模式产生数字。
没看的太明白,对不对?
没关系,我给你上一段代码,你就能恍然大悟上面这一段说的是啥事:
public static void main(String[] args) { randomString(-229985452); System.out.println("------------"); randomString(-229985452); } private static void randomString(int i) { Random ran = new Random(i); System.out.println(ran.nextInt()); System.out.println(ran.nextInt()); System.out.println(ran.nextInt()); System.out.println(ran.nextInt()); System.out.println(ran.nextInt()); }
这段代码,在我的机器上运行结果是这样的:
你拿过去跑,你的运行结果也一定是这样的。
这是为什么呢?
答案就在 Javadoc 上写着的:
如果用相同的种子创建了两个 Random 的实例,并且对每个实例进行了相同的方法调用序列,那么它们将生成并返回相同的数字序列。
在上面的代码中两个 -229985452
就是相同的种子,而三次 nextInt()
调用,就是相同的调用序列。
所以,他们生成并返回相同的、看起来是随机的数字。
在 new Random() 的时候,不会去指定一个值。
我们都知道 Random 是一个伪随机算法,而构建的时候指定了 seed 参数的就是一个更加伪的伪随机算法了。
因为如果我可以推测出你的 seed 的话,或者你的 seed 泄露了,那么理论上我就可以推测出你随机数生成序列。
这个我已经在前面的代码中演示了。
再看看问题
在前面稍微解释了 “seed” 的关键之处之后,我们再回过头去品一品这个问题,大概就能看出点端倪了。
主要看这个循环里面的代码。
首先 nextInt(27) 就限定了,当前返回的数 k 一定是在 [0,27) 之间的一个数字。
如果返回 0,那么循环结束,如果不为零。则做一个类型转换。
接下来就是一个 char 类型的强制转换。
看到数字转 char 类型,就应该条件反射的想到 ascii 码:
所以,下面这个代码的范围就是 [96+1,96+26]:
'`' + k
也就是 [97,122],即对应 ascii 码的 a-z。
所以,我带你再把上面的演示代码拆解一下。
首先 new Random(-229985452).nextInt(27) 的前五个返回是这样的:
而 new Random(-147909649).nextInt(27) 的前五个返回是这样的:
所以,对照着 ascii 码表看,就能看出其对应的字母了:
8 + 96 = 104 --> h
5 + 96 = 101 --> e
12 + 96 = 108 --> l
12 + 96 = 108 --> l
15 + 96 = 111 --> o
23 + 96 = 119 --> w
15 + 96 = 111 --> o
18 + 96 = 114 --> r
12 + 96 = 108 --> l
4 + 96 = 100 --> d
现在,对于这一段谜一样的代码为什么输出了 “hello world” 的原因,心里是不是拨开云雾见青天,心里跟明镜儿似的。
看穿了,也就是一个小把戏而已。