Sonar扫描笔记

记录Sonar扫描时碰到的问题以及解决办法。

Add a private constructor to hide the implicit public one.

如果一个类的里面的方法都是static修饰的静态方法(一般是工具类),那么需要给这个类定义一个非公共构造函数(添加私有构造函数以隐藏隐式公共构造函数),为了防止调用者不明白进行了实例化调用,如下:

1
2
3
4
5
6
7
public class UtilityClass {
// Suppress default constructor for noninstantiability
private UtilityClass() {
}

// ...
}

“entrySet()” should be iterated when both the key and value are needed

如果循环中只需要Map映射中的键时,可以迭代keySet。但是,当需要键和值时,推荐迭代entrySet。

不正确的做法:

1
2
3
4
5
6
7
public void doSomethingWithMap(Map<String,Object> map) {
// Noncompliant; for each key the value is retrieved
for (String key : map.keySet()) {
Object value = map.get(key);
// ...
}
}

正确的做法:

1
2
3
4
5
6
7
public void doSomethingWithMap(Map<String,Object> map) {
for (Map.Entry<String,Object> entry : map.entrySet()) {
String key = entry.getKey();
Object value = entry.getValue();
// ...
}
}

Use static access with “com.alibaba.fastjson.JSON” for “parseObject”

父类的静态成员不应该使用子类访问。

不正确的做法:

1
JSONObject jsonObject1 = JSONObject.parseObject(xxx);

正确的做法:

1
JSONObject jsonObject1 = JSON.parseObject(xxx);

“Random” objects should be reused

Creating a new Random object each time a random value is needed is inefficient and may produce numbers which are not random depending on the JDK. For better efficiency and randomness, create a single Random, then store, and reuse it.

The Random() constructor tries to set the seed with a distinct value every time. However there is no guarantee that the seed will be random or even uniformly distributed. Some JDK will use the current time as seed, which makes the generated numbers not random at all.

This rule finds cases where a new Random is created each time a method is invoked and assigned to a local random variable.

Random对象应该被重用,因为Random每次创建一个随机值时是低效的,而且根据JDK的不同,可能会产生非随机的数字。为了提高效率和随机性,创建一个单一的随机变量,然后存储和重用它。

Random()构造函数每次都尝试用不同的值设置种子。然而不能保证种子是随机的,甚至是均匀分布的。一些JDK将使用当前时间作为种子,这使得生成的数字根本不是随机的。

在每次调用方法时创建一个新的随机变量的情况会出现这个提示。

不正确的做法:

1
2
3
4
public void doSomethingCommon() {
Random rand = new Random(); // Noncompliant; new instance created with each invocation
int rValue = rand.nextInt();
//...

正确的做法:

1
2
3
4
5
private Random rand = SecureRandom.getInstanceStrong();  // SecureRandom is preferred to Random

public void doSomethingCommon() {
int rValue = this.rand.nextInt();
//...

SecureRandom.getInstanceStrong()阻塞问题

SecureRandom.getInstanceStrong()方法在Linux环境下使用/dev/random生成种子。但是/dev/random是一个阻塞数字生成器,如果它没有足够的随机数据提供,它就一直等,这迫使 JVM 等待。键盘和鼠标输入以及磁盘活动可以产生所需的随机性或熵。但在一个缺乏这样的活动服务器,可能会出现问题,当系统的熵池中数量不足时,就会阻塞当前线程。

  • /dev/random适用于对随机数质量要求比较高的请求,在熵池中数据不足时,读取dev/random设备时会返回小于熵池噪声总数的随机字节。/dev/random可生成高随机性的公钥或一次性密码本。若熵池空了,对/dev/random的读操作将会被阻塞,直到收集到了足够的环境噪声为止。这样的设计使得/dev/random是真正的随机数发生器,提供了最大可能的随机数据熵。
  • /dev/urandom,非阻塞的随机数发生器,它会重复使用熵池中的数据以产生伪随机数据。这表示对/dev/urandom的读取操作不会产生阻塞,但其输出的熵可能小于/dev/random的。它可以作为生成较低强度密码的伪随机数生成器,对大多数应用来说,随机性是可以接受的。

有了以上的的解释,我们就知道解决方案了,使用 /dev/urandom 这种非阻塞的方式来产生随机数即可解决问题,在Java中我们改成如下写法即可解决问题:

1
SecureRandom random = new SecureRandom();

new SecureRandom()使用/dev/urandom生成种子,不会产生阻塞。

参考


----------- 本文结束啦感谢您阅读 -----------

赞赏一杯咖啡

欢迎关注我的其它发布渠道