J2ee项目一般是大型项目,大型项目都需要团队协作开发。团队协作开发的成功需要一致的开发规范。对软件进行后续的维护成本远远要大于它最初的开发成本,保证开发的系统是可持续工作易于维护就至关重要了。可靠的OO设计原理实践及一个良好的JAVA编程规范在整个J2EE项目生命周期中变得十分重要。在团队开发中,应该保持所有的代码上都乎合- 1 -一个相同的标准,并且代码在风格上应该保持一致。
JAVA编程标准为什么会显得这么重要呢?我们经常会发生这样的情况:太注重于j2ee的某方面的技术细节,而忽略了良好的程序设计和编程习惯。太多的J2EE项目的失败就是因为在项目中含有太多草率的代码,使得整个系统变得脆弱、不可维护,最终只得放弃整个项目。
关于编程规范,它是一个巨大的领域。在这里不作详细的阐述。这里只列出了在实际J2EE开发中十分重要的问题进行讨论,同时对这些问题有不同的见解和解决方法。一切都应该以实践为主。
一、不要去发明自己的标准
几乎每一门编程语言都有自己的一套编程标准,但是千万不要去发明自己的编程标准。
Java是一门简单的语言,我们应该以sun的java编程标准作为准则。在它的基础上进行扩充。比如C++语言使用的匈牙利命名法,并不适合用于在JAVA。也千万不要在一种开发语言中使用另外一种开发语言的编程标准。
二、java类中代码的组织结构
1.按照方法的功能,而不是按照方法的可访问性来组织方法。
比如:不是把所有的public方法放在private方法的前面,而是把一个private方法放成使用它的那些public方法的附近。
2.类的组织约定
◆静态变量和方法
◆成员变量
◆构造函数
◆接口的实现方法,连同支持它们的私用方法
◆该类独有的公有方法(不包括已实现接口的公用方法)
◆抽像保护方法
◆提供给子类使用的保护方法
◆与前面任何一组都无关的实现方法
三、按职责进行分类,做你该做的事情!
每一个类应该具有一个明确的职责,不要把它相干和不相干的代码都填进来。那些不相干的代码应该重构为另一个单独的类或者是助手类。如果一个不同概念级别上的代码对相同的对象重复调用,那么它可能需提升为一个父类。而其它对象通过继承这个父类。
通过把相干的重复代码提取到父类和把不相干的代码重构为一个助手类是解决代码重复的常用方法。这样可以阻止一个类的爆炸式增长,变成一个臃肿的类。一个类,包括Javadoc和内部注释,如果超过了500行代码,它都是需要重构的候选者。因为它可能已经承担了太多的职责。可以运用策略模式来用一个接口把原类与助手类分开,实现助手类在不同的情况可以有不同的实现。来提高系统的灵活性。
每一个方法都应该具有一个单一而清晰明确的职责。否则这个方法应该进行重构。同时这也可以防止方法变得很长。
1public String addCounter(CounterDTO counter) throws BusinessException ...{
2String buffer ="00000000";
3//读取seqence
4String sequenceStr= (String)
this.getSqlMapClientTemplate().queryForObject(SqlID.GET_COUNTER_PK,null);
5int seqLength ;
6seqLength = sequenceStr.length();
7String counterNo ="";
8String regionCode = counter.getRegionCode();
9if(regionCode.length() > 2) ...{
10regionCode = regionCode.substring(0,2);
11}
12counterNo = regionCode + buffer.substring(0,buffer.length() - seqLength)+sequenceStr;
13counter.setCounterNo(counterNo);//设置主键
14
15sqlMap.insert(SqlID.ADD_COUNTER, counter);//插入柜面数据
16return pk;
17
18}
19
|
上面的方法是新增一个柜面信息,它有三个步骤:
1.读取指定的sequence,获得一个唯一柜面流水号。
2.根据柜面的机构代码和流水号产生一个唯一8位的柜面号,柜面号由两位的机构代码+6位的流水号组成。流水号不足6位,在前面以0补充。
3.设置柜面信息的柜面号为新产生的柜面号,并插入数据库,成功则返回新的柜面号。
重构后的代码:
1//读取sequence,获得流水号
2private String getCounterSequenceNo() throws BusinessException ...{
3return (String) this.getSqlMapClientTemplate().queryForObject(SqlID.GET_COUNTER_PK,null);
4}
5
6//根据机构代码来生成柜面号
7private String generateCounterNo(String regionCode)throws BusinessException ...{
8String buffer ="00000000";
9String sequenceStr = this.getCounterSequenceNo();
10int seqLength ;
11seqLength = sequenceStr.length();
12String counterNo ="";
13if(regionCode.length() > 2) ...{
14regionCode = regionCode.substring(0,2);
15}
16counterNo = regionCode + buffer.substring(0,buffer.length() - seqLength)+sequenceStr;
17return counterNo;
18}
19
20//新增柜面
21public String addCounter(CounterDTO counter) throws BusinessException ...{
22String pk=null;
23pk = this.generateCounterNo(counter.getRegionCode());//产生主键
24counter.setCounterNo(pk);//设置主键
25sqlMap.insert(SqlID.ADD_COUNTER, counter);//插入柜面数据
26return pk;
27}
28
|
重构后的代码大大的便于阅读和维护。使方法具有明确的责任。
注意在重构的时候,通过我们把那些相对独立的任务变成一个私有的方法。
关于一个方法应该限制为多长,没有明确的规定。它应该以人的舒服度为主。一个方法包括注释(不含javadoc)的实现代码能够在一屏中可以进行阅读。否则它就应该需要进行重构。一般来说,一个方法的代码控制在30-40行是最佳的。
#p#
四、避免重复的代码
在一个类中,可能在各个方法中产生了相同的代码块。这就是重复的代码。重复的代码是难以维护和难以理解的。有一个类其中有两个方法如下:
1//发送一个包裹
2public String deliveryWarePack(WarePackDTO warePack)
3throws BusinessException ...{
4CommonDAOFactory factory = (CommonDAOFactory) this.context
5.getBean(CommonDAOFactory.COMMON_DAO_FACTORY_BEAN_NAME);
6WarePackDAO warePackDAO = (WarePackDAO) factory
7.getDAOByRegionCode(this.WARE_PACK_DAO_NAME, "00");
8…………
9}
10
11//根据包号得到包信息
12public WarePackDTO getWarePackByPackNum(String packNum)
13throws BusinessException ...{
14CommonDAOFactory factory = (CommonDAOFactory) this.context
15.getBean(CommonDAOFactory.COMMON_DAO_FACTORY_BEAN_NAME);
16WarePackDAO warePackDAO = (WarePackDAO) factory
17.getDAOByRegionCode(this.WARE_PACK_DAO_NAME, "00");
18………
19}
20
|
这似乎没有任何问题,但是这样的代码重复可能会导致严重的问题。
1. 太多的代码:如果重构后只用两行就可以了。
2. 令阅读者迷惑不解,阅读者可能会对开发人员产生怀疑,认为这两处是两个不相同的代码,从而去进行比较,花费更多的时间和精力。
3. 造成修改的不一致,经常可能发现对一个方法进行了修改,而忘记也应该对另一个方法也进行相同的修改。增加代码的维护量。
修改后的代码:
1private WarePackDAO warePackDAO= getWarePackDAO();
2private CommonDAOFactory getFactory() ...{
3CommonDAOFactory factory =
4= (CommonDAOFactory) this.context
5.getBean(CommonDAOFactory.COMMON_DAO_FACTORY_BEAN_NAME);
6return factory;
7}
8
9Private WarePackDAO getWarePackDAO()...{
10WarePackDAO dao = (WarePackDAO)this. getFactory(). getDAOByRegionCode(
11this.WARE_DAO_NAME, "00");
12return dao;
13}
14
15//发送一个包裹
16public String deliveryWarePack(WarePackDTO warePack)
17throws BusinessException ...{
18String packNumber = this.warePackDAO.insertWarePack(warePack);
19}
20
21//根据包号得到包信息
22public WarePackDTO getWarePackByPackNum(String packNum)
23throws BusinessException ...{
24WarePackDTO warePack = this.warePackDAO.getWarePack(packNum);
25………………..
26}
27
|
五、最小的作用范围和权利
成员变量和方法通常应该拥有尽可能小的可见度(包括private ,protected,public和缺省的变量和方法)。变量应该尽可能局部地进行声明。
1. 绝不使用public成员变量
2. 成员变量首先考虑为private。避免使用protected的成员变量,唯一的例外可能是声明为final protected的成员变量。
成员变量声明为protected final 是可能需要提供给子类使用,但是这样同样也破坏了类的封装性。更好一点的做法是把成员变量声明为private final的。并提供一个protected 的方法供子类调用,在这个方法中返回 private final 的成员变量。
3. 一个类只应该有它所实现接口的公有方法和必要的成员变量的getter和setter方法以及这个类提供给外部访问的方法声明为public。其它的统统为protected 或private。
4. 一个类的方法需要提供给子类有不同的实现而进行重写,或者需要把实现交给子类去完成应该声明为protected。其它的统统声明为private 尽可能的把方法隐藏起来,public ,protected ,默认的方法,private; public方法越少这个类就越简洁。就更便于使用,测试和重构。
虽然方法的调用不像对成员变量的直接访问那样危险。但是尽可能的降低方法的可见度在于:降低了类之间的耦合度。把一个类的外在需求与一个类的实现逻辑分开来是十分有必要的。这既可防止这个类的内部状态遭到意外的破坏,同时也简化开发人员处理这个类的任务。通常只提供他们所需要的方法。
5. 变量应该尽可能的在使用它的地方声明。
作用范围内的变量越少,可阅读性就越高,也更加便于调试。杜绝使用C语言或delphi的变量声明方式(变量在方法的开始处声明)。
#p#
六、this关键字的使用
this是一个java中经常使用的关键字,但是我们也经常忘记使用this而使得在访问两个变量时造成意外的错误。下面的方法将会被编译通过,而并没有给成员变量name赋值。
1private String name;
2public void setName(String name)...{
3name = name;
4}
5
|
还有另外的一种形式也会被编译通过(在录入方法参数时候可能输错某个字母)。
public void setName(String nme)...{
name = name;
}
|
在上面的两种情况中,假如没有什么方法对name进行初始化,那么在运行的时候出现莫名其妙的空指针异常。
在第一个方法中把方法参数赋给方法参数自身,在第二方法中把成员变量赋给成员变量自身。除了没有达到预期的效果外都还会造成空指针异常。并且这种错误在编译时都能正常通过。
建议:采用下面良好的习惯来避免上面出现的错误:
1. 访问成员变量总是使用this关键字。这样使得我们总是给期望的成员变量进行访问。而解决变量的多议性。
1public void setName(String name)...{
2this.name = name;
3}
|
2. 给方法参数赋予一个有意义或有区别的名字(不要使用_下划线来命名变量,只有常量才使用下划线.正确的反映参数的用途或代表的意义,并且避免了我们刚才见过的问题。
1public void setName (String newName)...{
2this.name = newName;
3}
4
|
3. 局部变量名变该相对短而成员变量名应该相对冗长。例如,I应该是一个变部变量, userInfo应该是一个成员变量。
通常,成员变量名以一个小写字母开头的接口或类名,而局部变量名应在当前的上下文环境中代表它的含议。
1private SystemUserInfo systemUserInfo;
2public void methodXX()...{
3SystemUserInfo newUser = new SystemUserInfo();
4}
5
|
七、不要使用字面常量
除了一些大家都明白的如0值,空值null,和空串””外,不要在java代码中使用字面常量。
1if (ware.getWareDetailList() != null
2&& ware.getWareDetailList().size() > 100) ...{
3request.setAttribute(super.SERVICE_BUSINESS_INFO,
4"物品数量超过了上限,请您将物品数量控制在100件以内");
|
看上面的例子。无耐的是,我们经常在j2ee项目中大量出现这样的代码。
1.这样的代码没有明确的意义。必须被迫阅读代码来猜测100的含义。
2.代码容易出错,我们必须被迫比较不同的字面值来确信它们是相同的,而且我们很容易错误的输入。
3.修改100这个常数时,可能需要修改多处。
比较好的做法是使用一个常数,在JAVA中意味作使用static final的变量。
1private static final int WARE_COUNT_LIMIT = 100;
2if (ware.getWareDetailList() != null
3&& ware.getWareDetailList().size() > WARE_COUNT_LIMIT ) ...{
4request.setAttribute(super.SERVICE_BUSINESS_INFO,
"物品数量超过了上限,请您将物品数量控制在100件以内");
5
|
一般情况下,这已经OK了。但是在有些情况下它仍然不够好,假如对于每次发送的物品数量限制并不是总是相同的,今天的常量,在明天可能就是一个经常变化的变量。
可能下面的版本会更灵活一些:
1private static final int DEFAULT_WARE_COUNT_LIMIT = 100;
2protected int wareCountLimit()...{
3return DEFAULT_WARE_COUNT_LIMIT;
4}
5if (ware.getWareDetailList() != null
6&& ware.getWareDetailList().size() > wareCountLimit()) ...{
7request.setAttribute(super.SERVICE_BUSINESS_INFO,
"物品数量超过了上限,请您将物品数量控制在100件以内");
8
|
这样我们可以把对物品数量的控制延迟到子类进行,子类可以通过重写wareCountLimit()方法来灵活控制。
#p#
如何设计一个常量,可以有下面的标准来进行参照:
1.如果字面常量应该是代码中的一部分,不应该使用常量变量或方法。
比如:SQL语句。SQL语句本身就应该是代码的一部分,所以不应该再它们声明一个常量。
2. 永远都不会变化的常量或者变化的可能性相当少的常量,声明为static final的常量。例如:在所有应用服务器都相同的JNDI名。
3. 在编译时可能会发生改变的常量,声明子类可以重写的protected的方法。例如:在应用服务器中可能需要变化的JNDI名,如TransactionManager的名称。
4.在运行时可能会发生改变的常量,声明一个protected方法。例如:物品数量限制。
5.受国际化影响的常量,声明一个proteced的方法。
例如:根据不同的Local信息,需要显示不同的错误信息或语言。
这个protected方法返回一个从ResourceBundle中查找的值。
八、内部类和接口
在JAVA中使用内部类和接口可以避免命名空间污染,内部类通常用来帮助外部类有一个一致的责任。除非进行Swing编程,尽可能的少用匿名内部类。而使用一个有类名的内部类会带来更多的好处。比如可以使用构造函数。
使用内部类不能够实现代码重用。会导致重复代码的产生。在使用的时候要进行权衡。
九、Final 关键字
1. 方法重写与final方法
方法重写可能违反“理氏代换原则”和“开闭原则”。使用final方法有助于保证类的完整性。确保一些关键性的方法不让子类去重写。保证系统的安全性。
比如: DispatchServlet 需要调用一系列的init()方法。而这些方法都是十分重要的。
任何重写都可能会导致破坏系统的行为。通常的做法声明一个protected onInit()的空方法。没有任何代码。
在final init()方法的最后调用onInit()这个空方法。 而子类可以去重写onInit()这个方法来扩展功能,而又不破坏父类。
2. final 类
只有能够保证永远不变的对象才使用final类。
3. final成员变量
一个final的成员变量只可以被初始化一次,只能在两个地方进行初始化(声明时,或构造函数),此后就将不能改变。只有常量才使用final。
十、实现有用的toString方法
实现一个有用的toString方法对于我们调试是十分重要的。实现toString方法在java中有专门的规范。在这不作讨论,简单一点说,是应该能够显示一个类的成员变量等关键信息。可以考虑使用辅助类,来实现toString方法。使用ToStringBuilder辅助类的reflectionToString方法,来实现toString方法。
十一、使用StringBuffer而不是String
当需要进行多次字符串操作时优先考虑使用StringBuffer。而不是使用String。
String 是一个final类。
使用StringBuffer进行字符串操作,会带来更大的性能提升。
十二、可恶的空指针异常
NullPointException是一个常见的错误,由于NullPointException是non-checked Exception所以它总是隐式的出错,无需要我们人为的去捕获。产生NullPointException,它又没有相关的有用的帮助信息,所以很难以跟踪。我们只能通过一些编程标准来减少它在运行时出现的次数。
1. 处理空值的原则
a. 没有充分的依据,永远都不要假设对象在某一时刻不可能为空。
b. 假如认为空值是一个错误的代码调用,则应该以文字说明。不要让它简单的抛出NullPointException;而是要记录为业务异常,并有直接的异常说明。
c. 通常对方法参数进行空值检查。
2. 在页面参数传递中正确的对待空值和空串
Web编程是无状态编程,web页面只能传递字符串给后台。通常后台程序在获取页面参数的时候要进行特殊字符处理,去掉字符串两端的空格,制表格等特殊字符。
从页面获取的参数如果是空串应该认为它是空值。
典型的处理如下:
1String s = (String)request.getParameter(name);
2if(s != null ) ...{
3s = s.trim();
4}
5
6if("".equals(s)) ...{
7s = null;
8}
9
|
#p#
3. 正确处理从页面参数传递后把字符串转换为非字符串类型
我们通常需要获取页面参数并转换为非字符串类型。一般是数值或日期类型。这需要有两个步骤:
第一步是从页面获取字符串参数,如果是空串则要变为空值;
第二步是把字符串转为相应的类型。
例如:从页面获取一个年龄的参数。
1Stirng ageStr = request.getParameter(“age”);
2if(ageStr != null)...{
3ageStr = ageStr.trim();
4}
5
6if(“”.equals(ageStr))...{
7ageStr = null;
8}
9
10Int age = Integer. parseInt(ageStr);
11
|
在太多的项目中有这样的代码出现,能够正常编译通过。
但如果页面的age参数是一个录入域,可以由用户随便录入。并且在页面可能没有做相应完整的javascript控制的时候,很容易 就输入了一些不能进行转换为整数的非法字符串。Integer.parseInt()就会抛出NumberFormaterException。同样的情况也会出现在日期转换中。
SimpleDateFormat format = new SimpleDateFormat(“yyyy-MM-dd”);
format. Parse(dateStr);
|
这也有可能抛出ParseException。
NumberFormaterException和parseException都是non-checked Exception。都不需强迫用户去处理。不知不觉就会让系统报异常,使得系统十分脆弱。
象这种异常我们应该手动去处理,处理方法有两种:
1. 把异常当空值处理,凡是报这种异常我都把它处理为空值。
1Date birthday;
2SimpleDateFormat format = new SimpleDateFormat(“yyyy-MM-dd”);
3try...{
4birthday= format.parse(dateStr);
5}catch(ParseException e)...{
6birthday = null;
7}
8
|
2. 把异常当输入错误处理,应该把相应的提示信息返回输入页面,要求用户重新输入合法的数据。
9Date birthday;
10SimpleDateFormat format = new SimpleDateFormat(“yyyy-MM-dd”);
11String errorMessage = null;
12try...{
13birthday= format.parse(dateStr);
14}catch(ParseException e)...{
15errorMessage = “日期必须为yyyy-MM-dd格式”
16}
17ModelAndView mav = null;
18if(errorMessage != null)...{
19mav = new ModelAndView(“inputView”);
20mav.addObject(“error”,errorMessage);
21}
22
23
|
3. 编写辅助类来处理页面参数
1public class ServletRequestUtils ...{
2
3public static final String DEFAULT_DATE_FORMAT="MM/dd/yyyy";
4
5public static String getStringParameter(HttpServletRequest request,
String name) ...{
6String s = (String)request.getParameter(name);
7if(s != null ) ...{
8s = s.trim();
9}
10
11if(s == null || "".equals(s)) ...{
12return null;
13}...{
14return s;
15}
16}
17
18public static String getStringParameter(HttpServletRequest request,
String name, String defaultValue) ...{
19String s = (String)request.getParameter(name);
20if(s != null ) ...{
21s = s.trim();
22}
23
24if(s == null || "".equals(s)) ...{
25return defaultValue;
26}...{
27return s;
28}
29}
30
31public static Date getDateParameter(HttpServletRequest request,
String name)throws BusinessException ...{
32String dateString= getStringParameter(request,name);
33SimpleDateFormat format = new SimpleDateFormat(DEFAULT_DATE_FORMAT);
34try ...{
35if(dateString != null && !"".equals(dateString)) ...{
36return format.parse(dateString);
37}else ...{
38return null;
39}
40}catch(ParseException e) ...{
41throw new BusinessException("无效的日期输入:格式错误:"+dateString);
42}
43}
44
45public static Date getDateParameter(HttpServletRequest request ,
String name, String dateFormat)throws BusinessException...{
46String dateString= getStringParameter(request,name);
47SimpleDateFormat format = new SimpleDateFormat(dateFormat);
48try ...{
49if(dateString != null && !"".equals(dateString)) ...{
50return format.parse(dateString);
51}else ...{
52return null;
53}
54}catch(ParseException e) ...{
55throw new BusinessException("无效的日期输入:格式错误:"+dateString);
56}
57}
58
59public static Date getDateParameter(String dateStr)throws BusinessException...{
60
61SimpleDateFormat format = new SimpleDateFormat(DEFAULT_DATE_FORMAT);
62try ...{
63if(dateStr != null && !"".equals(dateStr)) ...{
64return format.parse(dateStr);
65}else ...{
66return null;
67}
68}catch(ParseException e) ...{
69throw new BusinessException("无效的日期输入:格式错误:"+dateStr);
70}
71}
72
73public static Date getDateParameter(String dateStr,String dateFmt)
throws BusinessException...{
74
75SimpleDateFormat format = new SimpleDateFormat(dateFmt);
76try ...{
77if(dateStr != null && !"".equals(dateStr)) ...{
78return format.parse(dateStr);
79}else ...{
80return null;
81}
82}catch(ParseException e) ...{
83throw new BusinessException("无效的日期输入:格式错误:"+dateStr);
84}
85}
86}
87
|
#p#
十三、正确的进行对象比较
1.总是把不为空的对象放在比较的前面。
String name = request.getParameter("name");
if(name.equals(""))...{
…………
}
|
在j2ee项目中,这样的代码并不少见。name很容易是一个空值。那么在运行时就很容易抛出NullPointException。换成第二种形式:
if("".equals(name))...{
…
}
|
这样当name为空值时条件为false,与我们期望相符。而第一种形式则是一种隐患。说不准它什么时候就抛出了空指针异常。
if(name == null || "".equals(name)) ...{
…
}
|
这样的写法也可以,但是没有必要。第二种写法更简单也同样安全。
2.使用短路计算
public void doXX(User user)...{
if(user.getAge() > 10)...{
…
}
}
|
在j2ee项目中,这样的代码也是频繁出象,没有做任何判断就调用对象的方法。谁又能保证调用者一定不会传一个空值对象进来呢?
public void doXX(User user)...{
if((user != null) && (user.getAge() > 10))...{
…
}
}
|
当user为空时,第二个测试条件不被执行,这样才是安全的代码!在&&布尔表达式, 当前面的有一个条件为false时,那么结果为假,后面的条件不再进行测试。
同样在||布尔表达式中,当前面的有一个条件为true时,结果为真,后面的条件不再进行测试。
If((user == null) || (user.getAge() > 10))...{
………………..
}
|
当user 为null时,结果为真,那么后面的条件将不进行测试。
十四、小心遍历集合对象
在对集合进行遍历时,经常是下面的代码:
List serviceDepartList = counter.getServiceDepartList();
for(int i = 0; i < serviceDepartList.size();i++) ...{
ServiceDepartmentDTO serviceDepart = (ServiceDepartmentDTO)serviceDepartList.get(i);
}
|
当serviceDepartList 为null时,那么它后面的遍历代码就抛出NullPointException.所以在遍历集合时应该总是小心一些。
for(int i = 0; serviceDepartList != null && i < serviceDepartList.size();
i++) ...{ServiceDepartmentDTO serviceDepart =
(ServiceDepartmentDTO)serviceDepartList.get(i);
}
|
这样的代码是安全的。我们时刻要防止可恶的nullPoint产生。可能有些代码是这样的:
if(serviceDepartList != null)...{
for(int i = 0; i < serviceDepartList.size();i++) ...{
ServiceDepartmentDTO serviceDepart = (ServiceDepartmentDTO)serviceDepartList.get(i);
}
}
|
但我觉得上面的方法更简洁一些,特别是没有理由进行太多的if语句嵌套。嵌套增加了代码的复杂性,更难以阅读和理解。
十五、对象创建是要付出代价的
重复的使用一个对象,而不是在每次需要的时候都去创建一个等价的新对象。对象创建是要付出内存消耗和时间代价。特别是当一个对象创建需要初始化外部环境时,比如数据库连接,ejb查找等。
String s = new String("hello");//千万别要这样做比
String s = "hello";
|
要付出更多的开销。如果第一种形式频繁的使用,那么会创建出成千上万个String变量。而第二种形式则不会,String s = "hello";只使用一个String实例。
#p#
1. 应当重用一个已有的对象时,就不要试着去创建新的对象。看看下面的方法:
public CounterDAO getCounterDao() ...{
CounterDAO counterDao =
(CounterDAO)this.getDaoFactory().getDAOByRegionCode(COUNTER_DAO_BEAN_NAME,
"00");//读中心数据库
return counterDao;
}
|
这是要根据不同的机构代码去访问不同的数据库,再获得一个DAO对象,每次都会创建一个新的CounterDAO对象。下面的方法会多次调用CounterDAO的方法。
public void updateCounterRelation(CounterDTO counter) throws BusinessException ...{
this.getCounterDao().removeCounterRelation(counter);//删除此柜面所有子柜面信息
List subCounterList = counter.getSubCounterList(); //本柜面的子柜面列表
for(int i = 0; subCounterList != null && i < subCounterList.size();i++) ...{
CounterDTO subCounter = (CounterDTO)subCounterList.get(i);
subCounter.setSuperCounterNo(counter.getCounterNo());
this.getCounterDao().updateCounterRelation(subCounter);
}}
|
特别是在this.getCounterDao().updateCounterRelation(subCounter)这个语句,需要去更新每一个柜员的关系,那么这样就会创建很多个CounterDao对象,而创建CounterDao对象又会每次都去连接数据库,花费大量的内存空间和连接时间。
重构后的代码:
private CounterDAO counterDao;
public CounterDAO getCounterDao() ...{
if(counterDao == null)
counterDao =
(CounterDAO)this.getDaoFactory().getDAOByRegionCode(COUNTER_DAO_BEAN_NAME, "00")
;//读中心数据库
return counterDao;
}
|
这样不管以后调用这个方法多少次,而只去设置数据库一次,不管使用多少次都只有一个CounterDAO对象创建,更节省内存开销和时间。提升系统性能。
2. 在真实需要的时候才去创建对象
List list = new ArrayList();
if (relationCounterNoString != null) ...{
…….
List.add();
}
|
上面的例子是从页面读取一个带逗号分隔符的字符串,然后把字符串分切为一个list.上面的例子不管要分隔的字符串是否为空,真是返回一个不为空的List,里面没有包括任何数据项的。白白的浪费空间。
List list =null;
if (relationCounterNoString != null) ...{
list = new ArrayList();
………..
list.add();
}
|
这样的代码会更好一些,总之记住,对象创建是有代价的,只有在真正的需要它的时候才去创建它。
十八、使用日志系统
日志系统提供帮助我们跟踪系统执行情况的功能。日志最重要的是能够帮助我们进行系统调试。在实际的项目中我们不得不考虑下面的问题:
1. 调试是经常的,而且是可变的。调试会话是短暂的,没有任何系统能够记下我们的调试内容,今天的问题,在明天可能还是问题。
2. 调试是十分烦琐和费时的,通常搜索一个日志记录要比去遍历代码来得容易。
3. 记录日志可以让我们在一开始就要思考程序的运行情况,可以管控程序的一些隐患错误。
4. 没有任何调试工具可以帮我们良好的解决调试的问题,特别是在分布式系统中。
下面是一些记录日志规范:
1. 绝不要使用System.out.print()语句来记录日志,控制台的输出无法灵活配置。比如说我们想关闭某个类的日志输出。在一些应用程序器中,控制台大量的输出是严重影响性能的。
2. 单元测试能够指出一个类出现了什么问题,但是不一定能详细的指出问题出在哪里。日志可以给我们提供更详细的信息,获得更有用的帮助。
3. 代码生成的日志一定是要可以配置的,否则不能使用于生产环境。
4. 为不同的日志消息选择不同的级别。
5. 在输出日志时先进行级别判断。例如:
if(logger.isDebugable()){
logger.debug(“”);
}
|
累了,先写这么多,接下来想写写,测试专题,异常处理专题,事务处理专题!
|