checkstyle guide

Java 编码规范

  1. tab4空格
  2. {+放在行尾
  3. 文件名/类名/变量名/包名
  4. 导包要求
  5. javadoc 检查
  6. 列长度120
  7. 类长度和方法长度限制
  8. 文件编码统一UTF-8
  9. 禁止重写finalize方法
  10. 空格使用要求
  11. {}使用要求
  12. 参考 google checkstyle

安装 checkstyle

Eclipse

  1. install eclipse plugin
  2. Window/Preferences
  3. Java/Code Style/Formatter
  4. import checkstyle.xml

IDEA

  1. install from Settings/Plugins: CheckStyle-IDEA
  2. Settings/Other Settings/Checkstyle
  3. import checksytle.xml

快捷键设置

  1. 代码格式化 Ctrl(⌘) + Alt + L
  2. 代码风格检查 Ctrl(⌘) + Alt + K

配置规则说明及示例

java 文件的设置规则

<property name="charset" value="UTF-8"/><property name="severity" value="warning"/><property name="fileExtensions" value="java"/><module name="FileTabCharacter">    <property name="eachLine" value="true"/></module><module name="NewlineAtEndOfFile"/><module name="FileLength">    <property name="max" value="1000"/></module>

文件内容结构顺序

  1. 可选的 license 注释说明
  2. 包路径
  3. 导包语句(不折行)
  4. 顶层类定义
  5. 上面不同代码块之间有一个空行分隔

tab 转4空格

<property name="tabWidth" value="4"/>

IDE 设置说明

  1. IDEA默认设置:Settings/Editor/Code Style/Java/Tabs and Indents中不要勾选Use tab character
  2. Eclipse4.7设置:Window/Preferences/Java/Code Style/Formatter/Edit/Indents中选择Spaces only

@tag 顺序

<module name="AtclauseOrder">    <property name="tagOrder" value="@author, @version, @param,          @return, @throws, @exception, @see, @since, @deprecated"/>    <property name="target" value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF,          METHOD_DEF, CTOR_DEF, VARIABLE_DEF"/></module>

包路径和导包语名不折行

<module name="NoLineWrap"/>

删除导入的多余包名

<module name="RedundantImport"/>

IDEA 自动导包设置

字段之间换行

<module name="EmptyLineSeparator">    <property name="allowNoEmptyLineBetweenFields" value="false"/></module>

Spring 源码示例

每个 Annotation 独立一行

<module name="AnnotationLocation">    <property name="allowSamelineSingleParameterlessAnnotation"              value="false"/></module>

不合格的例子

// 重写方法一定要加上 @Override// @Override should be alone on line@SuppressWarnings("unchecked")@Override public List<String> empty() { // AnnotationLocation violation    return new ArrayList();}

javadoc 规定

<module name="JavadocType">    <property name="scope" value="public"/>    <property name="allowMissingParamTags" value="true"/>    <property name="authorFormat" value="\S"/></module><module name="JavadocMethod">    <property name="scope" value="public"/>    <property name="tokens" value="METHOD_DEF"/>    <property name="allowMissingPropertyJavadoc" value="true"/>    <property name="allowMissingReturnTag" value="true"/>    <property name="allowMissingParamTags" value="true"/>    <property name="allowMissingThrowsTags" value="true"/>    <property name="allowUndeclaredRTE" value="true"/>    <property name="allowedAnnotations" value="Override, Test"/>    <property name="allowThrowsTagsForSubclasses" value="true"/></module><module name="JavadocStyle">    <property name="scope" value="public"/>    <property name="checkFirstSentence" value="false"/>    <property name="tokens" value="INTERFACE_DEF, METHOD_DEF"/></module>

Single line Javadoc

<module name="SingleLineJavadoc">    <property name="ignoreInlineTags" value="false"/></module>

不合格示例

包名和变量中大小写规定

<module name="LocalFinalVariableName"/><module name="LocalVariableName"/><module name="PackageName">    <property name="format" value="^[a-z]+(\.[a-z][a-z0-9]*)*$"/></module><module name="AbbreviationAsWordInName">    <property name="tokens" value="VARIABLE_DEF,CLASS_DEF"/>    <property name="ignoreStatic" value="false"/>    <property name="ignoreFinal" value="true"/>    <property name="allowedAbbreviationLength" value="3"/>    <property name="allowedAbbreviations" value="XML,URL,URI,DTO,VO"/></module>

命名检查之一

<module name="StaticVariableName"/><module name="TypeName"/><module name="ConstantName"/><module name="PackageName">    <property name="format" value="^[a-z]+(\.[a-z][a-z0-9]*)*$"/></module><module name="MemberName">    <property name="format" value="^[a-z][a-z0-9][a-zA-Z0-9]*$"/></module>

命名检查之二

<module name="MethodName">    <property name="format" value="^[a-z][a-z0-9][a-zA-Z0-9_]*$"/></module><module name="ParameterName">    <property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"/></module><module name="CatchParameterName">    <property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"/></module><module name="LocalVariableName">    <property name="tokens" value="VARIABLE_DEF"/>    <property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"/></module>

命名示例

  1. 枚举类名建议带上 Enum 后缀:OrderStatusEnum
  2. 接口名如 OrderService,实现类为:OrderServiceImpl
  3. 设计模式中一般加上模式名后缀,如:OrderObserver
  4. 数据传输对象名OrderDTO,表现层对象OrderVO
  5. 工具类名建议以 Utils 结尾:OrderUtils
  6. 插入保存方法用save
  7. 更新方法使用update前缀
  8. 单个对象获取用get前缀,列表获取用find前缀

单词拼写错误

不用拼音缩写

String hk = "hai kui";String fn = "feng niao";

少用英文缩写

String stat = "status";String cond = "condition";

项目里 main 测试类及时清理

<module name="UncommentedMain">    <property name="excludedClasses" value=".*[Application,Test]$"/></module>

以 Application 结尾的类

/** Application 和 Test 结尾的可以 */@SpringBootApplicationpublic class GatewayApplication {    public static void main(String[] args) {        new SpringApplicationBuilder(GatewayApplication.class).run(args);    }}

不要使用 System.out.println

<module name="Regexp">    <property name="format" value="System\.out\.println"/>    <property name="illegalPattern" value="true"/></module>

统一使用 org.slf4j.Logger

private static final Logger LOGGER = LoggerFactory.getLogger(...);

不要重写 Object.finalize() 方法

<module name="NoFinalizer"/>

不合格示例

@Overrideprotected void finalize() {    super.finalize(); // 如果重写一定要加此句    // ......}

同时重写 equals 和 hashCode 检查

<module name="EqualsHashCode"/>

不合格示例

@Overridepublic int hashCode() { // EqualsHashCode    return 1;}/** * Definition of hashCode() without corresponding definition of equals() * * @Override * public boolean equals(Object obj) { *     return super.equals(obj); * } */

检查 switch case 和 default 子句

<module name="FallThrough"/><module name="MissingSwitchDefault"/>

不合格示例

// case 子句需要有 break/return/throw/continue 等关键字switch (i) {    case 0:        i++; // fall through    case 1:    case 2:    case 3: {                i++; // fall through            }    case 4:            i++;            break;}

方法行数和参数数量限制

<module name="MethodLength">    <property name="tokens" value="METHOD_DEF"/>    <property name="max" value="80"/></module><module name="ParameterNumber">    <property name="max" value="5"/>    <property name="ignoreOverriddenMethods" value="true"/>    <property name="tokens" value="METHOD_DEF"/></module>

使用 java 风格的数组表示法

<module name="ArrayTypeStyle"/>

不要使用 c 语言的数组风格

public void arrayStyle() {    int[] array = new int[]{}; // prefer java style    int c[] = new int[]{};     // c style}

一行长度限制为120个字符

<module name="LineLength">    <property name="max" value="120"/></module>

IDEA 默认120列有标尺显示

Long 使用大写 L

<module name="UpperEll"/>

不合格示例

public long getOrderId() {    return 1l;}

空格使用规范

使用IDEA格式化处理空格

<module name="MethodParamPad"/><module name="TypecastParenPad"/><module name="ParenPad"/><module name="WhitespaceAfter"/><module name="WhitespaceAround"/>

, + ; 号放在行尾

<module name="OperatorWrap">    <property name="option" value="eol"/></module><module name="SeparatorWrap">    <property name="tokens" value="SEMI, COMMA, ARRAY_DECLARATOR"/>    <property name="option" value="eol"/></module>

可以用 IDEA 生成的 toString()

@Overridepublic String toString() {    return "User{" +            "id=" + id +            ", name='" + name + '\'' +            ", sex='" + sex + '\'' +            ", city='" + city + '\'' +            ", province='" + province + '\'' +            ", country='" + country + '\'' +            ", created=" + created +            '}';}

使用 IDE 来自动生成 Getter/Setter 方法

不合格示例

// 不要在 getter/setter 方法里加入业务逻辑public int getData() {    if (condition) {        return this.data + 100;    } else {        return this.data - 100;    }}

::. 运算符放在行首

<module name="SeparatorWrap">    <property name="tokens" value="DOT, METHOD_REF"/>    <property name="option" value="nl"/></module>
public void wrap(String[] stringArray) {    Arrays.sort(stringArray,            // "::" should be on a new line            String::            compareToIgnoreCase); // violation    Arrays.sort(stringArray, String            ::compareToIgnoreCase); // valid    // . shoule be on a new line    int i = stringArray.            length;}

不要有多余空格

<module name="NoWhitespaceAfter"/><module name="NoWhitespaceBefore"/>

不合格示例

public void spaceBeforeAfter() {    // int[] int后不要有空格    // , 和 ; 等符号前不要空格    int[] array = new int [] {} ;}

修饰符排序

<module name="ModifierOrder"/>

合格示例

private static final long serialVersionUID = 1L;private volatile boolean flag = true;

JLS 推荐顺序

  1. public protected private
  2. abstract default static
  3. final transient volatile
  4. synchronized native strictfp

提示多余的修饰符

<module name="RedundantModifier"/>

不合格示例

public class PublicClass {    class PackagePrivateClass {        // public 多余        public PackagePrivateClass() {        }    }}

代码块风格规定

<module name="EmptyBlock"/><module name="LeftCurly"/><module name="NeedBraces"/><module name="RightCurly"/><module name="EmptyStatement"/>

不合格示例

public void blockCurly() {    if (true)    { // LeftCurly      // EmptyBlock    } else {        System.out.println(); } // RightCurly    ; // EmptyStatement}

不要使用三目运算符

<module name="AvoidInlineConditionals"/>

不合格示例

public String avoidInlineConditionals(String a) {    // 不要使用三目运算符    return a.length() < 1 ? null : a.substring(1);}

方法参数和局部变量不要隐藏对象属性

<module name="HiddenField">    <property name="tokens" value="PARAMETER_DEF, VARIABLE_DEF"/></module>

不合格示例

public class User {    private int id;    public String getId() {        // id hides a field        String id = "id"; // HiddenField        return id;    }}

避免内嵌赋值

<module name="InnerAssignment"/>

不合格示例

int i = 0;String s = Integer.toString(i = 2);

简化boolean判断和返回值

<module name="SimplifyBooleanExpression"/><module name="SimplifyBooleanReturn"/>

不合格示例

public boolean simplifyBooleanExpression() {    if (!false) { // SimplifyBooleanExpression        // EmptyBlock    }    if (length() == 0) // SimplifyBooleanReturn        return false; // NeedBraces    else        return true; // NeedBraces}private int length () { // MethodParamPad    return 0;// NeedBraces WhitespaceAfter ;}

接口不要没有方法只定义常量

<module name="InterfaceIsType"/>

不合格示例

public interface CheckStyleViolate3 {    /**     * interfaces should describe a type and hence have methods     */    String KEY = "key"; // InterfaceIsType}

字段使用 private 修饰符

<module name="VisibilityModifier">    <property name="packageAllowed" value="true"/>    <property name="protectedAllowed" value="true"/></module>

不合格示例

/** * Must be private and have setter/getter methods if needs */public final String key = "key"; // VisibilityModifierprivate static String value = "value";

字符串比较使用 equals 方法

<module name="StringLiteralEquality"/>

不合格示例

if (x == "something" && y != "others") // 不合格

代码块嵌套深度

<module name="NestedForDepth">    <property name="max" value="3"/></module><module name="NestedIfDepth">    <property name="max" value="3"/></module><module name="NestedTryDepth">    <property name="max" value="3"/></module>

方法返回位置数量限制

<module name="ReturnCount">    <property name="max" value="5"/></module>

父方法调用检查

<module name="SuperClone"/><module name="SuperFinalize"/>

不合格示例

/** Java 设计缺陷的2个方法,不要重写,不要使用 */public class SuperMethodInvoke implements Cloneable {    @Override    protected SuperMethodInvoke clone() {        try {            return (CheckStyleViolate5) super.clone(); // 浅复制        } catch (CloneNotSupportedException e) {            throw new RuntimeException(e); // ugly        }    }    @Override    protected void finalize() throws Throwable {        super.finalize();    }}

异常捕获空 catch 块检查

<module name="EmptyCatchBlock">    <property name="exceptionVariableName" value="expected|ignore"/></module>

不合格示例

public void emptyCatchBlock() {    // exception variable name is "expected"    try {        throw new RuntimeException();    } catch (RuntimeException expected) {    }    // EmptyCatchBlock    try {        throw new RuntimeException();    } catch (RuntimeException myException) {    }}

一句代码折行缩进规范

<module name="Indentation">    <property name="lineWrappingIndentation" value="8"/>    <property name="forceStrictCondition" value="false"/></module>

合格示例

// valid, 12 > 8public static final Gson GSON = new GsonBuilder()            .setDateFormat(DateTimeConverter.DATE_FORMAT)            .disableHtmlEscaping()            .create();

不合格示例

// invalid, 4 < 8public static final Gson GSON = new GsonBuilder()    .setDateFormat(DateTimeConverter.DATE_FORMAT)    .disableHtmlEscaping()    .create();

注释的缩进位置保持与代码一致

<module name="CommentsIndentation"/>

合格示例

/** * comments style of multiply lines for javadoc * * @return constant 1 */public int method() {    // comment lines in method body    // Add a space after single line comment start    return 1;}

不合格示例

    // CommentsIndentation violationString key = "key";

IDEA 单行注释设置

参考 Spring 源代码风格

附录一:checkstyle.xml

<?xml version="1.0"?><!DOCTYPE module PUBLIC "-//Puppy Crawl//DTD Check Configuration 1.3//EN" "configuration_1_3.dtd"><module name="Checker">    <property name="charset" value="UTF-8"/>    <property name="severity" value="warning"/>    <property name="fileExtensions" value="java"/>    <module name="FileTabCharacter">        <property name="eachLine" value="true"/>    </module>    <module name="NewlineAtEndOfFile"/>    <module name="FileLength">        <property name="max" value="1000"/>    </module>    <module name="TreeWalker">        <property name="tabWidth" value="4"/>        <module name="EmptyLineSeparator">            <property name="allowNoEmptyLineBetweenFields" value="false"/>        </module>        <module name="RedundantImport"/>        <module name="AtclauseOrder">            <property name="tagOrder" value="@author, @version, @param,                  @return, @throws, @exception, @see, @since, @deprecated"/>            <property name="target" value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF, METHOD_DEF, CTOR_DEF, VARIABLE_DEF"/>        </module>        <module name="AnnotationLocation">            <property name="allowSamelineSingleParameterlessAnnotation" value="false"/>        </module>        <module name="JavadocType">            <property name="scope" value="public"/>            <property name="allowMissingParamTags" value="true"/>            <property name="authorFormat" value="\S"/>        </module>        <module name="JavadocMethod">            <property name="scope" value="public"/>            <property name="tokens" value="METHOD_DEF"/>            <property name="allowMissingPropertyJavadoc" value="true"/>            <property name="allowMissingReturnTag" value="true"/>            <property name="allowMissingParamTags" value="true"/>            <property name="allowMissingThrowsTags" value="true"/>            <property name="allowUndeclaredRTE" value="true"/>            <property name="allowedAnnotations" value="Override, Test"/>            <property name="allowThrowsTagsForSubclasses" value="true"/>        </module>        <module name="SingleLineJavadoc">            <property name="ignoreInlineTags" value="false"/>        </module>        <module name="JavadocStyle">            <property name="scope" value="public"/>            <property name="checkFirstSentence" value="false"/>            <property name="tokens" value="INTERFACE_DEF, METHOD_DEF"/>        </module>        <module name="LocalFinalVariableName"/>        <module name="LocalVariableName"/>        <module name="UncommentedMain">            <property name="excludedClasses" value=".*[Application,Test]$"/>        </module>        <module name="Regexp">            <property name="format" value="System\.out\.println"/>            <property name="illegalPattern" value="true"/>        </module>        <module name="AbbreviationAsWordInName">            <property name="tokens" value="VARIABLE_DEF,CLASS_DEF"/>            <property name="ignoreStatic" value="false"/>            <property name="ignoreFinal" value="true"/>            <property name="allowedAbbreviationLength" value="3"/>            <property name="allowedAbbreviations" value="XML,URL,URI,DTO,VO,SYNC"/>        </module>        <module name="StaticVariableName"/>        <module name="ConstantName"/>        <module name="PackageName">            <property name="format" value="^[a-z]+(\.[a-z][a-z0-9]*)*$"/>            <message key="name.invalidPattern" value="Package name ''{0}'' must match pattern ''{1}''."/>        </module>        <module name="TypeName">            <message key="name.invalidPattern" value="Type name ''{0}'' must match pattern ''{1}''."/>        </module>        <module name="MemberName">            <property name="format" value="^[a-z][a-z0-9][a-zA-Z0-9]*$"/>            <message key="name.invalidPattern" value="Member name ''{0}'' must match pattern ''{1}''."/>        </module>        <module name="MethodName">            <property name="format" value="^[a-z][a-z0-9][a-zA-Z0-9_]*$"/>            <message key="name.invalidPattern" value="Method name ''{0}'' must match pattern ''{1}''."/>        </module>        <module name="ParameterName">            <property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"/>            <message key="name.invalidPattern" value="Parameter name ''{0}'' must match pattern ''{1}''."/>        </module>        <module name="CatchParameterName">            <property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"/>            <message key="name.invalidPattern" value="Catch parameter name ''{0}'' must match pattern ''{1}''."/>        </module>        <module name="LocalVariableName">            <property name="tokens" value="VARIABLE_DEF"/>            <property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"/>            <message key="name.invalidPattern" value="Local variable name ''{0}'' must match pattern ''{1}''."/>        </module>        <module name="ClassTypeParameterName">            <property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)"/>            <message key="name.invalidPattern" value="Class type name ''{0}'' must match pattern ''{1}''."/>        </module>        <module name="MethodTypeParameterName">            <property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)"/>            <message key="name.invalidPattern" value="Method type name ''{0}'' must match pattern ''{1}''."/>        </module>        <module name="InterfaceTypeParameterName">            <property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)"/>            <message key="name.invalidPattern" value="Interface type name ''{0}'' must match pattern ''{1}''."/>        </module>        <module name="ArrayTypeStyle"/>        <module name="FallThrough"/>        <module name="UpperEll"/>        <module name="LineLength">            <property name="max" value="120"/>        </module>        <module name="MethodLength">            <property name="tokens" value="METHOD_DEF"/>            <property name="max" value="80"/>        </module>        <module name="ParameterNumber">            <property name="max" value="5"/>            <property name="ignoreOverriddenMethods" value="true"/>            <property name="tokens" value="METHOD_DEF"/>        </module>        <module name="MethodParamPad"/>        <module name="TypecastParenPad"/>        <module name="NoWhitespaceAfter"/>        <module name="NoWhitespaceBefore"/>        <module name="OperatorWrap">            <property name="option" value="eol"/>        </module>        <module name="NoLineWrap"/>        <module name="SeparatorWrap">            <property name="tokens" value="DOT, METHOD_REF"/>            <property name="option" value="nl"/>        </module>        <module name="SeparatorWrap">            <property name="tokens" value="SEMI, COMMA, ARRAY_DECLARATOR"/>            <property name="option" value="eol"/>        </module>        <module name="ParenPad"/>        <module name="WhitespaceAfter"/>        <module name="WhitespaceAround"/>        <module name="ModifierOrder"/>        <module name="RedundantModifier"/>        <module name="AvoidNestedBlocks"/>        <module name="EmptyBlock"/>        <module name="LeftCurly"/>        <module name="NeedBraces"/>        <module name="RightCurly"/>        <module name="EmptyStatement"/>        <module name="EqualsHashCode"/>        <module name="HiddenField">            <property name="tokens" value="VARIABLE_DEF"/>        </module>        <module name="AvoidInlineConditionals"/>        <module name="InnerAssignment"/>        <module name="MissingSwitchDefault"/>        <module name="SimplifyBooleanExpression"/>        <module name="SimplifyBooleanReturn"/>        <module name="FinalClass"/>        <module name="InterfaceIsType"/>        <module name="VisibilityModifier">            <property name="packageAllowed" value="true"/>            <property name="protectedAllowed" value="true"/>        </module>        <module name="StringLiteralEquality"/>        <module name="NestedForDepth">            <property name="max" value="3"/>        </module>        <module name="NestedIfDepth">            <property name="max" value="3"/>        </module>        <module name="ReturnCount">            <property name="max" value="3"/>        </module>        <module name="NestedTryDepth ">            <property name="max" value="3"/>        </module>        <module name="EmptyCatchBlock">            <property name="exceptionVariableName" value="expected"/>        </module>        <module name="Indentation">            <property name="lineWrappingIndentation" value="8"/>            <property name="forceStrictCondition" value="false"/>        </module>        <module name="CommentsIndentation"/>        <module name="SuperClone"/>        <module name="SuperFinalize"/>        <module name="NoFinalizer"/>    </module></module>

附录二:configuration_1_3.dtd

<!-- Add the following to any file that is to be validated against this DTD:<!DOCTYPE module PUBLIC    "-//Checkstyle//DTD Checkstyle Configuration 1.3//EN"    "https://checkstyle.org/dtds/configuration_1_3.dtd">--><!ELEMENT module (module|property|metadata|message)*><!ATTLIST module name NMTOKEN #REQUIRED><!ELEMENT property EMPTY><!ATTLIST property    name NMTOKEN #REQUIRED    value CDATA #REQUIRED    default CDATA #IMPLIED><!--   Used to store metadata in the Checkstyle configuration file. This   information is ignored by Checkstyle. This may be useful if you want to   store plug-in specific information.   To avoid name clashes between different tools/plug-ins you are *strongly*   encouraged to prefix all names with your domain name. For example, use the   name "com.mycompany.parameter" instead of "parameter".   The prefix "com.puppycrawl." is reserved for Checkstyle.--><!ELEMENT metadata EMPTY><!ATTLIST metadata    name NMTOKEN #REQUIRED    value CDATA #REQUIRED><!--   Can be used to replaced some generic Checkstyle messages with a custom   messages.   The 'key' attribute specifies for which actual Checkstyle message the   replacing should occur, look into Checkstyle's message.properties for   the according message keys.   The 'value' attribute defines the custom message patterns including   message parameter placeholders as defined in the original Checkstyle   messages (again see message.properties for reference).--><!ELEMENT message EMPTY><!ATTLIST message    key NMTOKEN #REQUIRED    value CDATA #REQUIRED>

References

  1. checkstyle
  2. google checkstyle guide
  3. google checksytle whitespace characters