本规范的目的是使本组织能以标准的、规范的方式设计和编码。经过创建编码规范,使每一个开发人员养成良好的编码风格和习惯;并以此造成开发小组编码约定,提升程序的可靠性、可读性、可修改性、可维护性和一致性等,增进团队间的交流,并保证软件产品的质量。java
本规范适用于XXXX有限公司技术部全部软件开发人员,在整个软件开发过程当中必须遵循此规范。程序员
² 本文档为共享文档,不限转载,但请保持本文档的完整性算法
² 您能够修改本文档以符合您或组织、公司等之实际,但请在文档中保持对本文档的引用和说明。shell
² 未经产品开发中心受权,任何我的、组织或单位不得将本文档用于书面发表、转载、摘录等,亦不得用于其余商业行为。数据库
² 本人及本组织不承担任何因使用、参考本文档等而致使的任何可能责任或连带责任。express
² 《Java 编程指南》见RUP(Rational Unified Process)中文版。编程
² 《Java技术手册》(Java in a Nutshell)设计模式
² 《Sun Java语言编码规范》(Java Code Conventions)数组
² 《Effictive Java》安全
² 《Java Pitfalls》
² 《Java Rules》
对于代码,首要要求是它必须正确,可以按照设计预约功能去运行;第二是要求代码必须清晰易懂,使本身和其余的程序员可以很容易地理解代码所执行的功能等。然而,在实际开发中,每一个程序员所写的代码却常常自成一套,不多统一,致使理解困难,影响团队的开发效率及系统的质量等。所以,一份完整并被严格执行的开发规范是很是必须的,特别是对软件公司的开发团队而言。此规范参考自业界标准编程规范而制定。
最根本的原则:
² 代码虽然是给机器运行的,但倒是给人读的!
² 运用常识。当找不到任何规则或指导方针,当规则明显不能适用,当全部的方法都失效的时侯:运用常识并核实这些基本原则。这条规则比其它全部规则都重要。常识是必不可少。
² 当出现该状况时,应当及时收集并提交,以便对本规范进行修改。
代码的组织和风格的基本原则是:便于本身的开发,易于与他人的交流。
因我的习惯和编辑器等能够设置和造成本身的风格,但必须先后一致,并符合本规范的基本要求和原则。
本章所涉及到的内容通常均可在Java集成编辑环境中进行相应设置,也可由Ant等调用checkstyle等来进行自动规整。
子功能块当在其父功能块后缩进。
当功能块过多而致使缩进过深时当将子功能块提取出来作为子函数。
代码中以TAB(4个字符)缩进,在编辑器中请将TAB设置为以空格替代,不然在不一样编辑器或设置下会致使TAB长度不等而影响整个程序代码的格式。例如:
Table1.缩进示例
public void methodName(){ if(some condition){ for(…){ //some sentences }//end for }//end if } |
为便于阅读和理解,单个函数的有效代码长度当尽可能控制在100行之内(不包括注释行),当一个功能模块过大时每每形成阅读困难,所以当使用子函数等将相应功能抽取出来,这也有利于提升代码的重用度。
单个类也不宜过大,当出现此类状况时当将相应功能的代码重构到其余类中,经过组合等方式来调用,建议单个类的长度包括注释行不超过1500行。
尽可能避免使用大类和长方法。
页宽应该设置为150字符。通常不要超过这个宽度, 这会致使在某些机器中没法以一屏来完整显示, 但这一设置也能够灵活调整。在任何状况下, 超长的语句应该在一个逗号后或一个操做符前折行。一条语句折行后, 应该比原来的语句再缩进一个TAB或4个空格,以便于阅读。
类、方法及功能块间等应以空行相隔,以增长可读性,但不得有无规则的大片空行。操做符两端应当各空一个字符以增长可读性。
相应独立的功能模块之间可以使用注释行间隔,并标明相应内容,具体参看附录的代码示例。
关系密切的行应对齐,对齐包括类型、修饰、名称、参数等各部分对齐。
连续赋值时当对齐操做符。
当方法参数过多时当在每一个参数后(逗号后)换行并对齐。
当控制或循环中的条件比较长时当换行(操做符前)、对齐并注释各条件。
变量定义最好经过添加空格造成对齐,同一类型的变量应放在一块儿。以下例所示:
Table2.对齐示例
//变量对齐----------------------------------------------- int count = 100; int length = 0; String strUserName = null; Integer[] porductCode = new Integer(2); //产品编码数组 //参数对齐---------------------------------------------- public Connection getConnection(String url, String userName, String password) throws SQLException,IOException{ } //换行对齐---------------------------------------------- public final static String SQL_SELECT_PRODUCT = “SELECT * “ + “ FROM TProduct WHERE Prod_ID = ” + prodID; //条件对齐---------------------------------------------- if( Condition1 //当条件一 && Condition2 //而且条件二 || Condition3){ //或者条件三 } for(int i = 0; i < productCount.length; //循环终止条件 i++){ } |
7.
² 注释应该增长代码的清晰度。代码注释的目的是要使代码更易于被其余开发人员等理解。
² 若是你的程序不值得注释,那么它极可能也不值得运行。
² 避免使用装饰性内容。
² 保持注释的简洁。
² 注释信息不只要包括代码的功能,还应给出缘由。
² 不要为注释而注释。
² 除变量定义等较短语句的注释可用行尾注释外,其余注释当避免使用行尾注释。
对类、方法、变量等的注释须要符合JavaDoc规范,对每一个类、方法都应详细说明其功能、条件、参数等,并使用良好的HTML标记格式化注释,以使生成的JavaDoc易阅读和理解。
在每一个文件的头部都应该包含该文件的功能、做用、做者、版权以及建立、修改记录等。并在其中使用SVN标记自动跟踪版本变化及修改记录等信息。注意是/**/注释而不是/***/JavaDoc注释。
Table3 文件(File)注释模板
/* * Copyright (C) 2006-${year} 普信恒业科技发展(北京)有限公司. * 本系统是商用软件,未经受权擅自复制或传播本程序的部分或所有将是非法的. ============================================================ * FileName: ${file_name} * Created: [${date} ${time}] by ${user} * $$Id$$ * $$Revision$$ * $$Author$$ * $$Date$$ ============================================================ * ProjectName: ${project_name} * Description: ==========================================================*/ |
在类、接口定义以前当对其进行注释,包括类、接口的目的、做用、功能、继承于何种父类,实现的接口、实现的算法、使用方法、示例程序等,在做者和版本域中使用SVN标记自动跟踪版本变化等,具体参看注释模板。
Table4 类(Types)注释模板
/** * Description: * @author ${user} * @version 1.0 * <pre> * Modification History: * Date Author Version Description ------------------------------------------------------------------ * ${date} ${user} 1.0 1.0 Version * </pre> */ |
依据标准JavaDoc规范对方法进行注释,以明确该方法功能、做用、各参数含义以及返回值等。复杂的算法用/**/在方法内注解出。
参数注释时当注明其取值范围等
返回值当注释出失败、错误、异常时的返回状况。
异常当注释出什么状况、何时、什么条件下会引起什么样的异常。
Table5 方法(Methods)注释模板
/** * Description: * @param * @return ${return_type} * @throws * @Author ${user} * Create Date: ${date} ${time} */ |
应对重要的变量加以注释,以说明其含义等。
应对不易理解的分支条件表达式加注释。不易理解的循环,应说明出口条件。过长的方法实现,应将其语句按实现的功能分段加以归纳性说明。
对于异常处理当注明正常状况及异常状况或者条件,并说明当异常发生时程序当如何处理。
Table6 注释参考表
项目 |
注释内容 |
参数 |
参数类型 参数用来作什么 约束或前提条件 示例 |
字段/属性 |
字段描述 注释全部使用的不变量 示例 并行事件 可见性决策 |
类 |
类的目的 已知的问题 类的开发/维护历史、版本 注释出采用的不变量 并行策略 |
编译单元 (文件) |
每个类/类内定义的接口,含简单的说明 文件名和/或标识信息 修改/维护记录 版权信息 |
获取成员函数 |
若可能,说明为何使用滞后初始化 |
接口 |
目的 它应如何被使用以及如何不被使用 |
局部变量 |
用处/目的 |
成员函数注释 |
成员函数作什么以及它为何作这个 哪些参数必须传递给一个成员函数 成员函数返回什么 已知的问题 任何由某个成员函数抛出的异常 可见性决策 成员函数是如何改变对象的 包含任何修改代码的历史 如何在适当状况下调用成员函数的例子 适用的前提条件和后置条件 |
成员函数内部注释 |
控制结构 代码作了些什么以及为何这样作 局部变量 |
难或复杂的代码处理顺序 |
|
包 |
包的功能和用途 |
8.
规范的命名能使程序更易阅读,从而更易于理解。它们也能够提供一些标识功能方面的信息,有助于更好的理解代码和应用。
1) 使用能够准确说明变量/字段/类/接口/包等的完整的英文描述符。例如,采用相似 firstName,listAllUsers 或 CorporateCustomer 这样的名字,严禁使用汉语拼音及不相关单词命名,虽然Java支持Unicode命名,但本规范规定对包、类、接口、方法、变量、字段等不得使用汉字等进行命名。
2) 采用该领域的术语。若是用户称他们的“客户” (clients) 为“顾客” (customers),那么就采用术语 Customer 来命名这个类,而不用 Client。
3) 采用大小写混合,提升名字的可读性。通常应该采用小写字母,可是类和接口的名字的首字母,以及任何中间单词的首字母应该大写。包名所有小写。
4) 尽可能少用缩写,但若是必定要使用,当使用公共缩写和习惯缩写等,如实现(implement)可缩写成impl,经理(manager)可缩写成mgr等,具体参看附录之《经常使用缩写简表》,严禁滥用缩写。
5) 避免使用长名字(最好不超过 25 个字母)。
6) 避免使用类似或者仅在大小写上有区别的名字。
7) 避免使用数字,但可用2代替to,用4代替for等,如:go2Jsp。
1) 文件名当与其类严格相同,全部单词首字母大写。
2) 包名通常以项目或模块名命名,少用缩写和长名,一概小写。
3) 基本包:org.skyinn,全部包、文件都从属于此包。
4) 包名按以下规则组成:
[基本包].[项目名].[模块名].[子模块名]...
如:
org.skyinn.quasar
org.skyinn.skyhome.dao.hibernate
5) 不得将类直接定义在基本包下,全部项目中的类、接口等都当定义在各自的项目和模块包中。
6) 自主开发程序的命名规则为:com.creditease.XXXX
全部单词首字母大写。使用能确切反应该类、接口含义、功能等的词。通常采用名词。
接口文件名前添加大写字母“I”,其后使用able。
采用完整的英文大写单词,在词与词之间用下划线链接,如:DEFAULT_VALUE。
对不易清楚识别出该变量类型的变量应使用类型缩写做其前缀,如字符串使用strXXX,boolean使用isXXX,hasXXX等等。除第一各个单词外其他单词首字母大写。
对私有实例变量可以使用_前缀,但在其存取方法中则应该将其前缀去掉。
应采用完整的英文描述符命名组件(接口部件),遵循匈牙利命名法则。
如:btnOK,lblName。
一个集合,例如数组和矢量,应采用复数命名来表示队列中存放的对象类型。命名应采用完整的英文描述符,名字中全部非开头的单词的第一个字母应大写,适当使用集合缩写前缀。如:
Vector vProducts = new Vector(); //产品向量
Array aryUsers = new Array(); //用户列表
咱们在程序里常常会用到一些量,它是有特定的含义的。例如,如今咱们写一个薪金统计程序,公司员工有50人,咱们在程序里就会用50这个数去进行各类各样的运算。在这里,50就是"神秘的数"。当别的程序员在程序里看到50这个数,将很难知道它的含义,形成理解上的困难。
在程序里出现"神秘的数"会下降程序的可读性、可维护性和可扩展性,故规定不得出现此类"神秘的数"。避免的方法是把神秘的数定义为一个常量。注意这个常量的命名应该能表达该数的意义,而且应该所有大写,以与对应于变量的标识符区别开来。例如上面50这个数,咱们能够定义为一个名为NUM_OF_EMPLOYEES的常量来代替。这样,别的程序员在读程序的时候就能够容易理解了。
命名时应使用复数来表示它们表明多值。如:orderItems。
方法的命名应采用完整的英文描述符,大小写混合使用:全部中间单词的第一个字母大写。方法名称的第一个单词经常采用一个有强烈动做色彩的动词。
取值类使用get前缀,设值类使用set前缀,判断类使用is(has)前缀。
例: getName()
setSarry()
isLogon()
方法参数建议顺序:(被操做者,操做内容,操做标志,其余⋯)
例:public void replace(String sourceStr, //源字串
String oldStr, //被替换字串
String newStr){ //替换为字串
异常类名由表示该异常类型的单词和Exception组成,如ActionException。
异常实例通常使用e、ex等,在多个异常时使用该异常名或简写加E,Ex等组成,如:
SQLEx
ActionEx
Table7 命名约定表
操做项 |
命名约定 |
示例 |
|
参数 |
使用传递值/对象的完整的英文描述符。 |
userID |
|
字段/属性 |
字段采用完整的英文描述,第一个字母小写,任何中间单词的首字母大写。 |
firstName |
|
布尔型的获取成员函数 |
全部的布尔型获取函数必须用单词 is(has)作前缀。 |
isString() hasMoney() |
|
类 |
采用完整的英文描述符,全部单词的第一个字母大写。 |
Customer |
|
编译单元文件 |
使用类或接口的名字,或者若是文件中除了主类以外还有多个类时,加上前缀 java 来讲明它是一个源码文件。 |
Customer.java |
|
部件/组件 |
使用完整的英文描述来讲明组件的用途,将组件类型使用匈牙利命名法则做其前缀。 |
btnOK,cboTypeList |
|
构造函数 |
使用类名 |
Customer() |
|
析构函数 |
Java 没有析构函数,但一个对象在垃圾收集时,调用成员函数 finalize() 。 |
finalize() |
|
异常类名 |
由表示该异常类型等的单词和Exception组成 |
SQLException ActionException |
|
异常实例名 |
一般采用字母 e 、ex表示异常。 多个异常时使用异常名或其简写加E、Ex等构成 |
e SQLEx |
|
静态常量字段(常量) |
所有采用大写字母,单词之间用下划线分隔。采用静态常量获取成员函数。 |
DEFAULT_NAME |
|
获取成员函数 |
被访问字段名的前面加上前缀 get。 |
getUserName() |
|
接口 |
采用完整的英文描述符说明接口封装,全部单词的第一个字母大写。使用I前缀,其后使用able,或者 er等后缀,但这不是必需的 |
IRunnable IPrompter |
|
局部变量 |
采用完整的英文描述符,第一个字母小写,但不要隐藏已有字段。例如,若是有一个字段叫 firstName,不要让一个局部变量叫 firstName。 |
strName,totalMoney |
|
循环计数器 |
一般采用字母 i,j,k 或者 counter,index |
i,j,k,count,index |
|
包 |
采用完整的英文描述符,全部单词都小写,多个单词如下划线相连。全部包都属于 org.skyinn |
org.skyinn.quasar org.skyinn.skyhome.dao |
|
成员函数 |
采用完整的英文描述说明成员函数功能,第一个单词尽量采用一个生动的动词,除第一个单词外,每一个单词第一个字母小写。 |
openFile() addUser() |
|
设置成员函数 |
被访问字段名的前面加上前缀 set。 |
setName() setPower() |
9.
声明的基本原则是遵照Java语言规范,并听从习惯用法。
在导入包时当彻底限制代码所使用的类的名字,尽可能少用通配符的方式,但导入一些通用包,或用到一个包下大部分类时,则但是使用通配符方式,如:
import org.skyinn.quasar.services.Service;
import java.util.*;
同一包中的类导入时当声明在一块儿,可由编辑器自动完成此功能。
重要的包当添加注释。
类、接口定义语法规范:
[可见性][('abstract'|'final')] [Class|Interface] class_name
[('extends'|'implements')][父类或接口名]{
}
如:
public class LoginAction extends BaseAction implemnets ActionListener {
}
良好的程序设计应该尽量减少类与类之间耦合,所遵循的经验法则是:尽可能限制成员函数的可见性。若是成员函数不必公有 (public),就定义为保护 (protected);不必保护 (protected),就定义为私有 (private)。
方法定义语法规范:
[可见性][('abstract'|'final')] ['synchronized'][返回值类型] method_name(参数列表)[('throws')][异常列表]{
// 功能模块
}
如:
public List listAllUsers() throws DAOException{}
如有toString(),equals(),hashCode(),colone()等重载自Object的方法,应将其放在类的最后。
声明顺序:
构造方法
静态公共方法
静态私有方法
受保护方法
私有方法
继承自Object的方法
字段定义语法规范:
[(‘public’|’private’|’protected’)]
[(‘final’|’volatile’)][‘static’][‘transient’]
data_type field_name [ ‘=’ expression] ‘;’
若没有足够理由,不要把实例或类变量声明为公有。公共和保护的可见性应当尽可能避免,全部的字段都建议置为私有,由获取和设置成员函数(Getter、Setter)访问。
不容许“隐藏”字段,即给局部变量所取的名字,不可与另外一个更大范围内定义的字段的名字相同(或类似)。例如,若是把一个字段叫作 firstName ,就不要再生成一个局部变量叫作 firstName,或者任何易混肴的名字,如 fistName。
数组声明时当将"[]"跟再类型后,而不时字段名后,如:
Integer[] ai = new Integer (2); //一个整数对象数组,用于...
Integer aj[] = new Integer (3); //不要用这种数组声明方式
一行代码只声明一个变量,仅将一个变量用于一件事。
声明顺序:
常量
类变量
实例变量
公有字段
受保护字段
私有字段
能够将私有变量声明在类或接口的最后。
注意受保护变量、私有变量、“包”变量间的区别。
Table8
//常量--------------------------------------------------- public final static double PI = 3.141592653589793; // -------------------------------------------------类变量 protected static String key = “Love”; // -----------------------------------------------实例变量 //共有字段----------------------------------------------- public String userName = “Tom”; //受保护字段--------------------------------------------- protected float price = 0.0; //友元字段----------------------------------------------- Vector vPorducts = null; //私有字段----------------------------------------------- private int count; |
|
//构造方法----------------------------------------------- public Constructor(){ } //公共方法----------------------------------------------- public String getUserName(){ } //友元方法----------------------------------------------- void createProduct(){ } //受保护方法--------------------------------------------- protected void convert(){ } //私有方法----------------------------------------------- private void init(){ } //重载Object方法---------------------------------------- public String toString(){ } //main方法----------------------------------------------- public static void main(String[] args){ } |
|
10.
类的划分粒度,不可太大,形成过于庞大的单个类,也不可太细,从而使类的继承太深。通常而言,一个类只作一件事;另外一个原则是根据每一个类的职责进行划分,好比用User来存放用户信息,而用UserDAO来对用户信息进行数据访问操做(好比存取数据库),用UserBroker来封装用户信息的业务操做等等。
多使用设计模式,随时重构。
多个类中使用相同方法时将其方法提到一个接口中或使用抽象类,尽可能提升重用度。
将不但愿再被继承的类声明成final,例如某些实用类,但不要滥用final,不然会对系统的可扩展性形成影响。
将不但愿被实例化的类的缺省构造方法声明成private。
通常而言:接口定义行为,而抽象类定义属性和公有行为,注意二者间的取舍,在设计中,可由接口定义公用的行为,由一个抽象类来实现其部分或所有方法,以给子类提供统一的行为定义,可参考Java集合等实现。
多使用接口,尽可能作到面向接口的设计,以提升系统的可扩展性。
尽可能使用组合来代替继承,一则可使类的层次不至于过深,并且会使类与类,包与包之间的耦合度更小,更具可扩展性。
当须要使用多个构造函数建立类时,建议使用静态工厂方法替代这些构造方法(参考《Effictive Java》 Item1),例如:
public class User{
public User(){
super();
//do somethings to create user instance
}
public static User getInstance(String name,String password){
User u = new User();
u.setName(name);
u.setPassword(password);
return u;
}
参看String,Boolean的实现等:String.valueOf(Long l),Boolean.valueOf(String)...
每一个类都应该重载toString()方法,以使该类能输出必要和有用的信息等。
/** * @see java.lang.Object#toString() */ public String toString() { final StringBuffer sb = new StringBuffer("Actor:["); sb.append("ID = ").append(_id) .append(",Name = ").append(_name) .append(']'); return sb.toString(); } |
若一个类须要重载equals()方法,则必须同时重载hashCode()方法实现方式参考《Effictive Java》Item7,Item8
/** * @see java.lang.Object#equals(java.lang.Object) */ public boolean equals (Object obj) { //空值 if( null == obj){ return false; } //引用相等 if (obj == this) { return true; } //判断是否为当前类实例 if (!(obj instanceof Actor)) { return false; } //若ID相等则认为该对象相等 return this._id == ((Actor) obj).getId (); } /** * @see java.lang.Object#hashCode() */ public int hashCode () { int result = 17;//init result //String 对象hashCode result = (37 * result) + _name.hashCode (); //数值 result = (37 * result) + (int) (_id ^ (_id >>> 32)); //String 对象hashCode result = (37 * result) + _description.hashCode (); return result; } |
这些方法的重载实现也可参考以下实现(使用Jakarta commons-lang):
单例类使用以下方式声明,并将其缺省构造方法声明成private:
public class Singleton{
private static Singleton instance = new Singleton();
// 私有缺省构造方法,避免被其余类实例化
private Singleton(){
//do something
}
public static Singleton getInstance(){
if(null == instance){
instance = new Singleton;
}
return instance;
}
}//EOC Singleton
单例类若须要实现序列化,则必须提供readResolve()方法,以使反序列化出来的类仍然是惟一的实例,参见《Effictive Java》 Item57。
11.
一个方法只完成一项功能,在定义系统的公用接口方法外的方法应尽量的缩小其可见性。
避免用一个类是实例去访问其静态变量和方法。
避免在一个较长的方法里提供多个出口:
//不要使用这钟方式,当处理程序段很长时将很难找到出口点 if(condition){ return A; }else{ return B; } //建议使用以下方式 String result = null; if(condition){ result = A; }else{ result = B; } return result; |
避免过多的参数列表,尽可能控制在5个之内,若须要传递多个参数时,当使用一个容纳这些参数的对象进行传递,以提升程序的可读性和可扩展性。
参数类型和返回值尽可能接口化,以屏蔽具体的实现细节,提升系统的可扩展性,例如:
public void joinGroup(List userList){}
public List listAllUsers(){}
12.
表达式和语句当清晰、简洁,易于阅读和理解,避免使用晦涩难懂的语句。
每行至多包含一条执行语句,过长当换行。
避免在构造方法中执行大量耗时的初始化工做,应当将这中工做延迟到被使用时再建立相应资源,若是不可避免,则当使用对象池和Cache等技术提升系统性能。
避免在一个语句中给多个变量赋相同的值。它很难读懂。
不要使用内嵌(embedded)赋值运算符试图提升运行时的效率,这是编译器的工做。
尽可能在声明局部变量的同时初始化。惟一不这么作的理由是变量的初始值依赖于某些先前发生的计算。
通常而言,在含有多种运算符的表达式中使用圆括号来避免运算符优先级问题,是个好方法。即便运算符的优先级对你而言可能很清楚,但对其余人未必如此。你不能假设别的程序员和你同样清楚运算符的优先级。
不要为了表现编程技巧而过度使用技巧,简单就好。
判断中若有常量,则应将常量置与判断式的左侧。如:
if ( true == isAdmin())...
if ( null == user)...
尽可能不使用三目条件判断。
全部if语句必须用{}包括起来,即使是只有一句:
if (true){ //do something...... } if (true) i = 0; //不要使用这种 |
当有多个else分句时当分别注明其条件,注意缩进并对齐,如:
//先判断i 是否等于1 if (i == 1){//if_1 //..... }//而后判断i == 2 else if (i == 2){ //i == 2说明。。。。。。 j = i; }//若是都不是(i > 2 || i < 1) else{ //说明出错了 //.... }//end if_1 |
过多的else分句请将其转成switch语句或使用子函数。
每当一个case顺着往下执行时(由于没有break语句),一般应在break语句的位置添加注释。如:
switch (condition) { case ABC: //statements; //继续下一个CASE case DEF: //statements; break; case XYZ: //statements; break; default: //statements; break; }//end switch |
循环中必须有终止循环的条件或语句,避免死循环。
当在for语句的初始化或更新子句中使用逗号时,避免因使用三个以上变量,而致使复杂度提升。若须要,能够在for循环以前(为初始化子句)或for循环末尾(为更新子句)使用单独的语句。
由于循环条件在每次循环中多会执行一次,故尽可能避免在其中调用耗时或费资源的操做,比较一下两种循环的差别:
//不推荐方式____________________________________________ while(index < products.getCount()){ //每此都会执行一次getCount()方法, //若此方法耗时则会影响执行效率 //并且可能带来同步问题,如有同步需求,请使用同步块或同步方法 } //推荐方式______________________________________________ //将操做结构保存在临时变量里,减小方法调用次数 final int count = products.getCount(); while(index < count){ } |
13.
一般的思想是只对错误采用异常处理:逻辑和编程错误,设置错误,被破坏的数据,资源耗尽,等等。
一般的法则是系统在正常状态下以及无重载和硬件失效状态下,不该产生任何异常。
最小化从一个给定的抽象类中导出的异常的个数。
对于常常发生的可预计事件不要采用异常。
不要使用异常实现控制结构。
确保状态码有一个正确值。
在本地进行安全性检查,而不是让用户去作。
如有finally子句,则不要在try块中直接返回,亦不要在finally中直接返回。
已检查异常必须捕捉并作相应处理,不能将已检查异常抛到系统以外去处理。
对可预见的运行时异常当进行捕捉并处理,好比空指针等。一般,对空指针的判断不是使用捕捉NullPointException的方式,而是在调用该对象以前使用判断语句进行直接判断,如:
//若不对list是否为null进行检查,则在其为null时会抛出空指针异常
if(null != list && 0 < list.size()){
for(int i = 0; i < list.size(); i++){
}
}
建议使用运行时异常(RuntimeException)代替已检查异常(CheckedException),请参考网络资源以对此两种异常作更深刻理解。
捕捉异常是为了处理它,不要捕捉了却什么都不处理而抛弃之,最低限度当向控制台输出当前异常,若是你不想处理它,请将该异常抛给它的调用者,建议对每一个捕捉到的异常都调用printStackTrace()输出异常信息,避免因异常的湮没。
多个异常应分别捕捉并处理,避免使用一个单一的catch来处理。如:
try { //do something }catch(IllegalStateException IllEx){ IllEx.printStackTrace(); //deal with IllEx }catch(SQLException SQLEx){ SQLEx.printStackTrace(); throw SQLEx; //抛给调用者处理 }finally{ //释放资源 } |
14.
测试不经过的代码不得提交到SVN库或者发布。
不得隐瞒、忽略、绕过任何Bug,有Bug不必定是你的错,但有了Bug不做为就是你的不对了。
多作测试,测试要彻底,尽可能将各类可能状况都测试经过,尽可能将可能的Bug在开发中捕捉并处理掉。
测试要保证可再测试性。
测试应当对数据库等资源不留或少留痕迹,例如,当测试添加一个用户时,在其成功后当及时从数据库中删除该记录,以免脏数据的产生(由此衍生的一个经验是将添加、获取、删除一块儿测试)。
对关键功能必须测试并经过,对辅助功能及很是简单之功能可不作测试。
在Java应用中,单元测试使用Junit及其衍生工具。
在TestCase的setUp()中初始化应用,在tearDown()中释放资源。可由一个基础TestCase完成这些任务,其余TestCase继承之。
由持续集成工具来完成代码编译与自动部署,参见《持续集成规程》。
当系统出现Bug时当由该Bug的负责人(代码负责人)尽快修改之。
Bug的处理根据其优先级高低和级别高低前后处理。
Bug级别和优先级别参见《软件测试规程》。
禁止隐瞒Bug。
15.
性能的提高并非一蹴而就的,而是由良好的编程积累的,虽然任何良好的习惯和经验所提高的性能都十分有限,甚至微乎其微,但良好的系统性能倒是由这些习惯等积累而成,不积细流,无以成江海!
不要使用以下String初始化方法:
String str = new String(“abcdef”);
这将产生两个对象,应当直接赋值:
String str = “abcdef”;
在处理可变 String 的时候要尽可能使用 StringBuffer 类,StringBuffer 类是构成 String 类的基础。String 类将 StringBuffer 类封装了起来,(以花费更多时间为代价)为开发人员提供了一个安全的接口。当咱们在构造字符串的时候,咱们应该用 StringBuffer 来实现大部分的工做,当工做完成后将 StringBuffer 对象再转换为须要的 String 对象。好比:若是有一个字符串必须不断地在其后添加许多字符来完成构造,那么咱们应该使用 StringBuffer 对象及其 append() 方法。若是咱们用 String 对象代替 StringBuffer 对象的话,将会花费许多没必要要的建立和释放对象的 CPU 时间。
避免使用Vector和HashTable等旧的集合实现,这些实现的存在仅是为了与旧的系统兼容,并且因为这些实现是同步的,故而在大量操做时会带来没必要要的性能损失。在新的系统设计中不当出现这些实现,使用ArrayList代替Vector,使用HashMap代替HashTable。
若倒是须要使用同步集合类,当使用以下方式得到同步集合实例:
Map map = Collections.synchronizedMap(new HashMap());
因为数组、ArrayList与Vector之间的性能差别巨大(具体参见《Java fitball》),故在能使用数组时不要使用ArrayList,尽可能避免使用Vector。
避免在循环中频繁构建和释放对象。
再也不使用的对象应及时销毁。
如无必要,不要序列化对象。
在不须要同步操做时避免使用同步操做类,如能使用ArrayList时不要使用Vector。
尽可能少用同步方法,避免使用太多的 synchronized 关键字。
尽可能将同步最小化,即将同步做用到最须要的地方,避免大块的同步块或方法等。
将参数或方法声明成final可提升程序响应效率,故此:
注意绝对不要仅由于性能而将类、方法等声明成final,声明成final的类、方法必定要确信再也不被继承或重载!
不须要从新赋值的变量(包括类变量、实例变量、局部变量)声明成final
全部方法参数声明成final
私有(private)方法不须要声明成final,若方法肯定不会被继承,则声明成final。
不要过度依赖JVM的垃圾收集机制,由于你没法预测和知道JVM在何时运行GC。
尽量早的释放资源,再也不使用的资源请当即释放。
可能有异常的操做时必须在try的finally块中释放资源,如数据库链接、IO操做等:
Connection conn = null; try{ //do something }catch(Exception e){ //异常捕捉和处理 e.printStackTrack(); }finally{ //判断conn等是否为null if(null != conn){ conn.close(); } }//end try...catch...finally |
16.
主键类型:
字符串/整型值。推荐使用字符型作主键,如有性能要求,容许使用数值类型
生成机制:
字符串:生成UUID,或者GUID
整型值:使用数据库序列
生成方法:
根据公司数据库系统的实际状况,并遵循复杂度最小化原则,分别就hibernate及iBatis两个持久层框架的主键生成策略进行限定:
1. hibernate
要求使用如下主键生成策略的任意一种:
(1)sequence(序列):用于为整数类型的主键生成
数据库中的语法以下:
Oracle:create sequence seq_name increment by 1 start with 1;
须要主键值时能够调用seq_name.nextval或者seq_name.curval获得,数据库会帮助咱们维护这个sequence序列,保证每次取到的值惟一,示例以下:
insert into tbl_name(id, name) values(seq_name.nextval, ‘Jimliu’);
<id name="id" column="id" type="long">
<generator class="sequence">
<param name="sequence">seq_name</param>
</generator>
</id>
若是咱们没有指定sequence参数,则Hibernate会访问一个默认的sequence,是hibernate_sequence,咱们也须要在数据库中创建这个sequence。
此外,sequence还能够有另一个参数是paramters,能够查看Hibernate的API了解它的用法,见org.hibernate.id.SequenceGenerator。
调用数据库的sequence来生成主键,要设定序列名,否则hibernate没法找到:
<param name="sequence">NAME_SEQ</param>
(2)uuid.hex:用于为字符串类型的主键生成
使用一个128-bit的UUID算法生成字符串类型的标识符,UUID被编码成一个32位16进制数字的字符串。UUID包含:IP地址、JVM启动时间、系统时间(精确到1/4秒)和一个计数器值(JVM中惟一) hibernate会算出一个128位的惟一值插入。
配置示例以下:
<id name="id" column="id">
<generator class="uuid.hex" />
</id>
2. iBatis
要求使用如下主键生成策略:
(1)sequence(序列):用于为整数类型的主键生成
因为公司项目数据库多数使用oracle,可以使ibatis中对oracle数据表的主键自增加的使用方法。
假设有用户表User,该表主键为userid,当咱们但愿该字段可以自动增加而不须要人工插值的状况下,具体步骤以下:
先创建序列 Create sequence seq_user_userid minvalue 1 maxvalue 999999999 increment by 1 start with 1;
在须要对该表插入新记录的时候,可以使用序列seq_user_userid的nextval来代替主键字段的插入,如咱们能够在相应的xml配置文件中写:
<insert id=”insert_user” parameterClass=”user”>
<selectKey resultClass=”int ” type=”pre” keyProperty=”userid”>
Select seq_user_userid.nextval as value from dual //获取序列的最新值,并赋给userid
</selectKey>
Insert into user(userid,name,age) values (#userid#,#name#,#age#) //将userid表明的序列值插入新记录的主键字段
</insert>
在你的业务逻辑Dao中,具体方法调用insert_user项执行数据库操做便可。
(2)UUID:用于为字符串类型的主键生成
java.util.UUID.randomUUID().toString().replaceAll("-", "").toUpperCase()
生成结果:C8F6E30AE0994A16A87C4BFB7FC92F58
3. ORACLE中,要求使用如下主键生成策略
(1) 数值型主键生成方法:
略,同序列的使用。
(2) 字符型主键生成方法:
select sys_guid() from dual ,结果示例:47962A3B0FD34B5F800D4D0CEC8D7368