错误实例:
public class Foo {
public void bar() {
int x = 0;
if (foo) x++;//if(foo){x++;}
}
}
错误写法示意:
public void doSomething() {
while (true)
x++;
}
错误写法示意:
public void doSomething() {
// this is OK
if (foo) x++;
// but this is not
if (foo)
x=x+1;
else
x=x-1;
}
错误写法示意:
public void foo() {
for (int i=0; i<42;i++)
foo();
}
示例:
public class Foo {
private int bar =2;
public boolean isBarEqualsTo(int x) {
// this bit of code
if (bar == x) {
return true;
} else {
return false;
}
// can be replaced with a simple
// return bar == x;
}
}
解决办法:建议if嵌套不要超过2层。使用工具方法封装更多的if语句或者把嵌套的if表达式放到同一个层次中。
示例:
public class Foo {
public void bar(int x, int y, int z) {
if (x>y) {
if (y>z) {
if (z==x) {
// whew, too deep
}
}
}
}
}
解决方案:把过深的if逻辑封装到独立的工具方法内,同时尽量让if语句“扁平化”,减少if嵌套
public class Foo {
public void bar(int x, int y, int z) {
if (x>y&&y>z) {//如果这里的判断逻辑比较复杂,则需要使用工具方法封装。时刻别忘了提高代码可读性。
doSomething(z,x);
}
}
Public void doSomething(int z,int x){
If(z==x){
//....
}
}
}
示例:
public class Foo {
private void foo(String bar) {
bar = "something else";
}
}
示例:
public class Foo {
public void bar(int x) {
switch (x) {
case 1: {
// lots of statements
break;
} case 2: {
// lots of statements
break;
}
}
}
}
示例:
public class SeniorClass {
public SeniorClass(){
toString(); //may throw NullPointerException if overridden
}
public String toString(){
return "IAmSeniorClass";
}
}
public class JuniorClass extends SeniorClass {
private String name;
public JuniorClass(){
super(); //Automatic call leads to NullPointerException
name = "JuniorClass";
}
public String toString(){
return name.toUpperCase();
}
}
Since: PMD 1.04
Instantiation by way of private constructors from outside of the constructor's class often causes the generation of an accessor. A factory method, or non-privitization of the constructor can eliminate this situation. The generated class file is actually an interface. It gives the accessing class the ability to invoke a new hidden package scope constructor that takes the interface as a supplementary parameter. This turns a private constructor effectively into one with package scope, and is challenging to discern.
This rule is defined by the following Java class: net.sourceforge.pmd.rules.AccessorClassGeneration
Example:
public class Outer {
void method(){
Inner ic = new Inner();//Causes generation of accessor class
}
public class Inner {
private Inner(){}
}
}
示例:
public class Foo {
public final int BAR = 42; // this could be static and save some space
}
示例:
public class Bar {
public void foo() {
Connection c = pool.getConnection();
try {
// do stuff
} catch (SQLException ex) {
// handle exception
} finally {
// oops, should close the connection using 'close'!
// c.close();
}
}
}
非静态的初始化块将在构造器被调用的时候被访问(优先于调用构造器)。这是一个有效的语言结构,但使用很少且易造成迷惑
示例:
public class MyClass {
// this block gets run before any call to a constructor
{
System.out.println("I am about to construct myself");
}
}
调用Collection.toArray时使用集合的规模加上目标类型的空数组作为参数。
示例:
class Foo {
void bar(Collection x) {
// A bit inefficient
x.toArray(new Foo[0]);
// Much better; this one sizes the destination array, avoiding
// a reflection call in some Collection implementations
x.toArray(new Foo[x.size()]);
}
}
在if表达式伴随else子句时,避免在if测试中使用否定表达。例如:不要使用if (x != y) diff(); else same()这种表述,而应该使用if (x == y) same(); else diff(),大多数时候使用if(x!=y)形式时不包含else分句,所以一贯地使用此规则能让代码更易于阅读。此外,这也解决了一个细节的排序问题,比如“应该是判断为false的代码块在前面?”,还是“判断通过的代码块在前?”
示例:
public class Foo {
boolean bar(int x, int y) {
return (x != y) ? diff : same;
}
}
Example:
public class Foo {
// Should specify Locale.US (or whatever)
private SimpleDateFormat sdf = new SimpleDateFormat("pattern");
}
识别出一旦被声明就赋值或通过构造器赋值后就从不改变的私有域,它们将存在的类变成了不变类。这样的域可以是final的
Example:
public class Foo {
private int x; // could be final
public Foo() {
x = 7;
}
public void foo() {
int a = x + 2;
}
}
Example:
class Foo {
// BAD
if (x.toLowerCase().equals("list"))...
/*
This will not match "LIST" when in Turkish locale
The above could be
if (x.toLowerCase(Locale.US).equals("list")) ...
or simply
if (x.equalsIgnoreCase("list")) ...
*/
// GOOD
String z = a.toLowerCase(Locale.EN);
}
因为final类型的class不能被继承,所以不要使用protected域,通过使用private或包访问符来代替以修正你的意图。
Example:
public final class Bar {
private int x;
protected int y; // <-- Bar cannot be subclassed, so is y really private or package visible???
Bar() {}
}
不安全
Example:
public class StaticField {
static int x;
public FinalFields(int y) {
x = y; // unsafe
}
}
一个class只有私有的构造函数,但是没有任何static方法,也没有任何可用的field。
Example:
/* This class is unusable, since it cannot be
instantiated (private constructor),
and no static method can be called.
*/
public class Foo {
private Foo() {}
void foo() {}
}
块级别的同步可以确保内含真正需要同步的代码。
Example:
public class Foo {
// Try to avoid this
synchronized void foo() {
}
// Prefer this:
void bar() {
synchronized(this) {
}
}
}
为了保证所有监听线程有均等的机会
Example:
public class Foo {
void bar() {
x.notify();
// If many threads are monitoring x, only one (and you won't know which) will be notified.
// use instead:
x.notifyAll();
}
}
每个产生的异常类型都应该在自己的catch块中被处理
Example:
try { // Avoid this
// do something
} catch (Exception ee) {
if (ee instanceof IOException) {
cleanup();
}
}
try { // Prefer this:
// do something
} catch (IOException ee) {
cleanup();
}
当给予一个null作为参数时,instanceof返回false
Example:
class Foo {
void bar(Object x) {
if (x != null && x instanceof Bar) {
// just drop the "x != null" check
}
}
}
Example:
class Foo {
boolean bar(String a, String b) {
return a == b;
}
}
Example:
class Foo {
boolean bar(String x) {
return x.equals("2"); // should be "2".equals(x)
}
}
避免创建不必要的本地变量,不仅在return的场合需要注意这一点
Example:
public class Foo {
public int foo() {
int x = doSomething();
return x; // instead, just 'return doSomething();'
}
}
Example:
private static Foo foo = null;
//multiple simultaneous callers may see partially initialized objects
public static Foo getFoo() {
if (foo==null)
foo = new Foo();
return foo;
}
接口只应该是对行为建模
Example:
public interface ConstantsInterface {
public static final int CONSTANT1=0;
public static final String CONSTANT2="1";
}
SimpleDateFormat是非同步的。Sun公司建议对每个线程单独的format实例。如果多线程必须访问一个静态formatter,formatter必须在方法或块级别同步。
代码示例:
public class Foo {
private static final SimpleDateFormat sdf = new SimpleDateFormat();
void bar() {
sdf.format(); // bad
}
synchronized void foo() {
sdf.format(); // good
}
}
在一个catch块中抛出一个新的异常却不把原始的异常传递给新的异常会导致真正的追踪信息栈丢失,而且导致难以有效的调试。
代码示例:
public class Foo {
void good() {
try{
Integer.parseInt("a");
} catch(Exception e){
throw new Exception(e);
}
}
void bad() {
try{
Integer.parseInt("a");
} catch(Exception e){
throw new Exception(e.getMessage());
}
}
}
java.util.Collection类的isEmpty方法提供判断一个集合类是否包含元素。不要是使用size()和0比较来重复类库已经提供的方法。 这条原则告诉我们一个普遍的原则:复用。尽量复用,充分利用已有的资源,不要重复自己(DRY)。比如apache提供了大量工具类供我们使用,我们没有必要再自己写了。
代码示例:
public class Foo {
void good() {
List foo = getList();
if (foo.isEmpty()) {
// blah
}
}
void bad() {
List foo = getList();
if (foo.size() == 0) {
// blah
}
}
}
一个类只包含私有的构造器应该是final的,除非私有构造器被一个内部类访问。
public class Foo { //Should be final
private Foo() { }
}
一个抽象类中的空方法也应该是抽象的,因为开发者有可能会信任这个空的实现而不去编写恰当的代码。
Example:
public abstract class ShouldBeAbstract
{
public Object couldBeAbstract()
{
// Should be abstract method ?
return null;
}
public void couldBeAbstract()
{
}
}
域变量只在一个方法中被使用并且第一次使用时对这个域赋值。这种域可以改写为本地变量。
Example:
public class Foo {
private int x; //Why bother saving this?
public void foo(int y) {
x = y + 5;
return x;
}
}
对于任何返回数组的方法,返回一个空的数组是一个比返回null引用更好的做法。
Example:
public class Example
{
// Not a good idea...
public int []badBehavior()
{
// ...
return null;
}
// Good behavior
public String[] bonnePratique()
{
//...
return new String[0];
}
}
如果抽象类没有提供任何的方法,它可能只是一个不可被实例化的数据容器,在这种状况下,更好的方法是使用私有的或受保护的构造器以阻止实例化可以让类避免带有欺骗性的抽象。 这条原则也适用于普通的数据容器类,比如我们常用的常量类,如果是常量类,那么请提供私有的或受保护的构造器。
Example:
public class abstract Example {
String field;
int otherField;
}
声明以及捕获异常的指导原则
捕获Throwable意味着不能处理的异常(错误)你也想处理,比如OutOfMemoryError.
示例代码:
public class Foo {
public void bar() {
try {
// do something
} catch (Throwable th) { //Should not catch throwable
th.printStackTrace();
}
}
}
不要在方法签名处 throws Exception,除了增加调用者的反感外,没看到有什么实际的好处。正确的做法是要么抛出明确的checked exception要么抛出RuntimeException。Spring框架所有方法都只抛出RuntimeException,这样方便用户对异常的处理,很值得我们借鉴。
示例代码:
public void methodThrowingException() throws Exception {
}
这样做会产生类似GOTO语句的效果,增加代码理解的难度。
示例代码:
public class Foo {
void bar() {
try {
try {
} catch (Exception e) {
throw new WrapperException(e);
// this is essentially a GOTO to the WrapperException catch block
}
} catch (WrapperException e) {
// do some more stuff
}
}
}
除非你想把真正的错误掩盖掉。
示例代码:
public class Foo {
void bar() {
try {
// do something
} catch (NullPointerException npe) {
}
}
}
用户总是会以为NullPointerException应该是由JVM抛出的。
示例代码:
public class Foo {
void bar() {
throw new NullPointerException();
}
}
人们会怀疑你捕获它的目的
示例代码:
public class Foo {
void bar() {
try {
// do something
} catch (SomeException se) {
throw se;
}
}
}
Errors是系统级别的异常,我们不要扩展他们。
示例代码:
public class Foo extends Error { }
这样做会让人感到困惑,还可能掩盖代码的异常或者缺陷,也使代码的清理部分变得不可靠。
示例代码:
public class Foo
{
public void bar()
{
try {
// Here do some stuff
}
catch( Exception e) {
// Handling the issue
}
finally
{
// is this really a good idea ?
throw new Exception();
}
}
}
不管你信不信,我是真的看到过这样的代码。
实例代码:
public class Foo {
void bar() {
try {
// do something
} catch (SomeException se) {
// harmless comment
throw new SomeException(se);
}
}
}
就一个原则:没有使用的就去掉,保持代码的干净、整洁。
没有使用的代码包括:
Ø 没有使用的私有成员
Ø 没有使用的本地变量
Ø 没有使用的私有方法
Ø 没有使用的方法参数(参数定义了,但是方法内没有使用此参数)
如果代码中包含多个重复的字符串,那么定义一个字符串常量来代替它。
示例代码:
public class Foo {
private void bar() {
buz("Howdy");
buz("Howdy");
buz("Howdy");
buz("Howdy");
}
private void buz(String x) {}
}
通常情况下,这么做没有用处,只会破坏字符串的享元策略。
示例代码:
public class Foo {
private String bar = new String("bar"); // just do a String bar = "bar";
}
基本没人会这么做的。
示例代码:
public class Foo {
private String baz() {
String bar = "howdy";
return bar.toString();
}
}
equalsIgnoreCase() 比 toUpperCase/toLowerCase().equals()更快。
示例:
public class Foo {
public boolean bar(String buz) {
// should be buz.equalsIgnoreCase("baz")
return buz.toUpperCase().equals("baz");
// another unnecessary toUpperCase()
// return buz.toUpperCase().equalsIgnoreCase("baz");
}
}
示例代码:
public class Foo {
void bar() {
StringBuffer sb = new StringBuffer();
// this is bad
if(sb.toString().equals("")) {}
// this is good
if(sb.length() == 0) {}
}
}
比如String.indexOf('a')比String.indexOf("a")快,StringBuilder.append('a')比StringBuilder.append("a")快
示例代码:
public class Foo {
void bar() {
String s = "hello world";
// avoid this
if (s.indexOf("d") {}
// instead do this
if (s.indexOf('d') {}
}
}
不指定,则默认长度是16,这样当长度不够时,就会有扩容的动作了。
示例代码:
public class Foo {
void bar() {
StringBuffer bad = new StringBuffer();
bad.append("This is a long string, will exceed the default 16 characters");//bad
StringBuffer good = new StringBuffer(41);
good.append("This is a long string, which is pre-sized");//good
}
}
示例代码:
public String convert(int i) {
String s;
s = "a" + String.valueOf(i); // Bad
s = "a" + i; // Better
return s;
}
示例代码:
class Foo {
StringBuffer sb1 = new StringBuffer('c'); //Bad
StringBuffer sb2 = new StringBuffer("c"); //Better
}
示例代码:
class Foo {
boolean test(String s) {
if (s == "one") return true; //Bad
if ("two".equals(s)) return true; //Better
return false;
}
}
Example:
class Foo {
private StringBuffer memoryLeak;
}
性能优化方面的一些最佳实践
示例代码:
public class Bar {
public void foo () {
String a = "a"; //if a will not be assigned again it is better to do this:
final String b = "b";
}
}
示例代码:
public void foo (String param) {
// do stuff with param never assigning it
// better: public void foo (final String param) {
}
示例代码:
public class Something {
public static void main( String as[] ) {
for (int i = 0; i < 10; i++) {
Foo f = new Foo(); //Avoid this whenever you can it's really expensive
}
}
}
示例代码:
public class SimpleTest extends TestCase {
public void testX() {
Collection c = new Vector();
// This achieves the same with much better performance
// Collection c = new ArrayList();
}
}
如果你的程序对性能要求很苛刻,那么可以考虑使用String.charAt(0)==x 来代替String.startWith
示例代码:
public class Foo {
boolean checkIt(String x) {
return x.startsWith("a");
}
}
这个规则目前不靠谱,因为JDK5以上的版本已经对“+”进行了优化。
示例代码:
public class Foo {
void bar() {
String a;
a = "foo";
a += " bar";
// better would be:
// StringBuffer a = new StringBuffer("foo");
// a.append(" bar);
}
}
示例代码:
public class Test {
public void foo(Integer[] ints) {
// could just use Arrays.asList(ints)
List l= new ArrayList(10);
for (int i=0; i< 100; i++) {
l.add(ints[i]);
}
for (int i=0; i< 100; i++) {
l.add(a[i].toString()); // won't trigger the rule
}
}
}
示例代码:
public class Test {
public void bar() {
int[] a = new int[10];
int[] b = new int[10];
for (int i=0;i<10;i++) {
b[i]=a[i];
}
}
}
// this will trigger the rule
for (int i=0;i<10;i++) {
b[i]=a[c[i]];
}
}
}
Integer的各种解析方法应该直接调用。
示例代码:
public int convert(String s) {
int i, i2;
i = Integer.valueOf(s).intValue(); // this wastes an object
i = Integer.parseInt(s); // this is better
i2 = Integer.valueOf(i).intValue(); // this wastes an object
i2 = i; // this is better
String s3 = Integer.valueOf(i2).toString(); // this wastes an object
s3 = Integer.toString(i2); // this is better
return i2;
}
示例代码:
String s = "" + 123; // bad
String t = Integer.toString(456); // ok
人人都要遵守
空Catch,空If,空while,空try,空finally,空switch,空Synchronized块,空static块