PMD参数总结

括号使用规则

If表达式必须使用{},无论有多少语句

错误实例:

 public class Foo {

   public void bar() {

     int x = 0;

     if (foo) x++;//if(foo){x++;}

   }

 }

 

     

           

While循环必须使用{},无论有多少语句

错误写法示意:                

public void doSomething() {

  while (true)

      x++;

}

      

           

IfElse表达式必须使用{}                

错误写法示意:

 public void doSomething() {

   // this is OK

   if (foo) x++;

   // but this is not

   if (foo)

       x=x+1;

   else

       x=x-1;

 }

For循环语句必须使用{}

错误写法示意:

public void foo() {

 for (int i=0; i<42;i++)

   foo();

}

设计规则

如果方法返回boolean,那么注意避免不必要的if..then..else语句

示例:

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语句嵌套过深(会导致代码可读性降低)

解决办法:建议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";

 }

}

     

注意Switch语句的密度,不要在switch语句的每个case中做太多事情,注意“责任分解”的原则,如果你一定要用switch语句,那让它成为一个控制者,每个case中的执行细节交给工具方法或者使用策略模式简化复杂度。

示例:

public class Foo {

 public void bar(int x) {

   switch (x) {

     case 1: {

       // lots of statements

       break;

     } case 2: {

       // lots of statements

       break;

     }

   }

 }

}

 

      

           

不要在构造方法中调用Overridable方法,很显然这样存在风险。

示例:

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();

  }

}

  

AccessorClassGeneration

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(){}

 }

}

final类型的域可以(应该)同时是static 

示例:

public class Foo {

 public final int BAR = 42; // this could be static and save some space

}

  

      

           

记得及时关闭一些关键资源(Connection, Statement, ResultSet...)

示例:

                

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");

 }

}

              

   

优化toArray调用 

调用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;

 }

}          

在构建SimpleDateFormat时需要指定Locale

                   

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;

  }

}

忽略大小写进行字符串比较时,使用String.equalsIgnoreCase,不要使用String.toLowerCase.前者有更好的性能而且还可以避免后者带来的本地化问题

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类中使用protected 

因为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() {}

}

不要给一个非finalstatic类型赋值 

不安全

Example:

                

   

public class StaticField {

   static int x;

   public FinalFields(int y) {

    x = y; // unsafe

   }

}

在不可实例化的class中丢失静态方法

一个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) {

  }

 }

}

NotifyAll代替Notify

为了保证所有监听线程有均等的机会            

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块中使用instanceof 

每个产生的异常类型都应该在自己的catch块中被处理 

             

Example:

                

try { // Avoid this

 // do something

} catch (Exception ee) {

 if (ee instanceof IOException) {

  cleanup();

 }

}

try {  // Prefer this:

 // do something

} catch (IOException ee) {

 cleanup();

}

       

使用instanceof时不需要判断对象是否为null

当给予一个null作为参数时,instanceof返回false  

Example:

                

      

class Foo {

 void bar(Object x) {

  if (x != null && x instanceof Bar) {

   // just drop the "x != null" check

  }

 }

}      

使用Equals来比较对象,不要用==

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之前不必要的本地变量 

避免创建不必要的本地变量,不仅在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";

    }

    

      

           

不要同步静态的DateFormat 

SimpleDateFormat是非同步的。Sun公司建议对每个线程单独的format实例。如果多线程必须访问一个静态formatterformatter必须在方法或块级别同步。

代码示例:

                

    

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());

        }

    }

}

    

      

           

使用集合类的isEmpty方法 

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 

一个类只包含私有的构造器应该是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 

对于任何返回数组的方法,返回一个空的数组是一个比返回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

捕获Throwable意味着不能处理的异常(错误)你也想处理,比如OutOfMemoryError.

示例代码:

                

                

public class Foo {

 public void bar() {

  try {

   // do something

  } catch (Throwable th) {  //Should not catch throwable

   th.printStackTrace();

  }

 }

}

                

      

           

不要直接throws Exception

不要在方法签名处 throws Exception,除了增加调用者的反感外,没看到有什么实际的好处。正确的做法是要么抛出明确的checked exception要么抛出RuntimeExceptionSpring框架所有方法都只抛出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

  }

 }

}

不要捕获NullPointerException

除非你想把真正的错误掩盖掉。 

示例代码:

                  

public class Foo {

 void bar() {

  try {

   // do something

   }  catch (NullPointerException npe) {

  }

 }

}

         

不要抛出NullPointerException

用户总是会以为NullPointerException应该是由JVM抛出的。              

示例代码:

                

        

public class Foo {

 void bar() {

  throw new NullPointerException();

 }

}

  

      

           

不要捕获了某异常再把它抛出去

人们会怀疑你捕获它的目的 

示例代码:

                  

  public class Foo {

   void bar() {

    try {

    // do something

    }  catch (SomeException se) {

       throw se;

    }

   }

  }

  

    

           

不要继承JavaLangError

Errors是系统级别的异常,我们不要扩展他们。          

 示例代码:

                

        public class Foo extends Error { }

    

           

不要在Finally里抛出异常

这样做会让人感到困惑,还可能掩盖代码的异常或者缺陷,也使代码的清理部分变得不可靠。

示例代码:

                

            

                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);

      }

     }

    }

 

没有使用的代码

就一个原则:没有使用的就去掉,保持代码的干净、整洁。

没有使用的代码包括:

Ø 没有使用的私有成员

Ø 没有使用的本地变量

Ø 没有使用的私有方法

Ø 没有使用的方法参数(参数定义了,但是方法内没有使用此参数)

String StringBuffer

不要重复定义字符串

如果代码中包含多个重复的字符串,那么定义一个字符串常量来代替它。

示例代码:

public class Foo {

 private void bar() {

    buz("Howdy");

    buz("Howdy");

    buz("Howdy");

    buz("Howdy");

 }

 private void buz(String x) {}

}

    

           

不要实例化String对象

通常情况下,这么做没有用处,只会破坏字符串的享元策略。

示例代码:

                

public class Foo {

 private String bar = new String("bar"); // just do a String bar = "bar";

}

    

           

不要调用String.toString()

基本没人会这么做的。

示例代码:

                

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");

  }

 }

                 

       

           

计算StringBuffer的长度用StringBuffer.length()别用StringBuffer.toString().length()

示例代码:

                

  

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') {}

 }

}

           

构建StringBufferStringBuilder时,如果知道长度,请指定,这样性能更好

不指定,则默认长度是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

    }

}

    

           

把某种类型的数据(例如整数)拼接到字符串,不需要调用String.valueOf方法

示例代码:

public String convert(int i) {

  String s;

  s = "a" + String.valueOf(i); // Bad

  s = "a" + i; // Better

  return s;

}

          

           

初始化StringBuffer或者StringBuilder时,别用字符,因为字符会被转化为整数,这样就会错误地认为你是想指定字符序列的长度而不是初始值。

示例代码:

                

class Foo {

  StringBuffer sb1 = new StringBuffer('c'); //Bad

  StringBuffer sb2 = new StringBuffer("c"); //Better

}

    

           

比较字符串也要用equals别用==

  

示例代码:

class Foo {

  boolean test(String s) {

    if (s == "one") return true; //Bad

    if ("two".equals(s)) return true; //Better

    return false;

  }

}

    

           

别用StringBuffer作为类的私有成员

           

Example:

                

class Foo {

    private StringBuffer memoryLeak;

}


优化规则

性能优化方面的一些最佳实践

如果本地变量只被赋值一次,那么把它声明为Final

示例代码:

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";

 }

}

如果方法参数从来不会被重新赋值,那么把它声明为Final

示例代码:

                

  

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.startWith性能不如String.charAt(0)

如果你的程序对性能要求很苛刻,那么可以考虑使用String.charAt(0)==x 来代替String.startWith 

           

示例代码:

                

  

public class Foo {

  boolean checkIt(String x) {

      return x.startsWith("a");

  }

}

      

           

StringBuilder来进行字符串拼接而不是+

这个规则目前不靠谱,因为JDK5以上的版本已经对“+”进行了优化。

示例代码:

                

      

public class Foo {

 void bar() {

  String a;

  a = "foo";

  a += " bar";

  // better would be:

  // StringBuffer a = new StringBuffer("foo");

  // a.append(" bar);

 }

}

      

           

           

如果想由数组构建List,请使用Arrays.asList

      

示例代码:

                

   

   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

     }

    }

   }

   

数组复制,请使用System.arraycopy,别用循环

示例代码:

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;

}

""+123的方式把数字转换为String,不够高效

示例代码:     

        String s = "" + 123; // bad 

        String t = Integer.toString(456); // ok 

           

基本规则

人人都要遵守

避免代码中出现各种""的语句

Catch,空If,空while,try,finally,switch,空Synchronized块,空static

EqualsHashcode要同时复写Override

这两个方法要么同时复写,要么都不复写。

示例代码:

                

  

// this is bad

public class Bar {

  public boolean equals(Object o) {

      // do some comparison

  }

}

// and so is this

public class Baz {

  public int hashCode() {

      // return some hash value

  }

}

// this is OK

public class Foo {

  public boolean equals(Object other) {

      // do some comparison

  }

  public int hashCode() {

      // return some hash value

  }

}

DoubleChecked锁机制并不好使

示例代码:

public class Foo {

  Object baz;

  Object bar() {

    if(baz == null) { //baz may be non-null yet not fully created

      synchronized(this){

        if(baz == null){

          baz = new Object();

        }

      }

    }

    return baz;

  }

}

 

      

           

避免从finallyreturn

示例代码:

                

  

public class Bar {

 public String foo() {

  try {

   throw new Exception( "My Exception" );

  } catch (Exception e) {

   throw e;

  } finally {

   return "A. O. K."; // Very bad.

  }

 }

}

   

没必要的return

示例代码:

public class Foo {

 public void bar() {

  int x = 42;

  return;

 }

}

       

没条件的If表达式

如果表达式永远是true或者永远是false,那么就别用if语句了。这个低级错误往往是代码在后续重构中被“改”出来的。

示例代码:

public class Foo {

 public void close() {

  if (true) {

       // ...

   }

 }

}

           

不要初始化Boolean对象

可以直接饮用Boolean.TRUE, Boolean.FALSE或者调用 Boolean.valueOf()

示例代码:

                

   

public class Foo {

 Boolean bar = new Boolean("true"); // just do a Boolean bar = Boolean.TRUE;

 Boolean buz = Boolean.valueOf(false); // just do a Boolean buz = Boolean.FALSE;

}

没必要的final修饰符

如果一个classfinal那么它所有的方法都是final的,因此没必要给每个方法增加final修饰符。

示例代码:

public final class Foo {

    // This final modifier is not necessary, since the class is final

    // and thus, all methods are final

    private final void foo() {

    }

}

收缩If语句

不要让if语句嵌套过深,这样不便于理解。

示例代码:            

  

public class Foo {

 void bar() {

  if (x) {

   if (y) {

    // do stuff

   }

  }

 }

}

           

避免在BigDecimal构造函数中引入Decimal字面量

Since: PMD 3.4

你可能认为 "new BigDecimal(.1)" 等于 .1, 但实际上它等于.1000000000000000055511151231257827021181583404541015625. 这是因为 .1 无法精确表达一个double数,相反'new BigDecimal(".1")' 跟我们期望的一直,它等于.1。因此用字符串构造函数没错的。

示例代码:

                

import java.math.BigDecimal;

public class Test 

    public static void main(String[] args) {

      // this would trigger the rule

     BigDecimal bd=new BigDecimal(1.123);

      // this wouldn't trigger the rule

     BigDecimal bd=new BigDecimal("1.123");

      // this wouldn't trigger the rule

     BigDecimal bd=new BigDecimal(12);

    }

}

  

           

避免对不可变对象进行操作

对不可变对象的操作不会改变对象本身,返回结果实际上是新构建的对象,因此你不能忽略返回结果。

示例代码:

                

    

import java.math.*;

class Test {

 void method1() {

  BigDecimal bd=new BigDecimal(10);

  bd.add(new BigDecimal(5)); // this will trigger the rule

 }

 void method2() {

  BigDecimal bd=new BigDecimal(10);

  bd = bd.add(new BigDecimal(5)); // this won't trigger the rule

 }

}

放错位置的Null检查

示例代码:  

    

public class Foo {

 void bar() {

  if (a.equals(baz) && a != null) {}

 }

}

    

      

           

Example:

                

public class Foo {

 void bar() {

  if (a.equals(baz) || a == null) {}

 }

}

   

           

Equals方法中没有使用Null检查

在检查了一个对象不为null后,你应该紧接着调用这个对象的equals方法(因为你已经指导它不是null了),而不应该把它传给另外一个对象的equals方法(另外一个对象还没有判断是否为null)。

示例代码:

                

public class Test {

public String method1() { return "ok";}

public String method2() { return null;}

public void method(String a) {

String b;

/*

I don't know it method1() can be "null"

but I know "a" is not null..

I'd better write a.equals(method1())

*/

if (a!=null && method1().equals(a)) { // will

trigger the rule

//whatever

}

if (method1().equals(a) && a != null) { //

won't trigger the rule

//whatever

}

if (a!=null && method1().equals(b)) { // won't

trigger the rule

//whatever

}

if (a!=null && "LITERAL".equals(a)) { // won't

trigger the rule

//whatever

}

if (a!=null && !a.equals("go")) { // won't

trigger the rule

a=method2();

if (method1().equals(a)) {

//whatever

}

}

}

}

被打断的Null检查

由于抛出了NullPointerException ,导致null检查被打断。很可能你错误地使用||代替了&&

示例代码:

                

class Foo {

 String bar(String string) {

  // should be &&

  if (string!=null || !string.equals(""))

    return string;

  // should be ||

  if (string==null && string.equals(""))

    return string;

 }

}

BigInteger的实例化

不要创建已经存在的BigInteger对象 (BigInteger.ZERO, BigInteger.ONE) JDK1.5之后还有BigInteger.TEN BigDecimal (BigDecimal.ZERO, BigDecimal.ONE, BigDecimal.TEN)

示例代码:

                

public class Test {

 public static void main(String[] args) {

   BigInteger bi=new BigInteger(1);

   BigInteger bi2=new BigInteger("0");

   BigInteger bi3=new BigInteger(0.0);

   BigInteger bi4;

   bi4=new BigInteger(0);

 }

}

避免使用八进制

示例代码:

        public class Foo {

          int i = 012; // set i with 10 not 12

          int j = 010; // set j with 8 not 10

          k = i * j; // set k with 80 not 120

        }

避免对IP地址硬编码

示例代码:

                

        

    public class Foo {

      String ip = "127.0.0.1"; // This is a really bad idea !

    }

检查ResultSet的导航方法

Since: PMD 4.1

一定要检查ResultSet的导航方法 (next,previous,first,last) 的返回值。如果方法返回了false,我们就要处理了。

示例代码:

                

            

            // This is NOT appropriate !

            Statement stat = conn.createStatement();

            ResultSet rst = stat.executeQuery("SELECT name FROM person");

            rst.next(); // what if it returns a 'false' ?

            String firstName = rst.getString(1);

            // This is appropriate...

            Statement stat = conn.createStatement();

            ResultSet rst = stat.executeQuery("SELECT name FROM person");

            if (rst.next())

            {

                String firstName = rst.getString(1);

            }

            else

            {

                // here you deal with the error ( at least log it)

            }

尽量别使用一元操作符

这样的代码不易理解。                    

示例代码:  

            

            // These are typo bugs, or at best needlessly complex and confusing:

            int i = - -1;

            int j = + - +1;

            int z = ~~2;

            boolean b = !!true;

            boolean c = !!!true;

            // These are better:

            int i = 1;

            int j = -1;

            int z = 2;

            boolean b = true;

            boolean c = false;

            // And these just make your brain hurt:

            int i = ~-2;

            int j = -~7;

            

耦合规则

对象与对象之间,包与包之间过高的或者不恰当的耦合规则

对象间过度耦合

这个规则计算一个对象中的属性个数,本地变量和返回类型个数。这些数目如果比阀值过高,那么表明该对象有更高的耦合度。

Example:

                

import com.Blah;

import org.Bar;

import org.Bardo;

public class Foo {

 private Blah var1;

 private Bar var2;

 //followed by many imports of unique objects

 void ObjectC doWork() {

  Bardo var55;

  ObjectA var44;

  ObjectZ var93;

  return something;

 }

}

过分的导入(import

导入越多,就意味着该对象有更高的耦合度。这个规则默认的阀值是20

示例代码:

                

      

import blah.blah.Baz;

import blah.blah.Bif;

// 18 others from the same package elided

public class Foo {

 public void doWork() {}

}

松耦合(面向抽象编程)

依赖抽象,不要依赖具体。

                

实例代码:

import java.util.*;

public class Bar {

 // Use List instead

 private ArrayList list = new ArrayList();

 // Use Set instead

 public HashSet getFoo() {

  return new HashSet();

 }

}

 

导入相关的规则

这个规则很简单,IDE也已经很好的进行了check。简而言之就是做到不要重复导入,只导入代码用到的。

命名规则

命名非常重要,直接影响到代码可读性的好于坏。

Variable命名约定

1. 常量全大写

2. 变量以小写字符开头,遵循“驼峰式”书写规则

3. 尽量少用缩写(言外之意,你别定义类似sbnb之类的过短的变量名)

4. 如果变量是Collection的某个类型,建议变量后缀体现类型信息,比如

List xxList = 

Map xxMap = 

示例代码:

public class Foo {

 public static final int MY_NUM = 0;

 public String myTest = "";

 DataModule dmTest = new DataModule();

}

This rule has the following properties:

Method命名约定

1. 必须是个动词(动词词组)

2. 必须以小写字符开头,同时遵循“驼峰式”书写原则

3. 尽量避免使用缩写(言外之意,方法名不能太短,如果实在太长也可以在不影响别人理解其含义的前提下适当使用缩写)

                

示例代码:

public class Foo {

 public void fooStuff() {

 }

}

Class命名约定

1. 是个名词(名词词组)

2. 以大些字幕开头,同时遵循“驼峰式”书写原则

3. 尽量避免使用缩写(如果实在太长也可以在不影响别人理解其含义的前提下适当使用缩写)

示例代码:

public class Foo {}

抽象类命名以Abstract开始

示例代码:

public abstract class Foo { // should be AbstractFoo

}

命名时避免出现$

避免在variable/method/class/interface 名字中出现$

示例代码:

public class Fo$o {  // yikes!

 }

可疑的Hashcode方法名

同可疑的Equals方法名。尽量避免

示例代码:

public class Foo {

 public int hashcode() {

 // oops, this probably was supposed to be hashCode

 }

}

可疑的Constant

避免出现与java已有常量重名的情况,比如PI:

示例代码:

public class Foo {

 // this is bad, since someone could accidentally

 // do PI = 2.71828; which is actualy e

 // final double PI = 3.16; is ok

 double PI = 3.16;

}

可疑的Equals方法名

Since: PMD 2.0

方法名称和参数都与Object. equals(Object)类似让人以为你想override equals(Object) 方法.

示例代码:

                

        

public class Foo {

 public int equals(Object o) {

 // oops, this probably was supposed to be boolean equals

 }

 public boolean equals(String s) {

 // oops, this probably was supposed to be equals(Object)

 }

}

避免出现属性名称与类型(class)名称相同的情况

示例代码:

public class Foo extends Bar {

 // There's probably a better name for foo

 int foo;

}

避免出现属性名称与方法名称相同的情况

示例代码:

public class Foo {

Object bar;

// bar is data or an action or both?

void bar() {

}

}

Package名称中不要出现大写字符

示例代码:

package com.MyCompany;  // <- should be lower case name

public class SomeClass {

}

    

返回BooleanGet方法命名

按照约定,正确命名是'isX()',不应该是'getX()'

示例代码:

public boolean getFoo(); // bad

public boolean isFoo(); // ok

public boolean getFoo(boolean bar); // ok, unless checkParameterizedMethods=true

     

代码Size规则

非常重要的规则集。方法或者class的代码过长是普遍现象,这样的代码很有可能违反了单一职责原则(自然也就违反了开-闭原则)以及DRY原则,这样的代码不好理解,难于测试。而如何通过重构,合理的进行责任分解,把冗长的代码变短,则是值得不断总结和讨论的话题。

过分长的ParameterList

解决办法参考代码坏味道的“过长参数”,这个规则默认值是10,我们规定是5,即当一个方法参数超过5个时,就需要进行重构了。

                

public class Foo {

 public void addData(

  int p0, int p1, int p2, int p3, int p4, int p5,

  int p5, int p6, int p7, int p8, int p9, int p10) {

  }

 }

}

圈复杂度(CyclomaticComplexity

一个方法内的决策点的数目决定了这个方法的复杂度。决策点包括'if', 'while', 'for' 'case labels'。一般来说1-4是低复杂度,5-7是中复杂度,8-10是高复杂度,11之上则是非常高了。

示例代码:

// Cyclomatic Complexity = 12

public class Foo {

1   public void example()  {

2       if (a == b)  {

3           if (a1 == b1) {

                fiddle();

4           } else if a2 == b2) {

                fiddle();

            }  else {

                fiddle();

            }

5       } else if (c == d) {

6           while (c == d) {

                fiddle();

            }

7        } else if (e == f) {

8           for (int n = 0; n < h; n++) {

                fiddle();

            }

        } else{

            switch (z) {

9               case 1:

                    fiddle();

                    break;

10              case 2:

                    fiddle();

                    break;

11              case 3:

                    fiddle();

                    break;

12              default:

                    fiddle();

                    break;

            }

        }

    }

}

过分多的Public成员(fieldmethod

按照职责进行拆分。这个规则默认值是45。《代码大全2》中规定的数目要远远小于这个值,公有成员的数目,公有方法的数目各自<=7。我们初步采用二者数目<=20。公有的Getter/Setter不计算在内。

示例代码:

                

    

public class Foo {

 public String value;

 public Bar something;

 public Variable var;

 // [... more more public attributes ...]

 public void doWork() {}

 public void doMoreWork() {}

 public void doWorkAgain() {}

 // [... more more public methods ...]

}

    

太多的Field

一个有太多的fieldclass应该被重新设计减少field的数目,解决办法可以参考代码坏味道中的“数据泥团”。这个规则的默认值是15。我们规定阀值是10

示例代码:

public class Person {

   String one;

   int two;

   int three;

   [... many more public fields ...]

}

太多的方法

一个类中public method数目不能超过10个。

忽略注释的Method代码量

忽略注释和折行统计的有效的方法代码量,这个规则默认阀值是100,这个值有些宽松,实际上,当你去阅读一个100行方法的时候,你会发现这个方法的某些语句在一起完成一件事情,而另外一些语句则是做另外一件事情,你完全有理由用工具方法把他们封装起来。

解决办法可以参考代码坏味道中的“过长函数”

示例代码:

public class Foo extends Bar {

 public int methd() {

     super.methd();

 //this method only has 1 NCSS lines

      return 1;

 }

}

忽略注释的Type代码量

忽略注释和折行的class代码量,这个规则的默认阀值是1500,太宽松了。除非那些很底层的、系统级别的(这样的代码只有很少人会去阅读J)代码因为各种原因我们可以容忍一个class达到1000行以上的代码量,其他的业务逻辑class绝对不允许超过500行代码(我写的代码一般都控制在300行以内)

解决办法可以参考代码坏味道中的“过大类”

Example:

public class Foo extends Bar {

 public Foo() {

 //this class only has 6 NCSS lines

     super();

      super.foo();

 }

}

posted on 2011-08-14 12:43  wenfeng762  阅读(1551)  评论(0编辑  收藏  举报