Java EE编码规范

第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-else操作可以更改为三元操作符(?:),这样更精简。
if (foo()){
   return bar();
} else {
   return foobar();
}
---->>>>
return foo()?bar():foobar();

 

3.2  多余的if判断

某些场合下,if判断可以精简为一个赋值或者return语句。
if (foo()){
   return true ;
} else {
   return false ;
}
---->>>>
return foo();

 

3.3  接口调用是否进行了超时设置

远程的调用(如使用hessian、httpclient)必须设置超时,比如连接超时1秒,读取数据超时2秒


第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语句嵌套

在一个try语句中再嵌套一个try语句,这样的代码让人迷惑,应该考虑try合并。

4.7  慎重抛出异常

慎重抛出异常,其实异常实例化也是影响性能的,不要用异常来代替业务逻辑的错误,能用程序发现的,不要通过异常来处理

4.8  DAO和Manager大多直接抛出异常,AO捕获进行翻译和记录日志

这个是一般约定

4.9  不允许捕获异常而不做任何操作

如果捕获的异常不需要重新抛出,必须记录最原始的异常。比如Web层提示给用户的是友好的出错(异常)说明,但是原始异常要记录

4.10  如果使用log.warn记录跟踪调试信息,一定要注意量的问题

  如果使用log.warn记录跟踪调试信息,一定要注意量的问题,避免把服务器磁盘撑暴,没用了就拿掉

4.11  Log.xxxx(‘xxx’, exception),第二个参数才是异常

  Log.xxxx(‘xxx’, exception),第二个参数才是异常,如果不指定,错误堆栈不会打印出来

4.12  在log.debug前使用log.isDebugEnable

  如果有连续的log.debug或log.debug的参数是字符串的拼接或通过一些计算生成出来,在log.debug前使用log.isDebugEnable

4.13  系统调试过程中写的日志,在提交时要去掉

  系统调试过程中写的日志,在提交时要去掉,或者用if (log.isDebugEnabled()) { log.debug("updateBidAuction:" + bidAuction);}这样的方式

 

第5章:Class变量

5.1  匿名类包含太多的方法

  一个匿名类包含太多的方法,这个不便于理解,你可能需要考虑将其声明为内部类,这样逻辑更清晰。

5.2  覆盖equals方法且没有在其中最终调用super.equals()的类必须同时也覆盖hashCode方法

  hashCode的设计要求必须满足“通过equals判断相等的两个对象,hashCode必须相等”。

5.3  类继承java.lang.Object

  所有的Java类都会继承Object,所有没有必要在添加extends Object啦
public class User extends Object {
}
---->>>>
public class User {
}

 

5.4  实现Cloneable接口的类必须覆盖Object.clone方法。

  实现Cloneable接口的类必须覆盖Object.clone方法,这是JDK的约定。

5.5  Class代码中包含其子类

  当前Class代码中包含对其子类的引用,这样的引用让人迷惑,而且和OO设计冲突。

5.6  类的继承层次太深

  一个类的继承层次太深,这个会给理解带来麻烦,这个时候你可能需要考虑重构。例外:继承第三方开发包的类除外(因为已经无法更改)。

5.7  类包含太多的构造函数

  一个类包含太多的构造函数,可能会导致初始化错误,如果你修改一个构造函数后,没有考虑其他构造函数的情况,就可能导致相关的错误。如果一个类包含太多的构造函数,可以考虑转换为多个子类进行建模。

5.8  类包含太多的属性

  一个类包含太多的属性,那么它要做的事情太多的,你知道的太多啦 :),这个时候应该考虑分割为多个小的类,共同完成逻辑。

5.9  类包含太多的方法

  一个类包含太多的函数,那么它要做的事情太多的,变得臃肿,这个时候应该考虑分割为多个小的类,共同完成逻辑。

5.10 class没有package声明

  class没有package声明,这种class通常让人迷惑,有些框架处理不了这样的class。

5.11 实现Comparator接口的类应该实现Serializable接口

  实现Comparator接口的类当被用作构造有序集合时,只有此类背身可序列化时,构造出的集合才是可序列化的。这是一个很容易被忽略的问题,因此Comparator接口的类应该同时实现Serializable接口,以防止出现上述问题。

5.12 返回类型为Boolean的方法中不允许显式返回null

返回类型为Boolean的方法通常可以通过自动展开机制赋值给boolean基本类型的变量,如果返回null,将产生NullPointerException。

5.13 Hessian接口调用让系统不依赖于其他系统

对于一些不重要的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‘代替

在使用for语句对集合和数值操作时,可以考虑使用Java 5.0的'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()代替

在调用String.indexOf()作为条件判断时,可以考虑使用Java 5.0的String.contains()
if (name.indexOf( "-" )!=- 1 ) {
  ...
}
---->>>>
if (name.contains( "-" )) {
  ...
}

 

8.3 不必要的装箱操作

在Java 5.0中,装箱操作对元类型,如int, boolean,都是自动进行的,所以不需要再将元类型进行对象装换。

8.4 不必要的拆箱操作

在Java 5.0中,拆箱操作对某些对象,如Integer, Boolean,都是自动进行的,所以不需要再对这些对象进行拆箱操作。

 

第9章:性能

9.1 String.equals("")

不少人调用String.equals("")用来判断字符串是否为空,这个会有一些性能问题,可以考虑用String.length() == 0替代。
if (userName.equals( "" )) {
  ...
}
---->>>>
if (userName.length == 0 )) {
  ...
}

 

9.2 使用StringBuilder而不是StringBuffer

任何变量声明为java.lang.StringBuffer可以考虑使用java.lang.StringBuilder,StringBuilder是非线程安全的,所以效率更高一些。
StringBuffer content = new StringBuffer();
---->>>>
StringBuilder content = new StringBuilder();

 

9.3 String变量的size()==0可以使用isEmpty()替换

在判断一个空字符串时,调用size()==0可以考虑使用.isEmpty()替换。
if (userName.size()== 0 ) {
}
---->>>>
if (userName.isEmpty()) {
}

 

9.4 手动拷贝数组

在进行数字内容copy时,可以考虑使用System.arraycopy()
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 避免在循环中使用“+”拼接字符串。

循环中使用“+”拼接字符串将在每一次循环中都把String转换为StringBuffer/StringBuilder,拼接完成后又转换回String,这样可能导致大量不必要的冗余开销。建议显式的使用StringBuffer或StringBuilder通过循环拼接字符串。
// 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()时使用非常量字符串

在调用Runtime.exec()时,如果变量不是常量,那么依据动态变量构建的可执行命令可能包含非法指令,从而导致系统遭受攻击。

10.3 不安全的随机数生成规则

在使用java.lang.Randomjava.lang.math.Random()时,如果考虑完全不重复的可能性,java.secure.SecureRandom是更好的选择,它提供了更安全的随机数生成算法。

10.4 不能相信客户端的校验,服务器端仍要校验

怀疑一切,不留下任何一个机会

10.5 在单例的类中谨慎使用成员变量

在单例的类中谨慎使用成员变量(比如DAO、Manager、Screen、Action、Control),避免线程安全问题和内存泄漏问题

10.6 更新、删除操作,一定要校验操作人是否有对操作数据的操作权限

更新、删除操作,一定要校验操作人是否有对操作数据的操作权限(比如删除店铺分类,分类ID是链接传递的,要校验传递的店铺分类ID是否是属于操作人的)

10.7 保证程序是线程安全的

比如:如确保没有在servlet、screen、action等类存放request-scope的私有变量

 

第11章:资源管理

11.1 I/O资源打开后没有安全关闭

 通常情况下,IO资源会在try区块中打开,在finally区块中关闭,这样资源才能资源才能被有效关闭。如果资源不能有效地关闭,可能会导致资源泄漏,导致无法再次打开、内存问题等。I/O资源主要包括一下: java.io.InputStreamjava.io.OutputStreamjava.io.Readerjava.io.Writer andjava.io.RandomAccessFile 如果你对这些I/O对象进行封装,也需要正确关闭。

11.2 JDBC资源打开后没有安全关闭

  JDBC资源应该在try区块中打开,finally区块中关闭,这样不会出现异常抛出后资源没有被正常关闭,从而导致各种异常。JDBC资源主要包括如下: java.sql.Connection java.sql.Statement java.sql.PreparedStatement java.sql.CallableStatement , and  java.sql.ResultSet .

11.3 Socket打开后没有被安全关闭

  Socket资源应该在try区块中打开,在finally区块中关闭,不然在异常发生时资源会被安全关闭,从而导致各种异常。Socket的资源主要如下: java.net.Socket java.net.DatagramSocket , and  java.net.ServerSocket .

11.4 资源的使用要及时释放

  比如:不断地将用户信息put到一个map,但是从来没有将用户信息从这个map中清除

 

第12章:线程

12.1 不允许将Calendar用于类的静态成员

  Calendar是天生的多线程不安全的类,将其用于类的静态成员可能导致错误的在多线程中访问。更多信息,请参考Sun Bug #6231579 和 #6178997。

12.2 不允许将DateFormat用于类的静态成员

  DateFormat是天生的多线程不安全的类,将其用于类的静态成员可能导致错误的在多线程中访问。更多信息,请参考Sun Bug #6231579 和 #6178997。

12.3 延迟初始化的类成员应声明为volatile

  延迟初始化的类静态成员,如果没有用synchronized的加以保护,则必须以volatile修饰。这是由于编译器和CPU的乱序机制可能导致该成员的并发访问者看到一个尚未完全初始化完成的实例。

12.4 不允许在持有锁的时候调用Thread.sleep()

  在持有锁的时候调用Thread.sleep()很可能导致等待该锁的其它线程被长时间的挂起,从而严重影响程序性能和延展性。

12.5 不允许在Boolean对象上使用synchronized关键字

  由于Boolean对象通常仅以两个全局的常型实例出现,在其上使用synchronized关键字可能导致与其它共用该常型实例的完全不相关的代码形成互斥关系,这往往并不是程序设计者的初衷。

12.6 wait应置于条件循环中是使用,wait前检查所等待的条件已经满足,并避免意外唤醒的影响

  在wait前判断等待的条件是否已满足可以避免在wait之前的notify通知被忽略。(尽管条件判断与wait两步也并不能看作原子操作)

 

第13章:代码成熟度

13.1 clone()方法出现在非Cloneable类中

  clone()方法出现在非Cloneable类中,也就是该类没有实现Cloneable接口,这个通常是程序员的错误,忘记implements Cloneable

13.2 Abstract method call in constructor

  在构造函数中调用abstract方法,因为这个时候对象还没有初始化完成,如果在子类中实现这个abtract方法,会认为对象已经创建完毕,这个时候,可能会出一些异常。
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值

  给变量赋null值,可以触发垃圾收集,但是这样的结构通常会导致NullPointerException,而且主动给变量赋null值,语义不明显。

13.5 调用printStackTrace()

  报告使用 Throwable.printStackTrace() 方法,这个方法通常是临时调试使用,在线上环境中应该去除,而使用日志机制代替。
catch (SQLException e) {
    e.printStackTrace();
}
---->>>>
catch (SQLException e) {
    log.error( "sql error" ,e);
}

 

13.6 依恋情节

  依恋情节是指在一个函数中反复调用另一个class的方法三次以上,我们应当考虑这个功能放在目录的类中是否合适,需要考虑将将部分逻辑转移到被调用类中。例外:调用父类、第三方开发包的类除外。

13.7 文件分隔符硬编码

  在编码中使用斜杠(/)或者反斜杠(\)作为文件路径的分隔符,这样的硬编码可能会导致不能跨平台,应该使用File.separatorChar

13.8 Overridable method call in constructor

  This inspection reports any calls of overridable methods of the current class within a constructor. Methods are overridable if they are not declared  final , static  or  private . Such calls may result in subtle bugs, as the object is not guaranteed to be initialized before the method call occurs.

13.9 Serializable类没有包含serialVersionUID属性

  Serializable 类通常会提供一个 serialVersionUID 属性,不然对类的更改可能会导致无法读取之前序列化版本。

13.10 String相等判断应使用'equals()',而不是’==‘

  字符串相等判断应该使用 ".equals()" ,而不是 ==
if (user.getName() == user.getRealName()) {
}
---->>>>
if (user.getName().equals(user.getRealName()) {
}

 

13.11 无用的赋值操作

  下列情况说明变量赋值属于无用操作的: 1 变量赋值后没有进行任何读取操作 2 变量赋值后,在没有进行读取前,又被赋值

13.12 JDBC ResultSet的索引为0

  在访问 java.sql.ResultSet 的column时,传入0访问第一列。由于历史愿意, java.sql.ResultSet 的列从1开始,而不是0,否则会导致JDBC错误。

13.13 使用sun package下的类

  sun.* 下的开发包在不同的JVM下实现不一样,不具有可移植性,建议不要使用。

13.14 equals()和hashCode()不成对出现

  equals()和hashCode()通常都是成对出现,如果我们只覆盖其中的一个方法,可能会导致潜在错误,如向 Collection .中添加此类对象。

 

 

 

第14章:Java Collection

14.1 集合变量的类型为具体类,而不是接口

  在声明集合变量时,不应该使用具体的类,而且合适的集合接口。
ArrayList users = new ArrayList();
---->>>>
List users = new ArrayList();

 

14.2 过度类型强制转换

  在进行类型强制转换时,不要过度,适当的类型即可。如将一个对象强制转换为 ArrayList ,但是实际上 List 就可以啦。过度强制转会导致 ClassCastException 错误,而且也会给测试带来麻烦。
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层可以根据情况确定

 service层复杂的业务逻辑必须写单元测试

15.3 单元测试覆盖率必须在60%以上(第一阶段)

 单元测试覆盖率越高代码质量越可靠,自动发现问题的能力越强

15.4 各单元测试类继承自BaseTester

  BaseTester继承自JTester,其继承的子类可以再一个IOC容器中完成相关bean的载人、初始化过程,不需要每个单元测试类都启动IOC容器
@Test
@SpringApplicationContext ({ "test/data-source.xml" , "test/spring-context.xml" })
public class BaseTester extends JTester {
}

 

15.5 单元测试环境参考lifeix-demo/demo-test例子

 lifeix-demo/demo-test子工程提供了单元测试环境样例

15.6 单元测试代码集中在单独的子工程中,实现代码不同业务逻辑的代码的高度隔离

 实现单元测试代码的集中管理

 

第16章:MyBatis

16.1 ORM使用MyBatis完成

 MyBatis易于SQL分析、自动化封装对象、面向接口、简易的脚步语法等都是我们选择的理由

16.2 接口命名为 XxxMapper

 延续MyBatis 的编码风格,DAO层的接口以Mapper结尾
public interface ContextPhotoMapper {

 

16.3 参数传递不要使用Map

 Map作为参数传递对象对key值和value都没有明确的约束,重构或者其他开发同学接手时极易出错

16.4 使用注解的方式绑定参数命名

public List<ContextPhoto> findByPage( @Param ( "start" ) Integer start, @Param ( "limit" ) Integer limit);

 

16.5 MyBatis的配置文件需要指定sqlSessionFactory

 在做分库分表,多数据源的时候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章:其它

 


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值