1.类中方法全为静态方法时,私有化构造器
问题样例
class StringUtils { // Noncompliant
public static String concatenate(String s1, String s2) {
return s1 + s2;
}
}
原因:当类中的方法全部都是 static 关键字修饰时 ,它的构造方法最好作为 private 私有化,理由是方法全是 static, 不知道的人会去new对象去调用,需要调用构造方法。 但 static的方法直接用类名调用就行!
解决样例
class StringUtils { // Compliant
private StringUtils() {
throw new IllegalStateException("Utility class");
}
public static String concatenate(String s1, String s2) {
return s1 + s2;
}
}
2.多个异常使用多个方法块捕获
问题样例
try {
/* ... */
} catch (Exception e) {
if(e instanceof IOException) { /* ... */ } // Noncompliant
if(e instanceof NullPointerException{ /* ... */ } // Noncompliant
}
原因:应使用适当类型的多个捕获块,而不是捕获一般异常,然后对该类型进行测试。
解决样例
class MyClass {
private int my_field;
}
3.命名规范
问题样例
class MyClass {
private int my_field;
}
原因:共享一些命名约定是团队高效协作的关键。此规则允许检查字段名是否与提供的正则表达式匹配。驼峰命名
解决样例
class MyClass {
private int myField;
}
4.泛型定义具体类型
问题样例
List myList; // Noncompliant
Set mySet; // Noncompliant
原因:提供此泛型的参数化类型。
解决样例
List<String> myList;
Set<? extends Number> mySet;
5.移除未使用的变量、注释的代码
public int numberOfMinutes(int hours) {
int seconds = 0; // seconds is never used
return hours * 60;
}
public int numberOfMinutes(int hours) {
int seconds = 0; // seconds is never used
return hours * 60;
}
原因:删掉注释的代码行、未使用的变量、私有方法等、移除未使用的变量
public int numberOfMinutes(int hours) {
return hours * 60;
}
public int numberOfMinutes(int hours) {
return hours * 60;
}
6.常见空指针异常
String path = null;
path = UploadConfigurationRead.getInstance().getConfigItem(toUploadPath).trim();
if(path != null){
flag = "0";
}else{
flag = "1";
}
fileType = path.substring(path.length()-1,path.length());
String errNode = hyperV(document.selectSingleNode("//responseMessage"));
errNode = convterEcfErrCode(errNode);
原因:常见的空指针异常
String path = null;
path = UploadConfigurationRead.getInstance().getConfigItem(toUploadPath).trim();
if(path != null){
flag = "0";
fileType = path.substring(path.length()-1,path.length());
}else{
flag = "1";
fileType = path.substring(path.length()-1,path.length());
}
if(errNode==null) ///99999,无返回提示
errCode = "99999-无提示";
else{
errCode=convterEcfErrCode(errNode);
}
7.静态变量使用规范
public class MyClass {
public static final int SOME_CONSTANT = 0; // Compliant - constants are not checked
public String firstName; // Noncompliant
}
原因:静态变量加final、驼峰命名、或者使用私有变量并提供方法访问
public class MyClass {
public static final int SOME_CONSTANT = 0; // Compliant - constants are not checked
private String firstName; // Compliant
public String getFirstName() {
return firstName;
}
8.集合使用建议
Vector cats = new Vector();
原因:Java API的早期类(如Vector、Hashtable和StringBuffer)被同步以使其线程安全。不幸的是,同步对性能有很大的负面影响,即使在从单个线程使用这些集合时也是如此。
最好使用新的非同步替换:
ArrayList或LinkedList而不是Vector
Deque而不是Stack
HashMap而不是Hashtable
StringBuilder而不是StringBuffer
即使在同步上下文中使用,在使用它之前也应该三思,因为它的用法可能很棘手。如果您确信使用是合法的,则可以安全地忽略此警告。
ArrayList cats = new ArrayList();
//这些同步类的使用在重写方法的签名中被忽略。
@Override
public Vector getCats() {...}
9.定义专用异常
public void foo(String bar) throws Throwable { // Noncompliant
throw new RuntimeException("My Message"); // Noncompliant
}
原因:定义并抛出专用异常,而不是使用通用异常。
public void foo(String bar) {
throw new MyOwnRuntimeException("My Message");
}
10.IO等资源的正确使用
private void readTheFile() throws IOException {
Path path = Paths.get(this.fileName);
BufferedReader reader = Files.newBufferedReader(path, this.charset);
// ...
reader.close(); // Noncompliant
// ...
Files.lines("input.txt").forEach(System.out::println); // Noncompliant: The stream needs to be closed
}
@CheckForNull
String getName(){...}
public boolean isNameEmpty() {
return getName().length() == 0; // Noncompliant; 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(); // Noncompliant; stmt could be null if an exception was thrown in the try{} block
conn.close(); // Noncompliant; conn could be null if an exception was thrown
}
原因:1.IO资源未使用try()catch包裹 2.资源未正确关闭
绝不应取消引用/访问对null的引用。这样做将导致引发NullPointerException。充其量,这种异常会导致程序突然终止。最坏的情况是,它可能会暴露对攻击者有用的调试信息,或者允许攻击者绕过安全措施。
请注意,当它们出现时,该规则利用JSR-305中定义的@CheckForNull和@Nonnull注释来了解哪些值是可为null的,哪些值不可为null,除非@Nonnull用于equals的参数,根据约定,这些值应始终与null一起工作。
IO资源应该在使用后关闭。在try语句中使用了Connections, streams, files等,这些类实现了Closeable 或者AutoCloseable接口,必须在finally块中关闭,否则,如果出现异常就可能无法关闭。对于实现了AutoCloseable接口的类,最好使用“try-with-resource”语句来自动关闭。如果不能正确地关闭资源,就会导致资源泄漏,这可能会导致应用程序甚至整个系统的崩溃。
关于IO资源的处理问题,以下比较三种解决方案。
close()放在try块中
close()放在finally块中
使用try-with-resource语句
方法一 jdk1.7前推荐
PrintWriter out = null;
try {
out = new PrintWriter(
new BufferedWriter(
new FileWriter("out.txt", true)));
out.println("the text");
} catch (IOException e) {
e.printStackTrace();
} finally {
if (out != null) {
out.close();
}
方法二 jdk1.8实现了AutoCloseable接推荐
//try-with-resource statement
try (PrintWriter out2 = new PrintWriter(
new BufferedWriter(
new FileWriter("out.txt", true)))) {
out2.println("the text");
} catch (IOException e) {
e.printStackTrace();
}
11.浮点数的使用
double d = 1.1;
BigDecimal bd1 = new BigDecimal(d); // Noncompliant; see comment above
BigDecimal bd2 = new BigDecimal(1.1); // Noncompliant; same result
原因:由于浮点不精确,您不太可能从BigDecimal(double)构造函数中获得所需的值。相反,您应该使用BigDecimal.valueOf,它在封面下使用字符串来消除浮点舍入错误,或者使用string参数的构造函数
double d = 1.1;
BigDecimal bd1 = BigDecimal.valueOf(d);
BigDecimal bd2 = new BigDecimal("1.1"); // using String constructor will result in precise value
12.数据计算及数据类型转换
float twoThirds = 2/3; // Noncompliant; int division. Yields 0.0
long millisInYear = 1_000*3_600*24*365; // Noncompliant; int multiplication. Yields 1471228928
long bigNum = Integer.MAX_VALUE + 2; // Noncompliant. Yields -2147483647
long bigNegNum = Integer.MIN_VALUE-1; //Noncompliant, gives a positive result instead of a negative one.
Date myDate = new Date(seconds * 1_000); //Noncompliant, won't produce the expected result if seconds > 2_147_483
原因:当对整数执行算术运算时,结果将始终是整数。您可以通过自动类型转换将该结果分配给long、double或float,但如果以int或long开头,则结果可能不是您期望的结果。例如,如果将int除法的结果分配给浮点变量,则在分配之前精度将丢失。同样,如果乘法的结果被分配给long,那么在分配之前它可能已经溢出。无论哪种情况,结果都不会像预期的那样。相反,在操作发生之前,应至少将一个操作数强制转换或提升为最终类型。总的来说,要么强制转换,要么将其中一个变量提升
float twoThirds = 2f/3; // 2 promoted to float. Yields 0.6666667
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;
13. ==比较
String firstName = getFirstName(); // String overrides equals
String lastName = getLastName();
if (firstName == lastName) { ... }; // Non-compliant; false even if the strings have the same value
原因:使用引用等式==或!=来比较java.lang.String或java.lang.Integer等装箱类型的两个实例几乎总是错误的,因为它不是比较实际值,而是比较内存中的位置。
String firstName = getFirstName();
String lastName = getLastName();
if (firstName != null && firstName.equals(lastName)) { ... };
14. 不同类型的比较判断
InvDetail invDetail = new InvDetail();
if(null != invDetail && !"".equals(invDetail)){
}
原因:删除对“equals”的调用;不相关类型之间的比较总是返回false。
InvDetail invDetail = new InvDetail();
if(null != invDetail)){
}
15.循环中break的使用
for (int i = 0; i < 10; i++) { // noncompliant, loop only executes once
printf("i is %d", i);
break;
}
原因:一次循环建议使用if。如果在循环中使用break,建议加上判断条件
for (int i = 0; i < 10; i++) {
if (i == x) {
break;
} else {
printf("i is %d", i);
}
}
16.条件判断的注意事项
//什么都没做
if(resultMap!=null){
if(PUBParm.RT_STATE_N.equals(resultMap.get("state"))){
}else{
}
}else{
}
//做了一样的事情
if (b == 0) { // Noncompliant
doOneMoreThing();
} else {
doOneMoreThing();
}
//条件判断后 永远只进入一个分支
int b = a > 12 ? 4 : 4; // Noncompliant
switch (i) { // Noncompliant
case 1:
doSomething();
break;
case 2:
doSomething();
break;
case 3:
doSomething();
break;
default:
doSomething();
}
17.左右运算符条件一致
if(!sts.equals("8")&&!sts.equals("8")){}
if ( a == b && a == b ) { // if the first one is true, the second one is too
doX();
}
if(flag =0 0)
原因:运算符左右条件一致,更新判定条件
18.对于InterruptedExceptions的处理
public void run () {
try {
while (true) {
// do stuff
}
}catch (InterruptedException e) { // Noncompliant; logging is not enough
LOGGER.log(Level.WARN, "Interrupted!", e);
}
}
try {
while (true) {
// do stuff
}
latch.await();
} catch (Exception e) {
Either re-interrupt this method or rethrow the "InterruptedException" that can be caught here.
e.printStackTrace();
throw new ServiceException(e.getMessage());
}
return ipg;
原因:代码中决不应忽略InterruptedExceptions,在这种情况下,只需将异常计数记录为“忽略”即可。抛出InterruptedException将清除线程的中断状态,因此如果异常处理不当,则线程被中断的信息将丢失。相反,InterruptedExceptions应该立即或在清理方法状态后重新抛出,或者通过调用thread.crupt()来重新中断线程,即使这是一个单线程应用程序。任何其他操作过程都有延迟线程关闭的风险,并丢失线程被中断的信息——可能没有完成任务。
同样,也应传播ThreadDeath异常。根据其JavaDoc:如果ThreadDeath被某个方法捕获,则必须对其进行重新处理,以使线程实际死亡。
public void run () {
try {
while (true) {
// do stuff
}
}catch (InterruptedException e) {
LOGGER.log(Level.WARN, "Interrupted!", e);
// Restore interrupted state...
Thread.currentThread().interrupt();
}
}
19.fianlly块中不应使用retrun、break等语句
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; // Noncompliant - prevents the RuntimeException from being propagated
}
}
原因:使用finally块中的return、break、throw等将抑制try或catch块中引发的任何未处理Throwable的传播。当跳转语句(break、continue、return、throw和goto)将强制控制流离开finally块时,此规则会引发问题。
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
}
}
/* ... */
}
}
20.根据函数返回的状态进行操作
if (zipFile.exists()) {
zipFile.delete();
}
zipFile.createNewFile();
原因:
当函数调用的返回值包含操作状态代码时,应测试该值以确保操作成功完成。
当忽略以下项的返回值时,此规则会引发问题:
- java.io.返回状态代码的文件操作(mkdirs除外)
- 迭代器hasNext()
- 枚举.hhasMoreElements()
- Lock.tryLock()
- 非void Condition.await*方法
- CountDownLatch.await(long,TimeUnit)
- Semaphore.try获取
- BlockingQueue:提供,删除
可以直接根据调用函数返回的状态码值进行下一步操作
if (!zipFile.delete();) {
zipFile.createNewFile();
}
21.compareTo方法在判断中的使用建议
if (myClass.compareTo(arg) == -1) { // Noncompliant
// ...
}
原因:虽然大多数compareTo方法返回-1、0或1,但有些方法不返回,并且将compareTo的结果与0以外的特定值进行测试可能会导致错误否定。
if (myClass.compareTo(arg) < 0) {
// ...
}
22.可能存在的1/0异常
User.numFormat(Math.abs((value - lastValue) / lastValue) * 100,2)).append("%,");
原因:要确定 lastValue不为0
if (lastValue!=0) {
User.numFormat(Math.abs((value - lastValue) / lastValue) * 100,2)).append("%,");
}
23.重写“equals()”,因此也应重写“hashCode()
class MyClass { // Noncompliant - should also override "hashCode()"
@Override
public boolean equals(Object obj) {
/* ... */
}
}
//代码中的具体使用
public boolean equals(Object obj) {
return true;
}
原因:此类重写“equals()”,因此也应重写“hashCode()”。
class MyClass { // Compliant
@Override
public boolean equals(Object obj) {
/* ... */
}
@Override
public int hashCode() {
/* ... */
}
}
24.自我赋值
public void setName(String name) {
name = name;
}
原因:没有理由将变量重新分配给它自己。要么是多余的,应该删除,要么重新赋值是错误的,而另一个值或变量用于赋值。
public void setName(String name) {
this.name = name;
}
25.创建变量给参数重新赋值
public void doTheThing(String str, int i, List<String> strings) {
str = Integer.toString(i); // Noncompliant
for (String s : strings) {
s = "hello world"; // Noncompliant
}
26.调用函数有返回值要接收
propDeTypes.replace("|", "");
原因:调用函数有返回值要接收,当满足以下两个条件时,此规则不会产生问题:
方法调用位于带有关联catch子句的try块中。
方法名称以“parse”、“format”、“decode”或“valueOf”开头,或者方法是String.getBytes(Charset)。
public void handle(String command){
String formattedCommand = command.toLowerCase();
...
}
private boolean textIsInteger(String textToCheck) {
try {
Integer.parseInt(textToCheck, 10); // OK
return true;
} catch (NumberFormatException ignored) {
return false;
}
}
27.Random应当抽取使用
public void doSomethingCommon() {
Random rand = new Random(); // Noncompliant; new instance created with each invocation
int rValue = rand.nextInt();
//...
原因:每次需要随机值时创建一个新的Random对象是低效的,并且可能会产生不随机的数字,具体取决于JDK。为了获得更好的效率和随机性,请创建一个Random,然后存储并重用它。Random()构造函数每次都尝试用不同的值设置种子。然而,不能保证种子是随机的,甚至是均匀分布的。有些JDK将使用当前时间作为种子,这使得生成的数字完全不是随机的。可不修改
private Random rand = SecureRandom.getInstanceStrong(); // SecureRandom is preferred to Random
public void doSomethingCommon() {
int rValue = this.rand.nextInt();
//...
28.Calendar等类不应当定义为静态变量
public class MyClass {
private static SimpleDateFormat format = new SimpleDateFormat("HH-mm-ss"); // Noncompliant
private static Calendar calendar = Calendar.getInstance(); // Noncompliant
原因:并非标准Java库中的所有类都是线程安全的。以多线程方式使用它们极有可能在运行时导致数据问题或异常。当Calendar、DateFormat、javax.xml.xpath.XXPath或javax.xml.validation.SchemaFactory的实例标记为静态时,此规则会引发问题。
DateUtil工具中使用日历变量的,建议在代码中新增,而不使用静态日历变量
public class MyClass {
private SimpleDateFormat format = new SimpleDateFormat("HH-mm-ss");
private Calendar calendar = Calendar.getInstance();
29.正则表达式的使用规范
private static final String CHECK_TEL_VALUE ="/^[+]{0,1}(\\d){1,3}[ ]?([-]?((\\d)|[ ]){1,12})+$/";
Pattern.compile("$[a-z]+^"); // Noncompliant
原因:在正则表达式中,边界^和\A只能在输入的开头匹配(或者,如果^与MULTILINE标志组合,则为行的开头),$、\Z和\Z只能在结尾匹配。这些模式可能会被误用,例如,通过意外切换^和$来创建一个永远无法匹配的模式。
Pattern.compile("^[a-z]+$");
30.hibernate.hbm2ddl.auto使用
<session-factory>
<property name="hibernate.hbm2ddl.auto">update</property> <!-- Noncompliant -->
</session-factory>
原因:对hibernate.hbm2ddl.auto使用除“validate”以外的任何值都可能导致应用程序使用的数据库架构被更改、删除或清除所有数据。简而言之,使用该属性是有风险的,并且只有在生产中使用“validate”选项时,才能使用该属性
<session-factory>
<property name="hibernate.hbm2ddl.auto">validate</property> <!-- Compliant -->
</session-factory>
or
<session-factory>
<!-- Property deleted -->
</session-factory>