Java规范之Sonar规则的汉化【BUG类】
规则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
等进行跳转,将导致异常try
及catch
中抛出的异常,无法被正确处理。
- 错误的代码示例
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"
如此定义,将导致代码的误解,与对象的方法toString
、hashCode
以及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
- 例外情况
- 使用*,+,=时,可以相同
- 如果判断浮点数是否NaN时,可以相同;PS:推荐使用Double.isNaN方法
- 位移操作,两侧均为数字时,可以相同。
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 : 不可以使用类名进行类的比较
需要使用 instanceof
或 Class.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() {...}
}
-
例外情况:
如果线程将Runnable传给super,可以不重写run方法
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比较
- 不同
class
或interface
进行比较- 不同的
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;
- Exceptions
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
}
- 特殊情况
如果满足如下任一条件,不构成错误。
- 如果变量为final
final boolean debug = false;
//...
if (debug) {
// Print something
}
- 如果不使用变量,而直接使用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的replaceAll
,则匹配所有的字符,而不是一个字符。 -
|
会匹配任意两个字符之间的间隔。 -
File.separator
匹配所有路径分隔符,但是在Windows中会匹配转义字符。 -
错误的代码示例
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
- Exceptions
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
变量,将不会被检查,因为对象有可能被其他的类。