1. 诸葛修⻋车⽹网(zgxcw.com) 商城研发:李李亚双(liys) 张瑞旺(zhangrw) 魏秀涛(weixt) 卫明(
weiming)
漏漏洞洞 次要
enums ⼀一般被认为等同于constant,有public属性或者setter⽅方法的enum不不仅不不同于constant,
⽽而且易易受恶意代码的攻击。enum理理想的属性是private,在构造函数⾥里里设置值,如果不不能这样,
也应该尽量量减少访问等级。
错误的⽤用法
public enum Continent {
NORTH_AMERICA (23, 24709000),
// ...
EUROPE (50, 39310000);
public int countryCount; // Noncompliant
private int landMass;
Continent(int countryCount, int landMass) {
// ...
}
public void setLandMass(int landMass) { // Noncompliant
this.landMass = landMass;
}
正确的⽤用法
public enum Continent {
sonar java 规则
“enum” fields should not be publicly mutable
2. NORTH_AMERICA (23, 24709000),
// ...
EUROPE (50, 39310000);
private int countryCount;
private int landMass;
Continent(int countryCount, int landMass) {
// ...
}
漏漏洞洞 严重
⽤用File.createTempFile作为创建临时路路径的第⼀一步是不不稳定的,应该使⽤用
Files.createTempDirectory (Java 7+) 或者使⽤用Guava的库⽅方法Files.createTempDir。
接下来的步骤被执⾏行行时,此规则引发问题。
- 调⽤用File.createTempFile
- 删除⽂文件
- 在File对象调⽤用mkdir⽅方法
Note 当⼯工程的sonar.java.source 低于7的时候,这条规则是有缺陷的。
错误的⽤用法
File tempDir;
tempDir = File.createTempFile("", ".");
tempDir.delete();
tempDir.mkdir(); // Noncompliant
正确的⽤用法
Path tempPath = Files.createTempDirectory("");
File tempDir = tempPath.toFile();
“File.createTempFile” should not be used to create
a directory
3. 漏漏洞洞 严重
根据oracle java api,HttpServletRequest.getRequestedSessionId()⽅方法返回客户端指定的
sessionID.这不不同于当前请求的sessionID。如果客户端不不指定⼀一个sessionID,⽅方法返回null。
HttpServletRequest.getRequestedSessionId()⽅方法返回的sessionID或者从cookie中传输,或者
被定义为URL参数,终端⽤用户可以在HTTP请求中⼿手动修改这个sessionID的值。
下⾯面是⼀一个被修改了了的HTTP Header的例例⼦子:
GET /pageSomeWhere HTTP/1.1
Host: webSite.com
User-Agent: Mozilla/5.0
Cookie: JSESSIONID=Hacked_Session_Value'''">
鉴于终端⽤用户可以修改这个值,请求中的sessionID仅仅被servlet容器器(E.G. Tomcat or Jetty)
判断是否跟⼀一个存在的session匹配。如果不不匹配,可以认为这个⽤用户是不不可靠的。⽽而且,不不应
记录此sessionID以防⽌止拦截活跃活动的记录。
错误的⽤用法
if(isActiveSession(request.getRequestedSessionId()) ){
...
}
漏漏洞洞 次要
没有理理由声明⼀一个public static 属性不不⽤用final修饰。多数情况下,在不不同的对象共享⼀一个属性。
但是⽤用这种⽅方式,任何对象都能对共享属性做任何事情,⽐比如把它设置为null。
错误的⽤用法
“HttpServletRequest.getRequestedSessionId()” sh
ould not be used
“public static” fields should be constant
4. public class Greeter {
public static Foo foo = new Foo();
...
}
正确的⽤用法
public class Greeter {
public static final Foo FOO = new Foo();
...
}
坏味道 阻断
⽤用测试框架的时候,很容易易写出不不完整的断⾔言。这个规则要求强⾏行行完成断⾔言,根据下⾯面的示例例:
- Fest: assertThat 不不允许被断⾔言调⽤用
- AssertJ: assertThat 不不允许被断⾔言调⽤用
- Mockito: verify 不不允许被断⾔言调⽤用
错误的⽤用法
// Fest
boolean result = performAction();
// let's now check that result value is true
assertThat(result); // Noncompliant; nothing is actually checked, the test p
asses whether "result" is true or false
// Mockito
List mockedList = Mockito.mock(List.class);
mockedList.add("one");
mockedList.clear();
// let's check that "add" and "clear" methods are actually called
Mockito.verify(mockedList); // Noncompliant; nothing is checked here, oups n
o call is chained to verify()
正确的⽤用法
Assertions should be complete
5. // Fest
boolean result = performAction();
// let's now check that result value is true
assertThat(result).isTrue();
// Mockito
List mockedList = Mockito.mock(List.class);
mockedList.add("one");
mockedList.clear();
// let's check that "add" and "clear" methods are actually called
Mockito.verify(mockedList).add("one");
Mockito.verify(mockedList).clear();
异常
变量量赋值和return语句句跳过让helper⽅方法。
private BooleanAssert check(String filename, String key) {
String fileContent = readFileContent(filename);
performReplacements(fileContent);
return assertThat(fileContent.contains(key)); // No issue is raised here
}
@Test
public void test() {
check("foo.txt", "key1").isTrue();
check("bar.txt", "key2").isTrue();
}
漏漏洞洞 次要
“secure”属性阻⽌止通过诸如HTTP之类的明⽂文连接发送cookie,这些cookie将很容易易被窃听。 相
反,具有secure属性的cookie仅通过加密的HTTPS连接发送。
错误的⽤用法
Cookie c = new Cookie(SECRET, secret); // Noncompliant; cookie is not secur
e
Cookies should be “secure”
9. }
}
正确的⽤用法
public void doSomething(){
...
synchronized(anObject) {
...
anObject.notify();
...
}
}
坏味道 严重
从⾮非静态⽅方法正确更更新静态字段是棘⼿手的问题,如果有多个类实例例和/或多个线程的情况,很容
易易导致错误。 理理想情况下,静态字段仅从同步静态⽅方法更更新。
每当从⾮非静态⽅方法更更新静态字段时,此规则引发问题。
错误的⽤用法
public class MyClass {
private static int count = 0;
public void doSomething() {
//...
count++; // Noncompliant
}
}
漏漏洞洞 次要
Instance methods should not write to “static” field
s
10. 把ip地址硬编码到源⽂文件是不不对的,原因如下:
- 如果ip地址改变,需要重新编译
- 必须要求所有环境下的ip⼀一致(dev, sys, qa, prod)
- 修改ip地址的任务交给了了开发者
- 攻击者反编译代码,会发现潜在的敏敏感地址
错误的⽤用法
String ip = "127.0.0.1";
Socket socket = new Socket(ip, 6667);
正确的⽤用法
String ip = System.getProperty("myapplication.ip");
Socket socket = new Socket(ip, 6667);
坏味道 严重
JUnit 的断⾔言不不应该在Runnable的run⽅方法中,因为失败的断⾔言结果抛出AssertionErrors.如果抛
出错误的线程不不是测试的线程,线程将会推出,但是断⾔言不不会失败。
错误的⽤用法
public void run() {
// ...
Assert.assertEquals(expected, actual); // Noncompliant
}
坏味道 阻断
IP addresses should not be hardcoded
JUnit assertions should not be used in “run” meth
ods
11. JUnit 3的测试⽤用例例中suite ⽅方法如果没有被正确的定义,将不不会被使⽤用。例例如:⽅方法必须命名为
suite,没有参数,⽤用public static 修饰,⽽而且必须返回junit.framework.Test或者
junit.framework.TestSuite。
相似的,setUp和tearDown⽅方法没有合适的⼤大写也会被忽略略。
错误的⽤用法
public void run() {
// ...
Assert.assertEquals(expected, actual); // Noncompliant
}Test suite() { ... } // Noncompliant; must be public static
public static boolean suite() { ... } // Noncompliant; wrong return type
public static Test suit() { ... } // Noncompliant; typo in method name
public static Test suite(int count) { ... } // Noncompliant; must be no-arg
public void setup() { ... } // Noncompliant; should be setUp
public void tearDwon() { ... } // Noncompliant; should be tearDown
正确的⽤用法
public static Test suite() { ... }
public void setUp() { ... }
public void tearDown() { ... }
坏味道 阻断
覆盖⽗父类⽅方法会阻⽌止调⽤用该⽅方法,除⾮非在覆盖⽅方法中进⾏行行显式调⽤用。有些时候,不不调⽤用覆盖的⽗父
类⽅方法是没问题的,但是JUnit 3中测试⽤用例例的setUp and tearDown 不不⾏行行。
错误的⽤用法
public class MyClassTest extends MyAbstractTestCase {
JUnit framework methods should be declared pro
perly
JUnit test cases should call super methods
12. private MyClass myClass;
@Override
protected void setUp() throws Exception { // Noncompliant
myClass = new MyClass();
}
正确的⽤用法
public class MyClassTest extends MyAbstractTestCase {
private MyClass myClass;
@Override
protected void setUp() throws Exception {
super.setUp();
myClass = new MyClass();
}
漏漏洞洞 次要
可变对象作为interface的public static 成员是不不建议的,这些变量量应该被移动到类中并且它们的
可⻅见性降低。
类似地,直接访问的类和枚举的可变静态成员,⽽而不不是通过getter和setter,应该被保护到可能的
程度。这可以通过降低可⻅见性或⽤用final修饰(如果适当)来完成。
注意,使⼀一个可变字段(如⼀一个数组)final将保持变量量不不被重新分配,但这样做对数组内部状态
的可变性没有影响。
此规则会引起public static array,Collection,Date和awt.Point成员的问题。
错误的⽤用法
public interface MyInterface {
public static String [] strings; // Noncompliant
}
public class A {
public static String [] strings1 = {"first","second"}; // Noncompliant
Mutable fields should not be “public static”
13. public static String [] strings2 = {"first","second"}; // Noncompliant
public static List<String> strings3 = new ArrayList<>(); // Noncompliant
// ...
}
漏漏洞洞 阻断
根据美国国家标准与技术研究院(NIST),数据加密标准(DES)不不再被认为是安全的:
1977年年通过⽤用于联邦机构⽤用于保护敏敏感的,未分类的信息,DES被撤销,因为它不不再提供保护联邦政府
信息所需的安全性。
⿎鼓励联邦机构使⽤用⾼高级加密标准,这是⼀一种在2001年年被批准为FIPS 197的更更快更更强的算法。
错误的⽤用法
Cipher c = Cipher.getInstance("DESede/ECB/PKCS5Padding");
正确的⽤用法
Cipher c = Cipher.getInstance("AES/GCM/NoPadding");
漏漏洞洞 次要
当函数调⽤用的返回值包含操作状态代码时,应判断返回值以确保操作成功完成。
当忽略略以下的返回值时,此规则会引发问题:
- 返回状态码的java.io.File操作(mkdir除外)
- Iterator.hasNext()
Neither DES (Data Encryption Standard) nor DESe
de (3DES) should be used
Return values should not be ignored when they co
ntain the operation status code
14. - Enumeration.hasMoreElements()
- Lock.tryLock()
- 返回⾮非空的Condition.await*⽅方法
- CountDownLatch.await(long, TimeUnit)
- Semaphore.tryAcquire
- BlockingQueue: offer, remove, drainTo,
错误的⽤用法
public void doSomething(File file, Lock lock) {
file.delete(); // Noncompliant
// ...
lock.tryLock(); // Noncompliant
}
正确的⽤用法
public void doSomething(File file, Lock lock) {
if (!lock.tryLock()) {
// lock failed; take appropriate action
}
if (!file.delete()) {
// file delete failed; take appropriate action
}
}
漏漏洞洞 阻断
执⾏行行SQL命令的应⽤用程序应该避免这些命令中使⽤用的任何外部提供的值。如果不不这样做,可能允
许攻击者执⾏行行包含更更改查询输⼊入的意外SQL命令,或暴暴露露敏敏感数据。此规则检查⽅方法参数不不是直
接在⾮非Hibernate SQL语句句中使⽤用,并且参数绑定,⽽而不不是连接在Hibernate语句句中使⽤用。
* 错误的⽤用法
public User getUser(Connection con, String user) throws SQLException {
Statement stmt1 = null;
SQL binding mechanisms should be used
15. Statement stmt2 = null;
PreparedStatement pstmt;
try {
stmt1 = con.createStatement();
ResultSet rs1 = stmt1.executeQuery("GETDATE()"); // Compliant; parameter
s not used here
stmt2 = con.createStatement();
ResultSet rs2 = stmt2.executeQuery("select FNAME, LNAME, SSN " +
"from USERS where UNAME=" + user); // Noncompliant; parame
ter concatenated directly into query
pstmt = con.prepareStatement("select FNAME, LNAME, SSN " +
"from USERS where UNAME=" + user); // Noncompliant; parame
ter concatenated directly into query
ResultSet rs3 = pstmt.executeQuery();
//...
}
public User getUserHibernate(org.hibernate.Session session, String userInput
) {
org.hibernate.Query query = session.createQuery( // Compliant
"FROM students where fname = " + userInput); // Noncompliant; p
arameter binding should be used instead
// ...
}
正确的⽤用法
public User getUser(Connection con, String user) throws SQLException {
Statement stmt1 = null;
PreparedStatement pstmt = null;
String query = "select FNAME, LNAME, SSN " +
"from USERS where UNAME=?"
try {
stmt1 = con.createStatement();
ResultSet rs1 = stmt1.executeQuery("GETDATE()");
pstmt = con.prepareStatement(query);
pstmt.setString(1, user); // Compliant; PreparedStatements escape their
inputs.
ResultSet rs2 = pstmt.executeQuery();
//...
16. }
}
public User getUserHibernate(org.hibernate.Session session, String userInput
) {
org.hibernate.Query query = session.createQuery("FROM students where fnam
e = ?");
query = query.setParameter(0,userInput); // Parameter binding escapes all
input
// ...
漏漏洞洞 次要
Throwable.printStackTrace(…)将Throwable及其堆栈跟踪打印到某个流。默认情况下,
System.Err流可能会⽆无意中暴暴露露敏敏感信息。应该使⽤用⽇日志来打印Throwables,因为它们有许多优
点:
- ⽤用户能够轻松检索⽇日志。
- ⽇日志消息的格式是统⼀一的,并允许⽤用户轻松浏览⽇日志。
当使⽤用没有参数的printStackTrace时,即当堆栈跟踪打印到默认流时,此规则引发⼀一个问题。
错误的⽤用法
try {
/* ... */
} catch(Exception e) {
e.printStackTrace(); // Noncompliant
}
正确的⽤用法
try {
/* ... */
} catch(Exception e) {
LOGGER.log("context", e);
}
Throwable.printStackTrace(…) should not be calle
d
18. // ...
}
public class ValidationFilter implements javax.servlet.Filter {
public void doFilter(ServletRequest request, ServletResponse response, Fil
terChain chain) {
chain.doFilter(new ValidatingHttpRequest( (HttpServletRequest)request ),
response);
}
}
and
<filter>
<filter-name>ValidationFilter</filter-name>
<filter-class>com.myco.servlet.ValidationFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>ValidationFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
BUG 严重
⽤用“@NonNull“标记的值不不能被设置成null
标记为@NotNull,@NonNull或@Nonnull的字段,参数和返回值被假定为具有⾮非空值,并且在使
⽤用前通常不不进⾏行行空值检查。 因此,将这些值之⼀一设置为null,或者⽆无法在构造函数中设置这样的
类字段,可能会在运⾏行行时导致NullPointerExceptions。
错误的⽤用法
public class MainClass {
@Nonnull
private String primary;
private String secondary;
“@NonNull” values should not be set to null
19. public MainClass(String color) {
if (color != null) {
secondary = null;
}
primary = color; // Noncompliant; "primary" is Nonnull but could be set
to null here
}
public MainClass() { // Noncompliant; "primary" Nonnull" but is not initia
lized
}
@Nonnull
public String indirectMix() {
String mix = null;
return mix; // Noncompliant; return value is Nonnull, but null is retur
ned.}}
}
BUG 严重
“BigDecimal(double)”,这种⽤用法是不不正确的
由于浮点不不精确,所以从BigDecimal(double)构造函数中可能获得不不到期望的值。
JavaDocs上解释说,构造函数的结果是不不可测的,可以假设在Java中编写新的
BigDecimal(0.1)会创建⼀一个BigDecimal,它等于0.1,但实际上等于
0.1000000000000000055511151231257827021181583404541015625。这是因为0.1不不能被⼆二
进制表示,因此,传递给构造函数的值并不不完全等于0.1。
相反,您应该使⽤用BigDecimal.valueOf,它使⽤用覆盖下的字符串串消除浮点舍⼊入误差
错误的⽤用法
double d = 1.1;
BigDecimal bd1 = new BigDecimal(d); // Noncompliant; see comment above
BigDecimal bd2 = new BigDecimal(1.1); // Noncompliant; same result
Identical expressions should not be used on both
sides of a binary operator
20. 正确的⽤用法
double d = 1.1;
BigDecimal bd1 = BigDecimal.valueOf(d);
BigDecimal bd2 = BigDecimal.valueOf(1.1);
BUG 严重
Cloneables应该实现clone
只是实现Cloneable⽽而不不覆盖Object.clone()不不⼀一定能克隆隆类,虽然实现Cloneable接⼝口没有要求克
隆隆⽅方法,但通常来讲,clone()是必须要覆盖的,否则,将使⽤用默认的JVM克隆隆,也就是说它将原
始值和对象引⽤用从源复制到⽬目标,任何克隆隆的实例例都可能与源实例例共享成员。
删除实现并提供构造函数是复制类的另⼀一种⽅方式。
错误的⽤用法
class Team implements Cloneable { // Noncompliant
private Person coach;
private List<Person> players;
public void addPlayer(Person p) {...}
public Person getCoach() {...}
}
正确的⽤用法
class Team implements Cloneable {
private Person coach;
private List<Person> players;
public void addPlayer(Person p) { ... }
public Person getCoach() { ... }
@Override
public Object clone() {
Team clone = (Team) super.clone();
//...
“Cloneables” should implement “clone”
22. 对于⼤大多数集合来说,size()检索的时间是固定的,但执⾏行行ConcurrentLinkedQueue.size()
所需的时间与队列列中的元素数量量成正⽐比,当队列列很⼤大时,这是⼀一个很费时的操作,并且如果队列列
在执⾏行行期间被修改,则结果可能是不不准确的。
检查集合是否为空不不能使⽤用size(),应该使⽤用isEmpty()⽅方法。
错误的⽤用法
ConcurrentLinkedQueue queue = new ConcurrentLinkedQueue();
//...
log.info("Queue contains " + queue.size() + " elements");
BUG 严重
equals(Object obj)“应与”compareTo(T obj)“⽅方法⼀一起重写
java的⽂文档中提出强烈烈建议,但不不严格要求使⽤用这种⽅方式:(x.compareTo(y)0)
(x.equals(y))。⼀一般来说,任何实现Comparable接⼝口并违反正常⽐比较逻辑的的类都应该重
写⽅方法表达清楚。如果违反此规则,可能会发⽣生奇怪和不不可预测的故障。
例例如,在Java 5中,PriorityQueue.remove()⽅方法依赖compareTo(),但是由于Java 6依赖
equals()
错误的⽤用法
public class Foo implements Comparable<Foo> {
@Override
public int compareTo(Foo foo) { /* ... */ } // Noncompliant as the eq
uals(Object obj) method is not overridden
}
正确的⽤用法
public class Foo implements Comparable<Foo> {
@Override
“equals(Object obj)” should be overridden along w
ith the “compareTo(T obj)” method
23. public int compareTo(Foo foo) { /* ... */ } // Compliant
@Override
public boolean equals(Object obj) { /* ... */ }
}
BUG 严重
实现Externalizable接⼝口的类必须有⽆无参构造函数
Externalizable接⼝口⽤用来处理理⾃自⼰己的序列列化和反序列列化。在反序列列化期间,进程的第⼀一步是使⽤用类
的⽆无参数构造函数来默认实例例化。因此,不不具有⽆无参数构造函数的Externalizable类不不能反序列列
化。
错误的⽤用法
public class Tomato implements Externalizable { // Noncompliant; no no-arg
constructor
public Tomato (String color, int weight) { ... }
}
正确的⽤用法
public class Tomato implements Externalizable {
public Tomato() { ... }
public Tomato (String color, int weight) { ... }
}
BUG 严重
“Externalizable” classes should have no-argument
s constructors
“hashCode” and “toString” should not be called o
n array instances
24. “hashCode”和“toString”不不能在数组实例例上调⽤用
虽然hashCode和toString在数组上可⽤用,但它们在很⼤大程度上是⽆无⽤用的。hashCode返回数组
的“标识哈希代码”,toString返回⼏几乎相同的值。这两种⽅方法的输出实际上并不不反映数组的内
容。 相反,您应该将数组传递给相关的静态Arrays⽅方法。
public static void main( String[] args )
{
String argStr = args.toString(); // Noncompliant
int argHash = args.hashCode(); // Noncompliant
正确的⽤用法
public static void main( String[] args )
{
String argStr = Arrays.toString(args);
int argHash = Arrays.hashCode(args);
BUG 严重
“InterruptedException”不不应该被忽略略
InterruptedExceptions不不应该在代码中被忽略略,或者只是简单地记录下。相反,
InterruptedExceptions应该重新抛出并且处理理清理理⽅方法的状态,或者⽅方法应该被中断。任何其他
操作过程都有可能延迟线程关闭,并丢失线程被中断的信息,然后导致没有完成任务。
为什什么会抛出这个异常?
是因为某些⽅方法在调⽤用之后会⼀一直阻塞线程,包括⼤大量量耗时的计算、让线程等待或者睡眠,这些
⽅方法导致线程会花很⻓长时间卡在那或者永远卡在那。这时你会怎么办呢?为了了让程序继续运⾏行行,
避免卡在这种地⽅方,你需要中断这种卡死的线程并抛出是哪⾥里里导致线程阻塞。所以某些⽅方法就需
要声明InterruptedException,表明这个⽅方法会响应线程中断。⽽而之前讲过中断线程可以⽤用
Thread.interrupt()或者使⽤用⼈人⼯工的中断标志。
解决⽅方法:捕获InterruptedException之后应该适当地对它进⾏行行处理理-⼤大多数情况下适当的处理理指
的是完全地跳出当前任务/循环/线程。
吞掉InterruptedException不不是⼀一个好主意
“InterruptedException” should not be ignored
29. BUG 严重
“toArray”应该传递⼀一个正确类型的数组
没有参数,Collections.toArray⽅方法返回⼀一个Object [],如果你试图把它转换为⼀一个适当的类的数
组,这将导致在运⾏行行时ClassCastException(类型转换错误)。所以要正确类型的数组传递给调
⽤用。
错误的⽤用法
public String [] getStringArray(List<String> strings) {
return (String []) strings.toArray(); // Noncompliant; ClassCastException
thrown
}
正确的⽤用法
public String [] getStringArray(List<String> strings) {
return strings.toArray(new String[0]);
}
BUG 严重
重写toString()“和"clone()"⽅方法时,不不能够返回null
调⽤用对象上的toString()或clone()应该总是返回⼀一个字符串串或⼀一个对象。 返回null⽽而不不是与
⽅方法的隐式契约冲突。
错误的⽤用法
public override string ToString () {
“toArray” should be passed an array of the proper
type
toString()“ and "clone()” methods should not retur
n null
30. if (this.collection.Count == 0) {
return null; // Noncompliant
} else {
// ...
{code}
正确的⽤用法
public override string ToString () {
if (this.collection.Count == 0) {
return "";
} else {
// ...
{code}
BUG 严重
当持有锁时,应使⽤用“wait(…)”⽽而不不是“Thread.sleep(…)”
如果Thread.sleep(…)在当前线程持有锁时被调⽤用,那么它可能导致性能和可扩展性问题,甚
⾄至更更糟糕的是死锁,因为持有锁的线程的执⾏行行被冻结。 最好在monitor对象上调⽤用wait(…)来
临时释放锁,并允许其他线程运⾏行行。
错误的⽤用法
public void doSomething(){
synchronized(monitor) {
while(notReady()){
Thread.sleep(200);
}
process();
}
...
}
正确的⽤用法
"wait(…)” should be used instead of “Thread.sleep
(…)” when a lock is held
31. public void doSomething(){
synchronized(monitor) {
while(notReady()){
monitor.wait(200);
}
process();
}
...
}
BUG 严重
“wait(…)”,“notify()”和“notifyAll()”这些⽅方法才能被调⽤用的时候,必须保证对⼀一个对象
加锁
java规则限制,⽅方法Object.wait(…),Object.notify()和Object.notifyAll()应该由对象监
视器器的所有者的线程调⽤用。 如果不不是这种情况,则抛出IllegalMonitorStateException异常。 所
以调⽤用这些⽅方法必须在同步⽅方法或语句句内。
错误的⽤用法
private void removeElement() {
while (!suitableCondition()){
obj.wait();
}
... // Perform removal
}
or
private void removeElement() {
while (!suitableCondition()){
wait();
}
“wait(…)”, “notify()” and “notifyAll()” methods shou
ld only be called when a lock is obviously held on
an object
32. ... // Perform removal
}
正确的⽤用法
private void removeElement() {
synchronized(obj) {
while (!suitableCondition()){
obj.wait();
}
... // Perform removal
}
}
or
private synchronized void removeElement() {
while (!suitableCondition()){
wait();
}
... // Perform removal
}
BUG 严重
类不不应该按名称进⾏行行⽐比较
java中没有要求类名称是唯⼀一的,只是它们在包中是唯⼀一的。 因此,试图根据类名来确定对象的
类型是错误的。如果类名可以参与⽐比较, 那么恶意⽤用户将发送与受信任类相同名称的对象,从⽽而
获得可信访问。
如果要⽐比较类可以使⽤用instanceof运算符。
错误的⽤用法
package computer;
class Pear extends Laptop { ... }
Classes should not be compared by name
33. package food;
class Pear extends Fruit { ... }
class Store {
public boolean hasSellByDate(Object item) {
if ("Pear".equals(item.getClass().getSimpleName())) { // Noncompliant
return true; // Results in throwing away week-old computers
}
}
}
正确的⽤用法
class Store {
public boolean hasSellByDate(Object item) {
if (item instanceof food.Pear) {
return true;
}
}
}
BUG 严重
集合不不可以作为参数传递到⾃自⼰己的⽅方法中
将集合作为参数传递给集合⾃自⼰己的⽅方法要么是错误 - 要么是其他参数 - 或者只是⽆无意义的代码。
此外,因为⼀一些⽅方法要求参数在执⾏行行期间保持未修改,将集合传递给⾃自身可能导致未定义的⾏行行
为。
错误的⽤用法
List <Object> objs = new ArrayList<Object>();
objs.add("Hello");
objs.add(objs); // Noncompliant; StackOverflowException if objs.hashCode() c
Collections should not be passed as arguments to
their own methods
34. alled
objs.addAll(objs); // Noncompliant; behavior undefined
objs.containsAll(objs); // Noncompliant; always true
objs.removeAll(objs); // Noncompliant; confusing. Use clear() instead
objs.retainAll(objs); // Noncompliant; NOOP
BUG 严重
条件不不应⽆无条件地计算为“TRUE”或“FALSE”
使⽤用恒为true或者恒为false的语句句是⽆无效的,这样的语句句完全是冗余的,并且使得代码较不不可
读。这种逻辑可能也与程序员的意图不不匹配。应该删除该条件,或者应该更更新该条件,以使其不不
总是求值为TRUE或FALSE。
错误的⽤用法
//foo can't be both equal and not equal to bar in the same expression
if( foo == bar && something && foo != bar) {...}
or
private void compute(int foo) {
int four = 4;
if (foo == four ) {
doSomething();
// We know foo is equal to the four variable at this point, so the next
condition is always false
if (foo > four) {...}
...
}
...
}
or
private void compute(boolean foo) {
Conditions should not unconditionally evaluate to
“TRUE” or to “FALSE”
35. if (foo) {
return;
}
doSomething();
// foo is always false here
if (foo){...}
...
}
BUG 严重
⾃自定义序列列化⽅方法签名应该满⾜足要求
编写者编写的可序列列化类可以选择让Java的⾃自动机制处理理序列列化和反序列列化,或者他们可以选择
通过实现特定的⽅方法来⾃自⼰己处理理它。 如果这些⽅方法的访问修饰符不不规范或没有任何访问修饰符,
它们将被忽略略
错误的⽤用法
public class Watermelon implements Serializable {
// ...
void writeObject(java.io.ObjectOutputStream out)// Noncompliant; not priva
te
throws IOException
{...}
private void readObject(java.io.ObjectInputStream in)
{...}
public void readObjectNoData() // Noncompliant; not private
{...}
static Object readResolve() throws ObjectStreamException // Noncompliant;
this method may have any access modifier, may not be static
Watermelon writeReplace() throws ObjectStreamException // Noncompliant; th
is method may have any access modifier, but must return Object
{...}
}
Custom serialization method signatures should m
eet requirements
36. 正确的的⽤用法
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
{...}
BUG 严重
不不同的原始包装器器,如果不不显式转换,不不可以与三元运算符⼀一起使⽤用
如果在三元运算符(例例如,?b:c)中使⽤用包装的原始值(例例如,整数和浮点),则两个值都将
被取消装箱并强制为常⻅见类型,从⽽而潜在地导致意想不不到的结果。 要避免这种情况,请将显式强
制类型添加到兼容类型。
错误的⽤用法
Integer i = 123456789;
Float f = 1.0f;
Dissimilar primitive wrappers should not be used
with the ternary operator without explicit casting
37. Number n = condition ? i : f; // Noncompliant; i is coerced to float. n = 1
.23456792E8
正确的⽤用法
Integer i = 123456789;
Float f = 1.0f;
Number n = condition ? (Number) i : f; // n = 123456789
BUG 严重
“可序列列化”类中的字段应该是transient或serializable
Serializable类中的字段必须是可序列列化或临时的,即使该类从未被明确序列列化或反序列列化。 这
是因为在负载下,⼤大多数J2EE应⽤用程序框架将对象刷新到磁盘,并且带有⾮非瞬态,不不可序列列化数
据成员的所谓Serializable对象可能导致程序崩溃,并打开攻击者的⼤大⻔门。
此规则在⾮非可序列列化字段和收集字段(⾮非私有)(因为它们可能在外部分配⾮非可序列列化值)以及
在类中分配⾮非可序列列化类型时引发问题。
错误的⽤用法
public class Address {
//...
}
public class Person implements Serializable {
private static final long serialVersionUID = 1905122041950251207L;
private String name;
private Address address; // Noncompliant; Address isn't serializable
}
BUG 严重
Fields in a “Serializable” class should either be tra
nsient or serializable
38. 浮点数不不能直接拿来做等于不不等于的⽐比较
浮点数学是不不精确的,因为以⼆二进制表示存储这样的值的挑战。 更更糟的是,浮点数学不不是关联
的; 通过⼀一系列列简单的数学运算来推动浮点或双精度,并且由于在每个步骤发⽣生的舍⼊入,基于那
些运算的顺序的答案将是不不同的。
即使简单的浮点分配也不不简单:
float f = 0.1; // 0.100000001490116119384765625
double d = 0.1; // 0.1000000000000000055511151231257827021181583404541015625
(结果将根据编译器器和编译器器设置⽽而有所不不同);
因此,对float或double值使⽤用等号(==)和不不等式(!=)操作符⼏几乎总是⼀一个错误,并且使⽤用
其他⽐比较运算符(>,> =,<,<=)也是有问题的 因为它们对-0和NaN不不能正常⼯工作。
相反,最好的⽅方法是完全避免浮点⽐比较。 当这不不可能时,你应该考虑使⽤用Java的float处理理数
字,如BigDecimal,它可以正确处理理浮点⽐比较。 第三个选择是看不不是相等,⽽而是看看值是否⾜足
够接近。 也就是说 将存储值和期望值之间的差的绝对值与可接受误差的余量量进⾏行行⽐比较。 注意,
这不不包括所有情况(例例如NaN和Infinity)。
此规则检查对浮点和双精度的直接和间接等式/不不等式测试的使⽤用。
错误的⽤用法
float myNumber = 3.146;
if ( myNumber == 3.146f ) { //Noncompliant. Because of floating point imprec
ision, this will be false
// ...
}
if ( myNumber != 3.146f ) { //Noncompliant. Because of floating point imprec
ision, this will be true
// ...
}
if (myNumber < 4 || myNumber > 4) { // Noncompliant; indirect inequality tes
t
// ...
}
Floating point numbers should not be tested for eq
uality
39. float zeroFloat = 0.0f;
if (zeroFloat == 0) { // Noncompliant. Computations may end up with a value
close but not equal to zero.
}
Exceptions
因为NaN不不等于⾃自身,所以⽤用这个特性可以检测NaN
float f;
double d;
if(f != f) { // Compliant; test for NaN value
System.out.println("f is NaN");
} else if (f != d) { // Noncompliant
// ...
}
BUG 严重
⼀一般异常不不应该抛出
⾃自定义异常的时候,不不应该使⽤用Error, RuntimeException, Throwable, and Exception 等通⽤用异
常,应该⾃自定义⼀一个异常
错误的⽤用法
public void foo(String bar) throws Throwable { // Noncompliant
throw new RuntimeException("My Message"); // Noncompliant
}
正确的⽤用法
public void foo(String bar) {
throw new MyOwnRuntimeException("My Message");
}
Exceptions
Generic exceptions should never be thrown
40. 通⽤用的异常在覆盖⽅方法中可以忽略略
@Override
public void myMethod() throws Exception {...}
BUG 严重
getters和setter应该成对同步
当getter / setter对的其中⼀一个被同步时,另⼀一个也应该同步。 如果不不⼀一起可能会导致运⾏行行时不不
⼀一致的⾏行行为,因为调⽤用者会访问不不⼀一致的⽅方法状态。
当⽅方法或getter / setter对中的⼀一个⽅方法的内容同步⽽而另⼀一个⽅方法不不同时,此规则引发问题。
错误的⽤用法
public class Person {
String name;
int age;
public synchronized void setName(String name) {
this.name = name;
}
public String getName() { // Noncompliant
return this.name;
}
public void setAge(int age) { // Noncompliant
this.age = age;
}
public int getAge() {
synchronized (this) {
return this.age;
}
}
}
Getters and setters should be synchronized in pair
s
41. 正确的⽤用法
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;
}
}
}
BUG 严重
相同的表达式不不应该⽤用在⼆二元运算符的两边
在⼆二进制运算符的任⼀一侧使⽤用相同的值是错误的。 在逻辑运算符的情况下,它是⼀一个复制/粘贴
错误,因此是⼀一个错误,或者它是简单的浪费代码,应该简化。 在按位运算符和⼤大多数⼆二进制数
学运算符的情况下,在运算符的两侧具有相同的值产⽣生可预测的结果,并且应当被简化。
错误的⽤用法
Identical expressions should not be used on both
sides of a binary operator
42. 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
正确的⽤用法
doZ();
if ( a == b ) {
doX();
}
if ( a == b ) {
doW();
}
int j = 1;
int k = 0;
Exceptions
但是,也有特殊的情况,⽐比如做NaN的有效测试
或者,左移1到1在位掩码的构造中也是常⻅见的
float f;
if(f != f) { //test for NaN value
System.out.println("f is NaN");
}
int i = 1 << 1; // Compliant
int j = a << a; // Noncompliant
43. BUG 严重
不不应该进⾏行行不不恰当的集合调⽤用
集合可以使⽤用跟⾃自⼰己类型不不匹配的参数,但是这么做会引发错误,当List.contains或List.remove
的参数的类型与⽤用于列列表声明的类型⽆无关时,此规则将引发问题。
错误⽤用法
List<String> list = new ArrayList<String>();
Integer integer = Integer.valueOf(1);
if (list.contains(integer)) { // Noncompliant. Always false.
list.remove(integer); // Noncompliant. list.add(integer) doesn't compile,
so this will always return false
}
BUG 严重
不不应使⽤用不不适当的正则表达式
正则表达式是强⼤大但棘⼿手的,甚⾄至那些⻓长期以来使⽤用它们可以犯错误。
以下不不应⽤用作正则表达式:
. - 匹配任意单个字符。 ⽤用于replaceAll,它匹配⼀一切
| - 通常⽤用作选项分隔符。 使⽤用独⽴立,它匹配字符之间的空格
File.separator - 匹配平台特定的⽂文件路路径分隔符。 在Windows上,这将被视为转义字符
也就是说特殊字符需要转移
Inappropriate “Collection” calls should not be mad
e
Inappropriate regular expressions should not be u
sed
44. 错误⽤用法
String str = "/File|Name.txt";
String clean = str.replaceAll(".",""); // Noncompliant; probably meant to re
move only dot chars, but returns an empty string
String clean2 = str.replaceAll("|","_"); // Noncompliant; yields _/_F_i_l_e_
|_N_a_m_e_._t_x_t_
String clean3 = str.replaceAll(File.separator,""); // Noncompliant; exceptio
n on Windows
BUG 严重
Int和Long类型的变量量做偏移时不不能⼤大于它们的⻓长度-1
由于int是⼀一个32位变量量,移位超过+/- 31最多会引起混乱,最坏时会出错。 将int移位32与将其
移位0相同,并且将其移位33与将其移位1相同。
类似地,将⻓长度移位+/- 64与将其移位0相同,并且将其移位65与将其移位1相同。
错误⽤用法
public int shift(int a) {
return a << 48;
}
正确⽤用法
public int shift(int a) {
return a << 16;
}
BUG 严重
Ints and longs should not be shifted by zero or mo
re than their number of bits-1
45. 不不应该使⽤用空的“date” 类型的变量量
Date字段的有效值范围是否从0或1开始,字段不不同。 例例如,⽉月份从0开始,⽉月份从1开始。输⼊入
超过有效范围结束的⽇日期值,⽇日期将滚动⽽而没有错误或异常。 例例如,输⼊入12个⽉月,您将获得下
⼀一年年的1⽉月。
此规则检查与java.util.Date,java.sql.Date和java.util.Calendar结合使⽤用的错误值。 具体来说,
有效范围之外的值:
month 0-11
date (day) 0-31
hour 0-23
minute 0-60
second 0-61
请注意,此规则不不会检查⽆无效的闰年年,闰秒(秒= 61)或每⽉月第31天的⽆无效使⽤用。
错误⽤用法
Date d = new Date();
d.setDate(25);
d.setYear(2014);
d.setMonth(12); // Noncompliant; rolls d into the next year
Calendar c = new GregorianCalendar(2014, 12, 25); // Noncompliant
if (c.get(Calendar.MONTH) == 12) { // Noncompliant; 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) {
// ...
}
Invalid “Date” values should not be used
47. 循环的条件⾄至少应该有⼀一次是为真的
如果在第⼀一次循环迭代之前for循环的条件为假,则永远不不会执⾏行行循环。 这样的循环⼏几乎总是错
误,特别是当初始值和停⽌止条件是硬编码时。
错误⽤用法
for (int i = 10; i < 10; i++) { // Noncompliant
// ...
BUG 严重
math操作数应在赋值之前转换
当对int执⾏行行算术时,结果将始终为int。 您可以将该结果指定为具有⾃自动类型转换的⻓长整型,双
精度型或浮点型,但是以int开头,结果可能不不是您期望的结果。
例例如,如果int除法的结果分配给⼀一个浮点变量量,精度将在赋值之前丢失。 同样,如果乘法的结
果被赋值为long,它可能在赋值之前已经溢出。
在任⼀一情况下,结果将不不是预期的结果。 相反,在操作发⽣生之前,应该⾄至少⼀一个操作数被转换或
提升为最终类型。
错误⽤用法
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 resu
lt instead of a negative one.
Date myDate = new Date(seconds * 1_000); //Noncompliant, won't produce the e
xpected result if seconds > 2_147_483
...
public long compute(int factor){
return factor * 10_000; //Noncompliant, won't produce the expected result
Loop conditions should be true at least once
Math operands should be cast before assignment
48. if factor > 214_748
}
正确的⽤用法
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_48
3_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;
}
or
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_48
3_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;
}
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;
}
49. BUG 严重
⽅方法不不能被命名成为 “hashcode” 或者 “equal”
⽤用hashcode()或者equal()命名⼀一个⽅方法会报:错误形式的错误。 通常是在覆盖
Object.hashCode()(注意camelCasing)或Object.equals(注意末尾的“s”),才⽤用这种形
式,这么命名会混淆每个其他开发⼈人员,他们可能没有注意到命名差异,或者谁会认为这是⼀一个
错误。
在这两种情况下,都应重命名该⽅方法。
错误⽤用法
public int hashcode() { /* ... */ } // Noncompliant
public boolean equal(Object obj) { /* ... */ } // Noncompliant
正确的⽤用法
@Override
public int hashCode() { /* ... */ }
public boolean equals(Object obj) { /* ... */ }
BUG 严重
多线块应⽤用⼤大括号括起来
花括号可以从单⾏行行块中省略略,例例如使⽤用if语句句或for循环,但这样做可能会误导并导致错误。
当在⼀一⾏行行块之后的⾏行行的缩进指示意图将这些⾏行行包括在块中时,该规则引发问题,但省略略⼤大括号意
Methods should not be named “hashcode” or “equ
al”
Multiline blocks should be enclosed in curly brace
s
50. 味着⾏行行将被⽆无条件地执⾏行行⼀一次。
错误⽤用法
if (condition)
firstActionInBlock();
secondAction(); // Noncompliant; executed unconditionally
thirdAction();
String str = null;
for (int i = 0; i < array.length; i++)
str = array[i];
doTheThing(str); // Noncompliant; executed only on last array element
正确的⽤用法
if (condition) {
firstActionInBlock();
secondAction();
}
thirdAction();
String str = null;
for (int i = 0; i < array.length; i++) {
str = array[i];
doTheThing(str);
}
BUG 严重
对于可能是“MIN_VALUE”的数字,不不应使⽤用“Math.abs”或否定
可以调⽤用hashCode返回Integer.MIN_VALUE。 取这样的哈希码的绝对值,你仍然有⼀一个负数。
因为你的代码可能假设它是⼀一个正值,你的结果将是不不可靠的。
类似地,Integer.MIN_VALUE可以从Random.nextInt()或任何对象的compareTo⽅方法返回,
Long.MIN_VALUE可以从Random.nextLong()返回。 从这些⽅方法返回的值调⽤用Math.abs同样
Neither “Math.abs” nor negation should be used o
n numbers that could be “MIN_VALUE”
51. 是不不明智的。
-2147483648取绝对值是2147483648超出了了int的表示范围其实他俩的⼆二进制是⼀一样的
错误的⽤用法
public void doSomething(String str) {
if (Math.abs(str.hashCode()) > 0) { // Noncompliant
// ...
}
}
正确的⽤用法
public void doSomething(String str) {
if (str.hashCode() != 0) {
// ...
}
}
BUG 严重
⾮非公开⽅方法不不应该⽤用“@Transactional” 标记
标记⾮非公共⽅方法@Transactional是⽆无⽤用的和误导性的,因为Spring不不“看到”⾮非公共⽅方法,因此没
有规定它们的正确调⽤用。 Spring也没有规定它调⽤用的⽅方法调⽤用的⽅方法。
因此,标记私有⽅方法,例例如,@Transactional只能导致运⾏行行时错误或异常,如果该⽅方法实际写为
@Transactional。
错误的⽤用法
@Transactional // Noncompliant
private void doTheThing(ArgClass arg) {
// ...
}
Non-public methods should not be “@Transaction
al”
52. BUG 严重
不不可序列列化的类不不能被写出到⽂文件
⾮非可序列列化类中的任何东⻄西都不不会被写⼊入⽂文件,并且试图序列列化这样的类将导致抛出异常。 只有
实现Serializable的类或扩展这样的类的类可以成功地序列列化(或反序列列化)。
错误的⽤用法
public class Vegetable { // neither implements Serializable nor extends a c
lass 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); // Noncompliant. 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);
}
}
Non-serializable classes should not be written
53. BUG 严重
不不可序列列化的对象不不应存储在“HttpSession”对象中
如果你没有打算写⼀一个HttpSession对象到⽂文件,那么存储不不可序列列化的对象可能不不是⼀一个⼤大问
题。 但是⽆无论你是否明确地序列列化会话,它可能被写⼊入磁盘,因为服务器器在⼀一个称为“钝化”的进
程中管理理其内存使⽤用。 此外,⼀一些服务器器在关机时⾃自动将其活动会话写⼊入⽂文件,并在启动时反序
列列化任何此类会话。
关键是,即使HttpSession不不扩展Serializable,您仍然必须假定它将被序列列化,并且如果您在会
话中存储了了不不可序列列化的对象,将会导致错误。
错误的⽤用法
public class Address {
//...
}
//...
HttpSession session = request.getSession();
session.setAttribute("address", new Address()); // Noncompliant; Address is
n't serializable
BUG 严重
⾮非线程安全字段不不应该是静态的
并⾮非标准Java库中的所有类都被写为线程安全的。 以多线程⽅方式使⽤用它们很可能在运⾏行行时导致
数据问题或异常。
当Calendar,DateFormat,javax.xml.xpath.XPath或javax.xml.validation.SchemaFactory的实例例
标记为static时,此规则引发问题。
Non-serializable objects should not be stored in “
HttpSession” objects
Non-thread-safe fields should not be static
54. 错误的⽤用法
public class MyClass {
static private SimpleDateFormat format = new SimpleDateFormat("HH-mm-ss");
// Noncompliant
static private Calendar calendar = Calendar.getInstance(); // Noncomplian
t
正确的⽤用法
public class MyClass {
private SimpleDateFormat format = new SimpleDateFormat("HH-mm-ss");
private Calendar calendar = Calendar.getInstance();
BUG 严重
⽤用format格式化字符串串应该避免运⾏行行是异常的触发
因为printf样式格式字符串串在运⾏行行时被解释,⽽而不不是由Java编译器器验证,它们可能包含导致意外
⾏行行为或运⾏行行时错误的错误。 当调⽤用java.util.Formatter,java.lang.String,java.io.PrintStream,
MessageFormat和java.io.PrintWriter类的format(…)⽅方法时,此规则静态验证printf样式格式
的良好⾏行行为 和java.io.PrintStream或java.io.PrintWriter类的printf(…)⽅方法。
正确的⽤用法
String.format("The value of my integer is %d", "Hello World"); // Noncompli
ant; an 'int' is expected rather than a String
String.format("First {0} and then {1}", "foo", "bar"); //Noncompliant. Look
s like there is a confusion with the use of {{java.text.MessageFormat}}, par
ameters "foo" and "bar" will be simply ignored here
String.format("Duke's Birthday year is %tX", c); //Noncompliant; X is not a
supported time conversion character
String.format("Display %3$d and then %d", 1, 2, 3); //Noncompliant; the se
cond argument '2' is unused
String.format("Display %0$d and then %d", 1); //Noncompliant; arguments ar
e numbered starting from 1
Printf-style format strings should not lead to unex
pected behavior at runtime
55. String.format("Too many arguments %d and %d", 1, 2, 3); //Noncompliant; the
third argument '3' is unused
String.format("Not enough arguments %d and %d", 1); //Noncompliant; the sec
ond argument is missing
String.format("First Linen"); //Noncompliant; %n should be used in place
of n to produce the platform-specific line separator
String.format("%< is equals to %d", 2); //Noncompliant; the argument index
'<' refers to the previous format specifier but there isn't one
String.format("Is myObject null ? %b", myObject); //Noncompliant; when a n
on-boolean argument is formatted with %b, it prints true for any nonnull val
ue, and false for null. Even if intended, this is misleading. It's better to
directly inject the boolean value (myObject == null in this case)
String.format("value is " + value); // Noncompliant
String s = String.format("string without arguments"); // Noncompliant
MessageFormat.format("Result {1}.", value); // Noncompliant; Not enough argu
ments. (first element is {0})
MessageFormat.format("Result '{0}'.", value); // Noncompliant; String contai
ns no format specifiers. (quote are discarding format specifiers)
MessageFormat.format("Result {{0}.", value); // Noncompliant; Unbalanced num
ber of curly brace (single curly braces should be escaped)
MessageFormat.format("Result ' {0}", value); // Noncompliant; Unbalanced num
ber of quotes (single quote must be escaped)
MessageFormat.format("Result {0}.", value, value); // Noncompliant; 2nd arg
ument is not used
MessageFormat.format("Result {0}.", myObject.toString()); // Noncompliant; n
o need to call toString() on objects
正确的⽤用法
String.format("The value of my integer is %d", 3);
String.format("First %s and then %s", "foo", "bar");
String.format("Duke's Birthday year is %tY", c);
String.format("Display %2$d and then %d", 1, 3);
String.format("Display %1$d and then %d", 1);
String.format("Too many arguments %d %d", 1, 2);
String.format("Not enough arguments %d and %d", 1, 2);
String.format("First Line%n");
String.format("%d is equals to %<", 2);
String.format("Is myObject null ? %b", myObject == null);
String.format("value is %d", value);
String s = "string without arguments";
MessageFormat.format("Result {0}.", value);
MessageFormat.format("Result '{0}' = {0}", value);
MessageFormat.format("Result {0} & {1}.", value, value);
MessageFormat.format("Result {0}.", myObject);
59. public class MyServlet extends HttpServlet {
private String userName; //As this field is shared by all users, it's obv
ious that this piece of information should be managed differently
...
}
or
public class MyAction extends Action {
private String userName; //Same reason
...
}
BUG 严重
短路路逻辑应该在布尔上下⽂文中使⽤用
在布尔上下⽂文中使⽤用⾮非短路路逻辑可能是⼀一个错误 - 可能导致严重的程序错误,因为条件在错误的
情况下被评估。
错误的⽤用法
if(getTrue() | getFalse()) { ... } // Noncompliant; both sides evaluated
正确的⽤用法
if(getTrue() || getFalse()) { ... } // true short-circuit logic
BUG 严重
Short-circuit logic should be used in boolean cont
exts
Silly equality checks should not be made
60. 不不应该进⾏行行不不想关的平等检查
不不同类型的⽐比较总是返回false。 ⽐比较及其所有依赖代码可以简单地删除。 这包括:
将对象与null⽐比较
将⼀一个对象与⼀一个不不相关的基元(E.G.⼀一个带有int的字符串串)
⽐比较不不相关的类
⽐比较不不相关的类和接⼝口
⽐比较不不相关的接⼝口类型
将数组与⾮非数组进⾏行行⽐比较
⽐比较两个数组
特别是在数组的情况下,由于数组不不覆盖Object.equals(),在两个数组上调⽤用equals与⽐比较它
们的地址相同。 这意味着array1.equals(array2)等价于array1 == array2。
然⽽而,⼀一些开发⼈人员可能期望Array.equals(Object obj)⽐比⼀一个简单的内存地址⽐比较,⽐比较两个
数组的⼤大⼩小和内容。 相反,==运算符或Arrays.equals(array1,array2)应始终与数组⼀一起使
⽤用。
错误的⽤用法
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)) { // Noncompliant; unrelated classes
// ...
}
else if (spatula.equals(plant)) { // Noncompliant; unrelated class and inter
face
// ...
}
else if (tool.equals(plant)) { // Noncompliant; unrelated interfaces
61. // ...
}
else if (tool.equals(tools)) { // Noncompliant; array & non-array
// ...
}
else if (trees.equals(tools)) { // Noncompliant; incompatible arrays
// ...
}
else if (tree.equals(null)) { // Noncompliant
// ...
}
BUG 严重
“可序列列化”类的⾮非可串串⾏行行化超类应该有⼀一个⽆无参数构造函数
当可序列列化对象在其继承链中具有不不可序列列化的祖先时,对象反序列列化(从⽂文件中重新实例例化对
象)从第⼀一个不不可序列列化的类开始,并沿着链继续,添加每个后续⼦子类的属性, 直到最终对象已
经被实例例化。
为了了创建不不可序列列化的祖先,它的⽆无参构造函数被调⽤用。 因此,Serializable类的不不可序列列化祖
先必须有⼀一个⽆无参数构造函数。 否则类是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 { // Noncompliant; nonserializable ancestor
doesn't have no-arg constructor
private static final long serialVersionUID = 1;
The non-serializable super class of a “Serializable”
class should have a no-argument constructor
62. 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() {...}
}
BUG 严重
不不应该调⽤用Object.finalize()⽅方法
根据官⽅方的javadoc⽂文档,当垃圾回收确定没有对对象的更更多引⽤用时,对象上的垃圾收集器器调⽤用
此Object.finalize()。 调⽤用此⽅方法明确违反此合同,因此具有误导性。
错误的⽤用法
public class Fruit {
The Object.finalize() method should not be called
63. public void dispose() throws Throwable {
this.finalize(); // Noncompliant
}
BUG 严重
run⽅方法不不能直接被调⽤用
Thread.run()⽅方法的⽬目的是在⼀一个单独的专⽤用线程中执⾏行行代码。 直接调⽤用此⽅方法没有意义,因
为它导致其代码在当前线程中执⾏行行。
要获得预期的⾏行行为,请调⽤用Thread.start()⽅方法。
错误的⽤用法
Thread myThread = new Thread(runnable);
myThread.run(); // Noncompliant
正确的⽤用法
Thread myThread = new Thread(runnable);
myThread.start(); // Compliant
BUG 严重
值不不应⽆无⽤用增加
递增或递减然后不不存储的值最多是浪费的代码,最坏的是错误。
错误的⽤用法
Thread.run() should not be called directly
Values should not be uselessly incremented
64. public int pickNumber() {
int i = 0;
int j = 0;
i = i++; // Noncompliant; i is still zero
return j++; // Noncompliant; 0 returned
}
正确的⽤用法
public int pickNumber() {
int i = 0;
int j = 0;
i++;
return ++j;
}
BUG 严重
变量量不不应该是⾃自分配的
没有理理由重新分配⼀一个变量量给⾃自⼰己。 这个语句句是多余的,应该被删除,或者重新赋值是⼀一个错
误,⼀一些其他的值或变量量被赋予赋值。
public void setName(String name) {
name = name;
}
正确的⽤用法
public void setName(String name) {
this.name = name;
Variables should not be self-assigned
72. 正确的⽤用法
protected void finalize() {
releaseSomeResources();
super.finalize();
}
“for”循环更更新⼦子句句应将计数器器向正确的⽅方向移动
具有在错误⽅方向上移动的计数器器的for循环不不是⽆无限循环。 由于循环,循环最终将达到其停⽌止条
件,但在这样做时,它会运⾏行行许多,超过预期的次数,可能会导致意外的⾏行行为。
错误的⽤用法
public void doSomething(String [] strings) {
for (int i = 0; i < strings.length; i--) { // Noncompliant;
String string = strings[i]; // ArrayIndexOutOfBoundsException when i re
aches -1
//...
}
正确的⽤用法
public void doSomething(String [] strings) {
for (int i = 0; i < strings.length; i++) {
String string = strings[i];
//...
}
跳转语句句不不应该出现在“finally”块中
从finally块中返回,断开,抛出等等,抑制了了在try或catch块中抛出的任何未处理理的Throwable的
传播。
A “for” loop update clause should move the count
er in the right direction
Jump statements should not occur in “finally” bloc
ks
73. 当跳转语句句(break,continue,return,throw和goto)强制控制流离开finally块时,此规则引发
⼀一个问题。
错误的⽤用法
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
}
}
正确的⽤用法
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 {
74. for (int i = 0; i < 10; i ++) {
//...
if (q == i) {
break; // ignored
}
}
/* ... */
}
}
循环不不应该是⽆无限的
⽆无限循环是程序运⾏行行时永远不不会结束的循环。 你必须杀死程序才能退出循环。 ⽆无论是通过满⾜足
循环的结束条件还是通过中断,每个循环都应该有结束条件。
错误的⽤用法
for (;;) { // Noncompliant; 没有结束条件
// ...
}
for (int i = 0; i < 10; i--) { // Noncompliant;结束条件不不可达
//...
}
int j;
while (true) { // Noncompliant; 没有结束条件
j++;
}
int k;
boolean b = true;
while (b) { // Noncompliant; b从来没有写⼊入循环
k++;
}
正确的⽤用法
for (int i = 0; i < 10; i++) { // end condition now reachable
//...
Loops should not be infinite
75. }
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;
}
空指针不不应被引⽤用
对null的引⽤用不不应被取消引⽤用/访问。 这样做将导致抛出NullPointerException。 最多时,这样的
异常将导致程序突然终⽌止。 在最坏的情况下,它可能暴暴露露对攻击者有⽤用的调试信息,或者它可能
允许攻击者绕过安全措施。
注意,当它们存在时,此规则利利⽤用JSR-305中定义的@CheckForNull和@Nonnull注释来了了解哪些
值是不不可为空的。
@Nullable表示,在某些未指定的情况下,值可能为null。 为了了保持低误报率,此注释将被忽略略。
是否需要显式测试由开发⼈人员⾃自⾏行行决定。
错误的⽤用法
@CheckForNull
String getName(){...}
public boolean isNameEmpty() {
return getName().length() == 0; // Noncompliant; getName()的结果可以为null
,但没有为空校验
}
Connection conn = null;
Statement stmt = null;
Null pointers should not be dereferenced
76. try{
conn = DriverManager.getConnection(DB_URL,USER,PASS);
stmt = conn.createStatement();
// ...
}catch(Exception e){
e.printStackTrace();
}finally{
stmt.close(); // Noncompliant; 如果在try {}块中抛出异常,stmt可以为null
conn.close(); // Noncompliant; 如果抛出异常,conn可以为null
}
private void merge(@Nonnull Color firstColor, @Nonnull Color secondColor){..
.}
public void append(@CheckForNull Color color) {
merge(currentColor, color); // Noncompliant; color应该被null检查,因为merg
e(...)不不接受可空参数
}
void paint(Color color) {
if(color == null) {
System.out.println("Unable to apply color " + color.toString()); // Non
compliant; 将抛出NullPointerException
return;
}
...
}
反射不不应该⽤用于检查⾮非运⾏行行时注解
注释的编写者可以为其设置三个保留留策略略之⼀一:
@Retention(RetentionPolicy.SOURCE)注解仅存在于源码中,在class字节码⽂文件中不不包含
@Retention(RetentionPolicy.CLASS)默认的保留留策略略,注解会在class字节码⽂文件中存在,
但运⾏行行时⽆无法获得
@Retention(RetentionPolicy.RUNTIME)注解会在class字节码⽂文件中存在,在运⾏行行时可以通
Reflection should not be used to check non-runti
me annotations
77. 过反射获取到
错误的⽤用法
Method m = String.class.getMethod("getBytes", new Class[] {int.class,
int.class, byte[].class, int.class});
if (m.isAnnotationPresent(Override.class)) { // Noncompliant;test将总是返回fa
lse,即使在代码中存在@Override
资源应关闭
Java的垃圾收集不不能⽤用于清理理⼀一切。 具体来说,实现Closeable接⼝口或其超级接⼝口
(AutoCloseable)的连接,流,⽂文件和其他类必须在创建后⼿手动关闭。 此外,必须在finally块
中进⾏行行该close调⽤用,否则,异常可以保持调⽤用不不被调⽤用。
没有正确关闭资源将导致资源泄露露,这可能带来的应⽤用程序,然后可能是盒⼦子它的膝盖。
错误的⽤用法
private void readTheFile() throws IOException {
Path path = Paths.get(this.fileName);
BufferedReader reader = Files.newBufferedReader(path, this.charset)) {
// ...
reader.close(); // Noncompliant
}
private void doSomething() {
OutputStream stream = null;
try{
for (String property : propertyList) {
stream = new FileOutputStream("myfile.txt"); // Noncompliant
// ...
}
}catch(Exception e){
// ...
}finally{
stream.close(); // Multiple streams were opened. Only the last is close
d.
}
}
Resources should be closed
80. // ...
}
synchronized(lock3) {
// ...
}
应该检查从流读取返回的值
你不不能假设任何给定的流读取调⽤用将填充传递到⽅方法的byte []。 相反,您必须检查读取⽅方法返回
的值,以查看已读取的字节数。 没有这样做,你介绍的bug是既有害⼜又难以重现。
同样,您不不能假设InputStream.skip实际上会跳过所请求的字节数,但必须检查从⽅方法返回的
值。
当调⽤用接受byte []的InputStream.read⽅方法但未检查返回值,并且未检查InputStream.skip的返回
值时,此规则引发问题。 该规则也适⽤用于InputStream⼦子类。
错误的⽤用法
public void doSomething(String fileName) {
try {
InputStream is = new InputStream(file);
byte [] buffer = new byte[1000];
is.read(buffer); // Noncompliant
// ...
} 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) { ... }
The value returned from a stream read should be c
hecked
81. }
Throwable和Error不不应该被捕获
Throwable是Java中所有错误和异常的超类。
Error是所有错误的超类,这不不意味着被应⽤用程序捕获。
捕获Throwable或Error也会捕获OutOfMemoryError和InternalError,应⽤用程序不不应该从中尝试恢
复。
错误的⽤用法
try { /* ... */ } catch (Throwable t) { /* ... */ }
try { /* ... */ } catch (Error e) { /* ... */ }
正确的⽤用法
try { /* ... */ } catch (RuntimeException e) { /* ... */ }
try { /* ... */ } catch (MyException e) { /* ... */ }
“assert”应该只与布尔变量量⼀一起使⽤用
由于assert语句句不不是默认执⾏行行的(它们必须使⽤用JVM标志),开发⼈人员不不应该依赖于它们的执⾏行行
评估正确的程序函数所需的任何逻辑。
错误的⽤用法
assert myList.remove(myList.get(0)); // Noncompliant
正确的⽤用法
boolean removed = myList.remove(myList.get(0));
Throwable and Error should not be caught
“assert” should only be used with boolean variable
s
82. assert removed;
不不应检查“compareTo”结果的特定值
虽然⼤大多数compareTo⽅方法返回-1,0或1,但有些不不会。并且测试compareTo对⽐比⾮非0的特定值的
结果可能会导致错误。
错误的⽤用法
if (myClass.compareTo(arg) == -1) { // Noncompliant
// ...
}
正确的⽤用法
if (myClass.compareTo(arg) < 0) {
// ...
}
不不应使⽤用来⾃自Apache Commons Lang库的“DateUtils.truncate”
使⽤用Java 8中引⼊入的Instant类来截断⽇日期可以显着快于Commons Lang中的DateUtils类。
错误的⽤用法
public Date trunc(Date date) {
return DateUtils.truncate(date, Calendar.SECOND); // Noncompliant
}
正确的⽤用法
public Date trunc(Date date) {
“compareTo” results should not be checked for sp
ecific values
“DateUtils.truncate” from Apache Commons Lang
library should not be used
83. Instant instant = date.toInstant();
instant = instant.truncatedTo(ChronoUnit.SECONDS);
return Date.from(instant);
}
“entrySet()”应该在需要key和value的时候迭代
当在循环中只需要来⾃自映射的键时,迭代keySet是有意义的。 但是当需要键和值时,迭代
entrySet更更有效率,它将访问键和值。
错误的⽤用法
public void doSomethingWithMap(Map<String,Object> map) {
for (String key : map.keySet()) { // Noncompliant; 对于每个键,检索该值
Object value = map.get(key);
// ...
}
}
正确的⽤用法
public void doSomethingWithMap(Map<String,Object> map) {
for (Map.Entry<String,Object> entry : map.entrySet()) {
String key = entry.getKey();
Object value = entry.getValue();
// ...
}
}
“for”循环递增器器应该在循环停⽌止条件中修改要测试的变量量
当for循环的停⽌止条件和增⻓长器器不不作⽤用同⼀一变量量时,基本就是报错。 即使没有错误,它可能混淆
未来的代码维护者,应该避免。
“entrySet()” should be iterated when both the key
and value are needed
“for” loop incrementers should modify the variable
being tested in the loop’s stop condition
84. 错误的⽤用法
for (i = 0; i < 10; j++) { // Noncompliant
// ...
}
正确的⽤用法
for (i = 0; i < 10; i++) {
// ...
}
“indexOf”检查不不应该是正数
对indexOf值的⼤大多数检查将与-1进⾏行行⽐比较,因为0是有效的索引。 任何查找值> 0的检查忽略略第
⼀一个元素,这可能是⼀一个错误。 如果意图仅仅是检查在String或List中包含值,请考虑使⽤用
contains⽅方法。
当针对> 0测试从字符串串或列列表检索到的indexOf值时,此规则引发问题。
错误的⽤用法
String color = "blue";
String name = "ishmael";
List<String> strings = new ArrayList<String> ();
strings.add(color);
strings.add(name);
if (strings.indexOf(color) > 0) { // Noncompliant
// ...
}
if (name.indexOf("ish") > 0) { // Noncompliant
// ...
}
if (name.indexOf("ae") > 0) { // Noncompliant
// ...
}
“indexOf” checks should not be for positive numb
ers
85. 正确的⽤用法
String color = "blue";
String name = "ishmael";
List<String> strings = new ArrayList<String> ();
strings.add(color);
strings.add(name);
if (strings.indexOf(color) > -1) {
// ...
}
if (name.indexOf("ish") >= 0) {
// ...
}
if (name.contains("ae") {
// ...
}
应该删除总是返回“true”或“false”的“instanceof”运算符
instanceof运算符总是返回true或false是⽆无⽤用的或是误解的结果,可能导致意想不不到的⽣生产中的
⾏行行为。
错误的⽤用法
public boolean isSuitable(Integer param) {
...
String name = null;
if (name instanceof String) { // Noncompliant; always false since name is
null
//...
}
if(param instanceof Number) { // Noncompliant; always true unless param i
s null, because param is an Integer
doSomething();
}
...
“instanceof” operators that always return “true” or
“false” should be removed
86. }
正确的⽤用法
public boolean isSuitable(Integer param) {
...
doSomething();
...
}
“Iterator.next()”⽅方法应该抛出“NoSuchElementException”
按照规定,当迭代没有更更多元素时,java.util.Iterator.next()⽅方法的任何实现都应该抛出⼀一个
NoSuchElementException异常。 迭代完成后的任何其他⾏行行为可能会导致此Iterator出现异常。
错误⽤用法
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();
}
...
}
}
“Iterator.next()” methods should throw “NoSuchEl
ementException”