最近为公司整理了下技术编码规范,主要是统一下研发风格并从长远看提高整个团队的工作效率。如下:
一、Git工程
1.1 使用maven创建依赖工程,上传到公司git库http://10.1.xx.xx:8080/groups/koudai,初创者拥有owner权限,相关开发developer,其它一律guest
1.2 工程结构,以dump-consle子工程为例
- src/filters:工程配置目录,一般包括线上配置prod.properties和日常配置daily.properties
- src/main/java:代码目录
- src/main/resource:工程资源目录,包括bean配置子目录,脚本子目录bin
- src/main/webapp:标准web目录
- src/main/test:单元测试目录,和src/main/java对应
- target:本地编译打包目录
1.3 完善README.md,如果是项目工程,须包括:
- 项目背景
- 项目目标
- 项目环境
- 项目成员
- 项目说明
1.4 工程文档,在工程根目录下建立docs目录,存放相关需求分析、详细设计、测试文档、图片、参考资料等
1.5 建立.gitignore,禁止提交以下类型文件
- *.classpath
- *.project
- *.setting
- target
1.6 主干与分支
开发一律走分支,禁止主干开发。分支命名以“项目_分支日期_分支应用”格式,如“dump_20150618_ic-delta-binlog”。分支提交以一个完整的feature作为commit点,写清comment。分支的合并如果是持续性的功能开发分支建议用rebase来保持主干的线性变化,修改bug等临时分支用merge操作
1.7 版本控制
开发过程中被其它项目依赖时,需要提供内部二方包,如“category-client-1.0.0-snapshot.jar”,最好将源码包一并上传。正式release的不要加上snapshot,版本的演化用数字递增(如小改动升级1.0.1,大改动升级为2.0.0)
二、代码风格
以下内容均在eclipse调试配置通过,可以直接将附件的模版文件导入即可
2.1 clean up
- 删除非静态访问变量的“this”修饰符
- 删除未使用的import语句
- 删除未使用到局部变量
- 删除不必要的casts溯型
- 添加“@Override”注释
- 添加“@Deprecated”注释
- 添加继承接口中未实现的方法
- 使用声明成员变量替换对非静态变量的访问
- 控制语句增加块结构
2.2 code template
- 新建类自动添加类扫描
- 新建方法自动添加方法扫描
/**
* <p>文件名称:${file_name}</p>
* <p>文件描述:</p>
* <p>版权所有:版权所有(C)2011-2099</p>
* <p>公 司:口袋购物</p>
* <p>内容摘要:</p>
* <p>其他说明:</p>
* <p>完成日期:${date}</p>
*
* @version 1.0
* @author ${USER}
*//**
* <p>功能描述:</p>
* <p>创建人:${user}</p>
* <p>创建日期:${date} ${time}</p>
*
* ${tags}
*/
2.3 formatter
2.3.1 缩进
制表符tab使用4个空格,缩进大小也为4个空格,需要缩进情况:
- 类body声明
- 枚举类型/枚举常量声明
- annotation声明
- 方法/构造器声明
- 代码块声明
- switch body声明
- case body声明
- break语句
2.3.2 括号
- 类/接口声明
- 匿名类声明
- 方法/构造器声明
- 枚举类/枚举常量声明
- annotation声明
- 代码块声明
- case/switch语句
- 数组初始化
2.3.3 空格
类、成员变量、局部变量、构造器、方法、labels、annotations、枚举、lambda声明,如下
class MyClass implements I0, I1, I2 {
}
AnonClass = new AnonClass() {
void foo(Some s) {
}
};
int a = 0, b = 1, c = 2, d = 3;
MyClass() throws E0, E1 {
this(0, 0, 0);
}
MyClass(int x, int y, int z) throws E0, E1 {
super(x, y, z, true);
}
void foo() throws E0, E1 {
};
void bar(int x, int y) throws E0, E1 {
}
void format(String s, Object... args) {
}
label: for (int i = 0; i < list.length; i++) {
for (int j = 0; j < list[i].length; j++)
continue label;
}
@Annot(x = 23, y = -3)
public class A {
}
enum MyEnum {
GREEN(0, 1), RED() {
void process() {
}
}
}
@interface MyAnnotation {
String value();
}
@interface OtherAnnotation {
}
Runnable r = () -> process();
控制语句int a = 4;
foo();
bar(x, y);
if (true) {
return 1;
} else {
return 2;
}
if (condition) {
return foo;
} else {
return bar;
}
for (int i = 0, j = array.length; i < array.length; i++, j--) {
}
for (String s : names) {
}
switch (number) {
case RED:
return GREEN;
case GREEN:
return BLUE;
case BLUE:
return RED;
default:
return BLACK;
}
while (condition) {
}
;
do {
} while (condition);
synchronized (list) {
list.add(element);
}
try (FileReader reader1 = new FileReader("file1"); FileReader reader2 = newFileReader("file2")) {
}
try {
number = Integer.parseInt(value);
} catch (NumberFormatException e) {
}
assert condition : reportError();
return (o);
throw(e)
表达式foo();
bar(x, y);
String str = new String();
Point point = new Point(x, y);
MyClass() throws E0, E1 {
this(0, 0, 0);
}
MyClass(int x, int y, int z) throws E0, E1 {
super(x, y, z, true);
}
List list = new ArrayList();
int a = -4 + -9;
b = a++ / --number;
c += 4;
boolean value = true && false;
result = (a * (b + c + d) * (e + f));
String s = ((String) object);
String value = condition ? TRUE : FALSE;
数组
int[] array0 = new int[] {};
int[] array1 = new int[] {1, 2, 3};
int[] array2 = new int[3];
array[i].foo();
参数化类型
Map<String, Element> map = new HashMap<String, Element>();
x.<String, Element> foo();
class MyGenericType<S, T extends Element & List> {
}
Map<X<?>, Y<? extends K, ? super V>> t;
2.3.4 空白行
- package声明后空一行
- import语句前空一行
- 在import组之间空一行
- 在import声明后空一行
- 类声明之间空一行
- 内部类、方法、同类声明前空一行
2.3.5 换行
行宽160个字符,默认换行缩进2个字符,默认数组初始化缩进2个字符,需要换行情况
- 类、匿名类、方法body须新起一行
- 枚举类、枚举常量声明须新起一行
- 代码块须新起一行
- annotation新起一行
2.4 organize imports
- package声明必须在import语句之前
- import顺序,以包头为准 java > javax > org > com
- 禁止在import语句中使用通配符*
三、命名规范
3.1 package
以com.koudai.${project}.${module}开头,所有包名的单词均小写
3.2 类
以大写字母开头其它字母小写的单词,多个单词组成时首字母大写
3.3 常量
static final修饰,单词全部大写,多个单词之间用“_”分隔
3.4 方法
首字母小写的单词,多个单词除第一个外后面所有单词首字母大写
3.5 方法参数
使用有意义的参数命名,尽量和需要赋值的变量保持一致
3.6 数组
使用byte[] buffer 而不是 byte buffer[]
3.7 其它
- 接口实现后面加上Impl
- 异常定义后面加上Exception
- 抽象类以Abstarct开头
- 测试类后面加上Test
四、书写规范
4.1 返回值
void方法无需返回;如果方法返回集合对象,在无元素时应返回大小为0的空集合而不是null对象;当需要返回null对象时,优先考虑使用自定义的NullObject替换null
4.2 异常处理
- 尽早抛出,延迟捕获
- 不要为每个可能出现的异常使用try-catch
- 不要在代码中捕获或抛出Error
- 声明或捕获应是具体的类型异常,而非Throwable和Exception
- 在循环外而非循环中处理异常
- 千万不要捕获甚至吃掉自己不能处理的异常
4.3 表达式
复杂的算术、逻辑表达式须加括号,避免使用java默认的运算优先级,如(5 *(x - y))/ (4 + y)
尽可能使用二元赋值运算符,如 x+=1 而非 x = x + 1
4.4 控制语句
- 避免使用while(true)这样的循环判断
- 对与iterator迭代类型的集合,使用foreach替换for进行集合遍历
- for循环中的递增变量使用 ++i 而非 i++
- switch必须包含default语句
- 条件分支if-else中的else if保持在同一行代码
- 优先考虑条件运算符替换if-else判断
4.5 注释
- 常量、类型成员变量、方法尽量加注释
- 容易引起歧义的地方、算法、业务相关处理加注释
- 注释风格保持一致,避免中英文夹杂
- 行注释统一放在被注释的代码上一行
- 养成使用TODO、FIXME的习惯