记-阿⾥开发⼿册规范(JAVA)
从接触java以来,我们⼀直学习着各种技术和架构。但我们常忽略代码可读性和⼀些特殊的规范和约定。有很多⼈尤其是新⼈写的代码,运⾏可能没什么问题。但却不忍直视,甚⾄内涵风险。业内代码规范可能各有各的理解,这⾥介绍⼀下国内的⼤佬,也是⼤部分初⼊职场的同学都会看到阿⾥Java开发⼿册。这⾥只列⼀些个⼈觉得较重要的,还是建议搭建看⼀下完整版。
编程规范
命名规范
1. 禁⽌使⽤拼⾳和英⽂混合
这⾥还是建议⼤家使⽤英⽂命名,纯拼⾳也不要⽤,有助于理解和规范,不会可以百度翻译嘛
2. 类名使⽤驼峰,特殊模型相关可以忽略
正例:UserService / UserDAO / RemotingAdapter
反例:Userservice / UserDao / TCPUDPDeal
这⾥需要注意像TCP/UDP/XML等这种英⽂缩写建议也使⽤驼峰⽽⾮全⼤写
3. ⽅法名、参数名、变量都统⼀采⽤驼峰写法,⾸字母⼩写
4. 常量命名全⼤写,单次间使⽤下划线
如:MAX_CONNECTION_COUNT
5. 抽象类命名,以Abstract或Base开头;异常类以Exception结尾;测试类以Test结尾。
6. boolean类型变量,不要以is开头,避免部分序列化框架出错。
正例:boolean exists
反例:boolean isExists
7. 包名统⼀⼩写,使⽤点分割,尽可能点之间都是⼀个⾃然语义的英⽂单次。报名统⼀使⽤单数形式。
如:com.alibaba.open.util
8. 如果使⽤设计模式,在类名中体现,有利于阅读者理解
如:OrderFactory / LoginProxy / ResourceObserver
9. 类接⼝中属性和⽅法不要加任何修饰符
注:JDK8中接⼝允许默认实现,有default
10. 对于Service和DAO类,⼀定是接⼝,实现需要以Impl的后缀结尾。区别于接⼝
如:CacheService CacheServiceImpl
11. 枚举类名加上Enum后缀,成员名称全部⼤写,下划线分割
抽象类的使用枚举就是特殊常量类,所以命名按常量⽅式;类命名加Enum后缀,如果使⽤枚举是为了实现单例模式,可以不加Enum后缀
常量定义
1. 不允许出现魔法值,即:未经定义的常量
如:String key = “id=” + id;
2. long和Long初始赋值时,必须使⽤⼤写,为了避免和数字1搞混。
正例:Long a = 2L
反例:Long a = 2l
3. 不要使⽤⼀个常量类维护所有的常量,尽可能根据功能进⾏拆分归类,分开维护。便于理解和维护。
格式规范
1. ⼤括号使⽤规定
1. ⼤括号内若为空,则简洁写成{},不需换⾏
2. 左⼤括号前不换⾏,后换⾏
3. 右⼤括号前换⾏,后如果还有else等代码不换⾏;如果表⽰终⽌必须换⾏
2. 左括号与后⼀个字符之间不出现空格,右括号与前⼀个字符之间不出现空格
如:if (flag == 0)
3. if/for/while/switch/do等保留字与左右括号之间必须加空格
4. 任何运算符左右必须加空格
5. ⽅法参数在定义和传⼊时,多个参数逗号后必须加空格
如:method(“a”, “b”, “c”);
oop规范
1. 不要使⽤对象访问类的静态变量和静态⽅法,直接使⽤类名访问即可,避免增加编译器解析成本
2. 所有覆写⽅法,必须加@Override
3. 对外的接⼝,原则上避免修改,可以增加新接⼝,过时接⼝使⽤@Deprecated注解
4. 尽可能避免使⽤过时⽅法和类
5. 对象使⽤equals时,将常量或确定有值的对象放到前⾯
如:“test”.equals(object)
注:JDK1.7可以使⽤Objects.equals()⽅法
6. 同类型的类⽐较,使⽤值⽐较,即 :equals⽅法,不要使⽤ ==
注:Integer 赋值-128⾄127之间使⽤的是常量,即:此时使⽤==判断也是对的,但避免掉坑,使⽤equals判断。
7. 序列化类新增属性时,不要修改serialVersionUID字段,避免兼容反序列化失败。如果确定不兼容,可以修改serialVersionUID字
段。
8. 类中⽅法顺序规范
1. ⼀个类有多个构造⽅法时或同名⽅法,将这些⽅法顺序放在⼀起
2. 类内⽅法定义顺序:公有⽅法 > 保护⽅法 > 私有⽅法 > getter/setter⽅法
9. 循环体内字符串拼接,使⽤StringBuilder的append⽅法
10. final可提⾼程序响应效率,能声明final尽可能声明final
集合处理
1. 关于hashCode和equals
1. 只要重写equals,就必须重写hashCode
2. 因为Set存储不重复对象,所以需要重写hashCode和equals两个⽅法
3. 如果对象作为Map的键,就必须重写hashCode和equals
2. ArrayList的subList⽅法结果不可转成ArrayList
注:subList返回的是ArrayList的内部类,并不是ArrayList,⽽且是ArrayList的⼀个视图,对SubList所有的操作都会反映到ArrayList原列表上。
3. 使⽤集合转数组⽅法,必须使⽤集合的toArray(T[] array)
注:如果传⼊数组array空间不够⼤,toArray⽅法将重新分配内存空间,并返回新的数组地址;如果传⼊数组array⼤于实际所需空间,则下标list.size()的数组元素将被⾄为null,建议⼊参数组⼤⼩与元素个数⼀致
4. 使⽤⼯具类Arrays.asList()将数组转换成集合时,不能使⽤修改集合相关⽅法,如:add/remove/clear都将抛出
UnsupportedOperationException异常
注:asList返回对象是⼀个Arrays内部类,并没有实现集合的修改⽅法。该模式为适配器模式,只是转换接⼝,真实数据还是落在数组上。即:修改数组,list也会被修改
5. 不在foreach循环使⽤remove/add操作。remove元素请使⽤Iterator⽅式,并发情况需要加锁。
6. 在JDK7以上,Comparator需要满⾜⾃反性,传递性,对称性,不然sort会报错
注:如果没有考虑相等情况,使⽤中将可能出现异常。
7. Map遍历尽可能使⽤entrySet遍历,⽽⾮keySet遍历。因为keySet遍历,会造成两次遍历。
并发处理
1. 单例模式需要确保线程安全
2. 线程资源必须通过线程池提供,不允许在应⽤中⾃⾏显⽰创建线程,避免过多创建线程、减少创建和销毁线程开销
3. SimpleDateFormat是⾮线程安全的,⼀般不要定义为static,建议可以放到ThreadLocal中,与线程
绑定
4. 并发情况,多考虑锁的性能损耗。能不⽤锁就不⽤锁;能锁区块,就不要锁整个⽅法;能锁对象就别锁类
5. 对多资源加锁,需要保证加锁顺序⼀致性,否则会造成死锁
6. 如果访问冲突概率不⾼,推荐使⽤乐观锁
7. 执⾏定时任务,尽可能使⽤ScheduledExecutorService,⽽⾮Timer
8. 使⽤CountDownLatch进⾏同步转异步,线程推出前,必须调⽤coutDown,注意catch异常,确保countDown可以执⾏
9. 避免Random被多线程使⽤,虽然是线程安全的,但多线程竞争会导致性能下降
注:JDK7以后可以使⽤ThreadLocalRandom,在JDK7之前,可以使⽤ThreadLocal⾃⾏绑定
10. 使⽤docker-checked模式做延迟初始化操作时,为了避免指令重排序隐患。可以将⽬标属性声明成volatile型。如:docker-
checked单例模式
11. HashMap在并发情况下,resize时,会造成死链,导致CPU飙升
12. ThreadLocal对象建议使⽤static修饰
控制语句
1. switch块内,每个case要么通过break/return终⽌,要么注释说明将执⾏到哪⼀个case为⽌;在⼀个switch必须包含default,就算
是空的
2. 避免在判断条件中执⾏复杂的语句,可以将判断结果赋给⼀个boolean变量,从变量名可以阅读出含义
正例:if((file.open(fileName, “w”) != null) && (…) || (…))
反例:boolean existed = (file.open(fileName, “w”) != null) && (…) || (…)
if(existed)
3. 循环体内语句尽可能考虑性能,如:对象、变量、获取数据库连接、try-catch操作等,尽可能移⾄循环体外操作
注释规范
1. 类、类属性、类⽅法使⽤Javadoc规范,/* 内容 /格式,⽽⾮ //内容 ⽅式
2. 所有的抽象⽅法(包括接⼝中的⽅法),必须要⽤Javadoc注释
3. 所有类必须添加创建者信息,不要因为是坑就不敢留名
4. 特殊标记,TODO(待办事项) FIXME(错误标记)
异常⽇志
异常处理
1. 不要捕获继承⾃RuntimeException的运⾏时异常,这类异常需要⾃⾏检查规避,如:IndexOutOfBoundsException /
NullPointerException
2. 异常不要⽤来做流程控制,条件控制,因为异常处理效率⽐条件分⽀低
3. 尽量不要对⼤段代码try-catch,尽可能区分异常类型
4. 不要捕获异常后,什么都不做⼜抛出;如果不处理,请抛给调⽤者
5. 不要在finally执⾏return,否则不再执⾏try块中的return语句
⽇志约定
1. 不要执⾏使⽤⽇志实现类,⽽应该使⽤SLF4J的API接⼝,使⽤门⾯模式,容易做到所有类的⽇志处理⽅式统⼀
2. ⽇志⽂件保存⾄少15天,因为有些异常具备“周”发⽣频率
3. ⽇志⽂件命名尽可能划分规范,从⽇志上就分类,⽅便维护
4. 对于trace/debug/info⽇志,必须加条件输出判断或使⽤占位符,这样可以避免字符串拼接等资源浪费
5. 避免⽇志重复打印,在l中需要设置additivity=false
6. 异常信息必须包括,错误信息和堆栈
MySQL规范
1. 表达是否的字段,必须使⽤is_xxx的⽅式命名,数据类型是unsigned tinyint(1 是,0 否)
2. 表名 字段名必须使⽤⼩写字符或数字;禁⽌出现数字开头,禁⽌两个下划线之间只有数字
3. 表名不要使⽤复数名词
4. 唯⼀索引使⽤uk_字段名;普通索引使⽤inx_字段名
5. ⼩数使⽤decimal,禁⽌使⽤float和double
注:floathe double在存储时,存在进度损失问题
6. 如果存储字符串长度⼏乎相等,使⽤char定长字符串类型
7. varchar为变长字符串,长度不要超过5000,如果存储长度超过该值,使⽤text,并独⽴表出来
索引规范
1. 业务上具有唯⼀特性的字段。即使是组合字段,也必须构建唯⼀索引
2. 超过三个表禁⽌join,需要join的字段数据类型必须⼀致,多表关联查询时,保证被关联的字段需要有索引
注:即使是双表join也需要注意表索引,SQL性能
3. 在varchar字段上建索引,必须指定索引长度,没必要对全字段建⽴索引
注:⼀般对字符串类型数据,长度为20的索引,区分度就⾼达90%以上
4. 禁⽌左模糊匹配或全模糊匹配
5. 利⽤延迟关联或⼦查询优化超多分页场景
注:MySQL并不是跳过offset⾏,⽽是取offset+N⾏,然后放弃offset⾏
SQL规范
1. 不要使⽤count(列名/常量)替代count(*)
注:count(*)会统计NULL的⾏,⽽count(列名)不会
2. 当⼀⾏全为NULL时。count(col)为0,⽽sum(col)为NULL
3. 使⽤ISNULL()来判断是否是NULL值,因为NULL与任何值直接⽐较都是NULL
4. 不要在SQL层使⽤外键和级联,外键在应⽤层定义就好
ORM规范
1. 在表查询中,不要使⽤*作为查询的字段列表,需要哪些字段明确写出来
注:使⽤*,会增加查询分析成本,且增减字段容易造成与resultMap不⼀致
2. XML配置,使⽤#{},#param#,不要使⽤${},防⽌SQL注⼊
3. 不推荐使⽤ibatis⾃带的queryForList(statementName,start,size),因为内部实现是取出所有记录,再通过subList取⼦集
4. 不允许直接拿HashMap与HashTable作为查询结果集的输出
5. 不要写⼀个⼤⽽全的更新接⼝,传⼊POJO类,不管是否需要更新的字段都进⾏了update,这样造成很多⽆需改动的字段进⾏了修
改。容易出错、效率低、增加了binlog存储。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论