Java代码规范

最近帮团队整理了一份Java编码规范,总体上参考了 Google Java 代码规范

编码准则

尽可能让代码风格看起来像是一个人写的。另外不要盲从一下规则,只要你能给出合理的理由 ^_^

默认缩减 4 个空格,不要使用 Tab

字符集

文件编码使用 UTF-8

源文件结构

Package 语句

package语句写在一行里面,不要换行

Import 语句

  • 不要使用通配符,比如 import java.util.*
  • 一个导入语句不要换行
  • import语句按照下面顺序排列,看起来不乱,同时要按块组织,块与块之间空行
    • import static
    • <空行>
    • import com.<company_name> 比如 com.alibaba 自己公司的包
    • import org.springframework 第三方包导入,按照ASCII字符排列
    • <空行>
    • import java.
    • import javax.

代码示例

import static org.junit.assert.AssertNull;
// 空行
import com.alibaba.fastjson.JSONObject;
import org.aspectj.lang.annotation.Around;
import org.slf4j.Logger;
// 空行
import java.lang.Thread;
import java.util.Date;
import javax.swing.JFrame;

不推荐的样式

// 相同性质的包被分开放

import com.hellofalcon.blog.shared.model.*;

import com.hellofalcon.blog.util.AddressUtil;
import com.hellofalcon.blog.util.EncryptUtil;
import org.apache.commons.lang.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.hellofalcon.blog.api.QueryService;    // 可以和最上面的放到一起

类成员

遵从 相似的成员放在一起 的原则, 逻辑类似的成员放在一起 的原则

推荐样式

class Sample {
    // 静态变量放在一起
    private static final Logger LOG = LogFactory.getLogger(Sample.class);
    private static final String LOG_PREFIX = "sample-log";

    // 实例属性放在一起
    private String name;
    private String version;

    // 外部Service等放在一起
    private MailService mailService;
    private BeanService beanService;

}

不推荐的样式

class Sample {
    private static final Logger LOG = LogFactory.getLogger(Sample.class);

    private String name;

    private String mail;

    private MailService mailService;

    private String version;

    private BeanService beanService;

    // 静态变量随手放在一处
    private static final String LOG_PREFIX = "sample-log";

}

格式及排版

大括号

if, for, do, while, else 等即便只有一个语句也将大括号加上

大括号的格式

大括号的格式采用 K&R 风格

  • { 左侧不换行
  • { 右侧换行
  • } 左侧必须换行
  • } 右侧在以下情况不需要换行: 1. } 右侧是 else, 2. } 右侧是 , 逗号。其余情况都要换行

代码示例:

// 右括号换行示例
return new MyClass() {
  @Override public void method() {
    if (condition()) {
      try {
        something();
      } catch (ProblemException e) {
        recover();
      }
    }
  }
};

// 右括号不换行示例
if (a == 10) {
    // do something 
} else {
    // do something
}

空代码块

  • 如果代码块中没有任何代码,则不需要换行
  • 如果代码块从属于 if else try catch finally 那么代码块中需要换行

代码示例:

// 空代码块
void doNothing() {};

// 需要换行的空代码块
try {
    // do something
} catch (Exception e) {
    // do nothing
}

代码块缩进

代码块的开始需要和之前的语法元素保留 1个空格 的距离

代码示例:

void function() {
    // do something
}

if (a == 10) {
    // do something
}

try {

} catch (Exception e) {

}

每行一个语句

分号 ; 确定一个语句, 所以每行只有一个分号

每行代码的字符数不超过 120

考虑到目前屏幕的分辨率,单行 120 字符还是比较合适的

但是以下情况不需要限制字符数

  • package 语句
  • import 语句
  • 不可能满足列限制的行(例如,Javadoc中的一个长URL,或是一个长的JSNI方法参考)

代码换行的时机 (Line Wrapping)

时机: 在高级的语法结构处断开

  • 非赋值运算符(-, +, *, / 等),在运算符的前面断开
  • 赋值运算符 (=),在赋值运算符的后面断开

代码示例:

int sum = 
        a 
        + b
        + c;    

换行时的缩进

  • 同级语法元素可以有相同的缩减
  • 不同级别的多行缩减,每一个缩进要比上一行多缩进至少4个空格

代码示例:

int result = 
    someService.invoke(
        param1,
        (a 
            + b 
            + c
        )            
    )

何时使用空白

此处空白指: 空行 和 空格

空行使用的时机

空行的使用注意是用来帮助代码划分逻辑块

  • 类中连续的成员: 字段、构造器、方法、嵌套类、静态初始化块、实例初始化块,之间应该有空行
    • 但是有例外: 字段直接如果没有其他内容可以省略空行
  • 函数内的逻辑块之间可以用空行隔开
  • 类中第一个和最后一个成语前后的空行可以省略

代码示例:

class Sample {
    // 静态变量放在一起
    private static final Logger LOG = LogFactory.getLogger(Sample.class);
    private static final String LOG_PREFIX = "sample-log";

    // 实例属性放在一起
    private String name;
    private String version;

    // 外部Service等放在一起
    private MailService mailService;
    private BeanService beanService;

    // 静态初始化块
    static {
        // do something here
    }

    public Sample() {
        // do some initialization
    }

    public Sample(String name, String version) {
        this.name = name;
        this.version = version;
    }

    // innser class
    class InnerClass {

    }

    // static nested class
    static class NestedClass {

    }         
}

空白使用的时机

除了语法需要的空格外,以下几个地方也请使用空格:

  • 将所有保留字(例如 if, for, catch)和 ( 用空格分隔
  • 将所有保留字(例如 else, catch) 和 } 用空格分隔
  • { 之前都加上空格,除了以下两个例外
    • @SomeAnnotation({a, b}),这样是不需要空格的
    • String[][] x = foo;,这样也是不需要的
  • 所有的二元操作符左右都加上空格
  • , : ; ) 后都加上空格
  • 注释符合 // 的两次留空格

代码示例:

int sum = 1 + 2 + 3 * 19 + (4 % 3)

if (flag == true) {
    // do something
} else { 
    // do something
}

int result = service.invoke(p1, p2, p3);

int triple = flag ? 1 : 2; // comment example

特定语法结构的格式

变量声明

  • 每行一个变量声明,不要使用连续的变量声明,如 int a, b;
  • 变量在使用的时候再声明,并且尽快完成初始化

Array声明

Array声明按照代码块的约定来即可

new int[] {           new int[] {
  0, 1, 2, 3            0,
}                       1,
                        2,
new int[] {             3,
  0, 1,               }
  2, 3
}                     new int[]
                          {0, 1, 2, 3}

Switch语句

  • 每个Switch语句必须有 default 子句
  • 子句缩进 4 个空格

代码示例:

switch(rn) {
    case 'I': return 1;
    case 'V': return 5;
    case 'X': return 10;
    case 'L': return 50;
    case 'C': return 100;
    case 'D': return 500;
    case 'M': return 1000;
    default: return 0;
}    

注解 Annotation

  • 注解紧跟JavaDoc之后
  • 每行一个注解

代码示例:

@Override
@Nullable
public String getNameIfPresent() { ... }

注释的使用

  • 注释要和它所修饰的语句使用相同的缩进规则
  • 需要注释中的代码被格式化或者生产JavaDoc的时候使用 /* ... */ 来进行注释

注释示例:

/*
 * This is          // And so           /* Or you can
 * okay.            // is this.          * even do this. */
 */

修饰符

类和其成员有修饰符的时候按照下列顺序来显示

public protected private abstract static final transient volatile synchronized native strictfp

数字字面量

long 型的字面量赋值的时候使用 L 来结尾,例如 2000000L 而不是 200000l

命名的艺术

标识符命名的通用准则:

  • 标识符的组成中只使用 ASCII字母数字下划线(_) 来命名

  • lowerCamelCase: 小写驼峰命名法, 除了第一个单词的首字母小写,其他单词的首字母大写其余字母小写。

  • UpperCamelCase: 大写驼峰命名法, 所有单词的首字母大写,其余都小写

包命名

  • 全小写
  • 只能包含字母

包名示例:

// 推荐的
com.mycomp.service

// 不推荐的
com.mycomp.mail_service
com.mycomp.mailService

类命名

  • 命名规则: UpperCamelCase
  • 通常的类名是 名字 或者 名词短语,例如 ArrayList。 对于接口的命名则可用一些形容词,例如 Readable
  • 测试方法使用 test 开头或者 Test 结尾。例如 MailServiceTest, testGetMessage

方法命名

  • 命名规则: lowerCamelCase
  • 方法名通常是 动词动词短语
  • 测试方法可以用下划线(_)来做一些特殊标记,比如 testPop_empty, testPop_null

常量命名

  • 使用 static final 来修饰你的常量
  • 所有单词的所有字母大写,单词之间用下划线(_)来分隔

常量示例:

// Constants
static final int NUMBER = 5;
static final ImmutableList<String> NAMES = ImmutableList.of("Ed", "Ann");
static final Joiner COMMA_JOINER = Joiner.on(',');  // because Joiner is immutable
static final SomeMutableType[] EMPTY_ARRAY = {};
enum SomeEnum { ENUM_CONSTANT }

非常量字段的命名

  • 命名规则: lowerCamelCase。例如 computedValues

参数命名

  • 命名规则: lowerCamelCase。
  • 避免单个字母的变量名

本地变量命名

  • 命名规则: lowerCamelCase
  • 尽可能的简短
  • 通用要尽可能少用一个字母的变量,除非这个变量用在循环中暂时使用
  • 本地变量用 final 修饰的时候也不用遵循常量的命名规则

类型变量命名

类型变量,特别是泛型类型变量名可以用下面的两种方式来命名

  • 采用单个大写字符的命名,例如 T, F, E
  • 一个首字母大写的单词加上 T, 例如 RequestT, FooBarT

异常 Exceptoin

异常的捕获

不要忽略捕获到的异常,你可以打个日志什么的,但是就是不要忽略

异常的分类: Checked Exception 和 Unchecked Exception。归根到底可以归因到:服务方自己产生的异常,还是调用方使用不当产生的异常。

Exception 和 RuntimeException 使用的准则

  • 如果你希望调用方处理异常,那么用Exception
  • 如果你希望调用方可忽略异常,那么用RuntimeException
  • 使用时注意不要让底层异常越过太多层向上抛,否则上层应用也不知道怎么处理
  • 当你需要给调用方返回更多的错误信息的时候,可以自定义异常

异常的实践

  • 记得释放资源。特别是数据库还有网络资源,使用 try-catch-finally
  • 不要使用异常作控制流程之用
  • 不要忽略异常
  • 生成代码不要 printStatckTrace()

IDE相关

IDEA

基于 Intellij IDEA 14

组织import: Ctrl + Alt + O

代码格式化: Ctrl + Alt + L

设置工程编码:

  • Step 1: Ctrl + Alt + S (调出Settings)
  • Step 2: Editor => File Encoding
  • Step 3: IDE Encoding 指定全局默认编码, Project Encoding 指定当前工程编码

如何少些点代码?

IDEA 默认配置了各种代码模板,多多使用的话,写Java也不用感叹人生苦短

  • 快捷键
    • Ctrl + J,调用默认Code Template,这个是可以自己配置的
    • Ctrl + Alt + T,这个快捷键首先要先选中一个代码块(不知道肿么选中?自己面壁去),然后敲击这个快捷键,可以插入 if-elseswitchtry-catch-finally 来包裹代码块
  • 编辑器

    这个功能直接在编辑器写缩略短语即可,IDEA会自动提示,然后按Tab就可以

    • iter, 可以用来遍历Collection
    • new XXX().var, 用来快速生成一个 new 语句
    • <expression>.var, 同上,快速定义个变量并复制
    • ifn,用来快速生成 if(xxx == null) {} 这样的代码
    • … 其他还有很多,可以到官网下载小册子学习

Eclipse

欢迎补充

参考资料

  1. 文档 - https://google.github.io/styleguide/javaguide.html
  2. 文档 - http://www.onjava.com/pub/a/onjava/2003/11/19/exceptions.html?page=1
  3. 书籍 - 《编写可读代码的艺术》 (The Art of Readable Code)
  4. 书籍 - 《代码大全》 (Code Complete)