SlideShare a Scribd company logo
1 of 108
Download to read offline
诸葛修⻋车⽹网(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
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
漏漏洞洞 严重
根据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
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
// 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”
response.addCookie(c);
正确的⽤用法
Cookie c = new Cookie(SECRET, secret);
c.setSecure(true);
response.addCookie(c);
漏漏洞洞 阻断
因为从编译的应⽤用程序中提取字符串串很容易易,所以凭证不不应该是硬编码的。 这样做,他们⼏几乎可
以确保最终在攻击者的⼿手中。 分布式应⽤用程序更更是如此。
凭证应存储在强制保护的加密配置⽂文件或数据库中的代码之外。
错误的⽤用法
Connection conn = null;
try {
conn = DriverManager.getConnection("jdbc:mysql://localhost/test?" +
"user=steve&password=blue"); // Noncompliant
String uname = "steve";
String password = "blue";
conn = DriverManager.getConnection("jdbc:mysql://localhost/test?" +
"user=" + uname + "&password=" + password); // Noncompliant
java.net.PasswordAuthentication pa = new java.net.PasswordAuthentication("
userName", "1234".toCharArray()); // Noncompliant
正确的⽤用法
Connection conn = null;
try {
String uname = getEncryptedUser();
String password = getEncryptedPass();
conn = DriverManager.getConnection("jdbc:mysql://localhost/test?" +
"user=" + uname + "&password=" + password);
Credentials should not be hard-coded
漏漏洞洞 严重
在RSA加密中没有OAEP,攻击者更更容易易解密数据或从密⽂文推断加密⽅方式。 此规则在⽂文字值以
RSA / NONE开头时记录问题。
错误的⽤用法
Cipher rsa = javax.crypto.Cipher.getInstance("RSA/NONE/NoPadding");
正确的⽤用法
Cipher rsa = javax.crypto.Cipher.getInstance("RSA/ECB/OAEPWITHSHA-256ANDMGF1
PADDING");
漏漏洞洞 次要
即使servlet中的⽅方法的签名包含throws IOException,ServletException,但是抛出这样的异常并
不不是好主意。 不不能在servlet中捕获异常可能使系统处于易易受攻击的状态,可能导致拒绝服务攻击
或暴暴露露敏敏感信息,因为当servlet抛出异常时,servlet容器器通常将调试信息发送回 ⽤用户。 这些信
息对于攻击者来说是⾮非常有价值的。
此规则检查名为“do *”的⽅方法中的所有异常在servlet类中是否显式处理理。
错误的⽤用法
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException {
Cryptographic RSA algorithms should always inco
rporate OAEP (Optimal Asymmetric Encryption Pa
dding)
Exceptions should not be thrown from servlet met
hods
String ip = request.getRemoteAddr();
InetAddress addr = InetAddress.getByName(ip); // Noncompliant; getByName(S
tring) throws UnknownHostException
//...
}
正确的⽤用法
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException {
try {
String ip = request.getRemoteAddr();
InetAddress addr = InetAddress.getByName(ip);
//...
}
catch (UnknownHostException uhex) {
//...
}
}
坏味道 严重
根据Oracle Javadoc:
当线程尝试等待对象的监视器器或通知其他线程等待对象的监视器器⽽而不不拥有指定的监视器器时,抛出
IllegalMonitorStateException。
换句句话说,只有在设计不不当的情况下才能抛出此异常,因为不不应该对未拥有监视器器的对象调⽤用
Object.wait(…),Object.notify()和Object.notifyAll()⽅方法。
错误的⽤用法
public void doSomething(){
...
try {
...
anObject.notify();
...
} catch(IllegalMonitorStateException e) {
...
IllegalMonitorStateException should not be caught
}
}
正确的⽤用法
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
把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
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
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”
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
- 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
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();
//...
}
}
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
漏漏洞洞 严重
没有理理由在web应⽤用⾥里里⾯面包含mian⽅方法。在应⽤用的开发阶段可能⽤用来调试,但是不不应该使⽤用于⽣生
产环境。在web应⽤用中包含main⽅方法,相当于给攻击者留留了了⼀一道找不不到的⻔门(当⼼心被找到),但
它是⼀一个⻢马⻁虎的做法,并且可能存在其他问题。
当servilet或者EJB中存在mian⽅方法时,此规则引发⼀一个问题。
错误的⽤用法
public class MyServlet extends HttpServlet {
public void doGet(HttpServletRequest req, HttpServletResponse res) throws
ServletException, IOException {
if (userIsAuthorized(req)) {
updatePrices(req);
}
}
public static void main(String[] args) { // Noncompliant
updatePrices(req);
}
}
漏漏洞洞 严重
在web.xml中配置⼀一个拦截所有请求的验证过滤器器,便便于统⼀一处理理HTTP请求。
为此,您需要定义验证器器和使⽤用它的过滤类,然后在web.xml中设置过滤器器的使⽤用。
* 正确的⽤用法
public class ValidatingHttpRequest extends HttpServletRequestWrapper {
Web applications should not have a “main” metho
d
Web applications should use validation filters
// ...
}
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
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
正确的⽤用法
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”
}
}
BUG 严重
“compareTo"不不能返回"Integer.MIN_VALUE”
从设计上讲,compareTo是表示等于或者不不等于,⽤用Integer.MIN_VALUE也表示不不出更更多的情
况,compareTo的返回值有时会反转,期望的负值会变成正值,⽽而Integer.MIN_VALUE反转后还
是Integer.MIN_VALUE,⽽而不不是Integer.MAX_VALUE。所以会导致异常
错误的⽤用法
public int compareTo(MyClass) {
if (condition) {
return Integer.MIN_VALUE; // Noncompliant
}
正确的⽤用法
public int compareTo(MyClass) {
if (condition) {
return -1;
}
BUG 严重
ConcurrentLinkedQueue.size()不不能够使⽤用
“compareTo” should not return “Integer.MIN_VALU
E”
“ConcurrentLinkedQueue.size()” should not be
used"
对于⼤大多数集合来说,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
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
“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
如果线程在⾮非阻塞调⽤用中被打断了了,这时应该使⽤用isInterrupted()。当线程早已被打断(也就是被
标记为interrupted)时,⼀一旦进⼊入阻塞⽅方法就应该⽴立刻抛出InterruptedException。
中断是⼀一种协作机制。当⼀一个线程中断另⼀一个线程时,被中断的线程不不⼀一定要⽴立即停⽌止正在做的
事情。如果活动在正在进⾏行行更更新的时候被取消,那么程序数据结构可能处于不不⼀一致状态。中断允
许⼀一个可取消活动来清理理正在进⾏行行的⼯工作,恢复不不变量量,通知其他活动它要被取消,然后才终
⽌止。http://www.ibm.com/developerworks/cn/java/j-jtp05236.html
* 错误的⽤用法
public void run () {
try {
while (true) {
// do stuff
}
}catch (InterruptedException e) { // Noncompliant; logging is not enough
LOGGER.log(Level.WARN, "Interrupted!", e);
}
}
正确的⽤用法
public void run () throws InterruptedException{
try {
while (true) {
// do stuff
}
}catch (InterruptedException e) {
LOGGER.log(Level.WARN, "Interrupted!", e);
// clean up state...
throw e;
}
}
or
public void run () {
try {
while (true) {
// do stuff
}
}catch (InterruptedException e) {
LOGGER.log(Level.WARN, "Interrupted!", e);
// clean up state...
Thread.currentThread().interrupt();
}
}
BUG 严重
“Object.wait(…)”和“Condition.await(…)”应该在“while”循环中调⽤用
根据Java Condition接⼝口的⽂文档:
⼀一般来说,作为对底层平台语义的让步,⼀一个条件发⽣生等待的时候,可能会发⽣生“伪唤醒”的情
况。当然,对⼤大多数应⽤用程序来说,因为条件应该总是在循环中等待,所以是没有实际影响的,
测试正在等待的状态。实现可以⾃自由地消除伪唤醒的可能性,但是建议应⽤用程序员总是假定它们
可以发⽣生,因此总是在循环中等待
错误的⽤用法
synchronized (obj) {
if (!suitableCondition()){
obj.wait(timeout); //the thread can wakeup whereas the condition is st
ill false
}
... // Perform action appropriate to condition
}
正确的⽤用法
synchronized (obj) {
while (!suitableCondition()){
obj.wait(timeout);
}
... // Perform action appropriate to condition
}
“Object.wait(…)” and “Condition.await(…)” should
be called inside a “while” loop
BUG 严重
实现Serializable接⼝口的内部类应该定义成static的
对⾮非静态内部类进⾏行行序列列化将导致尝试对外部类进⾏行行序列列化。如果外部类不不可序列列化,则序列列化
将失败,导致运⾏行行时错误。
使内部类静态(即“嵌套”)避免了了这个问题,因此内部类应该是静态的,如果可能的话。到内部
类和嵌套类之间存在语义差异:
•内部类只能在外部类的实例例的上下⽂文中实例例化。
•嵌套(静态)类可以独⽴立于外部类来实例例化。
错误的⽤用法
public class Pomegranate {
// ...
public class Seed implements Serializable { // Noncompliant; serializatio
n will fail
// ...
}
}
正确的⽤用法
public class Pomegranate {
// ...
public static class Seed implements Serializable {
// ...
}
}
BUG 严重
“Serializable” inner classes of non-serializable cla
sses should be “static”
“SingleConnectionFactory”实例例应该设置为“reconnectOnException”
使⽤用Spring SingleConnectionFactory⽽而不不启⽤用reconnectOnException设置,在连接发⽣生异常时不不
会⾃自动连接恢复。
这是因为reconnectOnException属性默认为false。 因此,即使使⽤用此连接⼯工⼚厂(Spring的
DefaultMessageListenerContainer或您⾃自⼰己的代码)的代码具有重新连接逻辑,该代码也不不会⼯工
作,因为SingleConnectionFactory将像单连接池⼀一样通过防⽌止连接关闭调⽤用从实际关闭。 因
此,后续的⼯工⼚厂创建操作只会传回原来断开的Connection。
错误的⽤用法
<bean id="singleCF" class="org.springframework.jms.connection.SingleConnecti
onFactory"> <!-- Noncompliant -->
<constructor-arg ref="dummyConnectionFactory" />
</bean>
正确的⽤用法
<bean id="singleCF" class="org.springframework.jms.connection.SingleConnect
ionFactory" p:reconnectOnException="true">
<constructor-arg ref="dummyConnectionFactory" />
</bean>
or
<bean id="singleCF" class="org.springframework.jms.connection.SingleConnecti
onFactory">
<constructor-arg ref="dummyConnectionFactory" />
<property name="reconnectOnException"><value>true</value></property>
</bean>
“SingleConnectionFactory” instances should be s
et to “reconnectOnException”
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
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
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
... // 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
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
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”
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
正确的的⽤用法
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
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
浮点数不不能直接拿来做等于不不等于的⽐比较
浮点数学是不不精确的,因为以⼆二进制表示存储这样的值的挑战。 更更糟的是,浮点数学不不是关联
的; 通过⼀一系列列简单的数学运算来推动浮点或双精度,并且由于在每个步骤发⽣生的舍⼊入,基于那
些运算的顺序的答案将是不不同的。
即使简单的浮点分配也不不简单:
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
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
通⽤用的异常在覆盖⽅方法中可以忽略略
@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
正确的⽤用法
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
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
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
错误⽤用法
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
不不应该使⽤用空的“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
BUG 严重
锁应该释放
如果锁在⼀一个⽅方法中被获取和释放,那么它必须沿着该⽅方法的所有执⾏行行路路径被释放。
如果不不这样做,会将条件锁定逻辑暴暴露露给⽅方法的调⽤用者,因此很容易易出现死锁。
错误⽤用法
public class MyClass {
private Lock lock = new Lock();
public void doSomething() {
lock.lock(); // Noncompliant
if (isInitialized()) {
// ...
lock.unlock();
}
}
}
正确⽤用法
public class MyClass {
private Lock lock = new Lock();
public void doSomething() {
if (isInitialized()) {
lock.lock();
// ...
lock.unlock();
}
}
}
BUG 严重
Locks should be released
循环的条件⾄至少应该有⼀一次是为真的
如果在第⼀一次循环迭代之前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
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;
}
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
味着⾏行行将被⽆无条件地执⾏行行⼀一次。
错误⽤用法
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”
是不不明智的。
-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”
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
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
错误的⽤用法
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
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);
BUG 严重
原始字节值不不应与位移结合使⽤用
当读取字节以便便构建其他原始值(例例如int或long)时,字节值会⾃自动提升,但是升级可能会产⽣生
意外的结果。
例例如,整数640的⼆二进制表示是0b0000_0010_1000_0000,其也可以⽤用(⽆无符号)字节[2,128]的
数组写⼊入。 但是,由于Java使⽤用⼆二进制补码,因此整数的有符号字节表示形式为[2,-128](因
为字节0b1000_0000被提升为int 0b1111_1111_1111_1111_1111_1111_1000_0000)。 因此,
尝试通过在不不考虑符号的情况下移位和添加字节的值来重构初始整数将不不会产⽣生预期的结果。
为了了防⽌止这种意外的值转换,使⽤用位和(&)将字节值与0xff(255)组合,并关闭所有较⾼高的
位。
此规则在任何时候将字节值⽤用作没有&0xff的操作数时引发问题,当结合移位时。
错误的⽤用法
int intFromBuffer() {
int result = 0;
for (int i = 0; i < 4; i++) {
result = (result << 8) | readByte(); // Noncompliant
}
return result;
}
正确的⽤用法
int intFromBuffer() {
int result = 0;
for (int i = 0; i < 4; i++) {
result = (result << 8) | (readByte() & 0xff);
}
return result;
}
Raw byte values should not be used in bitwise ope
rations in combination with shifts
BUG 严重
相关的“if / else if”语句句不不应该有相同的条件
⼀一个if / else if语句句链从上到下计算。 最多只会执⾏行行⼀一个分⽀支:第⼀一个分⽀支的条件为true。
因此,复制条件会⾃自动导致死代码。 通常,这是由于复制/粘贴错误。 最好的,它只是死代码,
最糟糕的是,这是⼀一个错误,可能会诱发进⼀一步的错误,因为代码被维护,并且显然它可能导致
意想不不到的⾏行行为。
错误的⽤用法
if (param == 1)
openWindow();
else if (param == 2)
closeWindow();
else if (param == 1) // Noncompliant
moveWindowToTheBackground();
}
正确的⽤用法
if (param == 1)
openWindow();
else if (param == 2)
closeWindow();
else if (param == 3)
moveWindowToTheBackground();
}
BUG 严重
Related “if/else if” statements should not have the
same condition
返回值不不应该被忽略略
当对函数的调⽤用没有任何副作⽤用时,如果忽略略结果,调⽤用的点是什什么? 在这种情况下,函数调⽤用
是⽆无⽤用的,应该删除或源代码的⾏行行为不不如预期。
为了了防⽌止⽣生成任何假阳性,此规则仅在Java API中的以下预定义的不不可变类列列表中触发问题:
String,Boolean,Integer,Double,Float,Byte,Character,Short,StackTraceElement。
错误的⽤用法
public void handle(String command){
command.toLowerCase(); // Noncompliant; result of method thrown away
...
}
正确的⽤用法
public void handle(String command){
String formattedCommand = command.toLowerCase();
...
}
BUG 严重
Servlet不不应该有可变的实例例字段
通过合同,servlet容器器创建每个servlet的⼀一个实例例,然后将专⽤用线程附加到每个新的传⼊入HTTP
请求以处理理该请求。 所以所有线程都共享servlet实例例和扩展实例例字段。 为了了防⽌止运⾏行行时的任何
误解和意外⾏行行为,所有servlet字段应该是静态的和/或final的,或者只是删除。
使⽤用Struts 1.X,org.apache.struts.action.Action上存在相同的约束。
错误的⽤用法
Return values should not be ignored when functio
n calls don’t have any side effects
Servlets should not have mutable instance fields
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
不不应该进⾏行行不不想关的平等检查
不不同类型的⽐比较总是返回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
// ...
}
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
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
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
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
}
BUG 主要
“.equals()”⽅方法不不应该被⽤用来测试“原⼦子”类的值。
atomicinteger,atomiclong继续了了NUMBER,但他们与integer和long不不同,必须 被区别对待。
atomicinteger是被设计为⽀支持⽆无需锁、线程安全编程的简单变量量。所以,⼀一个atomicinteger将永
远只能是equals于其本身。相反,你应该⽤用get()去作⽐比较。
这适⽤用于所有的原⼦子类:atomicinteger,atomiclong,atomicboolean。
错误的⽤用法
AtomicInteger aInt1 = new AtomicInteger(0);
AtomicInteger aInt2 = new AtomicInteger(0);
if (aInt1.equals(aInt2)) { ... } // Noncompliant
正确的⽤用法
AtomicInteger aInt1 = new AtomicInteger(0);
AtomicInteger aInt2 = new AtomicInteger(0);
if (aInt1.get() == aInt2.get()) { ... }
“equals(Object obj)”和“hashCode()”应成对覆盖
根据Java语⾔言规范,在equals(Object)和hashCode()之间有⼀一个契约:
“.equals()” should not be used to test the values of
“Atomic” classes
“equals(Object obj)” and “hashCode()” should be o
verridden in pairs
如果两个对象根据equals(Object)⽅方法相等,那么对两个对象中的每⼀一个调⽤用hashCode⽅方法必须产
⽣生相同的整数结果。
如果两个对象根据equals(java.lang.Object)⽅方法不不相等,那么对两个对象中的每⼀一个调⽤用hashCo
de⽅方法不不⼀一定产⽣生不不同的整数结果。
然⽽而,程序员应该意识到,为不不等对象产⽣生不不同的整数结果可以提⾼高散列列表的性能。
为了了遵守本契约,这些⽅方法应该既被继承,⼜又被两者覆盖。
错误的⽤用法
class MyClass { // Noncompliant - should also override "hashCode()"
@Override
public boolean equals(Object obj) {
/* ... */
}
}
正确的⽤用法
class MyClass { // Compliant
@Override
public boolean equals(Object obj) {
/* ... */
}
@Override
public int hashCode() {
/* ... */
}
}
“equals(Object obj)”应该检测参数类型
因为equals⽅方法使⽤用通⽤用Object作为参数,所以任何类型的对象都可以传递给它。 该⽅方法不不应该
“equals(Object obj)” should test argument type
假设它将只⽤用于测试其类类型的对象(传进来的对象就是我们需要的对象)。 必须检查参数的类
型。
错误的⽤用法
java
public boolean equals(Object obj) {
MyClass mc = (MyClass)obj; // Noncompliant
// ...
}
正确的⽤用的
“`java
public boolean equals(Object obj) {
if (obj == null)
return false;
if (this.getClass() != obj.getClass())
return false;
MyClass mc = (MyClass)obj;
// …
}
”`
“Iterator.hasNext()”不不应该调⽤用“Iterator.next()”
next(), 是返回当前元素, 并指向下⼀一个元素。
hasNext(), 则是判断当前元素是否存在,并指向下⼀一个元素(即所谓的索引)
调⽤用Iterator.hasNext()不不应该有任何副作⽤用,因此不不应该更更改迭代器器的状态。 Iterator.next()使迭
代器器前进⼀一个项。 因此,在Iterator.hasNext()中调⽤用它,打破了了hasNext()契约,并且将导致⽣生产
中的意外⾏行行为。
错误的⽤用法
public class FibonacciIterator implements Iterator<Integer>{
...
“Iterator.hasNext()” should not call “Iterator.next()”
@Override
public boolean hasNext() {
if(next() != null) {
return true;
}
return false;
}
...
}
应使⽤用有效索引调⽤用“PreparedStatement”和“ResultSet”⽅方法
PreparedStatement中的参数从1开始编号,⽽而不不是0,因此使⽤用⼩小于1的PreparedStatement的任
何“set”⽅方法是⼀一个错误,因为使⽤用的索引⾼高于参数数量量。 类似地,ResultSet索引也从1开始,
⽽而不不是从0开始
错误的⽤用法
PreparedStatement ps = con.prepareStatement("SELECT fname, lname FROM em
ployees where hireDate > ? and salary < ?");
ps.setDate(0, date); // Noncompliant
ps.setDouble(3, salary); // Noncompliant
ResultSet rs = ps.executeQuery();
while (rs.next()) {
String fname = rs.getString(0); // Noncompliant
// ...
}
正确的⽤用法
PreparedStatement ps = con.prepareStatement("SELECT fname, lname FROM em
ployees where hireDate > ? and salary < ?");
ps.setDate(1, date);
ps.setDouble(2, salary);
ResultSet rs = ps.executeQuery();
while (rs.next()) {
String fname = rs.getString(1);
// ...
“PreparedStatement” and “ResultSet” methods sh
ould be called with valid indices
}
应使⽤用“read”和“readLine”返回值
当调⽤用返回从某个数据源读取的数据的⽅方法时,该数据应该存储,⽽而不不是丢弃。 任何其他操作⼀一
定是⼀一个错误。
当忽略略任何以下内容的返回值或仅对其进⾏行行null检查时,此规则引发问题:
BufferedReader.readLine(),Reader.read()和任何⼦子类中的这些⽅方法.
错误的⽤用法
public void doSomethingWithFile(String fileName) {
BufferedReader buffReader = null;
try {
buffReader = new BufferedReader(new FileReader(fileName));
while (buffReader.readLine() != null) { // Noncompliant
// ...
}
} 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) {
// ...
}
}
“read” and “readLine” return values should be use
d
“runFinalizersOnExit”不不应该被调⽤用
默认情况下禁⽤用在JVM exit上运⾏行行终结器器。 它可以使⽤用System.runFinalizersOnExit和
Runtime.runFinalizersOnExit启⽤用,但是两个⽅方法都被弃⽤用,因为它们本质上是不不安全的。它可
能对正在使⽤用的对象调⽤用终结⽅方法,⽽而其他线程正在操作这些对象,从⽽而导致不不正确的⾏行行为或死
锁。
根据Oracle Javadoc:
“`
它可能导致在活动对象上调⽤用finalizer,⽽而其他线程正在同时操作这些对象,从⽽而导致不不稳定的
⾏行行为或死锁。
如果真的想在虚拟机开始其关闭序列列时执⾏行行某些操作,则应该附加⼀一个关闭挂接。
”`
错误的⽤用法
public static void main(String [] args) {
...
System.runFinalizersOnExit(true); // Noncompliant
...
}
protected void finalize(){
doSomething();
}
正确的⽤用法
public static void main(String [] args) {
Runtime.addShutdownHook(new Runnable() {
public void run(){
doSomething();
}
});
//...
“runFinalizersOnExit” should not be called
“ScheduledThreadPoolExecutor” should not have
“ScheduledThreadPoolExecutor”不不应该有0个核⼼心线程
java.util.concurrent.ScheduledThreadPoolExecutor的池的⼤大⼩小与corePoolSize有关,所以设置
corePoolSize为零意味着执⾏行行器器将没有线程,也没有什什么可以运⾏行行。
此规则通过其setter或对象构造函数检测corePoolSize设置为零的实例例。
错误的⽤用法
public void do(){
ScheduledThreadPoolExecutor stpe1 = new ScheduledThreadPoolExecutor(0); //
Noncompliant
ScheduledThreadPoolExecutor stpe2 = new ScheduledThreadPoolExecutor(POOL_S
IZE);
stpe2.setCorePoolSize(0); // Noncompliant
“super.finalize()”应该在“Object.finalize()”实现的结尾处调⽤用
覆盖Object.finalize()⽅方法必须⼩小⼼心处理理⼀一些系统资源。
finalize的作⽤用是释放类的相关资源,⽐比如打开的⽂文件,内存中占⽤用的空间等等
如果是⼦子类直接覆盖了了⽗父类,那么⽗父类的资源可能得不不到有效释放,所以要求调⽤用⼀一次⽗父类的
finalize⽅方法
* 错误的⽤用法
protected void finalize() { // Noncompliant; no call to super.finalize();
releaseSomeResources();
}
protected void finalize() {
super.finalize(); // Noncompliant; this call should come last
releaseSomeResources();
}
0 core threads
“super.finalize()” should be called at the end of “O
bject.finalize()” implementations
正确的⽤用法
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
当跳转语句句(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 {
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
}
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
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
过反射获取到
错误的⽤用法
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
正确的⽤用法
private void readTheFile() throws IOException {
Path path = Paths.get(this.fileName);
BufferedReader reader = null;
try {
reader = Files.newBufferedReader(path, this.charset)) {
// ...
} finally {
if (reader != null) {
reader.close();
}
}
}
private void doSomething() {
OutputStream stream = null;
try{
stream = new FileOutputStream("myfile.txt");
for (String property : propertyList) {
// ...
}
}catch(Exception e){
// ...
}finally{
stream.close();
}
}
Java 7引⼊入了了try-with-resources语句句,隐含地关闭了了Closeables。 此规则忽略略在try-with-
resources语句句中打开的所有资源。
static String readFirstLineFromFile(String path) throws IOException {
try (BufferedReader br = new BufferedReader(new FileReader(path))) {
return br.readLine();
}
}
static String readFirstLineFromFileWithFinallyBlock(String path) throws IOEx
ception {
BufferedReader br = new BufferedReader(new FileReader(path));
try {
return br.readLine();
} finally {
if (br != null) br.close();
}
}
同步不不应该基于字符串串或包装类
被合并和潜在重⽤用的对象不不应该⽤用于同步。它可能导致⽆无关的线程与⽆无⽤用的堆栈跟踪死锁。 具体
来说,字符串串⽂文字和诸如整数之类的框化原语不不应该⽤用作锁定对象,因为它们被合并和重⽤用。 对
于布尔对象,会更更糟糕,因为只有两个Boolean,Boolean.TRUE和Boolean.FALSE的实例例,每个
使⽤用布尔值的类都将引⽤用其中的⼀一个。
错误的⽤用法
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) { // Noncompliant
// ...
}
synchronized(iLock) { // Noncompliant
// ...
}
synchronized(sLock) { // Noncompliant
// ...
}
正确的⽤用法
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) {
Synchronization should not be based on Strings or
boxed primitives
// ...
}
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
}
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
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
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
错误的⽤用法
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
正确的⽤用法
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
}
正确的⽤用法
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”
Sonar java -Write Clean Code,Detect Bugs
Sonar java -Write Clean Code,Detect Bugs
Sonar java -Write Clean Code,Detect Bugs
Sonar java -Write Clean Code,Detect Bugs
Sonar java -Write Clean Code,Detect Bugs
Sonar java -Write Clean Code,Detect Bugs
Sonar java -Write Clean Code,Detect Bugs
Sonar java -Write Clean Code,Detect Bugs
Sonar java -Write Clean Code,Detect Bugs
Sonar java -Write Clean Code,Detect Bugs
Sonar java -Write Clean Code,Detect Bugs
Sonar java -Write Clean Code,Detect Bugs
Sonar java -Write Clean Code,Detect Bugs
Sonar java -Write Clean Code,Detect Bugs
Sonar java -Write Clean Code,Detect Bugs
Sonar java -Write Clean Code,Detect Bugs
Sonar java -Write Clean Code,Detect Bugs
Sonar java -Write Clean Code,Detect Bugs
Sonar java -Write Clean Code,Detect Bugs
Sonar java -Write Clean Code,Detect Bugs
Sonar java -Write Clean Code,Detect Bugs
Sonar java -Write Clean Code,Detect Bugs

More Related Content

More from 诸葛修车网-诸葛商城

More from 诸葛修车网-诸葛商城 (12)

组合、备忘录、建造者模式、原型
组合、备忘录、建造者模式、原型组合、备忘录、建造者模式、原型
组合、备忘录、建造者模式、原型
 
设计模式-单例、享元、工厂与抽象工厂
设计模式-单例、享元、工厂与抽象工厂设计模式-单例、享元、工厂与抽象工厂
设计模式-单例、享元、工厂与抽象工厂
 
面向对象设计原则
面向对象设计原则面向对象设计原则
面向对象设计原则
 
单元测试(Unit Test)- Spock应用
单元测试(Unit Test)- Spock应用单元测试(Unit Test)- Spock应用
单元测试(Unit Test)- Spock应用
 
Maven技术及诸葛商城应用(1)
Maven技术及诸葛商城应用(1)Maven技术及诸葛商城应用(1)
Maven技术及诸葛商城应用(1)
 
Push-推送技术
Push-推送技术Push-推送技术
Push-推送技术
 
Nginx+tomcat https 配置
Nginx+tomcat  https 配置Nginx+tomcat  https 配置
Nginx+tomcat https 配置
 
如何更好地设计测试用例-BQConf
如何更好地设计测试用例-BQConf如何更好地设计测试用例-BQConf
如何更好地设计测试用例-BQConf
 
App开发过程的演变之路
App开发过程的演变之路App开发过程的演变之路
App开发过程的演变之路
 
浅谈项目管理(诸葛B2B电商研发部版改)
浅谈项目管理(诸葛B2B电商研发部版改)浅谈项目管理(诸葛B2B电商研发部版改)
浅谈项目管理(诸葛B2B电商研发部版改)
 
Java多线程技术
Java多线程技术Java多线程技术
Java多线程技术
 
Git基础培训
Git基础培训Git基础培训
Git基础培训
 

Sonar java -Write Clean Code,Detect Bugs

  • 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”
  • 6. response.addCookie(c); 正确的⽤用法 Cookie c = new Cookie(SECRET, secret); c.setSecure(true); response.addCookie(c); 漏漏洞洞 阻断 因为从编译的应⽤用程序中提取字符串串很容易易,所以凭证不不应该是硬编码的。 这样做,他们⼏几乎可 以确保最终在攻击者的⼿手中。 分布式应⽤用程序更更是如此。 凭证应存储在强制保护的加密配置⽂文件或数据库中的代码之外。 错误的⽤用法 Connection conn = null; try { conn = DriverManager.getConnection("jdbc:mysql://localhost/test?" + "user=steve&password=blue"); // Noncompliant String uname = "steve"; String password = "blue"; conn = DriverManager.getConnection("jdbc:mysql://localhost/test?" + "user=" + uname + "&password=" + password); // Noncompliant java.net.PasswordAuthentication pa = new java.net.PasswordAuthentication(" userName", "1234".toCharArray()); // Noncompliant 正确的⽤用法 Connection conn = null; try { String uname = getEncryptedUser(); String password = getEncryptedPass(); conn = DriverManager.getConnection("jdbc:mysql://localhost/test?" + "user=" + uname + "&password=" + password); Credentials should not be hard-coded
  • 7. 漏漏洞洞 严重 在RSA加密中没有OAEP,攻击者更更容易易解密数据或从密⽂文推断加密⽅方式。 此规则在⽂文字值以 RSA / NONE开头时记录问题。 错误的⽤用法 Cipher rsa = javax.crypto.Cipher.getInstance("RSA/NONE/NoPadding"); 正确的⽤用法 Cipher rsa = javax.crypto.Cipher.getInstance("RSA/ECB/OAEPWITHSHA-256ANDMGF1 PADDING"); 漏漏洞洞 次要 即使servlet中的⽅方法的签名包含throws IOException,ServletException,但是抛出这样的异常并 不不是好主意。 不不能在servlet中捕获异常可能使系统处于易易受攻击的状态,可能导致拒绝服务攻击 或暴暴露露敏敏感信息,因为当servlet抛出异常时,servlet容器器通常将调试信息发送回 ⽤用户。 这些信 息对于攻击者来说是⾮非常有价值的。 此规则检查名为“do *”的⽅方法中的所有异常在servlet类中是否显式处理理。 错误的⽤用法 public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { Cryptographic RSA algorithms should always inco rporate OAEP (Optimal Asymmetric Encryption Pa dding) Exceptions should not be thrown from servlet met hods
  • 8. String ip = request.getRemoteAddr(); InetAddress addr = InetAddress.getByName(ip); // Noncompliant; getByName(S tring) throws UnknownHostException //... } 正确的⽤用法 public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { try { String ip = request.getRemoteAddr(); InetAddress addr = InetAddress.getByName(ip); //... } catch (UnknownHostException uhex) { //... } } 坏味道 严重 根据Oracle Javadoc: 当线程尝试等待对象的监视器器或通知其他线程等待对象的监视器器⽽而不不拥有指定的监视器器时,抛出 IllegalMonitorStateException。 换句句话说,只有在设计不不当的情况下才能抛出此异常,因为不不应该对未拥有监视器器的对象调⽤用 Object.wait(…),Object.notify()和Object.notifyAll()⽅方法。 错误的⽤用法 public void doSomething(){ ... try { ... anObject.notify(); ... } catch(IllegalMonitorStateException e) { ... IllegalMonitorStateException should not be caught
  • 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
  • 17. 漏漏洞洞 严重 没有理理由在web应⽤用⾥里里⾯面包含mian⽅方法。在应⽤用的开发阶段可能⽤用来调试,但是不不应该使⽤用于⽣生 产环境。在web应⽤用中包含main⽅方法,相当于给攻击者留留了了⼀一道找不不到的⻔门(当⼼心被找到),但 它是⼀一个⻢马⻁虎的做法,并且可能存在其他问题。 当servilet或者EJB中存在mian⽅方法时,此规则引发⼀一个问题。 错误的⽤用法 public class MyServlet extends HttpServlet { public void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { if (userIsAuthorized(req)) { updatePrices(req); } } public static void main(String[] args) { // Noncompliant updatePrices(req); } } 漏漏洞洞 严重 在web.xml中配置⼀一个拦截所有请求的验证过滤器器,便便于统⼀一处理理HTTP请求。 为此,您需要定义验证器器和使⽤用它的过滤类,然后在web.xml中设置过滤器器的使⽤用。 * 正确的⽤用法 public class ValidatingHttpRequest extends HttpServletRequestWrapper { Web applications should not have a “main” metho d Web applications should use validation filters
  • 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”
  • 21. } } BUG 严重 “compareTo"不不能返回"Integer.MIN_VALUE” 从设计上讲,compareTo是表示等于或者不不等于,⽤用Integer.MIN_VALUE也表示不不出更更多的情 况,compareTo的返回值有时会反转,期望的负值会变成正值,⽽而Integer.MIN_VALUE反转后还 是Integer.MIN_VALUE,⽽而不不是Integer.MAX_VALUE。所以会导致异常 错误的⽤用法 public int compareTo(MyClass) { if (condition) { return Integer.MIN_VALUE; // Noncompliant } 正确的⽤用法 public int compareTo(MyClass) { if (condition) { return -1; } BUG 严重 ConcurrentLinkedQueue.size()不不能够使⽤用 “compareTo” should not return “Integer.MIN_VALU E” “ConcurrentLinkedQueue.size()” should not be used"
  • 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
  • 25. 如果线程在⾮非阻塞调⽤用中被打断了了,这时应该使⽤用isInterrupted()。当线程早已被打断(也就是被 标记为interrupted)时,⼀一旦进⼊入阻塞⽅方法就应该⽴立刻抛出InterruptedException。 中断是⼀一种协作机制。当⼀一个线程中断另⼀一个线程时,被中断的线程不不⼀一定要⽴立即停⽌止正在做的 事情。如果活动在正在进⾏行行更更新的时候被取消,那么程序数据结构可能处于不不⼀一致状态。中断允 许⼀一个可取消活动来清理理正在进⾏行行的⼯工作,恢复不不变量量,通知其他活动它要被取消,然后才终 ⽌止。http://www.ibm.com/developerworks/cn/java/j-jtp05236.html * 错误的⽤用法 public void run () { try { while (true) { // do stuff } }catch (InterruptedException e) { // Noncompliant; logging is not enough LOGGER.log(Level.WARN, "Interrupted!", e); } } 正确的⽤用法 public void run () throws InterruptedException{ try { while (true) { // do stuff } }catch (InterruptedException e) { LOGGER.log(Level.WARN, "Interrupted!", e); // clean up state... throw e; } } or public void run () { try { while (true) { // do stuff } }catch (InterruptedException e) { LOGGER.log(Level.WARN, "Interrupted!", e); // clean up state...
  • 26. Thread.currentThread().interrupt(); } } BUG 严重 “Object.wait(…)”和“Condition.await(…)”应该在“while”循环中调⽤用 根据Java Condition接⼝口的⽂文档: ⼀一般来说,作为对底层平台语义的让步,⼀一个条件发⽣生等待的时候,可能会发⽣生“伪唤醒”的情 况。当然,对⼤大多数应⽤用程序来说,因为条件应该总是在循环中等待,所以是没有实际影响的, 测试正在等待的状态。实现可以⾃自由地消除伪唤醒的可能性,但是建议应⽤用程序员总是假定它们 可以发⽣生,因此总是在循环中等待 错误的⽤用法 synchronized (obj) { if (!suitableCondition()){ obj.wait(timeout); //the thread can wakeup whereas the condition is st ill false } ... // Perform action appropriate to condition } 正确的⽤用法 synchronized (obj) { while (!suitableCondition()){ obj.wait(timeout); } ... // Perform action appropriate to condition } “Object.wait(…)” and “Condition.await(…)” should be called inside a “while” loop
  • 27. BUG 严重 实现Serializable接⼝口的内部类应该定义成static的 对⾮非静态内部类进⾏行行序列列化将导致尝试对外部类进⾏行行序列列化。如果外部类不不可序列列化,则序列列化 将失败,导致运⾏行行时错误。 使内部类静态(即“嵌套”)避免了了这个问题,因此内部类应该是静态的,如果可能的话。到内部 类和嵌套类之间存在语义差异: •内部类只能在外部类的实例例的上下⽂文中实例例化。 •嵌套(静态)类可以独⽴立于外部类来实例例化。 错误的⽤用法 public class Pomegranate { // ... public class Seed implements Serializable { // Noncompliant; serializatio n will fail // ... } } 正确的⽤用法 public class Pomegranate { // ... public static class Seed implements Serializable { // ... } } BUG 严重 “Serializable” inner classes of non-serializable cla sses should be “static”
  • 28. “SingleConnectionFactory”实例例应该设置为“reconnectOnException” 使⽤用Spring SingleConnectionFactory⽽而不不启⽤用reconnectOnException设置,在连接发⽣生异常时不不 会⾃自动连接恢复。 这是因为reconnectOnException属性默认为false。 因此,即使使⽤用此连接⼯工⼚厂(Spring的 DefaultMessageListenerContainer或您⾃自⼰己的代码)的代码具有重新连接逻辑,该代码也不不会⼯工 作,因为SingleConnectionFactory将像单连接池⼀一样通过防⽌止连接关闭调⽤用从实际关闭。 因 此,后续的⼯工⼚厂创建操作只会传回原来断开的Connection。 错误的⽤用法 <bean id="singleCF" class="org.springframework.jms.connection.SingleConnecti onFactory"> <!-- Noncompliant --> <constructor-arg ref="dummyConnectionFactory" /> </bean> 正确的⽤用法 <bean id="singleCF" class="org.springframework.jms.connection.SingleConnect ionFactory" p:reconnectOnException="true"> <constructor-arg ref="dummyConnectionFactory" /> </bean> or <bean id="singleCF" class="org.springframework.jms.connection.SingleConnecti onFactory"> <constructor-arg ref="dummyConnectionFactory" /> <property name="reconnectOnException"><value>true</value></property> </bean> “SingleConnectionFactory” instances should be s et to “reconnectOnException”
  • 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
  • 46. BUG 严重 锁应该释放 如果锁在⼀一个⽅方法中被获取和释放,那么它必须沿着该⽅方法的所有执⾏行行路路径被释放。 如果不不这样做,会将条件锁定逻辑暴暴露露给⽅方法的调⽤用者,因此很容易易出现死锁。 错误⽤用法 public class MyClass { private Lock lock = new Lock(); public void doSomething() { lock.lock(); // Noncompliant if (isInitialized()) { // ... lock.unlock(); } } } 正确⽤用法 public class MyClass { private Lock lock = new Lock(); public void doSomething() { if (isInitialized()) { lock.lock(); // ... lock.unlock(); } } } BUG 严重 Locks should be released
  • 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);
  • 56. BUG 严重 原始字节值不不应与位移结合使⽤用 当读取字节以便便构建其他原始值(例例如int或long)时,字节值会⾃自动提升,但是升级可能会产⽣生 意外的结果。 例例如,整数640的⼆二进制表示是0b0000_0010_1000_0000,其也可以⽤用(⽆无符号)字节[2,128]的 数组写⼊入。 但是,由于Java使⽤用⼆二进制补码,因此整数的有符号字节表示形式为[2,-128](因 为字节0b1000_0000被提升为int 0b1111_1111_1111_1111_1111_1111_1000_0000)。 因此, 尝试通过在不不考虑符号的情况下移位和添加字节的值来重构初始整数将不不会产⽣生预期的结果。 为了了防⽌止这种意外的值转换,使⽤用位和(&)将字节值与0xff(255)组合,并关闭所有较⾼高的 位。 此规则在任何时候将字节值⽤用作没有&0xff的操作数时引发问题,当结合移位时。 错误的⽤用法 int intFromBuffer() { int result = 0; for (int i = 0; i < 4; i++) { result = (result << 8) | readByte(); // Noncompliant } return result; } 正确的⽤用法 int intFromBuffer() { int result = 0; for (int i = 0; i < 4; i++) { result = (result << 8) | (readByte() & 0xff); } return result; } Raw byte values should not be used in bitwise ope rations in combination with shifts
  • 57. BUG 严重 相关的“if / else if”语句句不不应该有相同的条件 ⼀一个if / else if语句句链从上到下计算。 最多只会执⾏行行⼀一个分⽀支:第⼀一个分⽀支的条件为true。 因此,复制条件会⾃自动导致死代码。 通常,这是由于复制/粘贴错误。 最好的,它只是死代码, 最糟糕的是,这是⼀一个错误,可能会诱发进⼀一步的错误,因为代码被维护,并且显然它可能导致 意想不不到的⾏行行为。 错误的⽤用法 if (param == 1) openWindow(); else if (param == 2) closeWindow(); else if (param == 1) // Noncompliant moveWindowToTheBackground(); } 正确的⽤用法 if (param == 1) openWindow(); else if (param == 2) closeWindow(); else if (param == 3) moveWindowToTheBackground(); } BUG 严重 Related “if/else if” statements should not have the same condition
  • 58. 返回值不不应该被忽略略 当对函数的调⽤用没有任何副作⽤用时,如果忽略略结果,调⽤用的点是什什么? 在这种情况下,函数调⽤用 是⽆无⽤用的,应该删除或源代码的⾏行行为不不如预期。 为了了防⽌止⽣生成任何假阳性,此规则仅在Java API中的以下预定义的不不可变类列列表中触发问题: String,Boolean,Integer,Double,Float,Byte,Character,Short,StackTraceElement。 错误的⽤用法 public void handle(String command){ command.toLowerCase(); // Noncompliant; result of method thrown away ... } 正确的⽤用法 public void handle(String command){ String formattedCommand = command.toLowerCase(); ... } BUG 严重 Servlet不不应该有可变的实例例字段 通过合同,servlet容器器创建每个servlet的⼀一个实例例,然后将专⽤用线程附加到每个新的传⼊入HTTP 请求以处理理该请求。 所以所有线程都共享servlet实例例和扩展实例例字段。 为了了防⽌止运⾏行行时的任何 误解和意外⾏行行为,所有servlet字段应该是静态的和/或final的,或者只是删除。 使⽤用Struts 1.X,org.apache.struts.action.Action上存在相同的约束。 错误的⽤用法 Return values should not be ignored when functio n calls don’t have any side effects Servlets should not have mutable instance fields
  • 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
  • 65. } BUG 主要 “.equals()”⽅方法不不应该被⽤用来测试“原⼦子”类的值。 atomicinteger,atomiclong继续了了NUMBER,但他们与integer和long不不同,必须 被区别对待。 atomicinteger是被设计为⽀支持⽆无需锁、线程安全编程的简单变量量。所以,⼀一个atomicinteger将永 远只能是equals于其本身。相反,你应该⽤用get()去作⽐比较。 这适⽤用于所有的原⼦子类:atomicinteger,atomiclong,atomicboolean。 错误的⽤用法 AtomicInteger aInt1 = new AtomicInteger(0); AtomicInteger aInt2 = new AtomicInteger(0); if (aInt1.equals(aInt2)) { ... } // Noncompliant 正确的⽤用法 AtomicInteger aInt1 = new AtomicInteger(0); AtomicInteger aInt2 = new AtomicInteger(0); if (aInt1.get() == aInt2.get()) { ... } “equals(Object obj)”和“hashCode()”应成对覆盖 根据Java语⾔言规范,在equals(Object)和hashCode()之间有⼀一个契约: “.equals()” should not be used to test the values of “Atomic” classes “equals(Object obj)” and “hashCode()” should be o verridden in pairs
  • 66. 如果两个对象根据equals(Object)⽅方法相等,那么对两个对象中的每⼀一个调⽤用hashCode⽅方法必须产 ⽣生相同的整数结果。 如果两个对象根据equals(java.lang.Object)⽅方法不不相等,那么对两个对象中的每⼀一个调⽤用hashCo de⽅方法不不⼀一定产⽣生不不同的整数结果。 然⽽而,程序员应该意识到,为不不等对象产⽣生不不同的整数结果可以提⾼高散列列表的性能。 为了了遵守本契约,这些⽅方法应该既被继承,⼜又被两者覆盖。 错误的⽤用法 class MyClass { // Noncompliant - should also override "hashCode()" @Override public boolean equals(Object obj) { /* ... */ } } 正确的⽤用法 class MyClass { // Compliant @Override public boolean equals(Object obj) { /* ... */ } @Override public int hashCode() { /* ... */ } } “equals(Object obj)”应该检测参数类型 因为equals⽅方法使⽤用通⽤用Object作为参数,所以任何类型的对象都可以传递给它。 该⽅方法不不应该 “equals(Object obj)” should test argument type
  • 67. 假设它将只⽤用于测试其类类型的对象(传进来的对象就是我们需要的对象)。 必须检查参数的类 型。 错误的⽤用法 java public boolean equals(Object obj) { MyClass mc = (MyClass)obj; // Noncompliant // ... } 正确的⽤用的 “`java public boolean equals(Object obj) { if (obj == null) return false; if (this.getClass() != obj.getClass()) return false; MyClass mc = (MyClass)obj; // … } ”` “Iterator.hasNext()”不不应该调⽤用“Iterator.next()” next(), 是返回当前元素, 并指向下⼀一个元素。 hasNext(), 则是判断当前元素是否存在,并指向下⼀一个元素(即所谓的索引) 调⽤用Iterator.hasNext()不不应该有任何副作⽤用,因此不不应该更更改迭代器器的状态。 Iterator.next()使迭 代器器前进⼀一个项。 因此,在Iterator.hasNext()中调⽤用它,打破了了hasNext()契约,并且将导致⽣生产 中的意外⾏行行为。 错误的⽤用法 public class FibonacciIterator implements Iterator<Integer>{ ... “Iterator.hasNext()” should not call “Iterator.next()”
  • 68. @Override public boolean hasNext() { if(next() != null) { return true; } return false; } ... } 应使⽤用有效索引调⽤用“PreparedStatement”和“ResultSet”⽅方法 PreparedStatement中的参数从1开始编号,⽽而不不是0,因此使⽤用⼩小于1的PreparedStatement的任 何“set”⽅方法是⼀一个错误,因为使⽤用的索引⾼高于参数数量量。 类似地,ResultSet索引也从1开始, ⽽而不不是从0开始 错误的⽤用法 PreparedStatement ps = con.prepareStatement("SELECT fname, lname FROM em ployees where hireDate > ? and salary < ?"); ps.setDate(0, date); // Noncompliant ps.setDouble(3, salary); // Noncompliant ResultSet rs = ps.executeQuery(); while (rs.next()) { String fname = rs.getString(0); // Noncompliant // ... } 正确的⽤用法 PreparedStatement ps = con.prepareStatement("SELECT fname, lname FROM em ployees where hireDate > ? and salary < ?"); ps.setDate(1, date); ps.setDouble(2, salary); ResultSet rs = ps.executeQuery(); while (rs.next()) { String fname = rs.getString(1); // ... “PreparedStatement” and “ResultSet” methods sh ould be called with valid indices
  • 69. } 应使⽤用“read”和“readLine”返回值 当调⽤用返回从某个数据源读取的数据的⽅方法时,该数据应该存储,⽽而不不是丢弃。 任何其他操作⼀一 定是⼀一个错误。 当忽略略任何以下内容的返回值或仅对其进⾏行行null检查时,此规则引发问题: BufferedReader.readLine(),Reader.read()和任何⼦子类中的这些⽅方法. 错误的⽤用法 public void doSomethingWithFile(String fileName) { BufferedReader buffReader = null; try { buffReader = new BufferedReader(new FileReader(fileName)); while (buffReader.readLine() != null) { // Noncompliant // ... } } 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) { // ... } } “read” and “readLine” return values should be use d
  • 70. “runFinalizersOnExit”不不应该被调⽤用 默认情况下禁⽤用在JVM exit上运⾏行行终结器器。 它可以使⽤用System.runFinalizersOnExit和 Runtime.runFinalizersOnExit启⽤用,但是两个⽅方法都被弃⽤用,因为它们本质上是不不安全的。它可 能对正在使⽤用的对象调⽤用终结⽅方法,⽽而其他线程正在操作这些对象,从⽽而导致不不正确的⾏行行为或死 锁。 根据Oracle Javadoc: “` 它可能导致在活动对象上调⽤用finalizer,⽽而其他线程正在同时操作这些对象,从⽽而导致不不稳定的 ⾏行行为或死锁。 如果真的想在虚拟机开始其关闭序列列时执⾏行行某些操作,则应该附加⼀一个关闭挂接。 ”` 错误的⽤用法 public static void main(String [] args) { ... System.runFinalizersOnExit(true); // Noncompliant ... } protected void finalize(){ doSomething(); } 正确的⽤用法 public static void main(String [] args) { Runtime.addShutdownHook(new Runnable() { public void run(){ doSomething(); } }); //... “runFinalizersOnExit” should not be called “ScheduledThreadPoolExecutor” should not have
  • 71. “ScheduledThreadPoolExecutor”不不应该有0个核⼼心线程 java.util.concurrent.ScheduledThreadPoolExecutor的池的⼤大⼩小与corePoolSize有关,所以设置 corePoolSize为零意味着执⾏行行器器将没有线程,也没有什什么可以运⾏行行。 此规则通过其setter或对象构造函数检测corePoolSize设置为零的实例例。 错误的⽤用法 public void do(){ ScheduledThreadPoolExecutor stpe1 = new ScheduledThreadPoolExecutor(0); // Noncompliant ScheduledThreadPoolExecutor stpe2 = new ScheduledThreadPoolExecutor(POOL_S IZE); stpe2.setCorePoolSize(0); // Noncompliant “super.finalize()”应该在“Object.finalize()”实现的结尾处调⽤用 覆盖Object.finalize()⽅方法必须⼩小⼼心处理理⼀一些系统资源。 finalize的作⽤用是释放类的相关资源,⽐比如打开的⽂文件,内存中占⽤用的空间等等 如果是⼦子类直接覆盖了了⽗父类,那么⽗父类的资源可能得不不到有效释放,所以要求调⽤用⼀一次⽗父类的 finalize⽅方法 * 错误的⽤用法 protected void finalize() { // Noncompliant; no call to super.finalize(); releaseSomeResources(); } protected void finalize() { super.finalize(); // Noncompliant; this call should come last releaseSomeResources(); } 0 core threads “super.finalize()” should be called at the end of “O bject.finalize()” implementations
  • 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
  • 78. 正确的⽤用法 private void readTheFile() throws IOException { Path path = Paths.get(this.fileName); BufferedReader reader = null; try { reader = Files.newBufferedReader(path, this.charset)) { // ... } finally { if (reader != null) { reader.close(); } } } private void doSomething() { OutputStream stream = null; try{ stream = new FileOutputStream("myfile.txt"); for (String property : propertyList) { // ... } }catch(Exception e){ // ... }finally{ stream.close(); } } Java 7引⼊入了了try-with-resources语句句,隐含地关闭了了Closeables。 此规则忽略略在try-with- resources语句句中打开的所有资源。 static String readFirstLineFromFile(String path) throws IOException { try (BufferedReader br = new BufferedReader(new FileReader(path))) { return br.readLine(); } } static String readFirstLineFromFileWithFinallyBlock(String path) throws IOEx ception { BufferedReader br = new BufferedReader(new FileReader(path)); try { return br.readLine(); } finally { if (br != null) br.close(); }
  • 79. } 同步不不应该基于字符串串或包装类 被合并和潜在重⽤用的对象不不应该⽤用于同步。它可能导致⽆无关的线程与⽆无⽤用的堆栈跟踪死锁。 具体 来说,字符串串⽂文字和诸如整数之类的框化原语不不应该⽤用作锁定对象,因为它们被合并和重⽤用。 对 于布尔对象,会更更糟糕,因为只有两个Boolean,Boolean.TRUE和Boolean.FALSE的实例例,每个 使⽤用布尔值的类都将引⽤用其中的⼀一个。 错误的⽤用法 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) { // Noncompliant // ... } synchronized(iLock) { // Noncompliant // ... } synchronized(sLock) { // Noncompliant // ... } 正确的⽤用法 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) { Synchronization should not be based on Strings or boxed primitives
  • 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”