Java规范之Sonar规则的汉化【BUG类】

2020-08-23  本文已影响0人  onmeiei

规则S1111 : 不可调用Object.finalize()

根据JAVADOC的定义,方法Object.finalize()应该由垃圾回收器在进行垃圾回收时调用。
在代码中调用该方法,将会影响到垃圾回收。

public void dispose() throws Throwable {
  this.finalize();                       // 错误的使用方式
}

规则S1114 : “super.finalize()”应该在"Object.finalize()"的最后被调用

只有在会导致资源泄漏时,才进行 Object.finalize() 的重写。

非常建议在该方法的最后,调用 super.finalize() 以保证父类的资源被合理的释放。

protected void finalize() {   // 错误的使用方式; no call to super.finalize();
  releaseSomeResources();
}

protected void finalize() {
  super.finalize();  // 错误的使用方式; this call should come last
  releaseSomeResources();
}
protected void finalize() {
  releaseSomeResources();
  super.finalize();
}

规则S1143 : 在"finally"代码块中不可出现跳转

finally代码块中,使用 return, break, throw等进行跳转,将导致异常trycatch中抛出的异常,无法被正确处理。

public static void main(String[] args) {
  try {
    doSomethingWhichThrowsException();
    System.out.println("OK");   // incorrect "OK" message is printed
  } catch (RuntimeException e) {
    System.out.println("ERROR");  // this message is not shown
  }
}

public static void doSomethingWhichThrowsException() {
  try {
    throw new RuntimeException();
  } finally {
    for (int i = 0; i < 10; i ++) {
      //...
      if (q == i) {
        break; // ignored
      }
    }

    /* ... */
    return;      // 错误的使用方式 - prevents the RuntimeException from being propagated
  }
}
public static void main(String[] args) {
  try {
    doSomethingWhichThrowsException();
    System.out.println("OK");
  } catch (RuntimeException e) {
    System.out.println("ERROR");  // "ERROR" is printed as expected
  }
}

public static void doSomethingWhichThrowsException() {
  try {
    throw new RuntimeException();
  } finally {
    for (int i = 0; i < 10; i ++) {
      //...
      if (q == i) {
        break; // ignored
      }
    }

    /* ... */
  }
}

规则S1145 : 无用的 "if(true) {...}"和"if(false){...}"代码块应当被移除

永远为true或false的代码块,将影响代码的可读性及维护性。

if (true) {
  doSomething();
}
...
if (false) {
  doSomethingElse();
}

if (2 < 3 ) { ... }  // 错误的使用方式; always false

int i = 0;
int j = 0;
// ...
j = foo();

if (j > 0 && i > 0) { ... }  // 错误的使用方式; always false - i never set after initialization

boolean b = true;
//...
if (b || !b) { ... }  // 错误的使用方式
doSomething();
...

This rule is deprecated; use {rule:java:S2583} instead.

规则S1175 : 禁止使用方法名"finalize()"

Object.finalize() 会被垃圾回收调用,以释放对象使用的系统资源。禁止非特殊需要而重写该方法。如果使用该方法名,将可能导致错误的垃圾回收行为。

public int finalize(int someParameter) {        // 错误的使用方式
  /* ... */
}
public int someBetterName(int someParameter) {  // Compliant
  /* ... */
}

规则S1201 : "equals"方法应当使用"Object"类型作为参数

"equals"用于对比两个对象 Object.equals(Object) 以避免对象冲突。所以,需要传入Object类型作为参数。

class MyClass {
  private int foo = 1;

  public boolean equals(MyClass o) {  // 错误的使用方式; does not override Object.equals(Object)
    return o != null && o.foo == this.foo;
  }

  public static void main(String[] args) {
    MyClass o1 = new MyClass();
    Object o2 = new MyClass();
    System.out.println(o1.equals(o2));  // Prints "false" because o2 an Object not a MyClass
  }
}

class MyClass2 {
  public boolean equals(MyClass2 o) {  // Ignored; `boolean equals(Object)` also present
    //..
  }

  public boolean equals(Object o) {
    //...
  }
}
class MyClass {
  private int foo = 1;

  @Override
  public boolean equals(Object o) {
    if (this == o) {
        return true;
    }
    if (o == null || getClass() != o.getClass()) {
      return false;
    }

    MyClass other = (MyClass)o;
    return this.foo == other.foo;
  }

  /* ... */
}

class MyClass2 {
  public boolean equals(MyClass2 o) {
    //..
  }

  public boolean equals(Object o) {
    //...
  }
}

规则S1206 : "equals(Object obj)"和 "hashCode()"应该同时定义,不可只定义其中一个

根据Java官方文档的定义,这两个方法被同时用于对象的比较。所以在定义equals或hashcode方法是,需要同时定义两个方法。

class MyClass {    // 错误的使用方式 - should also override "hashCode()"

  @Override
  public boolean equals(Object obj) {
    /* ... */
  }

}
class MyClass {    // Compliant

  @Override
  public boolean equals(Object obj) {
    /* ... */
  }

  @Override
  public int hashCode() {
    /* ... */
  }

}

规则S1217 : 禁止直接使用"Thread.run()"

Thread.run()的设计目的时在专用的额外线程中执行该方法,直接调用将导致在当前同一个线程中执行方法。需要使用Thread.start()

Thread myThread = new Thread(runnable);
myThread.run(); // 错误的使用方式
Thread myThread = new Thread(runnable);
myThread.start(); // Compliant

规则S1221 : 方法不应被定义为 "tostring", "hashcode"或"equal"

如此定义,将导致代码的误解,与对象的方法toStringhashCode以及equals混淆。

public int hashcode() { /* ... */ }  // 错误的使用方式

public String tostring() { /* ... */ } // 错误的使用方式

public boolean equal(Object obj) { /* ... */ }  // 错误的使用方式
@Override
public int hashCode() { /* ... */ }

@Override
public String toString() { /* ... */ }

@Override
public boolean equals(Object obj) { /* ... */ }

规则S1226 : 不可赋值给:方法参数、捕获到的异常、foreach的变量

赋值给这些变量将导致代码可维护性差,代码逻辑混乱。

public void doTheThing(String str, int i, List<String> strings) {
  str = Integer.toString(i); // 错误的使用方式

  for (String s : strings) {
    s = "hello world"; // 错误的使用方式
  }
}

规则S1244 : 不可以对浮点数使用==!=进行判断。

不可以对浮点数(float或double)进行==!=进行判断,将导致结果不可预期。

float myNumber = 3.146;
if ( myNumber == 3.146f ) { // 错误示范. Because of floating point imprecision, this will be false
  // ...
}
if ( myNumber != 3.146f ) { // 错误示范. Because of floating point imprecision, this will be true
  // ...
}

if (myNumber < 4 || myNumber > 4) { // 错误的使用方式; indirect inequality test
  // ...
}

float zeroFloat = 0.0f;
if (zeroFloat == 0) {  // 错误的使用方式. Computations may end up with a value close but not equal to zero.
}

由于 NaN与其本身进行比较,会得到false,因此可以使用==!=进行判断。

不过,依然更推荐使用 Double.isNaN进行判断,而不是使用==!=

float f;
double d;
if(f != f) { // Compliant; test for NaN value
  System.out.println("f is NaN");
} else if (f != d) { // 错误的使用方式
  // ...
}

规则S1317 : 不可以使用char进行"StringBuilder"和"StringBuffer"的初始化

使用单个char进行初始化,其实会使用char对应的数字,对StringBuilder和StringBuffer进行初始化,而不是使用char进行初始化。

StringBuffer foo = new StringBuffer('x');   //equivalent to StringBuffer foo = new StringBuffer(120);
StringBuffer foo = new StringBuffer("x");

规则S1656 : 变量不可以赋值给自身

赋值给自身将导致,代码逻辑易读性差,无法维护。

public void setName(String name) {
  name = name;
}
public void setName(String name) {
  this.name = name;
}

规则S1751 : 最多执行一次的循环逻辑,必须被重构

循环逻辑是用于多次执行的,如果一个逻辑只执行一次,但是使用了循环语法,将会导致代码的误解。

没有程序员会故意设计只执行一次的循环逻辑。

for (int i = 0; i < 10; i++) { // 错误的使用方式, loop only executes once
  printf("i is %d", i);
  break;
}
...
for (int i = 0; i < 10; i++) { // 错误的使用方式, loop only executes once
  if (i == x) {
    break;
  } else {
    printf("i is %d", i);
    return;
  }
}
for (int i = 0; i < 10; i++) {
  printf("i is %d", i);
}
...
for (int i = 0; i < 10; i++) {
  if (i == x) {
    break;
  } else {
    printf("i is %d", i);
  }
}

规则S1764 : 二元操作符的两元,不可以相同

如果二元操作符的两元相同,将导致逻辑永远为错误或正确。导致代码逻辑与设计不同。

if ( a == a ) { // always true
  doZ();
}
if ( a != a ) { // always false
  doY();
}
if ( a == b && a == b ) { // if the first one is true, the second one is too
  doX();
}
if ( a == b || a == b ) { // if the first one is true, the second one is too
  doW();
}

int j = 5 / 5; //always 1
int k = 5 - 5; //always 0

c.equals(c); //always true
float f;
if(f != f) { //test for NaN value
  System.out.println("f is NaN");
}

int i = 1 << 1; // Compliant
int j = a << a; // 错误的使用方式

规则S1849 : "Iterator.hasNext()"中,不可调用"Iterator.next()"方法

在实现hasNext()方法时,可以调用next()方法;否则将导致游标发生变化。

public class FibonacciIterator implements Iterator<Integer>{
...
@Override
public boolean hasNext() {
  if(next() != null) {
    return true;
  }
  return false;
}
...
}

规则S1850 : 不可以使用永远为true或false的"instanceof" 判断

会导致代码误解,可读性差。

public boolean isSuitable(Integer param) {
...
  String name = null;

  if (name instanceof String) { // 错误的使用方式; always false since name is null
    //...
  }

  if(param instanceof Number) {  // 错误的使用方式; always true unless param is null, because param is an Integer
    doSomething();
  }
...
}
public boolean isSuitable(Integer param) {
...
  doSomething();
...
}

规则S1860 : Synchronized不可以使用String、Int、Long等原始对象

由于JVM对这一类对象的缓存行为,将导致整个系统的线程同步不可控。

private static final Boolean bLock = Boolean.FALSE;
private static final Integer iLock = Integer.valueOf(0);
private static final String sLock = "LOCK";

public void doSomething() {

  synchronized(bLock) {  // 错误的使用方式
    // ...
  }
  synchronized(iLock) {  // 错误的使用方式
    // ...
  }
  synchronized(sLock) {  // 错误的使用方式
    // ...
  }
private static final Object lock1 = new Object();
private static final Object lock2 = new Object();
private static final Object lock3 = new Object();

public void doSomething() {

  synchronized(lock1) {
    // ...
  }
  synchronized(lock2) {
    // ...
  }
  synchronized(lock3) {
    // ...
  }

规则S1862 : "if/else if" 不可以使用相同的判断条件。

if (param == 1)
  openWindow();
else if (param == 2)
  closeWindow();
else if (param == 1)  // 错误的使用方式
  moveWindowToTheBackground();
}
if (param == 1)
  openWindow();
else if (param == 2)
  closeWindow();
else if (param == 3)
  moveWindowToTheBackground();
}

规则S1872 : 不可以使用类名进行类的比较

需要使用 instanceofClass.isAssignableFrom() 进行判断。

package computer;
class Pear extends Laptop { ... }

package food;
class Pear extends Fruit { ... }

class Store {

  public boolean hasSellByDate(Object item) {
    if ("Pear".equals(item.getClass().getSimpleName())) {  // 错误的使用方式
      return true;  // Results in throwing away week-old computers
    }
    return false;
  }

  public boolean isList(Class<T> valueClass) {
    if (List.class.getName().equals(valueClass.getName())) {  // 错误的使用方式
      return true;
    }
    return false;
  }
}
class Store {

  public boolean hasSellByDate(Object item) {
    if (item instanceof food.Pear) {
      return true;
    }
    return false;
  }

  public boolean isList(Class<T> valueClass) {
    if (valueClass.isAssignableFrom(List.class)) {
      return true;
    }
    return false;
  }
}

规则S2055 : 不可序列化的"Serializable"必须设计无参构造函数

public class Fruit {
  private Season ripe;

  public Fruit (Season ripe) {...}
  public void setRipe(Season ripe) {...}
  public Season getRipe() {...}
}

public class Raspberry extends Fruit
        implements Serializable {  // 错误的使用方式; nonserializable ancestor doesn't have no-arg constructor
  private static final long serialVersionUID = 1;

  private String variety;

  public Raspberry(Season ripe, String variety) { ...}
  public void setVariety(String variety) {...}
  public String getVarity() {...}
}
public class Fruit {
  private Season ripe;

  public Fruit () {...};  // Compliant; no-arg constructor added to ancestor
  public Fruit (Season ripe) {...}
  public void setRipe(Season ripe) {...}
  public Season getRipe() {...}
}

public class Raspberry extends Fruit
        implements Serializable {
  private static final long serialVersionUID = 1;

  private String variety;

  public Raspberry(Season ripe, String variety) {...}
  public void setVariety(String variety) {...}
  public String getVarity() {...}
}

规则S2060 : "Externalizable"类必须设计无参构造函数

public class Tomato implements Externalizable {  // 错误的使用方式; no no-arg constructor

  public Tomato (String color, int weight) { ... }
}
public class Tomato implements Externalizable {

  public Tomato() { ... }
  public Tomato (String color, int weight) { ... }
}

规则S2061 : 自定义序列化方法的权限需要符合要求

public class Watermelon implements Serializable {
  // ...
  void writeObject(java.io.ObjectOutputStream out)// 错误的使用方式; not private
        throws IOException
  {...}

  private void readObject(java.io.ObjectInputStream in)
  {...}

  public void readObjectNoData()  // 错误的使用方式; not private
  {...}

  static Object readResolve() throws ObjectStreamException  // 错误的使用方式; this method may have any access modifier, may not be static

  Watermelon writeReplace() throws ObjectStreamException // 错误的使用方式; this method may have any access modifier, but must return Object
  {...}
}
public class Watermelon implements Serializable {
  // ...
  private void writeObject(java.io.ObjectOutputStream out)
        throws IOException
  {...}

  private void readObject(java.io.ObjectInputStream in)
        throws IOException, ClassNotFoundException
  {...}

  private void readObjectNoData()
        throws ObjectStreamException
  {...}

  protected Object readResolve() throws ObjectStreamException
  {...}

  private Object writeReplace() throws ObjectStreamException
  {...}

规则S2066 : "Serializable"内部类必须声明为"static"

public class Pomegranate {
  // ...

  public class Seed implements Serializable {  // 错误的使用方式; serialization will fail
    // ...
  }
}
public class Pomegranate {
  // ...

  public static class Seed implements Serializable {
    // ...
  }
}

规则S2095 : 所有"Closeable"资源必须关闭

连接、文件、网络等资源,必须调用close()进行关闭。

private void readTheFile() throws IOException {
  Path path = Paths.get(this.fileName);
  BufferedReader reader = Files.newBufferedReader(path, this.charset);
  // ...
  reader.close();  // 错误的使用方式
  // ...
  Files.lines("input.txt").forEach(System.out::println); // 错误的使用方式: The stream needs to be closed
}

private void doSomething() {
  OutputStream stream = null;
  try {
    for (String property : propertyList) {
      stream = new FileOutputStream("myfile.txt");  // 错误的使用方式
      // ...
    }
  } catch (Exception e) {
    // ...
  } finally {
    stream.close();  // Multiple streams were opened. Only the last is closed.
  }
}
private void readTheFile(String fileName) throws IOException {
    Path path = Paths.get(fileName);
    try (BufferedReader reader = Files.newBufferedReader(path, StandardCharsets.UTF_8)) {
      reader.readLine();
      // ...
    }
    // ..
    try (Stream<String> input = Files.lines("input.txt"))  {
      input.forEach(System.out::println);
    }
}

private void doSomething() {
  OutputStream stream = null;
  try {
    stream = new FileOutputStream("myfile.txt");
    for (String property : propertyList) {
      // ...
    }
  } catch (Exception e) {
    // ...
  } finally {
    stream.close();
  }
}

可以使用try-with-resource语法进行自动关闭。

try (BufferedReader br = new BufferedReader(new FileReader(fileName))) {
  //...
}
catch ( ... ) {
  //...
}

规则S2097 : "equals(Object obj)"方法需要先判断对象类型

public boolean equals(Object obj) {
  MyClass mc = (MyClass)obj;  // 错误的使用方式
  // ...
}
public boolean equals(Object obj) {
  if (obj == null)
    return false;

  if (this.getClass() != obj.getClass())
    return false;

  MyClass mc = (MyClass)obj;
  // ...
}

规则S2109 : 非Runtime注解,不可以使用反射

Method m = String.class.getMethod("getBytes", new Class[] {int.class,
int.class, byte[].class, int.class});
if (m.isAnnotationPresent(Override.class)) {  // 错误的使用方式; test will always return false, even when @Override is present in the code

规则S2110 : 不可以使用非法"Date"值

由于计数都是从0开始的,所以Date值应严格参考可用范围。

字段 取值范围
month 0-11
date/day 0-31
hour 0-23
minute 0-60
second 0-61
Date d = new Date();
d.setDate(25);
d.setYear(2014);
d.setMonth(12);  // 错误的使用方式; rolls d into the next year

Calendar c = new GregorianCalendar(2014, 12, 25);  // 错误的使用方式
if (c.get(Calendar.MONTH) == 12) {  // 错误的使用方式; invalid comparison
  // ...
}
Date d = new Date();
d.setDate(25);
d.setYear(2014);
d.setMonth(11);

Calendar c = new Gregorian Calendar(2014, 11, 25);
if (c.get(Calendar.MONTH) == 11) {
  // ...
}

规则S2111 : 不可使用"BigDecimal(double)"构造函数

因为float和double的精度问题,使用该构造函数会导致不可控的问题。应当使用valueOf和字符串参数进行构建。

double d = 1.1;

BigDecimal bd1 = new BigDecimal(d); // 错误的使用方式; see comment above
BigDecimal bd2 = new BigDecimal(1.1); // 错误的使用方式; same result
double d = 1.1;

BigDecimal bd1 = BigDecimal.valueOf(d);
BigDecimal bd2 = new BigDecimal("1.1"); // using String constructor will result in precise value

规则S2114 : "Collections"不可作为方法参数传给自己

List <Object> objs = new ArrayList<Object>();
objs.add("Hello");

objs.add(objs); // 错误的使用方式; StackOverflowException if objs.hashCode() called
objs.addAll(objs); // 错误的使用方式; behavior undefined
objs.containsAll(objs); // 错误的使用方式; always true
objs.removeAll(objs); // 错误的使用方式; confusing. Use clear() instead
objs.retainAll(objs); // 错误的使用方式; NOOP

规则S2116 : 不可对数组对象进行"hashCode" 和 "toString"

public static void main( String[] args )
{
    String argStr = args.toString(); // 错误的使用方式
    int argHash = args.hashCode(); // 错误的使用方式

public static void main( String[] args )
{
    String argStr = Arrays.toString(args);
    int argHash = Arrays.hashCode(args);

规则S2118 : Non-serializable类不可以进行写入。

public class Vegetable {  // neither implements Serializable nor extends a class that does
  //...
}

public class Menu {
  public void meal() throws IOException {
    Vegetable veg;
    //...
    FileOutputStream fout = new FileOutputStream(veg.getName());
    ObjectOutputStream oos = new ObjectOutputStream(fout);
    oos.writeObject(veg);  // 错误的使用方式. Nothing will be written
  }
}
public class Vegetable implements Serializable {  // can now be serialized
  //...
}

public class Menu {
  public void meal() throws IOException {
    Vegetable veg;
    //...
    FileOutputStream fout = new FileOutputStream(veg.getName());
    ObjectOutputStream oos = new ObjectOutputStream(fout);
    oos.writeObject(veg);
  }
}

规则S2119 : "Random"对象必须进行复用

不复用,将导致Random不可控,会返回相同的值。

public void doSomethingCommon() {
  Random rand = new Random();  // 错误的使用方式; new instance created with each invocation
  int rValue = rand.nextInt();
  //...
private Random rand = SecureRandom.getInstanceStrong();  // SecureRandom is preferred to Random

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

规则S2121 : 不可进行无效的"String"操作

String speech = "Now is the time for all good people to come to the aid of their country.";

String s1 = speech.substring(0); // 错误的使用方式. Yields the whole string
String s2 = speech.substring(speech.length()); // 错误的使用方式. Yields ";
String s3 = speech.substring(5,speech.length()); // 错误的使用方式. Use the 1-arg version instead

if (speech.contains(speech)) { // 错误的使用方式
 // always true
}
String speech = "Now is the time for all good people to come to the aid of their country.";

String s1 = speech;
String s2 = ";
String s3 = speech.substring(5);

规则S2122 : "ScheduledThreadPoolExecutor"不可以设置线程

public void do(){

  ScheduledThreadPoolExecutor stpe1 = new ScheduledThreadPoolExecutor(0); // 错误的使用方式

  ScheduledThreadPoolExecutor stpe2 = new ScheduledThreadPoolExecutor(POOL_SIZE);
  stpe2.setCorePoolSize(0);  // 错误的使用方式

规则S2123 : 不可以无用的进行值加操作

public int pickNumber() {
  int i = 0;
  int j = 0;

  i = i++; // 错误的使用方式; i is still zero

  return j++; // 错误的使用方式; 0 returned
}
public int pickNumber() {
  int i = 0;
  int j = 0;

  i++;
  return ++j;
}

规则S2127 : "Double.longBitsToDouble"的参数可用"int"

参数必须为long,不可以为int

int i = 42;
double d = Double.longBitsToDouble(i);  // 错误的使用方式

规则S2134 : "java.lang.Thread"的子类,必须重写"run"

public class MyRunner extends Thread { // 错误的使用方式; run method not overridden

  public void doSometing() {...}
}
class MyThread extends Thread { // Compliant - calling super constructor with a Runnable
  MyThread(Runnable target) {
    super(target); // calling super constructor with a Runnable, which will be used for when Thread.run() is executed
    // ...
  }
}

规则S2141 : 未自定义"hashCode()"方法的类,不可用于Hash操作

Object的hashCode不可控,所以未自定义hashCode方法的类不可用于Hash操作。否则,结果具有不可预测性。

public class Student {  // no hashCode() method; not hash-able
  // ...

  public boolean equals(Object o) {
    // ...
  }
}

public class School {
  private Map<Student, Integer> studentBody = // okay so far
          new HashTable<Student, Integer>(); // 错误的使用方式

  // ...
public class Student {  // has hashCode() method; hash-able
  // ...

  public boolean equals(Object o) {
    // ...
  }
  public int hashCode() {
    // ...
  }
}

public class School {
  private Map<Student, Integer> studentBody = new HashTable<Student, Integer>();

  // ...

规则S2142 : 不可以忽略"InterruptedException",只能重新抛出或调用Thread.currentThread().interrupt()。

public void run () {
  try {
    while (true) {
      // do stuff
    }
  }catch (InterruptedException e) { // 错误的使用方式; logging is not enough
    LOGGER.log(Level.WARN, "Interrupted!", e);
  }
}
public void run () {
  try {
    while (true) {
      // do stuff
    }
  }catch (InterruptedException e) {
    LOGGER.log(Level.WARN, "Interrupted!", e);
    // Restore interrupted state...
    Thread.currentThread().interrupt();
  }
}

规则S2151 : 不可以调用"runFinalizersOnExit"

使用Runtime.addShutdownHook代替。

public static void main(String [] args) {
  ...
  System.runFinalizersOnExit(true);  // 错误的使用方式
  ...
}

protected void finalize(){
  doSomething();
}
public static void main(String [] args) {
  Runtime.addShutdownHook(new Runnable() {
    public void run(){
      doSomething();
    }
  });
  //...

规则S2153 : 不应显式进行封箱和拆箱

JVM会自动进行封箱和拆箱操作,不可显式进行操作。

public void examineInt(int a) {
  //...
}

public void examineInteger(Integer a) {
  // ...
}

public void func() {
  int i = 0;
  Integer iger1 = Integer.valueOf(0);
  double d = 1.0;

  int dIntValue = new Double(d).intValue(); // 错误的使用方式

  examineInt(new Integer(i).intValue()); // 错误的使用方式; explicit box/unbox
  examineInt(Integer.valueOf(i));  // 错误的使用方式; boxed int will be auto-unboxed

  examineInteger(i); // Compliant; value is boxed but not then unboxed
  examineInteger(iger1.intValue()); // 错误的使用方式; unboxed int will be autoboxed

  Integer iger2 = new Integer(iger1); // 错误的使用方式; unnecessary unboxing, value can be reused
}
public void examineInt(int a) {
  //...
}

public void examineInteger(Integer a) {
  // ...
}

public void func() {
  int i = 0;
  Integer iger1 = Integer.valueOf(0);
  double d = 1.0;

  int dIntValue = (int) d;

  examineInt(i);

  examineInteger(i);
  examineInteger(iger1);
}

规则S2154 : 不同类型的基本类型变量,不可以直接进行赋值,需要显式进行强转

Integer i = 123456789;
Float f = 1.0f;
Number n = condition ? i : f;  // 错误的使用方式; i is coerced to float. n = 1.23456792E8
Integer i = 123456789;
Float f = 1.0f;
Number n = condition ? (Number) i : f;  // n = 123456789

规则S2159 : 不可进行无效的equals比较

无效的equals操作包括:

  • 与null进行比较
  • 不同类型进行equals比较
  • 不同基本类型进行equals比较
  • 不同classinterface进行比较
  • 不同的interface类型进行比较
  • 数组与非数组进行比较
  • 数组之间进行比较
interface KitchenTool { ... };
interface Plant {...}

public class Spatula implements KitchenTool { ... }
public class Tree implements Plant { ...}
//...

Spatula spatula = new Spatula();
KitchenTool tool = spatula;
KitchenTool [] tools = {tool};

Tree tree = new Tree();
Plant plant = tree;
Tree [] trees = {tree};

if (spatula.equals(tree)) { // 错误的使用方式; unrelated classes
  // ...
}
else if (spatula.equals(plant)) { // 错误的使用方式; unrelated class and interface
  // ...
}
else if (tool.equals(plant)) { // 错误的使用方式; unrelated interfaces
  // ...
}
else if (tool.equals(tools)) { // 错误的使用方式; array & non-array
  // ...
}
else if (trees.equals(tools)) { // 错误的使用方式; incompatible arrays
  // ...
}
else if (tree.equals(null)) { // 错误的使用方式
  // ...
}

规则S2162 : "equals"方法应当保证父子类的对象之间的比较具有对等性

典型的操作,比如a.equals(b)b.equals(a)的结果需要一致,具有对等性。

使用instanceof进行判断,将破坏这种对等性,例如,父类对象 instanceof 子类是false,而子类对象 instanceof 父类是true。
例如:

Fruit fruit = new Fruit();
Raspberry raspberry = new Raspberry();

if (raspberry instanceof Fruit) { ... } // true
if (fruit instanceof Raspberry) { ... } // false

如果instanceof用于了两个类的equals方法,将导致:

raspberry.equals(fruit); // false
fruit.equals(raspberry); //true

final类,中使用Class.equals,如果equals方法被继承,也将导致类似的情况。

public class Fruit extends Food {
  private Season ripe;

  public boolean equals(Object obj) {
    if (obj == this) {
      return true;
    }
    if (obj == null) {
      return false;
    }
    if (Fruit.class == obj.getClass()) { // 错误示范; broken for child classes
      return ripe.equals(((Fruit)obj).getRipe());
    }
    if (obj instanceof Fruit ) {  // 错误示范; broken for child classes
      return ripe.equals(((Fruit)obj).getRipe());
    }
    else if (obj instanceof Season) { // 错误示范; symmetry broken for Season class
      // ...
    }
    //...
public class Fruit extends Food {
  private Season ripe;

  public boolean equals(Object obj) {
    if (obj == this) {
      return true;
    }
    if (obj == null) {
      return false;
    }
    if (this.getClass() == obj.getClass()) {
      return ripe.equals(((Fruit)obj).getRipe());
    }
    return false;
}

规则S2164 : 不要在float上进行数学操作

使用BigDecimal 进行代理,如果必须使用基本数据类型,则使用double进行操作

float a = 16777216.0f;
float b = 1.0f;
float c = a + b; // 错误示范; yields 1.6777216E7 not 1.6777217E7

double d = a + b; // 错误示范; addition is still between 2 floats
float a = 16777216.0f;
float b = 1.0f;
BigDecimal c = BigDecimal.valueOf(a).add(BigDecimal.valueOf(b));

double d = (double)a + (double)b;

This rule doesn't raise an issue when the mathematical expression is only used to build a string.

System.out.println("["+getName()+"] " +
           "\n\tMax time to retrieve connection:"+(max/1000f/1000f)+" ms.");

规则S2167 : "compareTo" 不可以返回"Integer.MIN_VALUE"

public int compareTo(MyClass) {
  if (condition) {
    return Integer.MIN_VALUE;  // 错误示范
  }
public int compareTo(MyClass) {
  if (condition) {
    return -1;
  }

规则S2168 : 不可以使用锁进行双重检查

懒加载中经常使用的手段是“加锁双重检查”,但是该操作在不同的JVM版本中表现不一致,导致结果具有不可预测性。所以不要使用这种操作。

@NotThreadSafe
public class DoubleCheckedLocking {
    private static Resource resource;

    public static Resource getInstance() {
        if (resource == null) {
            synchronized (DoubleCheckedLocking.class) {
                if (resource == null)
                    resource = new Resource();
            }
        }
        return resource;
    }

    static class Resource {

    }
}
@ThreadSafe
public class SafeLazyInitialization {
    private static Resource resource;

    public static synchronized Resource getInstance() {
        if (resource == null)
            resource = new Resource();
        return resource;
    }

    static class Resource {
    }
}

使用内部类进行懒加载:

@ThreadSafe
public class ResourceFactory {
    private static class ResourceHolder {
        public static Resource resource = new Resource(); // This will be lazily initialised
    }

    public static Resource getResource() {
        return ResourceFactory.ResourceHolder.resource;
    }

    static class Resource {
    }
}

使用关键字"volatile":

class ResourceFactory {
  private volatile Resource resource;

  public Resource getResource() {
    Resource localResource = resource;
    if (localResource == null) {
      synchronized (this) {
        localResource = resource;
        if (localResource == null) {
          resource = localResource = new Resource();
        }
      }
    }
    return localResource;
  }

  static class Resource {
  }
}

规则S2175 : 不可进行不恰当的"Collection" 操作

有一些集合的设计中,如果传入不恰当的参数变量,只会返回false或null,而不会报错。例如:

  • Collection.remove(Object o)
  • Collection.removeAll(Collection<?>)
  • Collection.contains(Object o)
  • List.indexOf(Object o)
  • List.lastIndexOf(Object o)
  • Map.containsKey(Object key)
  • Map.containsValue(Object value)
  • Map.get(Object key)
  • Map.getOrDefault(Object key, V defaultValue)
  • Map.remove(Object key)
  • Map.remove(Object key, Object value)
public class S2175 {

  public static void main(String[] args) {
    String foo = "42";
    Map<Integer, Object> map = new HashMap<>();
    map.remove(foo); // 错误示范; will return 'null' for sure because 'map' is handling only Integer keys

    // ...

    List<String> list = new ArrayList<String>();
    Integer integer = Integer.valueOf(1);
    if (list.contains(integer)) { // 错误示范; always false.
      list.remove(integer); // 错误示范; list.add(integer) doesn't compile, so this will always return 'false'
    }
  }

}

规则S2177 : 子类重写父类方法,必须添加override注解

如果不添加注解,父类子类方法定义一致,但是参数类型虽然名字一致,但是引用自不同的package,将导致override失败。

// Parent.java
import computer.Pear;
public class Parent {

  public void doSomething(Pear p) {
    //,,,
  }

  public static void doSomethingElse() {
    //...
  }
}

// Child.java
import fruit.Pear;
public class Child extends Parent {

  public void doSomething(Pear p) {  // 错误示范; this is not an override
    // ...
  }

  public void doSomethingElse() {  // 错误示范; parent method is static
    //...
  }
}
// Parent.java
import computer.Pear;
public class Parent {

  public void doSomething(Pear p) {
    //,,,
  }

  public static void doSomethingElse() {
    //...
  }
}

// Child.java
import computer.Pear;  // import corrected
public class Child extends Parent {

  public void doSomething(Pear p) {  // true override (see import)
    //,,,
  }

  public static void doSomethingElse() {
    //...
  }
}

规则S2183 : 位移操作的范围需要正确,Int和long进行位移,位移必须大于0且小于类型bits - 1

public int shift(int a) {
  int x = a >> 32; // 错误示范
  return a << 48;  // 错误示范
}
public int shift(int a) {
  int x = a >> 31;
  return a << 16;
}
bytes[loc+0] = (byte)(value >> 8);
bytes[loc+1] = (byte)(value >> 0);

规则S2184 : 数学操作需要先进行强转再操作

float twoThirds = 2/3; // 错误示范; int division. Yields 0.0
long millisInYear = 1_000*3_600*24*365; // 错误示范; int multiplication. Yields 1471228928
long bigNum = Integer.MAX_VALUE + 2; // 错误示范. Yields -2147483647
long bigNegNum =  Integer.MIN_VALUE-1; // 错误示范, gives a positive result instead of a negative one.
Date myDate = new Date(seconds * 1_000); // 错误示范, won't produce the expected result if seconds > 2_147_483
...
public long compute(int factor){
  return factor * 10_000;  // 错误示范, won't produce the expected result if factor > 214_748
}

public float compute2(long factor){
  return factor / 123;  // 错误示范, will be rounded to closest long integer
}
float twoThirds = 2f/3; // 2 promoted to float. Yields 0.6666667
long millisInYear = 1_000L*3_600*24*365; // 1000 promoted to long. Yields 31_536_000_000
long bigNum = Integer.MAX_VALUE + 2L; // 2 promoted to long. Yields 2_147_483_649
long bigNegNum =  Integer.MIN_VALUE-1L; // Yields -2_147_483_649
Date myDate = new Date(seconds * 1_000L);
...
public long compute(int factor){
  return factor * 10_000L;
}

public float compute2(long factor){
  return factor / 123f;
}

or

float twoThirds = (float)2/3; // 2 cast to float
long millisInYear = (long)1_000*3_600*24*365; // 1_000 cast to long
long bigNum = (long)Integer.MAX_VALUE + 2;
long bigNegNum =  (long)Integer.MIN_VALUE-1;
Date myDate = new Date((long)seconds * 1_000);
...
public long compute(long factor){
  return factor * 10_000;
}

public float compute2(float factor){
  return factor / 123;
}

规则S2189 : 不可以无限循环

for (;;) {  // 错误示范; end condition omitted
  // ...
}

int j;
while (true) { // 错误示范; end condition omitted
  j++;
}

int k;
boolean b = true;
while (b) { // 错误示范; b never written to in loop
  k++;
}
int j;
while (true) { // reachable end condition added
  j++;
  if (j  == Integer.MIN_VALUE) {  // true at Integer.MAX_VALUE +1
    break;
  }
}

int k;
boolean b = true;
while (b) {
  k++;
  b = k < Integer.MAX_VALUE;
}

规则S2200 : "compareTo"的结果,不可以进行特定值比较

虽然大部分都返回 -1,0,1,但是根据实现不同也有其他的情况存在。

if (myClass.compareTo(arg) == -1) {  // 错误示范
  // ...
}
if (myClass.compareTo(arg) < 0) {
  // ...
}

规则S2201 : 方法的返回,不应当被忽略掉

public void handle(String command){
  command.toLowerCase(); // 错误示范; result of method thrown away
  ...
}
public void handle(String command){
  String formattedCommand = command.toLowerCase();
  ...
}

如果同时满足如下两个条件,则可以忽略返回值

  • 方法在try catch块中,并且catch了该方法的异常
  • 如果方法名以"parse", "format", "decode"开始 或为 "valueOf" 或为String.getBytes(Charset)
private boolean textIsInteger(String textToCheck) {

    try {
        Integer.parseInt(textToCheck, 10); // OK
        return true;
    } catch (NumberFormatException ignored) {
        return false;
    }
}

规则S2204 : "Atomic"对象,不可以使用".equals()"方法

AtomicInteger aInt1 = new AtomicInteger(0);
AtomicInteger aInt2 = new AtomicInteger(0);

if (aInt1.equals(aInt2)) { ... }  // 错误示范
AtomicInteger aInt1 = new AtomicInteger(0);
AtomicInteger aInt2 = new AtomicInteger(0);

if (aInt1.get() == aInt2.get()) { ... }

规则S2222 : 所有的"Lock"必须保证正确释放

public class MyClass {
  private Lock lock = new Lock();

  public void doSomething() {
    lock.lock(); // 错误示范
    if (isInitialized()) {
      // ...
      lock.unlock();
    }
  }
}
public class MyClass {
  private Lock lock = new Lock();

  public void doSomething() {
    if (isInitialized()) {
      lock.lock();
      // ...
      lock.unlock();
    }
  }
}

规则S2225 : "toString()" 和 "clone()"不可以返回null

public String toString () {
  if (this.collection.isEmpty()) {
    return null; // 错误示范
  } else {
    // ...
public String toString () {
  if (this.collection.isEmpty()) {
    return ";
  } else {
    // ...

规则S2226 : "Servlet"或"Controller"或"Service"等共享对象,所有属性必须不可修改

public class MyServlet extends HttpServlet {
  private String userName;  //As this field is shared by all users, it's obvious that this piece of information should be managed differently
  ...
}

or

public class MyAction extends Action {
  private String userName;  //Same reason
  ...
}

规则S2229 : 同一个类中的方法,不可以调用有"@Transactional"的方法

由于Spring事务传递的设计,不可以调用的方法顺序如下:

起始方法 被调用方法
non-@Transactional MANDATORY, NESTED, REQUIRED, REQUIRES_NEW
MANDATORY NESTED, NEVER, NOT_SUPPORTED, REQUIRES_NEW
NESTED NESTED, NEVER, NOT_SUPPORTED, REQUIRES_NEW
NEVER MANDATORY, NESTED, REQUIRED, REQUIRES_NEW
NOT_SUPPORTED MANDATORY, NESTED, REQUIRED, REQUIRES_NEW
REQUIRED or @Transactional NESTED, NEVER, NOT_SUPPORTED, REQUIRES_NEW
REQUIRES_NEW NESTED, NEVER, NOT_SUPPORTED, REQUIRES_NEW
SUPPORTS MANDATORY, NESTED, NEVER, NOT_SUPPORTED, REQUIRED, REQUIRES_NEW

@Override
public void doTheThing() {
  // ...
  actuallyDoTheThing();  // 错误示范
}

@Override
@Transactional
public void actuallyDoTheThing() {
  // ...
}

规则S2230 : 非public方法,不可以声明为"@Transactional"

因为会导致@Transactional注解不起作用。

@Transactional  // 错误示范
private void doTheThing(ArgClass arg) {
  // ...
}

规则S2236 : 不可以调用Thread对象的方法"wait(...)", "notify()"以及 "notifyAll()"

Thread myThread = new Thread(new RunnableJob());
...
myThread.wait(2000);

规则S2251 : "for"循环需要正确的进行变量的变化

public void doSomething(String [] strings) {
  for (int i = 0; i < strings.length; i--) { // 错误示范;
    String string = strings[i];  // ArrayIndexOutOfBoundsException when i reaches -1
    //...
  }
public void doSomething(String [] strings) {
  for (int i = 0; i < strings.length; i++) {
    String string = strings[i];
    //...
  }

规则S2252 : 循环条件至少可以满足一次

for (int i = 10; i < 10; i++) {  // 错误示范
  // ...

规则S2259 : 不可引用或访问"Null"

@CheckForNull
String getName(){...}

public boolean isNameEmpty() {
  return getName().length() == 0; // 错误示范; the result of getName() could be null, but isn't null-checked
}
Connection conn = null;
Statement stmt = null;
try{
  conn = DriverManager.getConnection(DB_URL,USER,PASS);
  stmt = conn.createStatement();
  // ...

}catch(Exception e){
  e.printStackTrace();
}finally{
  stmt.close();   // 错误示范; stmt could be null if an exception was thrown in the try{} block
  conn.close();  // 错误示范; conn could be null if an exception was thrown
}
private void merge(@Nonnull Color firstColor, @Nonnull Color secondColor){...}

public  void append(@CheckForNull Color color) {
    merge(currentColor, color);  // 错误示范; color should be null-checked because merge(...) doesn't accept nullable parameters
}
void paint(Color color) {
  if(color == null) {
    System.out.println("Unable to apply color " + color.toString());  // 错误示范; NullPointerException will be thrown
    return;
  }
  ...
}

规则S2272 : "Iterator.next()"应当抛出"NoSuchElementException",而不应该返回null

public class MyIterator implements Iterator<String>{
  ...
  public String next(){
    if(!hasNext()){
      return null;
    }
    ...
  }
}
public class MyIterator implements Iterator<String>{
  ...
  public String next(){
    if(!hasNext()){
      throw new NoSuchElementException();
    }
    ...
  }
}

规则S2273 : "wait", "notify" 以及 "notifyAll"只能对象被当前代码块所持有时访问

当前Thread必须持有一个对象的监听器,才可以调用对象的wait,notify,notifyAll方法。

private void removeElement() {
  while (!suitableCondition()){
    obj.wait();
  }
  ... // Perform removal
}

or

private void removeElement() {
  while (!suitableCondition()){
    wait();
  }
  ... // Perform removal
}
private void removeElement() {
  synchronized(obj) {
    while (!suitableCondition()){
      obj.wait();
    }
    ... // Perform removal
  }
}

or

private synchronized void removeElement() {
  while (!suitableCondition()){
    wait();
  }
  ... // Perform removal
}

规则S2275 : Printf样式的代码,必须符合参数类型。

String.format("The value of my integer is %d", "Hello World");  // 错误示范; an 'int' is expected rather than a String
String.format("Duke's Birthday year is %tX", c);  // 错误示范; X is not a supported time conversion character
String.format("Display %0$d and then %d", 1);   // 错误示范; arguments are numbered starting from 1
String.format("Not enough arguments %d and %d", 1);  // 错误示范; the second argument is missing
String.format("%< is equals to %d", 2);   // 错误示范; the argument index '<' refers to the previous format specifier but there isn't one

MessageFormat.format("Result {1}.", value); // 错误示范; Not enough arguments. (first element is {0})
MessageFormat.format("Result {{0}.", value); // 错误示范; Unbalanced number of curly brace (single curly braces should be escaped)
MessageFormat.format("Result ' {0}", value); // 错误示范; Unbalanced number of quotes (single quote must be escaped)

java.util.logging.Logger logger;
logger.log(java.util.logging.Level.SEVERE, "Result {1}!", 14); // 错误示范 - Not enough arguments.

org.slf4j.Logger slf4jLog;
org.slf4j.Marker marker;

slf4jLog.debug(marker, "message {}"); // 错误示范 - Not enough arguments.

org.apache.logging.log4j.Logger log4jLog;
log4jLog.debug("message {}"); // 错误示范 - Not enough arguments.
String.format("The value of my integer is %d", 3);
String.format("Duke's Birthday year is %tY", c);
String.format("Display %1$d and then %d", 1);
String.format("Not enough arguments %d and %d", 1, 2);
String.format("%d is equals to %<", 2);

MessageFormat.format("Result {0}.", value);
MessageFormat.format("Result {0} & {1}.", value, value);
MessageFormat.format("Result {0}.", myObject);

java.util.logging.Logger logger;
logger.log(java.util.logging.Level.SEVERE, "Result {1},{2}!", 14, 2);

org.slf4j.Logger slf4jLog;
org.slf4j.Marker marker;

slf4jLog.debug(marker, "message {}", 1);

org.apache.logging.log4j.Logger log4jLog;
log4jLog.debug("message {}", 1);

规则S2276 : 如果持有对象锁,则应当使用"wait(...)"代替"Thread.sleep(...)"

否则有可能造成死锁。

public void doSomething(){
  synchronized(monitor) {
    while(notReady()){
      Thread.sleep(200);
    }
    process();
  }
  ...
}
public void doSomething(){
  synchronized(monitor) {
    while(notReady()){
      monitor.wait(200);
    }
    process();
  }
  ...
}

规则S2441 : "HttpSession"中只可以存储”可序列化对象“

public class Address {
  //...
}

//...
HttpSession session = request.getSession();
session.setAttribute("address", new Address());  // 错误示范; Address isn't serializable

规则S2445 : synchronized只能用于"private final"属性

否则无法保证有序性。

private String color = "red";

private void doSomething(){
  synchronized(color) {  // 错误示范; lock is actually on object instance "red" referred to by the color variable
    //...
    color = "green"; // other threads now allowed into this block
    // ...
  }
  synchronized(new Object()) { // 错误示范 this is a no-op.
     // ...
  }
}
private String color = "red";
private final Object lockObj = new Object();

private void doSomething(){
  synchronized(lockObj) {
    //...
    color = "green";
    // ...
  }
}

规则S2446 : 应当使用"notifyAll",而非”notify“

class MyThread extends Thread{

  @Override
  public void run(){
    synchronized(this){
      // ...
      notify();  // 错误示范
    }
  }
}
class MyThread extends Thread{

  @Override
  public void run(){
    synchronized(this){
      // ...
      notifyAll();
    }
  }
}

规则S2583 : 条件代码必须保证可达性

a = false;
if (a) { // 错误示范
  doSomething(); // never executed
}

if (!a || b) { // 错误示范; "!a" is always "true", "b" is never evaluated
  doSomething();
} else {
  doSomethingElse(); // never executed
}

如果满足如下任一条件,不构成错误。

  1. 如果变量为final
final boolean debug = false;
//...
if (debug) {
  // Print something
}
  1. 如果不使用变量,而直接使用true或false
if (true) {
  // do something
}

如上代码,可以认为是开发人员有意而为之。

规则S2637 : "@NonNull"不可以设置为null

public class MainClass {

  @Nonnull
  private String primary;
  private String secondary;

  public MainClass(String color) {
    if (color != null) {
      secondary = null;
    }
    primary = color;  // 错误示范; "primary" is Nonnull but could be set to null here
  }

  public MainClass() { // 错误示范; "primary" Nonnull" but is not initialized
  }

  @Nonnull
  public String indirectMix() {
    String mix = null;
    return mix;  // 错误示范; return value is Nonnull, but null is returned.}}
  }

规则S2639 : 不可以使用非法的表达式

例如

String str = "/File|Name.txt";

String clean = str.replaceAll(".",""); // 错误示范; probably meant to remove only dot chars, but returns an empty string
String clean2 = str.replaceAll("|","_"); // 错误示范; yields _/_F_i_l_e_|_N_a_m_e_._t_x_t_
String clean3 = str.replaceAll(File.separator,"); // 错误示范; exception on Windows

String clean4 = str.replaceFirst(".","); // 错误示范;
String clean5 = str.replaceFirst("|","_"); // 错误示范;
String clean6 = str.replaceFirst(File.separator,"); // 错误示范;

规则S2674 : stream操作的所有返回值必须进行检查

public void doSomething(String fileName) {
  try {
    InputStream is = new InputStream(file);
    byte [] buffer = new byte[1000];
    is.read(buffer);  // 错误示范
    // ...
  } catch (IOException e) { ... }
}
public void doSomething(String fileName) {
  try {
    InputStream is = new InputStream(file);
    byte [] buffer = new byte[1000];
    int count = 0;
    while (count = is.read(buffer) > 0) {
      // ...
    }
  } catch (IOException e) { ... }
}

规则S2676 : "Math.abs"以及负号不得用于所有可能为"MIN_VALUE"的变量

public void doSomething(String str) {
  if (Math.abs(str.hashCode()) > 0) { // 错误示范
    // ...
  }
}
public void doSomething(String str) {
  if (str.hashCode() != 0) {
    // ...
  }
}

规则S2677 : "read" 以及 "readLine" 返回值必须被使用。

public void doSomethingWithFile(String fileName) {
  BufferedReader buffReader = null;
  try {
    buffReader = new BufferedReader(new FileReader(fileName));
    while (buffReader.readLine() != null) { // 错误示范
      // ...
    }
  } catch (IOException e) {
    // ...
  }
}
public void doSomethingWithFile(String fileName) {
  BufferedReader buffReader = null;
  try {
    buffReader = new BufferedReader(new FileReader(fileName));
    String line = null;
    while ((line = buffReader.readLine()) != null) {
      // ...
    }
  } catch (IOException e) {
    // ...
  }
}

规则S2689 : 使用“append”模式打开的文件,不可以用于"ObjectOutputStream"

FileOutputStream fos = new FileOutputStream (fileName , true);  // fos opened in append mode
ObjectOutputStream out = new ObjectOutputStream(fos);  // 错误示范
FileOutputStream fos = new FileOutputStream (fileName);
ObjectOutputStream out = new ObjectOutputStream(fos);

规则S2695 : "PreparedStatement" 以及 "ResultSet" 方法必须使用有效的下标

这两个类的下标从1开始,而不是从0开始。

PreparedStatement ps = con.prepareStatement("SELECT fname, lname FROM employees where hireDate > ? and salary < ?");
ps.setDate(0, date);  // 错误示范
ps.setDouble(3, salary);  // 错误示范

ResultSet rs = ps.executeQuery();
while (rs.next()) {
  String fname = rs.getString(0);  // 错误示范
  // ...
}
PreparedStatement ps = con.prepareStatement("SELECT fname, lname FROM employees where hireDate > ? and salary < ?");
ps.setDate(1, date);
ps.setDouble(2, salary);

ResultSet rs = ps.executeQuery();
while (rs.next()) {
  String fname = rs.getString(1);
  // ...
}

规则S2757 : "=+" 不可以用于替换"+="

int target = -5;
int num = 3;

target =- num;  // 错误示范; target = -3. Is that really what's meant?
target =+ num; // 错误示范; target = 3
int target = -5;
int num = 3;

target = -num;  // Compliant; intent to assign inverse value of num is clear
target += num;

规则S2761 : 一元操作不可以重复

一元操作包括:!, ~, -, and +.

int i = 1;

int j = - - -i;  // 错误示范; just use -i
int k = ~~~i;    // 错误示范; same as i
int m = + +i;    // 错误示范; operators are useless here

boolean b = false;
boolean c = !!!b;   // 错误示范
int i =  1;

int j = -i;
int k = ~i;
int m =  i;

boolean b = false;
boolean c = !b;

规则S2789 : "Optional"本身不可以为null

public void doSomething () {
  Optional<String> optional = getOptional();
  if (optional != null) {  // 错误示范
    // do something with optional...
  }
}

@Nullable // 错误示范
public Optional<String> getOptional() {
  // ...
  return null;  // 错误示范
}
public void doSomething () {
  Optional<String> optional = getOptional();
  optional.ifPresent(
    // do something with optional...
  );
}

public Optional<String> getOptional() {
  // ...
  return Optional.empty();
}

规则S2885 : static变量必须保证线程安全性

public class MyClass {
  private static SimpleDateFormat format = new SimpleDateFormat("HH-mm-ss");  // 错误示范
  private static Calendar calendar = Calendar.getInstance();  // 错误示范
public class MyClass {
  private SimpleDateFormat format = new SimpleDateFormat("HH-mm-ss");
  private Calendar calendar = Calendar.getInstance();

规则S2886 : Getters和Setters需要成对进行synchronized

public class Person {
  String name;
  int age;

  public synchronized void setName(String name) {
    this.name = name;
  }

  public String getName() {  // 错误示范
    return this.name;
  }

  public void setAge(int age) {  // 错误示范
    this.age = age;
  }

  public int getAge() {
    synchronized (this) {
      return this.age;
    }
  }
}
public class Person {
  String name;
  int age;

  public synchronized void setName(String name) {
    this.name = name;
  }

  public synchronized String getName() {
    return this.name;
  }

  public void setAge(int age) {
    synchronized (this) {
      this.age = age;
   }
  }

  public int getAge() {
    synchronized (this) {
      return this.age;
    }
  }
}

规则S3020 : "toArray"需要传入类型参数

public String [] getStringArray(List<String> strings) {
  return (String []) strings.toArray();  // 错误示范; ClassCastException thrown
}
public String [] getStringArray(List<String> strings) {
  return strings.toArray(new String[0]);
}

规则S3032 : JEE 应用不可以使用"getClassLoader"

ClassLoader cl = this.getClass().getClassLoader();  // 错误示范
ClassLoader cl = Thread.currentThread().getContextClassLoader();

规则S3034 : 原始byte数据,不可以直接用于组合位操作

  int intFromBuffer() {
    int result = 0;
    for (int i = 0; i < 4; i++) {
      result = (result << 8) | readByte(); // 错误示范
    }
    return result;
  }
  int intFromBuffer() {
    int result = 0;
    for (int i = 0; i < 4; i++) {
      result = (result << 8) | (readByte() & 0xff);
    }
    return result;
  }

规则S3039 : "String"操作不可以越界

String speech = "Now is the time for all good people to come to the aid of their country.";

String substr1 = speech.substring(-1, speech.length());  // 错误示范; start and end values both bad
String substr2 = speech.substring(speech.length(), 0); // 错误示范, start value must be < end value
char ch = speech.charAt(speech.length());  // 错误示范
String speech = "Now is the time for all good people to come to the aid of their country.";

String substr1 = speech; // Closest correct values to original code yield whole string
String substr2 = new StringBuilder(speech).reverse().toString()
char ch = speech.charAt(speech.length()-1);

规则S3046 : 如果同时持有多个对象锁,则不应该调用"wait"

否则有可能导致死锁。

synchronized (this.mon1) {  // threadB can't enter this block to request this.mon2 lock & release threadA
    synchronized (this.mon2) {
        this.mon2.wait();  // 错误示范; threadA is stuck here holding lock on this.mon1
    }
}

规则S3064 : 如果使用双锁检查机制,则懒加载的赋值必须最后进行

public class MyClass {

  private volatile List<String> strings;

  public List<String> getStrings() {
    if (strings == null) {  // check#1
      synchronized(MyClass.class) {
        if (strings == null) {
          strings = new ArrayList<>();  // 错误示范
          strings.add("Hello");  //When threadA gets here, threadB can skip the synchronized block because check#1 is false
          strings.add("World");
        }
      }
    }
    return strings;
  }
}
public class MyClass {

  private volatile List<String> strings;

  public List<String> getStrings() {
    if (strings == null) {  // check#1
      synchronized(MyClass.class) {
        if (strings == null) {
          List<String> tmpList = new ArrayList<>();
          tmpList.add("Hello");
          tmpList.add("World");
          strings = tmpList;
        }
      }
    }
    return strings;
  }
}

规则S3065 : "Min"和"Max"用于组合操作时,不应该返回同一个值

  private static final int UPPER = 20;
  private static final int LOWER = 0;

  public int doRangeCheck(int num) {    // Let's say num = 12
    int result = Math.min(LOWER, num);  // result = 0
    return Math.max(UPPER, result);     // 错误示范; result is now 20: even though 12 was in the range
  }
  private static final int UPPER = 20;
  private static final int LOWER = 0;

  public int doRangeCheck(int num) {    // Let's say num = 12
    int result = Math.max(LOWER, num);  // result = 12
    return Math.min(UPPER, result);     // Compliant; result is still 12
  }

  private static final int UPPER = 20;
  private static final int LOWER = 0;

  public int doRangeCheck(int num) {    // Let's say num = 12
    int result = Math.min(UPPER, num);  // result = 12
    return Math.max(LOWER, result);     // Compliant; result is still 12
  }

规则S3067 : 不可以将"getClass"用于synchronized

public class MyClass {
  public void doSomethingSynchronized(){
    synchronized (this.getClass()) {  // 错误示范
      // ...
    }
  }
public class MyClass {
  public void doSomethingSynchronized(){
    synchronized (MyClass.class) {
      // ...
    }
  }

规则S3077 : "volatile"只用于基本类型(int、bool等)

private volatile int [] vInts;  // 错误示范
private volatile MyObj myObj;  // 错误示范
private AtomicIntegerArray vInts;
private MyObj myObj;

规则S3078 : "volatile"不可用于复合赋值运算符

private volatile int count = 0;
private volatile boolean boo = false;

public void incrementCount() {
  count++;  // 错误示范
}

public void toggleBoo(){
  boo = !boo;  // 错误示范
}
private AtomicInteger count = 0;
private boolean boo = false;

public void incrementCount() {
  count.incrementAndGet();
}

public synchronized void toggleBoo() {
  boo = !boo;
}

规则S3306 : 使用构造函数注入代替属性值注入

class MyComponent {  // Anyone can call the default constructor

  @Inject MyCollaborator collaborator;  // 错误示范

  public void myBusinessMethod() {
    collaborator.doSomething();  // this will fail in classes new-ed by a caller
  }
}
class MyComponent {

  private final MyCollaborator collaborator;

  @Inject
  public MyComponent(MyCollaborator collaborator) {
    Assert.notNull(collaborator, "MyCollaborator must not be null!");
    this.collaborator = collaborator;
  }

  public void myBusinessMethod() {
    collaborator.doSomething();
  }
}

规则S3346 : "assert"操作不应当对数据进行操作和影响

assert myList.remove(myList.get(0));  // 错误示范
boolean removed = myList.remove(myList.get(0));
assert removed;

规则S3422 : "Dependencies"不可使用"system" 作用域

<dependency>
  <groupId>javax.sql</groupId>
  <artifactId>jdbc-stdext</artifactId>
  <version>2.0</version>
  <scope>system</scope>  <!-- Noncompliant -->
  <systemPath>/usr/bin/lib/rt.jar</systemPath>  <!-- remove this -->
</dependency>

规则S3436 : 值传递类(Optional等)不可用于锁操作

Optional<Foo> fOpt = doSomething();
synchronized (fOpt) {  // 错误示范
  // ...
}

规则S3438 : Spring的"SingleConnectionFactory" 应当设置"reconnectOnException"

 <bean id="singleCF" class="org.springframework.jms.connection.SingleConnectionFactory">  <!-- Noncompliant -->
   <constructor-arg ref="dummyConnectionFactory" />
 </bean>
 <bean id="singleCF" class="org.springframework.jms.connection.SingleConnectionFactory" p:reconnectOnException="true">
   <constructor-arg ref="dummyConnectionFactory" />
 </bean>

or

 <bean id="singleCF" class="org.springframework.jms.connection.SingleConnectionFactory">
   <constructor-arg ref="dummyConnectionFactory" />
   <property name="reconnectOnException"><value>true</value></property>
 </bean>

规则S3439 : "DefaultMessageListenerContainer" 在重启期间不应丢弃消息

<bean id="listenerContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">  <!-- Noncompliant -->
   <property name="connectionFactory" ref="connFactory" />
   <property name="destination" ref="dest" />
   <property name="messageListener" ref="serviceAdapter" />
   <property name="autoStartup" value="true" />
   <property name="concurrentConsumers" value="10" />
   <property name="maxConcurrentConsumers" value="10" />
   <property name="clientId" value="myClientID" />
</bean>
<bean id="listenerContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
   <property name="connectionFactory" ref="connFactory" />
   <property name="destination" ref="dest" />
   <property name="messageListener" ref="serviceAdapter" />
   <property name="autoStartup" value="true" />
   <property name="concurrentConsumers" value="10" />
   <property name="maxConcurrentConsumers" value="10" />
   <property name="clientId" value="myClientID" />
   <property name="acceptMessagesWhileStopping" value="true" />
</bean>

规则S3518 : 不可以除以0

void test_divide() {
  int z = 0;
  if (unknown()) {
    // ..
    z = 3;
  } else {
    // ..
  }
  z = 1 / z; // 错误示范, possible division by zero
}
void test_divide() {
  int z = 0;
  if (unknown()) {
    // ..
    z = 3;
  } else {
    // ..
    z = 1;
  }
  z = 1 / z;
}

规则S3546 : 自定义资源应当关闭

自定义资源必须关闭,否则将导致资源泄露。

<ul>
<li> {rule:java:S2095} - Resources should be closed </li>
</ul>

规则S3551 : 子类对父类的线程同步的操作需要具有继承性

public class Parent {

  synchronized void foo() {
    //...
  }
}

public class Child extends Parent {

 @Override
  public foo () {  // 错误示范
    // ...
    super.foo();
  }
}
public class Parent {

  synchronized void foo() {
    //...
  }
}

public class Child extends Parent {

  @Override
  synchronized foo () {
    // ...
    super.foo();
  }
}

规则S3599 : "Double Brace Initialization"不可以使用

会造成内存泄漏。

Map source = new HashMap(){{ // 错误示范
    put("firstName", "John");
    put("lastName", "Smith");
}};
Map source = new HashMap();
// ...
source.put("firstName", "John");
source.put("lastName", "Smith");
// ...

规则S3655 : 只能在"isPresent()"判断之后进行,"Optional"变量的访问。

Optional<String> value = this.getOptionalValue();

// ...

String stringValue = value.get(); // 错误示范
Optional<String> value = this.getOptionalValue();

// ...

if (value.isPresent()) {
  String stringValue = value.get();
}

or

Optional<String> value = this.getOptionalValue();

// ...

String stringValue = value.orElse("default");

规则S3750 : Spring "@Controller"类不应当使用"@Scope"

@Scope("prototype")  // 错误示范
@Controller
public class HelloWorld {
@Controller
public class HelloWorld {

规则S3753 : Spring "@Controller"类中,如果使用"@SessionAttributes",则必须调用"SessionStatus"的"setComplete"方法

@Controller
@SessionAttributes("hello")  // 错误示范; this doesn't get cleaned up
public class HelloWorld {

  @RequestMapping("/greet", method = GET)
  public String greet(String greetee) {

    return "Hello " + greetee;
  }
}
@Controller
@SessionAttributes("hello")
public class HelloWorld {

  @RequestMapping("/greet", method = GET)
  public String greet(String greetee) {

    return "Hello " + greetee;
  }

  @RequestMapping("/goodbye", method = POST)
  public String goodbye(SessionStatus status) {
    //...
    status.setComplete();
  }

}

规则S3822 : "Hibernate"不可以更新"schemas"

<session-factory>
  <property name="hibernate.hbm2ddl.auto">update</property>  <!-- Noncompliant -->
</session-factory>
<session-factory>
  <property name="hibernate.hbm2ddl.auto">validate</property>  <!-- Compliant -->
</session-factory>

or

<session-factory>
  <!-- Property deleted -->
</session-factory>

规则S3923 : 条件代码中,不同的分支内部的代码逻辑不能完全一致

if (b == 0) {  // 错误示范
  doOneMoreThing();
} else {
  doOneMoreThing();
}

int b = a > 12 ? 4 : 4;  // 错误示范

switch (i) {  // 错误示范
  case 1:
    doSomething();
    break;
  case 2:
    doSomething();
    break;
  case 3:
    doSomething();
    break;
  default:
    doSomething();
}

如果if逻辑没有else,只有else-if;或者,switch没有default的逻辑。

if(b == 0) {    //no issue, this could have been done on purpose to make the code more readable
  doSomething();
} else if(b == 1) {
  doSomething();
}

规则S3958 : 中间流的返回值必须被使用

widgets.stream().filter(b -> b.getColor() == RED); // 错误示范
int sum = widgets.stream()
                      .filter(b -> b.getColor() == RED)
                      .mapToInt(b -> b.getWeight())
                      .sum();
Stream<Widget> pipeline = widgets.stream()
                                 .filter(b -> b.getColor() == GREEN)
                                 .mapToInt(b -> b.getWeight());
sum = pipeline.sum();

规则S3959 : "Consumed Stream"的结果不可以重复使用。

Stream<Widget> pipeline = widgets.stream().filter(b -> b.getColor() == RED);
int sum1 = pipeline.sum();
int sum2 = pipeline.mapToInt(b -> b.getWeight()).sum(); // 错误示范

规则S3981 : 集合的大小和数组的长度的比较操作必须有效

if (myList.size() >= 0) { ... }

if (myList.size() < 0) { ... }

boolean result = myArray.length >= 0;

if (0 > myArray.length) { ... }
if (!myList.isEmpty()) { ... }

if (myArray.length >= 42) { ... }

规则S3984 : "Exception"如果被创建,则必须被抛出。

if (x < 0)
  new IllegalArgumentException("x must be nonnegative");
if (x < 0)
  throw new IllegalArgumentException("x must be nonnegative");

规则S3986 : 不应当使用【周年】格式 ("YYYY")

Date date = new SimpleDateFormat("yyyy/MM/dd").parse("2015/12/31");
String result = new SimpleDateFormat("YYYY/MM/dd").format(date);   // 错误示范; yields '2016/12/31'
Date date = new SimpleDateFormat("yyyy/MM/dd").parse("2015/12/31");
String result = new SimpleDateFormat("yyyy/MM/dd").format(date);   //Yields '2015/12/31' as expected
Date date = new SimpleDateFormat("yyyy/MM/dd").parse("2015/12/31");
String result = new SimpleDateFormat("YYYY-ww").format(date);  //compliant, 'Week year' is used along with 'Week of year'. result = '2016-01'

规则S4143 : 不应当重复的进行数据的重复写

letters.put("a", "Apple");
letters.put("a", "Boy");  // 错误示范

towns[i] = "London";
towns[i] = "Chicago";  // 错误示范

规则S4275 : Getters和Setters应当访问适当的属性

class A {
  private int x;
  private int y;

  public void setX(int val) { // 错误示范: field 'x' is not updated
    this.y = val;
  }

  public int getY() { // 错误示范: field 'y' is not used in the return value
    return this.x;
  }
}
class A {
  private int x;
  private int y;

  public void setX(int val) {
    this.x = val;
  }

  public int getY() {
    return this.y;
  }
}

规则S4348 : "iterator"不可以返回"this"

class FooIterator implements Iterator<Foo>, Iterable<Foo> {
  private Foo[] seq;
  private int idx = 0;

  public boolean hasNext() {
    return idx < seq.length;
  }

  public Foo next() {
    return seq[idx++];
  }

  public Iterator<Foo> iterator() {
    return this; // 错误示范
  }
  // ...
}
class FooSequence implements Iterable<Foo> {
  private Foo[] seq;

  public Iterator<Foo> iterator() {
    return new Iterator<Foo>() {
      private int idx = 0;

      public boolean hasNext() {
        return idx < seq.length;
      }

      public Foo next() {
        return seq[idx++];
      }
    };
  }
  // ...
}

规则S4351 : "compareTo"不可以重载,只允许重写

public class Foo {
  static class Bar implements Comparable<Bar> {
    public int compareTo(Bar rhs) {
      return -1;
    }
  }

  static class FooBar extends Bar {
    public int compareTo(FooBar rhs) {  // 错误示范: Parameter should be of type Bar
      return 0;
    }
  }
}
public class Foo {
  static class Bar implements Comparable<Bar> {
    public int compareTo(Bar rhs) {
      return -1;
    }
  }

  static class FooBar extends Bar {
    public int compareTo(Bar rhs) {
      return 0;
    }
  }
}

规则S4517 : "InputSteam.read()"的实现,不应该返回一个signed byte

@Override
public int read() throws IOException {
  if (pos == buffer.length()) {
    return -1;
  }
  return buffer.getByte(pos++); // 错误示范, a signed byte value is returned
}
@Override
public int read() throws IOException {
  if (pos == buffer.length()) {
    return -1;
  }
  return buffer.getByte(pos++) & 0xFF; // The 0xFF bitmask is applied
}

规则S4602 : "@SpringBootApplication"和"@ComponentScan"不应该在default package

import org.springframework.boot.SpringApplication;

@SpringBootApplication // 错误示范; RootBootApp is declared in the default package
public class RootBootApp {
...
}
@ComponentScan(")
public class Application {
...
}
package hello;

import org.springframework.boot.SpringApplication;

@SpringBootApplication // Compliant; RootBootApp belongs to the "hello" package
public class RootBootApp {
...
}

规则S4973 : "String"以及封箱的数据类型应当使用"equals()"进行比较

String firstName = getFirstName(); // String overrides equals
String lastName = getLastName();

if (firstName == lastName) { ... }; // Non-compliant; false even if the strings have the same value
String firstName = getFirstName();
String lastName = getLastName();

if (firstName != null && firstName.equals(lastName)) { ... };

规则S5164 : 在不使用的时候,"ThreadLocal"应当及时清理

public class ThreadLocalUserSession implements UserSession {

  private static final ThreadLocal<UserSession> DELEGATE = new ThreadLocal<>();

  public UserSession get() {
    UserSession session = DELEGATE.get();
    if (session != null) {
      return session;
    }
    throw new UnauthorizedException("User is not authenticated");
  }

  public void set(UserSession session) {
    DELEGATE.set(session);
  }

   public void incorrectCleanup() {
     DELEGATE.set(null); // 错误示范
   }

  // some other methods without a call to DELEGATE.remove()
}
public class ThreadLocalUserSession implements UserSession {

  private static final ThreadLocal<UserSession> DELEGATE = new ThreadLocal<>();

  public UserSession get() {
    UserSession session = DELEGATE.get();
    if (session != null) {
      return session;
    }
    throw new UnauthorizedException("User is not authenticated");
  }

  public void set(UserSession session) {
    DELEGATE.set(session);
  }

  public void unload() {
    DELEGATE.remove(); // Compliant
  }

  // ...
}

非私有的 ThreadLocal 变量,将不会被检查,因为对象有可能被其他的类。

上一篇下一篇

猜你喜欢

热点阅读