第1章:命名规范
1.1 类名以package名称为前缀
类名以package名称为前缀(不考虑大小写),这种属于比较差的命名规范,应该避免。
package
com.taobao.ic.web.module.screen.user;
public
class
UserView {
}
---->>>>
public
class
View {
}
|
1.2 异常类的类目不以Exception为后缀
Exception类的名称应该以Exception为后缀,能充分表达其意思,使代码的可读性强。
1.3 长整形数值以小写'l'结尾
对于长整形数值来说,以小写'l'结尾,会被人为人为1,所以需要更改为大写'L'。
long
userId=23l;
---->>>>
long
userId=23L;
|
1.4 函数名称和类名相同
函数名称和类名相同,这个会让人疑惑,是不是构造函数,命名习惯不推荐这样做。
1.5 函数的参数名和其覆盖的函数名称不一致
如果覆盖一个函数,确保函数的参数和原来一致,虽然这个是合法的,但是使用不同的参数名后,会让人费解。
1.6 变参的方法不允许传入原始数据类型的数组
由于Java的自动包装机制,当传递原始数据类型的数组给变参方法时,编译器会再将其包装为一个容量为1数组存放传入的数组。这可能并非预期的处理结果。
1.7 标准变量名称
下面的变量名称和类型基本都被大家公认,如果没有充足的理由,不要改变其类型和用途,否则会让人费解: ul>
- i, j, k, m, n - int
- f - float
- d - double
- b - byte
- c, ch - char
- l - long
- s, str - String
1.8 方法的参数不要超过5个
如果参数过多,使用对象包装(比如Query)
private
Result updateItem(BidItemVO vo, String sellerID, String itemID, List lvo, String hasUpload,
long
limitCode) {
}
---->>>>
public
Result getAuctionDetail4Mercury(ItemDetailQuery query) {
}
|
1.9 太复杂的判断条件,比如有多个and、or的情况,加括号分类,分行
这样代码的可读性会好很多
第2章:代码风格
2.1 C语言风格的数组创建
在Java代码中,使用C语言风格语法创建数组,而不是Java方式的。
String matrix[] =
null
;
//C-style
---->>>>
String[] martrix =
null
;
//Java-style
|
2.2 声明包含javadoc错误
声明通常包含的javadoc错误如下: 1 没有javadoc 2 必要的javadoc tag丢失 3 不合法的javadoc tag 4 描述丢失,如参数描述,exception描述等 5 名称不对,如参数名不匹配 6 多余的javadoc tag,如参数已经删除,但是javadoc中还有
2.3 一次声明多个变量
在一个声明语句中声明多个变量,这个做法对Java不友好,应该一次声明一个变量。
String nick, realName;
---->>>>
String nick;
String realName;
|
2.4 合理地进行参数校验
Public类型的方法,在做业务处理之前,要保证传入的参数符合你的假设;参数校验的顺序要合理,校验代价小的优先做
public
boolean
isMatchMD5(String source, String encodedStr) {
if
(StringUtil.isBlank(source) || StringUtil.isBlank(encodedStr)) {
return
false
;
}
if
(!CodesUtil.encodeMD5(source).equals(encodedStr)) {
return
false
;
}
return
true
;
}
|
2.5 对用户提交的内容进行HTML的过滤
避免xss漏洞
StringUtil.escapeSpecialHTML(mailtext,
true
);
|
2.6 在已有的类中增加方法,如果这个类不是自己创建的,建议加上作者,时间,注释。
/**
* 根据订单号获取订单详情
*
* @author <a href="mailto:zhenbei@taobao.com">震北</a>
* @since 2008-6-9 下午03:44:04
*
* @param orderIdStr 订单号串:如:123 or 123:456
* @param userId
* @return
*/
Result getOrderDetail(String orderIdStr, Long userId);
|
2.7 在已有的方法中加入条件分支或者其他业务逻辑,一定要注释
免得这个方法的原作者都看不懂了,其他人更搞不灵清
if
(ucBaseDO ==
null
|| ucBaseDO.isGradeNormal()) {
// 加上如果是普通会员的话,要去取一下
// 商城VIP卡
if
(user.isRegieUser()) {
// 取商城VIP卡 wanjian 2007.03.26
UserCardBaseDO mallVipCardDO =
this
.userCardManager.findMallVIPCard(query.getLoginUserId(), user.getUserId(), query.getTrackNick());
if
(mallVipCardDO !=
null
) {
if
(ucBaseDO ==
null
) {
ucBaseDO = mallVipCardDO;
}
else
if
(mallVipCardDO.getCurGradeIndex() > ucBaseDO.getCurGradeIndex()) {
ucBaseDO = mallVipCardDO;
}
}
}
}
|
2.8 DO要尽可能利用自身的数据完成业务逻辑
就是说DO之间尽量不要相互依赖
2.9 避免无用的代码
没有无用的import,没有无用的成员变量,没有无用的private方法避免类似import java.io.*;的引用
2.10 方法的注释中,参数的说明和返回值的说明要写详细
可以参考StringUtil
/**
* 检查字符串是否为<code>null</code>或空字符串<code>""</code>。
*
* StringUtil.isEmpty(null) = true
* StringUtil.isEmpty("") = true
* StringUtil.isEmpty(" ") = false
* StringUtil.isEmpty("bob") = false
* StringUtil.isEmpty(" bob ") = false
*
*
* @param str 要检查的字符串
*
* @return 如果为空, 则返回<code>true</code>
*/
public
static
boolean
isEmpty(String str) {
return
((str ==
null
) || (str.length() ==
0
));
}
|
2.11 在程序的分支和复杂的逻辑处写详细的注释
代码特殊处理(比如特殊约定,类似字符串按一定规则组合或拆分,用List或Map返回不同类型对象,用事务时事务中返回的对象)的地方要注释 在一些逻辑复杂或重要(比如出价、类目属性、时间程序等)的代码中修改,建议加上 作者,时间,注释。
2.12 DTO的每个属性要给出完整的注释
方便调用者使用,可以跟数据字典对应起来
2.13 不可以将自己新编写的代码和其它代码一起格式化,developer只需要将自己新编写的代码进行格式化后提交
第3章:控制流程
3.1 if-else操作更改为三元操作符(?:)
if
(foo()){
return
bar();
}
else
{
return
foobar();
}
---->>>>
return
foo()?bar():foobar();
|
3.2 多余的if判断
if
(foo()){
return
true
;
}
else
{
return
false
;
}
---->>>>
return
foo();
|
3.3 接口调用是否进行了超时设置
第4章:异常处理
4.1 捕获一般异常
报告代码中catch一般异常,处于清晰、精确考虑,推荐捕获特定异常,这样更便于我们理解代码。一般异常包括:
- java.lang.Throwable
- java.lang.Exception
- java.lang.RuntimeException
4.2 finally中包含return语句
在finally块中包含return语句,虽然有意为之,但是会导致调试困难。
4.3 catch语句中的throw语句忽略了已经捕获的错误
在catch语句中,可能会再抛出一个新的异常,这个时候,需要封装已经捕获的exception,而不是构建一个新的异常,不然异常的原信息就会丢失,给调试增加难度。
catch
(SQLException e) {
throw
new
ManagerException(
"数据库异常"
);
}
---->>>>
catch
(SQLException e) {
throw
new
ManagerException(e);
}
|
4.4 不允许捕获IllegalMonitorStateException
IllegalMonitorStateException 通常只会在代码本身包含设计上的缺陷时出现(在没有持有对象锁的对象上调用wait或notify方法),因此不应被捕获。
4.5 捕获所有Exception时不应忽略对RuntimeException的处理
直接捕获所有Exception是常见的集中捕获各类异常的做法,但却最容易忽略对RuntimeException的判断,这可能导致非预期的RuntimeException被意外吞掉。因此,当捕获所有Exception时,请务必考虑是否需要重新抛出RuntimeException。
4.6 try语句嵌套
4.7 慎重抛出异常
4.8 DAO和Manager大多直接抛出异常,AO捕获进行翻译和记录日志
4.9 不允许捕获异常而不做任何操作
4.10 如果使用log.warn记录跟踪调试信息,一定要注意量的问题
4.11 Log.xxxx(‘xxx’, exception),第二个参数才是异常
4.12 在log.debug前使用log.isDebugEnable
4.13 系统调试过程中写的日志,在提交时要去掉
第5章:Class变量
5.1 匿名类包含太多的方法
5.2 覆盖equals方法且没有在其中最终调用super.equals()的类必须同时也覆盖hashCode方法
5.3 类继承java.lang.Object
public
class
User
extends
Object {
}
---->>>>
public
class
User {
}
|
5.4 实现Cloneable接口的类必须覆盖Object.clone方法。
5.5 Class代码中包含其子类
5.6 类的继承层次太深
5.7 类包含太多的构造函数
5.8 类包含太多的属性
5.9 类包含太多的方法
5.10 class没有package声明
5.11 实现Comparator接口的类应该实现Serializable接口
5.12 返回类型为Boolean的方法中不允许显式返回null
5.13 Hessian接口调用让系统不依赖于其他系统
5.14 对于发消息通知用户的过程最好使用异步方式
第6章:函数变量
6.1 返回类型为数组却可能返回null的时候,考虑返回尺寸为0的数组,而非null
这样设计通常可以避免调用者对返回值为null的额外判断开销。
6.2 函数有多个return语句
一个函数有多个return语句,太多的return语句会让人迷惑,同时增加维护难度。
6.3 函数包含太多的参数
函数包含太多的参数后,会给维护和调用造成困难,应该使用一个好的方法进行重构,通常函数的参数应该在5个以下。
6.4 过度复杂的函数
过度复杂的函数,也就是这个函数太长啦,这样的长函数不便于理解代码,所以要考虑对代码进行重构。
6.5 throws语句中不必要的exception
函数签名中包含了exception抛出声明,但是实际的实现代码中并没有这样的异常,这个会容易引发理解错误。
public
void
openFile(String fileName)
throws
IOException, SQLException {
}
---->>>>
public
void
openFile(String fileName)
throws
IOException {
}
|
6.6 equals方法中应判断传入对象是否为null
这是equals方法的基本设计要求。
第7章:变量度量
7.1 在for循环中对循环因子赋值
在for循环中,再次给循环因子(通常为i)赋值,如增加或降低循环因子的值,虽然是有意为之,但是这样的代码相当让人迷惑,某些情况下可能是笔误。
for
(
int
i=
0
;i<
30
;i++) {
.....
i = i +
2
;
}
应该避免这样的代码
|
7.2 给函数参数赋值
将函数的参数作为变量,再次赋值,并对其进行相关操作。虽然大多数都是有意为之,但是这样的代码非常让人迷惑,可能都是笔误造成的。
public
void
findWife(User user) {
user = getWife(user.getId());
......
return
user;
}
---->>>>
public
void
findWife(User user) {
User wife = getWife(user.getId());
......
return
wife;
}
|
7.3 不应用==或!=(而非equals)判断String
==只能判断引用相同,大部分情况下可能并不适用于判断字符串的内容相同,除非参与比较的两个字符串都是常量,或者通过String.intern()方法进入了内部字符串池。
7.4 不允许用==(而非isNaN)判断NaN
按照NaN的语法定义,“没有任何数是等于NaN的”。因此不应用==判断NaN,而要用Float.isNaN或Double.isNaN进行判断。
7.5 不必要的局部变量
报告各种不必要的具备变量,这些多余变量对程序理解没有任何帮助。通常有几种情形:本地变量马上返回的,本地变量赋值给另一个变量而根本没有使用,本地变量和另一变量为相同值等。
User user = findUserByNick(nick);
return
user;
->
return
findUserByNick(nick);
|
7.6 重复使用局部变量
将局部变量进行重用,给其赋一个毫不关联的值,这样的变量很让人迷惑,而且语义混乱,容易引发错误。
String nick = getUserNick(userId);
....
nick = getUserRealName(userId);
....
---->>>>
String nick = getUserNick(userId);
....
String realName = getUserRealName(userId);
....
|
7.7 合理使用常量,尽量避免出现硬编码(写死的)数字、字符串
建议放入统一的参数类里面或放入本类或本方法的顶端
7.8 使用包装类型
基本类型没有办法表达null 的意思,方法签名的入参和domain对象的基本类型参数的自动赋值将导致程序错误
第8章:Java 7
8.1 for循环使用'for each‘代替
List<Rule> rules =
new
ArrayList<Rule>();
for
(
int
i =
0
; i < rules.size(); i++) {
Rule rule = rules.get(i);
...
}
---->>>>
for
(Rule rule : rules) {
...
}
|
8.2 indexOf()可以使用contains()代替
if
(name.indexOf(
"-"
)!=-
1
) {
...
}
---->>>>
if
(name.contains(
"-"
)) {
...
}
|
8.3 不必要的装箱操作
8.4 不必要的拆箱操作
第9章:性能
9.1 String.equals("")
if
(userName.equals(
""
)) {
...
}
---->>>>
if
(userName.length ==
0
)) {
...
}
|
9.2 使用StringBuilder而不是StringBuffer
StringBuffer content =
new
StringBuffer();
---->>>>
StringBuilder content =
new
StringBuilder();
|
9.3 String变量的size()==0可以使用isEmpty()替换
if
(userName.size()==
0
) {
}
---->>>>
if
(userName.isEmpty()) {
}
|
9.4 手动拷贝数组
String[] array1 =
new
String[]{};
String[] array2 =
new
String[]{};
for
(
int
i =
0
; i < array1.length; i++) {
array2[i] = array1[i];
}
---->>>>
System.arraycopy(array1,
0
, array2,
0
, array1.length);
|
9.5 避免在循环中使用“+”拼接字符串。
// This is bad
String s =
""
;
for
(
int
i =
0
; i < field.length; ++i) {
s = s + field[i];
}
// This is better
StringBuffer buf =
new
StringBuffer();
for
(
int
i =
0
; i < field.length; ++i) {
buf.append(field[i]);
}
String s = buf.toString();
|
第10章:安全
10.1 访问系统属性
10.2 调用Runtime.exec()时使用非常量字符串
10.3 不安全的随机数生成规则
10.4 不能相信客户端的校验,服务器端仍要校验
10.5 在单例的类中谨慎使用成员变量
10.6 更新、删除操作,一定要校验操作人是否有对操作数据的操作权限
10.7 保证程序是线程安全的
第11章:资源管理
11.1 I/O资源打开后没有安全关闭
11.2 JDBC资源打开后没有安全关闭
11.3 Socket打开后没有被安全关闭
11.4 资源的使用要及时释放
第12章:线程
12.1 不允许将Calendar用于类的静态成员
12.2 不允许将DateFormat用于类的静态成员
12.3 延迟初始化的类成员应声明为volatile
12.4 不允许在持有锁的时候调用Thread.sleep()
12.5 不允许在Boolean对象上使用synchronized关键字
12.6 wait应置于条件循环中是使用,wait前检查所等待的条件已经满足,并避免意外唤醒的影响
第13章:代码成熟度
13.1 clone()方法出现在非Cloneable类中
13.2 Abstract method call in constructor
public
User() {
abstractMethod1();
this
.name=
"leijuan"
;
}
public
void
abtractMethod1() {
if
(
this
.name.equals(
"leijuan"
)) {
//空指针
}
}
|
13.3 通过对新实例访问静态变量
if
(shop.getType() == shop.B2C_Type) {
...
}
---->>>>
if
(shop.getType() == Shop.B2C_Type) {
...
}
|
13.4 给变量赋null值
13.5 调用printStackTrace()
catch
(SQLException e) {
e.printStackTrace();
}
---->>>>
catch
(SQLException e) {
log.error(
"sql error"
,e);
}
|
13.6 依恋情节
13.7 文件分隔符硬编码
13.8 Overridable method call in constructor
13.9 Serializable类没有包含serialVersionUID属性
13.10 String相等判断应使用'equals()',而不是’==‘
if
(user.getName() == user.getRealName()) {
}
---->>>>
if
(user.getName().equals(user.getRealName()) {
}
|
13.11 无用的赋值操作
13.12 JDBC ResultSet的索引为0
13.13 使用sun package下的类
13.14 equals()和hashCode()不成对出现
第14章:Java Collection
14.1 集合变量的类型为具体类,而不是接口
ArrayList users =
new
ArrayList();
---->>>>
List users =
new
ArrayList();
|
14.2 过度类型强制转换
printUsers((ArrayList)getUsers());
---->>>>
printUsers((List)getUsers());
|
第15章:单元测试
15.1 使用test4j进行单元测试
Test4J原名叫jTester,其特性如下:
单元测试功能
Fluent方式的断言,内置了大部分常用的断言语法,特别是对象反射断言功能尤其强大。
Junit和testNg语法扩展,使用@DataFrom方式扩展junit的数据驱动测试功能;@Group语法让junit支持分组测试;模块嵌入的方式让junit和testng支持功能扩展。
集成jMockit框架,让mock更自由自在。
对象自动填充功能,反射工具。
集成测试工具包
支持Spring集成测试,spring容器可以mock对象,自定义对象无缝集成。
数据库测试支持,使用DataMap对象,Json数据准备数据,或者验证数据,同时支持数据库数据的Fluent断言。
业务驱动测试工具包
支持编写可读的用例,并在用例中嵌入测试用数据,框架自动转换为可执行代码。
支持用例步骤的重复利用,简化用例编写难度。
15.2 service层必须做单元测试,web层可以根据情况确定
15.3 单元测试覆盖率必须在60%以上(第一阶段)
15.4 各单元测试类继承自BaseTester
@Test
@SpringApplicationContext
({
"test/data-source.xml"
,
"test/spring-context.xml"
})
public
class
BaseTester
extends
JTester {
}
|
15.5 单元测试环境参考lifeix-demo/demo-test例子
15.6 单元测试代码集中在单独的子工程中,实现代码不同业务逻辑的代码的高度隔离
第16章:MyBatis
16.1 ORM使用MyBatis完成
16.2 接口命名为 XxxMapper
public
interface
ContextPhotoMapper {
|
16.3 参数传递不要使用Map
16.4 使用注解的方式绑定参数命名
public
List<ContextPhoto> findByPage(
@Param
(
"start"
) Integer start,
@Param
(
"limit"
) Integer limit);
|
16.5 MyBatis的配置文件需要指定sqlSessionFactory
<
bean
id
=
"firsttimes-sqlSessionFactory"
class
=
"org.mybatis.spring.SqlSessionFactoryBean"
>
<
property
name
=
"dataSource"
ref
=
"dataSource"
/>
</
bean
>
<!-- 注册Mapper方式一 -->
<
bean
id
=
"contextPhotoMapper"
class
=
"org.mybatis.spring.mapper.MapperFactoryBean"
>
<
property
name
=
"mapperInterface"
value
=
"com.lifeix.firsttime.module.mapper.ContextPhotoMapper"
/>
<
property
name
=
"sqlSessionFactory"
ref
=
"firsttimes-sqlSessionFactory"
/>
</
bean
>
|
16.6 一个Mapper的java文件和xml文件名称必须相同,分别放到不同目录下去
例如:
src\main\java\com\lifeix\firsttime\module\mapper\ContextPhotoMapper.java
src\main\resources\com\lifeix\firsttime\module\mapper\ContextPhotoMapper.xml
第17章:Velocity
第18章:Cookie
第19章:其它