Java 编程规范

zgzlwm 贡献于2012-11-06

作者   创建于2005-05-12 03:04:00   修改者whysea  修改于2006-05-29 16:21:00字数38028

文档摘要: Java编码规范一般原则规则1.遵循原始代码的风格修改一个现有软件时,一定要遵循原始代码的风格,不要在修改过程中引入新的风格。如果在同一个源文件中使用了不同的编码风格,只会使阅读和理解更加困难。规则2.遵循最小惊讶原则不要在你的程序中做一些出人意料的事情,你的程序必须是可预测的和一致的。如果不能做到这些,那么,在文档中必须清楚地定义并说明使用这些独特的样式或行为的理由。在你的设计、实现和文档中,应该强调下面的特性:简单:构建可以满足需要的,最简单的类和方法。清楚:确保每一个类、接口、方法、变量和对象,都意义明确。要说明它们用在哪里、什么时间用、为什么这样做,以及怎样做。完整:提供用户能使用的最基本的功能,建立完整的文档,说明所有的特性和功能。一致:相似的实体应该有相似的外观和行为,尽可能的建立和应用一致的标准。
关键词:

 Java编码规范 密级 Java编码规范 Prepared by 拟制 Date 日期 yyyy-mm-dd Reviewed by 评审人 Date 日期 yyyy-mm-dd Approved by 批准 Date 日期 yyyy-mm-dd Authorized by 签发 Date 日期 yyyy-mm-dd All rights reserved 版权所有 侵权必究 Revision record 修订记录 All rights reserved 版权所有 侵权必究 Page , Total 45 第页,共45页 Java编码规范 密级 Date 日期 Revision Version 修订 版本 CR ID / Defect ID CR号 Section Number 修改 章节 Change Description 修改描述 Author 作者 Java编码规范 1 一般原则 1.1 规则1.遵循原始代码的风格 修改一个现有软件时,一定要遵循原始代码的风格,不要在修改过程中引入新的风格。如果在同一个源文件中使用了不同的编码风格,只会使阅读和理解更加困难。 1.2 规则2.遵循最小惊讶原则 不要在你的程序中做一些出人意料的事情,你的程序必须是可预测的和一致的。如果不能做到这些,那么,在文档中必须清楚地定义并说明使用这些独特的样式或行为的理由。 在你的设计、实现和文档中,应该强调下面的特性: l 简单:构建可以满足需要的,最简单的类和方法。 l 清楚:确保每一个类、接口、方法、变量和对象,都意义明确。要说明它们用在哪里、什么时间用、为什么这样做,以及怎样做。 l 完整:提供用户能使用的最基本的功能,建立完整的文档,说明所有的特性和功能。 l 一致:相似的实体应该有相似的外观和行为,尽可能的建立和应用一致的标准。 l 健壮:提供可预言的文档行为响应错误和异常。不要隐藏错误,也不要强迫用户去探测错误 1.3 规则3.第一次就做正确 All rights reserved 版权所有 侵权必究 Page , Total 47 第页,共47页 Java编码规范 密级 要将这些规则应用到你所有的编码过程中,而不是仅仅用在生产中。因为你写的原型或者试验性的代码,可能会有一些代码片断用到最终产品中。即使这些代码不会被使用,仍然会有人阅读它, 希望任何一个人读了你的代码后,都会赞赏你从一开始就使用一致规范的专业精神和远见。 1.4 规则4.记录下任何一个偏差 没有完美或者万能的标准,有些时候你需要偏离既定的标准。在你否定一条规则之前,你首先要确定,你明白这条规则为什么存在,以及废除这条规则所引起的后果。如果你确定你必须背离一条规则,请纪录下你这样做的原因。 2 格式 2.1 规则5. 缩进嵌套代码 l 用4个空格作为缩进排版的一个单位;例如: class MyClass { ....void function (int arg) { …. ..if (arg < 0) { ……....for (int index = 0; index <= arg; index++) { …….. ....//… …….. ..} …...} ....} } <% if ( tableHeaderRequired ) { %> …. …….. …….. <% } %> …. ….
Last NameFirst Name
l 除了缩进声明块的内容以外,注释也要缩进4个空格,使之容易被注意到。例如: void function (int arg) { ....loop: …. .. ..for (int index = 0; index <= arg; index++) { …….. .. ..switch (index) { ……. .. ...case 0: ……….. .. .. ..//.. ……….. ... ...break loop; ..// exit the for statement …….. .. ..default: ……….. .. .. ..//.. ………. …. .. break; .. // exit the switch statement …….. .. .. } …….. } …..} } All rights reserved 版权所有 侵权必究 Page , Total 47 第页,共47页 Java编码规范 密级 l 左大括号“{”放置在一行的末尾,右大括号“}”要与声明语句的第一个字符对齐;下面是各种情况的使用规则示例: Class definitions: public class MyClass { … } Inner class definitions: public class OuterClass { … class InnerClass { … } … } Method definitions: void method (int j) { … } Static blocks: static { … } For-loop statements: for (int I = 0; I <= j; I++) { … } If and else statements: if (j < 0) { … } else if (j >0) { … } else { … } Try,catch,and finally blocks: try { … } catch (Exception ex) { … } finally { … } Swatch statements: switch (value) { case 0: … break; default: … All rights reserved 版权所有 侵权必究 Page , Total 47 第页,共47页 Java编码规范 密级 break; } While statements: while (++k <= j) { … } Anonymous inner classes: button.addActionListener( new ActionEventListener() { public void actionPerformed() { … } } ); Do-while statements: do { … } while (++k <= j); l 如果是分行缩进8个字符: 2.2 规则6. 分行 l 方法1:每行的最大列数为100: l 方法2:从较高级别表达式的一个逗号后面断开: double length = Math.sqrt(Math.pow(x,2.0), Math.pow(y,2.0)); l 方法3:从较高级别表达式的一个操作符前面断开: class MyClass { private int field; … boolean equals(Object obj) { return this == obj || (this.obj instanceof MyClass && this.field == obj.field); } … } l 方法4: 重复使用方法1和方法2,直到每一行的长度少于要求的最大长度(一般不大于80个字符); l 方法5:在类方法前不能换行。 Axxx.Get() l 方法6:方法的参数分行显示,缩进8个字符。 Axxx.Get( String Str1, String Str2) Throws 2.3 规则7. 包含空白 All rights reserved 版权所有 侵权必究 Page , Total 47 第页,共47页 Java编码规范 密级 使用空白符会使阅读和理解更加方便。 l 使用单个空格的方式: Ø 右括号“)”,或者右花括号“}”,和一个关键字之间;关键字和左括号“(”,或者左花括号“{”之间;右括号“}”,和一个左花括号“{”之间。 for • ( … ) • { … } while • ( … ) • { … } do • { … } • while • ( … ); switch • ( … ) • { … } if • ( … ) • { … } else • if • ( … ) • { … } else • { … } try • { … } catch • ( … ) • { … } finally • { … } Ø 任何一个二元操作符(“●”除外),操作符和操作数之间: double length = Math.sqrt(x * x + y * y); double xNorm = length > 0.0 ? (x / length) : x; Ø 页面标签和内容之间: <%./*.a short comment .*/.%> l 使用空行的方式: Ø 方法体中逻辑相关的代码段: void handleMessage(Message message) { DataInput content = message.getDataInput(); int messageType = content.readInt(); switch (messageType) { case WARING: … do some stuff here … All rights reserved 版权所有 侵权必究 Page , Total 47 第页,共47页 Java编码规范 密级 break; case ERROR: … do some stuff here … break; default: … do some stuff here … break; } } Ø 类或者接口中定义的成员之间: public class Foo { /** * Defines an inner class. */ class InnerFoo { … } /** * The Bar associated with this Foo. */ private Bar bar; /** * Construct a Foo with the specified Bar. */ Foo (Bar bar) { this.bar = bar; } } Ø 源文件中定义的类或者接口之间: /** * … file description … */ package com.company.xyz; /** * … interface description … */ interface FooInterface { … } /** * … class description … */ public class Foo implements FooInterface { … } 2.4 规则8. 不要使用制表符 All rights reserved 版权所有 侵权必究 Page , Total 47 第页,共47页 Java编码规范 密级 不要使用制表符来缩进代码,因为制表符在不同的环境中,会有不同的显示。要用空白符代替。 3 命名 3.1 规则9. 使用有意义的名字 使用有意义的单词命名你程序中的类、变量、方法和常量,不要使用单个的字符或一般性的名字。要让读你代码的程序员明白这些名字所代表的含义。在循环中用来计数,或作为索引的变量除外。 比如下面这段代码: if (a < 65) { // what property does ‘a’ describe? y = 65 – a; // what is being calculated here? } else { y = 0; } 将变量名称改为有意义的名字后,我们会很容易地明白这段代码的含义: if (age < RETIREMENT_AGE) { yearsToRetirement = RETIREMENT_AGE – age; } else { yearsToRetirement = 0; } 3.2 规则10. 使用约定俗成的名字 比如大家习惯用“Customers”来代表“Clients”,在命名你的类时就应该使用Customer,而不是Client。 3.3 规则11. 质疑太长的名字 一个名字应该能够表明它所代表的对象的功能,如果一个类、接口、变量、或者方法,有一个太长的名字,多半是你想让这个对象完成太多的功能。遇到这种情况,应该重新审视它的设计目的,看是否能够从中分化出新的类、接口、方法、或者变量。 3.4 规则12. 不要用去掉元音的方式缩写名字 不要尝试用去掉名字中元音字符,只保留辅音字符的方式来缩写一个名字,缩写名称不仅会减少代码的易读性,还会使代码的含义变得含糊不清。比如下面这段代码我们很容易读懂: public Message appendSignature(Message message, String signature) { … } 改用缩写后增加了我们理解的困难: public Msg appendSignature(Msg msg, String sgntr) { … } 如果你需要缩短这个名称,那么,你就应该问一下这个名称是否合适(参见:规则11)。 All rights reserved 版权所有 侵权必究 Page , Total 47 第页,共47页 Java编码规范 密级 3.5 规则13. 只大写首字母 这种风格可以分隔名称中的单词,从而避免混乱。特别是当一个首字母缩写词紧跟在其他单词后面时。比如: setDSTOffset() 应该写作 setDstOffset() loadXMLDocument() 应该写作 loadXmlDocument() 该规则不适用于以下情况: l 常量的名字都要大写(参见:规则31): static final String XML_DOCUMENT = “text/xml”; l 方法名字中第一个单词的首字母,变量或参数的名字的首字母,都要小写: private Document xmlDocument; 3.6 规则14. 不要在同一个案例中使用有歧义的名称 Java编译器可以区分,同一案例中两个有细微差别的名字,但是人们阅读时却很难看出它们的差别。 例如,一个变量命名为theSQLInputStream,和另一个命名为theSqlInputStream的变量不在同一个作用域中,不会影响代码的编译,但从全局考虑,可能会给阅读者造成混淆。 3.7 规则15. 用全部小写的翻转的组织的顶级域名作为包的前缀 比如说Rogue Wave Software公司,公司的网址是www.roguewave.com,开发了一个应用程序服务器的包叫“Server”,那么,他们会命名这个包为: com.roguewave.server. 还有,不要使用“java”和“javax”作为包名。 3.8 规则16. 用一个小写的单词命名每个包的根的名字 用一个小写的,能够清楚地表明这个包的目的和意义的单词,命名这个包。也可以使用一个意义明确的缩写来命名一个包。例如: 标准java包中的java.io和java.net包 3.9 规则17. 当新旧包是二进制兼容时使用相同的名字,否则一定要用新的名字命名新版本的包 这条规则的意义是确保两个二进制兼容的包具有相同的名字。 因为java实行的是动态绑定机制,当两个包同时存在时,你无法确保用户在运行你的应用程序时,使用的是你期望的版本。所以,当你的新包不和旧包兼容时,要为他起一个新的名字。但最安全也最简单的方法是在你的包中加入版本号,比如: com.roguewave.server.v1 com.roguewave.server.v2 3.10 规则18. 类或接口名字中的每一个单词的首字母要大写 这样会清楚的分隔开名字中的每一个单词,便于区分两个类、接口、或变量的名字。例如: public class PrintStream extends FilterOutputStream { … } All rights reserved 版权所有 侵权必究 Page , Total 47 第页,共47页 Java编码规范 密级 public interface ActionListener extends EventListener { … } 3.11 规则19. 用名词为类命名 例如: class CustomerAccount { … } public abstract class KeyAdapter implements KeyListener { … } 3.12 规则20. 对于包含相关属性群组,或静态服务群组,或常量群组的类,要用复数命名 例如java.awt.font.LineMetrics类定义了一个管理属性群组的对象: /** * The LineMetrics class gives access to the * metrics needed to layout characters along a line and to layout * of set of lines. */ public class LineMetrics { public LineMetrics() public abstract int getNumChars(); public abstract float getAscent(); … } 包含静态服务群组的类java.beans.Beans: /** * The Beans class provides some * general purpose beans control methods. */ public class Beans { public static Object instantiate (…) { … } public static Object getInstanceOf ( … ) { … } public static boolean isGuiAvailable () { … } public static boolean isDesignTime () { … } public static void setGuiAvailable (…) { … } … } 包含常量群组的类java.sql.Types: /** * The Types class defines constants * that are used to identify SQL types. */ public class Types { public final static int BIT = -7; public final static int TINYINT = -6; All rights reserved 版权所有 侵权必究 Page , Total 47 第页,共47页 Java编码规范 密级 public final static int SMALLINT = 5; public final static int INTEGER = 4; public final static int BIGINT = -5; } 3.13 规则21. 用名词或者形容词命名接口 如果该接口声明一个对象所能提供的服务,则用名词命名: public interface ActionListener { public void actionPerformed (ActionEvent e); } 如果该接口描述一个对象的性能,则用形容词命名: public interface Runnable { public void run(); } 3.14 规则22. 方法名的第一个单词的首字母要小写,其余单词的首字母要大写 例如: class MyImage extends Image { public MyImage() { … } public void flush() { … } public Iamge getScaledInstance() { … } … } 3.15 规则23. 使用动词命名方法 方法通常定义一个动作,应此要用动词命名。例如: class Account { private int balance; … public void withdraw(int amount) { deposit (-1 * amount); } public void deposit (int amount) { this.balance += amount; } } 3.16 规则24. 在命名属性存取的方法时要遵循JavaBeans的规范 在JavaBean中,访问boolean属性的方法要用“is”: boolean isValid() { return this.isValid; } 在JavaBean中,提取属性的方法要用“get”: String getName() { return this.name; All rights reserved 版权所有 侵权必究 Page , Total 47 第页,共47页 Java编码规范 密级 } 在JavaBean中,设置属性的方法要用“set”: void setName(String name) { this.name = name; } 3.17 规则25. 命名变量时,第一个单词的首字母要小写,其余单词的首字母要大写 示例代码: class Customer { … private Address address; private Phone daytimePhone; … public Address setAddress (Address address) { Address oldAddress = this.address; this.address = address; return oldAddress; } … 3.18 规则26. 用名词命名变量 如: class Customer { … private Address billingAddress; private Address shippingAddress; private Phone daytimePhone; private Vector openOrders; … } 3.19 规则27. 集合的引用要用复数命名 如果一个变量或字段,是一个集合类型的对象的引用,就要用复数命名它。从而使阅读代码的人,能够将代表多个值的变量和代表单个值的变量区分开来。例如下面的代码: Customer[] customers = new Customer[MAX_CUSTOMERS]; void addCustomer (int index, Customer customer) { this.customers[index] = customer; } Vector orderItems = new Vector(); void addOrderItem (OrderItem orderItem) { this.orderItems.addElement(orderItem); } 3.20 规则28. 临时变量要用标准的“名称集”中的名字命名 例如: Character c, d, e Coordinate x, y, z Exception e, ex All rights reserved 版权所有 侵权必究 Page , Total 47 第页,共47页 Java编码规范 密级 Graphics g Object o Stream in, out, inOut String s 3.21 规则29. 使用“this”限定符来区分本地变量 区分本地变量最简单的方法,就是用“this”来限定本地的变量或者字段。例如: public class AtomicAdder { private int count; … public AtomicAdder(int count) { this.count = count; } public synchronized int fetchAndAdd(int value) { int temp =this.count; this.count += value; return temp; } } 3.22 规则30. 构造函数或“set”方法中的参数关联到一个字段时要用一与字段名称相同的名字命名它 参数名字和字段名字相同,可以为阅读代码的人提供一个为字段赋值的线索。例如: class Dude { private String name; public Dude (String name) { this.name = name; } public setName(String name) { this.name = name; } } 3.23 规则31. 用大写字母命名常量而且每个单词之间用下划线分开 示例代码: class Byte { public static final byte MAX_VALUE = 255; public static final byte MIN_VALUE = 0; public static final Class TYPE = Byte.class; } 4 文档(注释) All rights reserved 版权所有 侵权必究 Page , Total 47 第页,共47页 Java编码规范 密级 4.1 规则32. 为那些必须阅读和维护你的代码的人写文档 文档化你代码中,公共的程序接口,让其他的人能够正确而有效的使用它们。 文档化你代码中,私有的接口和内部的实现细节,让其他的人可以维护和扩展它。 要永远假定最终读你代码的人不完全理解它。事实上,如果时间太久的话,你自己也未必可以完全明白。因此,这个人也许就是你! 4.2 规则33. 保持注释和代码的同步 Norm Schryer, Bell Labs曾经说过“当代码和注释不一致时,两者很可能都错了。”,因此,当你修改代码时,一定要同步更新与之有关的注释。而且,代码和文档一起发布,所以,二者是同等的重要的。 4.3 规则34. 使用有活力的而不是不需要的词语 注释的语言,应该是那种适合于技术文档的,简短有力,而又简明清楚的语言。 Java支持下面三种类型的注释: l 文档注释,开始标记“/**”,结束标记“*/”: /** * 文档注释 */ l 标准的、C语言风格的注释,开始标记“/*”,结束标记“*/” /* * 标准的注释。 */ l 单行注释,或者尾端注释,以“//”标记开始,直到这一行结束: // 单行注释。 class MyClass { int myField; // 尾端注释。 4.4 规则35. 用文档注释描述应用程序接口 你应该在你代码中的所有类、接口、方法、构造函数,或者字段前面,使用文档注释。 因为,Java提供了一个Javadoc工具,它可以提取Java源文件中,所有有关public和protected的类、接口、方法、构造函数,或者字段的信息,并将这些信息保存到HTML格式的文档中。而你代码中的文档注释,会为Javadoc提供有用的信息。 但是,Javadoc对文档注释有以下的限制,在使用时要注意这些限制: l Javadoc只能识别直接放置在类、接口、构造函数、方法、或者字段声明前面的文档注释。 l Javadoc会忽略方法体中的任何文档注释。 l 对每一个声明,Javadoc只承认一个文档注释块。 下面的示例代码,说明怎样使用文档注释,描述一个内部类、方法、字段、和构造函数: /** * The Rectangle2D class describes * a rectangle defined by location (x,y) and * dimensions (w,h). * … */ public abstract class Rectangle2D extends RectangularShape { All rights reserved 版权所有 侵权必究 Page , Total 47 第页,共47页 Java编码规范 密级 /** * The Double class defines a * rectangle specified in double coordinates… */ static class Double extends Rectanle2D { … } … /** * The bitmask that indicates that a point lies * below this Rectangle2D… */ static int OUT_BOTTOM; … /** * Adds a Rectangle2D to this Rectangle2D… */ public void add (Rectangle2D r) { … } … /** * This is an abstract class that cannot be * instantiated directly… */ protected Rectangle2D () { … } … } 4.5 规则36. 用标准注释的方式隐藏暂不使用的代码段 对于你程序中暂时不想执行的代码段,可以用标准的C语言风格的注释,将之注释掉。但不要在里面嵌套同样的注释,因为这看起来很像文档注释,这会引起混淆。 下面代码演示怎样用标准的注释,注释掉一个成员函数: /** * … * @deprecated */ /* I have temporarily removed this method because it has been deprecated for some time, and I want to determine whether any other packages are still using it! _ J.Kirk on 9 Dec 1997 public void thisOldFunction () { // There has got to be a better way! … } */ 4.6 规则37. 用单行注释解释实现的细节 用一个或更多的单行注释注解: l 专用变量或者表达式的目的; l 实现级别的设计决策; l 为一个复杂算法提供一些源素材; l 缺陷的补救或变通方法; l 可以使程序更加优化和精致的代码; l 已知的问题、限制,或者缺点; All rights reserved 版权所有 侵权必究 Page , Total 47 第页,共47页 Java编码规范 密级 使用注释时,要尽可能的避免下列情况: l 在程序代码中间嵌套注释; l 简单的报告这段代码做什么; l 仅仅描述出代码本身的信息; 下面的代码演示怎样使用单行注释: double totalCost; // Used to total invoice … // Apply the discount to all invoices over $1000. if (totalCost > 10000.0) { // : TODO: Use constant? // The discount is hard-coded because current // customers all use the same discount rate. // We will need to replace this constant with a // variable if we ever get a customer who needs // a different rate, or one that wants to apply // multiple discount rates! totalCost = totalCost * DISCOUNT; } 4.7 规则38. 在写代码之前先描述程序的接口 最好在开发的前期就建立API参考文档,用文档或者文档注释去描述每个类或者接口可能实现的目的、性能,以及它们的使用方法。当程序的构思还在你的脑海时,就要写注释,而不需要等到所有的方法都被实现。只要Java源文件包含的类中的方法,有了一个简单的框架,不需要实现它们的主体,就可以运行Javadoc。 这些最初的文档和注释,不仅仅是用来指导那些具体实现程序细节的开发者的,而且还是最终API参考文档的基础。 4.8 规则39. 为所有的public,protected,package,和private成员建立文档 为所有的成员提供文档注释,包括那些package、protected、和private存取操作。这会为今后维护代码的人提供极大的方便。 4.9 规则40. 为每一个包提供一个总体的描述 Javadoc工具可以帮助我们,为每一个包建立一个概括性的总体描述文档: l 建立一个名为“package.html”的文件,并和包的源文件放在同一目录下。Javadoc会自动找寻该文件。 l 将对包的描述信息,放在和这两个HTML标记之间。 l 可以在包的描述信息中,使用除“@link”以外的任何Javadoc标记,但如果使用“@see”标记,一定要使用完整的限制名。 4.10 规则41. 为每一个应用程序或者包组提供一个总体的描述 Javadoc工具可以帮助我们,为每一个应用程序或者包组建立一个概括性的总体描述文档: l 建立一个扩展名为“.html”的文件,并且,用-overview指令告诉Javadoc该文件的位置。 l 将对包的描述信息,放在和这两个HTML标记之间。 All rights reserved 版权所有 侵权必究 Page , Total 47 第页,共47页 Java编码规范 密级 l 可以在包的描述信息中,使用除“@link”以外的任何Javadoc标记,但如果使用“@see”标记,一定要使用完整的限制名。 4.11 规则42. 所有的文档注释使用统一的结构和风格 严格遵循下面的格式: l 和注释有关的代码的第一行的首字符,要和注释的开始标 记“/**”中的斜杠对齐; l 注释中的“*”标记,要和开始标记“/**”中的第一个“*”字符 对齐; l “*”符号和描述文本,或Javadoc的标记之间,用空格分开; l 描述文本和含有Javadoc标记的注释块之间,用空白行分开; l 结束标记“*/”中的星号,要和其他的星号对齐; l Jsp/javascript/html使用同一方式注释; 例如: /** * Descriptive text for this entity. * * @tag Descriptive text for this tag. */ <% /** * Descriptive text for this entity. * * @tag Descriptive text for this tag. */ %> 4.12 规则43. 关键字、标识符、和常量用标记对包装 因为,HTML标签对让浏览器以标准方式输出它所包含的内容,因此,要将文档注释中的关键字、包名、类名、接口名、方法名、字段名、参数名、常量名,和常量值,包装在其中。如下所示: /** * Allocates a Flag object * representing the value argument * … */ public Flag(boolean value) { … } 4.13 规则44. 用
标签对包装代码
标签对告诉浏览器,要按照原文样式显示其中的内容,包括其中的缩进格式和行结束符。因此,要将文档注释中的代码包装到这个标签对中。如: /** * The following example uses a Class object * to print the class name of an object: * *
     * void printClassName(Object o) {     *   System.out.println(“The class of  “      *       + o     *       + “ is “     *       + o.getClass().getName());     * }     All rights reserved   版权所有 侵权必究   Page  , Total 47  第页,共47页          Java编码规范     密级       *
*… */ 4.14 规则45. 用{@link}标签标记第一次出现的标识符 文档注释中的每一个包、类、接口、方法、和字段的名字,都可以用一个超级链接的“{@link}”标签代码来代替。但并非要将注释块中的每一个标识符,都建立一个{@link}链接标识。而仅仅是将关联引用的元素建立{@link}链接标识。如 /** * Allocates a Flag object representing * the value argument, Use this form of * constructor as an alternative to the {@link #Flag(String)} form. *… */ public Flag (boolean value) { … } /** * Allocates a Flag object representing * the value true argument, if the String argument * is not null and is equal to the string “true”. * Use this form of constructor as an alternative to * the {@link #Flag(boolean)} form. *… */ public Flag (String s) { … } 4.15 规则46. 建立和使用固定的Javadoc的标记指令 Sun Microsystem 推荐以下的Javadoc tag 指令: l 用在类和接口描述中的标记: /** * Description. * * @author * @version * * @see * @since * @deprecated */ 考虑在每一个类或者接口的声明中包含@author和@version标记。 如果有多名作者,要按创建类或接口的年月日顺序排列@author标签。 l 用在方法描述中的标记: /** * Description. * * @param * @return * @exception * * @see * @since * @deprecated */ All rights reserved 版权所有 侵权必究 Page , Total 47 第页,共47页 Java编码规范 密级 用@param标记表明每一个参数 ,用@return标记表明方法的返回值类型 (void除外),用@exception标记表明异常。 l 用在字段描述中的标记: /** * Description. * * @see * @since * @deprecated */ 多个@see标记要根据它们在文档导航和名称限制的队列中的间距,来排序: /** * … * @see #field * @see #Constructor() * @see #Constructor(Type…) * @see #method() * @see #method(Type…) * @see Class * @see Class#field * @see Class#Constructor() * @see Class#Constructor(Type…) * @see Class#method() * @see Class#method(Type…) * @see package.Class * @see package.Class#field * @see package.Class#Constructor() * @see package.Class#Constructor(Type…) * @see package.Class#method() * @see package.Class#method(Type…) * @see package * @see label * @see “String” * … * / 4.16 规则47. 书写第三人称注解格式 描述类、接口、方法的目的和性能时,使用第三人称代词(如:they和it),或者第三人称动词(如:sets和gets)。 下面是API文档通常使用的第三人称动词: adds deallocates removes allocates destroys returns computes gets sets constructs provides tests converts reads writes 4.17 规则48. 用单独的一行书写概括性的描述 Javadoc工具将文档注释中,以空格、制表符,或者行结束符,或者Javadoc标记结束的第一行语句,作为这个类、接口、方法,或者字段的概括性的描述。因此,要在第一行,用简单明了的语言,用单独一行的形式进行概括性的描述。 All rights reserved 版权所有 侵权必究 Page , Total 47 第页,共47页 Java编码规范 密级 比如下面这段代码: /** * Use this function sparingly! * Applies the Foo-bar algorithm to this node. */ public void doFooBar () { … } Javadoc解析这段代码后,会为方法doFooBar提供如下的概括性描述: Use this function sparingly! 这显然不是你想要的结果,因此,应修改上面的代码为: /** * Applies the Foo-bar algorithm to this node. * Use this function sparingly! */ public void doFooBar () { … } 如果是一个重载的方法,一定要在概括性的描述中区分出它们的差别: /** * Allocates a Flag object * representing the value argument. * … */ public Flag (boolean value) { … } /** * Allocates a Flag object * representing the value true if the string argument is not null * and is equal to the string “true”. */ public Flag (String s) { … } 4.18 规则49. 概述一个动作或者服务时可以省略主语 因为主语可以从上下文判断出来,所以在总述一个动作或服务时可以省略主语。 比如下面的代码,就带有冗余的描述: /** * This method applies the Foo-Bar * algorithm to this node. * … */ public void doFooBar () { … } /** * The doFooBar method applies the * Foo-Bar algorithm to this node. * … */ public void doFooBar () { … } 下面是省略主语的注释: /** * Applies the Foo-Bar algorithm to this node. * … */ All rights reserved 版权所有 侵权必究 Page , Total 47 第页,共47页 Java编码规范 密级 public void doFooBar () { … } 4.19 规则50. 概述一些事情时可以省略主语和动词 如果在概述一个类、接口、或者字段时,仅仅描述它们作些什么,则可以省略主语和动词。因为即使省略去,也不会影响对其的理解。如不省略,反而会增加一些冗余的信息。比如下面这段代码,就包含了不必要的主语和动词: /** * A thread group represents a set of threads. * … */ public class ThreadGroup { … } 而去掉那些冗余信息后,代码会变得简洁明了: /** * A set of threads. * … */ public class ThreadGroup { … } 4.20 规则51. 当引用当前类的实例时使用“this”而不是“the” 当描述一个方法时,使用“this”而不是“the”,引用定义这个方法的类的对象: /** * Retrurns a String representing the * value of this Flag object. * … */ public String toString() {… } 4.21 规则52. 注释中提到的普遍性的方法或构造函数的名称不要使用圆括号 对于注释中的方法或者构造函数的名称,不要使用圆括号,除非它们是重载的方法或者构造函数,而你又要明确的指明其中的一个。 例如java.lang.String这个类的代码: public class String { … public String toLowerCase() { … } public String toLowerCase(Locale locale) { … } … } 如果你在注释中用“toLowerCase()”的名称指明任何一个,或者所有的“toLowerCase”方法,但阅读你文档的人可能会认为你指的是第一个方法,这会造成混淆。正确的使用方式是: toLowerCase 指任何一个或全部的方法 toLowerCase() 指第一个方法 toLowerCase(locale) 指第二个方法 4.22 规则53. 为每一个类、接口、字段,和方法,提供一个总的描述 在每一个类、接口、字段,和方法的文档注释的开始,用一句简明扼要的语言,作为被描述的实体的总的说明。 All rights reserved 版权所有 侵权必究 Page , Total 47 第页,共47页 Java编码规范 密级 4.23 规则54. 充分地说明每个方法的详细特征 在每个方法的文档注释中,应该包含以下的详细说明: l 每个参数的说明; l 每个已经验证的异常的说明; l 每个有关的,尚未验证的异常的说明; l 返回值的说明; 4.24 规则55. 在注释中举一些简明的例子 用一个具体的例子解释怎样使用你的程序,是最容易让大家明白的注解方法。尝试在一个有些特别的类或方法的描述中,例举它们的使用方法,而且,用HTML标签对
包装这些例子,以保证它们显示的格式保持不变。例如: /** * … * If you are formatting multiple numbers, it is more efficient to * get the format just once so the system dose not * have to fetch the information about the local * language ang country conventions multiple times: *
     *  DateFormat df = DateFormat.getDateInstance();     *  for (int I = 0; I < a.length; ++I ) {     *    output.println(df.format(myDate[I]) + “; “);     *  }       * 
* To format a number for a different Locale, specify the * local in the call to getDateInstance: *
     *  DateFormat df;     *  df = DateFormat.getDateInstance(Locale.US);     * 
* … */ public abstract class DateFormat extends Format { … } 4.25 规则56. 必须说明先决条件,后续条件,和恒定条件 先决条件是那些只有满足这些条件后,方法才可以执行的条件。比如一个,限定方法变量存取范围的典型的先决条件。 后续条件是指,如果一个方法已经正确、完全地执行,则这些条件就必须被满足。一个典型的场景是,有一个方法返回一个对象的状态,那么,当满足它的初始状态和他所请求的参数后,它就一定会返回这个对象的的状态。 恒定条件是指一个对象总是被满足的条件。比如一个限定整型字段的值在0和25之间的条件。 在注释中说明先决条件、后续条件,以及恒定条件是非常重要的,因为,如果要在运行期判定这些条件,将会付出高昂的代价; All rights reserved 版权所有 侵权必究 Page , Total 47 第页,共47页 Java编码规范 密级 4.26 规则57. 说明已知的缺陷和故障 标识并说明任何一个类或者方法中,未解决的问题,以及可以替代或变通的方法,如有可能,要说明可能的解决方法。 4.27 规则58. 标明同步符synchronization Javadoc可以包含这个修饰符,作为一个方法名的一部分,因此要使用synchronization修饰同步方法,以使阅读你文档的人明白这是一个同步方法。 4.28 规则59. 内部注释的提供仅仅是帮助其他的人理解代码 避免在内部注释中提供无用的和不重要的信息,比如: public int occurrencesOf (Object item) { // This turned out to be much simpler // than I expected. Let’s Go Mets! return (find(item) != null) ? 1 : 0; } 内部注释应该仅仅是帮助其他人理解这段代码是怎样工作的: public int occurrencesOf(Object item) { // This works because no duplicates are allowed: return (find(item) != null) ? 1 : 0: 4.29 规则60. 说明这段代码为什么这样做,而不是做什么 好的代码是可以自我描述的。其他的开发者看到书写良好的代码,会明白它的作用。 例如下面这段代码,验证发票总额如果超过一千元,就给一个百分之五的折扣: if (this.invoiceTotal > 1000.0) { this.invoiceTotal = this.invoiceTotal * 0.95; } 下面的注释提供少量的附加信息: // Appy a 5% discount to all invoices // over a thousand dollars: if (this.invoiceTotal > 1000.0) { this.invoiceTotal = this.invoiceTotal * 0.95; } 但阅读了这段代码后,可能仍然会有人问: • 为什么折扣是百分之五? • 谁来决定折扣率和应该打折的金额? • 什么时间和什么理由改变这个应提供折扣的发票总额? 要注解代码中那些需要让人理解的,特殊的部分: // This term corrects for the effects of Jupiter, // Venus, and the flattening of earth: sigma += (c1 * Angle.sin(a1) + c2 * Angle.sin(Angle.minus(L1,F)) + c3 * Angle.sin(a2) ); 4.30 规则61. 避免使用尾端注释 All rights reserved 版权所有 侵权必究 Page , Total 47 第页,共47页 Java编码规范 密级 最好只在本地变量的声明处使用尾段注释,如: double totalCost; // Used to total invoice 或者是用来标明嵌套的花括号的关闭端(参见规则64)。 其他情况下,另起一行用单行方式注释。 4.31 规则62. 用尾段注释注解本地变量 如果本地变量需要一个简短的描述,请使用尾段注释。使用尾段注释时,不需考虑对齐的问题。举例如下: int cur = 0; // Index of current pattern element int prev = 0; // Index of previous pattern element 4.32 规则63. 规定使用一个关键字的集合标志未解决的问题 对那些目前尚未解决,但在代码全部完成之前必须解决的问题,规定一个关键字的集合,让你和其他的开发者共同用其标志这些问题。有些注释会包含日期,和最初抛出这个问题的人的名字。 要注意的是,这些关键字出现在其他地方的可能性,应该很小。比如下面的例子,用一对冒号包装关键字“UNRESOLVED”,以减小被其他地方使用的可能性: // :UNRESOLVED: EBW, 11 July 1999 // This still does not handle the case where // the input overflows the internal buffer!! while (everMoreInput) { … } 4.33 规则64. 对于多重嵌套的花括号要在相应的闭合端进行标注 如果花括号嵌套太多,很容易造成混淆,此时可以考虑,用一个尾段注释,对闭合端的花括号进行标注,例如: for ( i…) { for (j … ) { while ( … ) { if ( … ) { switch ( … ) { … } // end switch } //end if } // end while }// end for j } //end for I 4.34 规则65. 在switch语句中,如果两个case标签之间没有break,要插入一个fall-through的注释标记 如果不这样,其他的开发者不知道这是你有意如此做的,反而会惊讶你竟然犯下如此低级的错误。例如: switch (command) { case FAST_FORWARD: isFastForward = true; // Fall through! All rights reserved 版权所有 侵权必究 Page , Total 47 第页,共47页 Java编码规范 密级 case PLAY: case FORWARD: isForward = true; break; case FAST_REWIND: isFastRewind = true; // Fall through! case REWIND: isRewind = true; break; … } 4.35 规则66. 标注空的语句 如果在你的代码块中,比如while或者for的循环体中,有空的语句,一定要给予注解: // Strip leading space while ( (c = reader.read()) == SPACE); // Empty! 5 编程 5.1 规则67. 考虑将一个描述基本数据类型的类声明为final类 有些简单的类仅仅是描述基本的数据类型,比如engineering包中的类ComplexNumber,像这样的类应该考虑声明成final类。虽然这样的类不能够被扩展,但通常是没有扩展这些类的需要的。更多情况下是被重复的使用。 5.2 规则68. 从本地类型和其他的固有类型中构建现有的类型 构建一个新的类型时,如果在其界面中存在非本地的、非固有的类型,那么,这些类型的改变,不但会影响你构建的这个类型,而且还会影响调用你这个类型的客户端程序。因此,要尽量使用不会变化的本地类型,或者其他的固有类型,而避免使用那些易变的类型。 比如java.util.BitSet这个类的界面: public final class BitSet … { … public void set(int) { … } public void clear(int) { … } public boolean get(int) { … } public void and(BitSet) { … } public void or(BitSet) { … } … public Object equals(Object) { … } public String toString() { … } } 在其中除了自我引用以外,引用的类型还有基本类型int和boolean,java本地类型object和string,而这些都是稳定不变的。因此,外部的变化,就很少可能的影响到它,以及调用它的客户程序。 5.3 规则69. 定义短小精悍的类和方法 All rights reserved 版权所有 侵权必究 Page , Total 47 第页,共47页 Java编码规范 密级 短小精悍的类和方法,不但易于设计、编码、测试、注释和阅读,还容易理解和使用。因为这样的类包含较少的方法和简单的概念。 尝试在类的界面中,限制接口方法的数目,只提供必要功能的必需的方法。一定要避免仅仅为了方便而建立一个方法。如果一个类或者方法太大则应考虑通过某种方式将其分割成较小的几个类或方法。 5.4 规则70. 定义那些可以在任意地方代替超类使用的子类 子类是超类的一个专门用途的的版本,它继承了超类定义的所有实例变量和方法,并且为它自己增添了独特的元素。它可以运用重写的方法,改变或限制超类的某些行为。而且,子类的实例可以有限制的替代它的超类的实例。但是,在超类可以使用的地方,子类并非总是可以使用。 如果一个子类的特性和其超类的类型协调一致的话,则子类的实例就可完全替代超类的实例。这样的子类仅仅扩展了超类的服务,并增添了一些和超类有关联的子类型,而并非是重写超类的方法。 下面的设计原则针对的就是可替代的问题: l Liskov的替代原则 Ø 方法中引用的基类,必须可以被它的扩展类的对象所替代。 依照这条原则,派生类的对象可以替代其超类对象,这是一个典型的好的设计。而且,这样的设计相比那些不遵循这条原则的设计,往往也是更稳定和更可靠的。当一个设计坚持这条原则时,它通常就会要求设计者,在构建程序界面时,做好基本的抽象和概括工作。 l Open-Closed原则 Ø 软件实体(Classes, Modules, Functions等等),要开放扩展其范围的权利,但要禁止修改的权利。 比如下面的例子: class Shape { … public Shape getNext() { return this.next;} public int getDepth() { return this.depth } … } class Circle extends Shape { … } class Rectangle extends Shape { … } class Canvas { public void drawShapes(ShapeList list) { Shape shape = list.getNextShape(); // Use null to detect end of list while (shape != null) { drawShape(shape); shape = list.getNextShape(); } } public void drawShape(Shape shape) { // Use derived type to call relevant method if (shape instanceof Circle) drawCircle((Circle)shape); else if (shape instanceof Rectangle) drawRectangle((Rectangle)shape); } All rights reserved 版权所有 侵权必究 Page , Total 47 第页,共47页 Java编码规范 密级 public void drawCircle(Circle circle) { … } public void drawRectangle(Rectangle rectangle) { … } … } class ShapeList { … protected Shape first; protected int entries; public int getEntries() {return this.entries} public Shape getNextShape() { Shape temp = this.first; if (null != this.first) { this.first = temp.getNext(); this.entries--; } // Return null when empty return temp; } … } Shape对象储存在ShapeList对象中,而且通过Canvas对象描绘出来。在Canvas对象的drawShapes方法中,通过调用ShapeList对象的getNextShape方法,从ShapeList对象中,读出每一个Shape对象,并调用一个适当的draw方法画出它来。这个过程会遍历整个Shape对象的列表,直到getNextShape方法返回一个null值。 现在,假设我们设计了一个类DepthFilteredShapeList,这个类扩展了ShapeList的功能,它可以过滤出Depth值不在指定范围的Shape对象: class DepthFilteredShapeList extends ShapeList { protected int min; protected int max; public Shape getNextShape() { Shape temp = this.first; if (null != this.first) { this.first = temp.getNext(); this.entries--; int depth = temp.getDepth(); // Is the shape in range? if (this.min > depth || depth > this.max) { // No – return null instead temp = null; } } // Return null when filtered! return temp; } } 在这个扩展类中已经出现了问题。因为在它的getNextShape方法中,不仅仅在链表的末端返回一个空值,而且,遇到被过滤的Shape对象时也会返回一个空值。因此,如果Canvas需要过滤一个Shape对象,而调用这个ShapeList的子类时,它就无法得到Shape对象链表的结束标志,从而无法遍历这个链表。这个例子违背了“Liskov替代原则”。 All rights reserved 版权所有 侵权必究 Page , Total 47 第页,共47页 Java编码规范 密级 在这个案例中,我们可以修改getNextShape方法,使之符合“Liskov替代原则”。即,让getNextShape方法遇到一个需过滤的Shape对象时,继续遍历链表,直到遇到一个符合要求的Shape(返回这个Shape),或者到链表的结尾返回空值。 “Liskov替代原则”也同样适用于方法的设计。一个方法应该被设计成不需要知道它所引用的超类的具体的、特殊的实现。如此说来,在Canvas中的drawShapes方法是有问题的。在这个方法中,需要判断每一个进来的Shape对象的类型,再根据具体的类型调用一个针对它的绘画程序。因此,如果有新的Shape类型的出现,就要改变Canvas类和它的drawShape方法。 解决这个问题的办法是,在Shape类中添加一个抽象方法drawSelf,每一个Shape的子类,都要重写这个方法,并调用Canvas中基本的绘图方法。然后,在Canvas的方法drawShapes中,让Shape的对象画出它们自身: class Shape { … public abstract void drawSelf(Canvas canvas); … } class Circle extends Shape { … public void drawSelf(Canvas canvas) { … } … } class Canvas { … public void drawShapes(ShapeList list) { Shape shape = list.getNextShape(); // Use null to detect end of list while (shape != null) { // Tell the shape to draw itself shape.drawSelf(this); shape = list.getNextShape(); } } // Define the operations the shapes will use public void drawLine(int x1,int y1,int x2,int y2) { … } public void drawCircle(int x,int y,int radius) { … } … } 5.5 规则71. 让一切字段都尽可能地“私有” 类中的成员数据都要私有,访问它们的唯一方法,是通过类中对外公开的公共方法。 5.6 规则72. 使用多态代替“instanceof” 不要用“instanceof”去选择一个对象的不同类型。而应该充分利用多态机制,使程序的可扩展性最大。 具体见规则70中的例子。 5.7 规则73. 用java.lang.object提供的静态类型检查机制包装“general-purpose ”类 All rights reserved 版权所有 侵权必究 Page , Total 47 第页,共47页 Java编码规范 密级 包装一个通用的类管理具体的对象类型,以确保类型安全。下面的例子演示,怎样通过包装一个“general-purpose”队列,去创建一个具体的类型: public class Queue { public void enqueue(Object object) { … } public Object dequeue() { … } } public class OrderQueue { private Queue queue; public OrderQueue() { this.queue = new Queue(); } public void enqueue(Order order) { this.queue.equeue(order); } public Order dequeue() { return (Order)this.queue.dequeue(); } } 5.8 规则74. 将枚举值封装成一个类 例如: public class Color { private static int count = 0; public static final Color RED = new Color(count++); public static final Color GREEN = new Color(count++); public static final Color BLUE = new Color(count++); private int value; private Color(int value) { this.value = value; } public boolean equals(Color other) { return this.value == other.value; } public static int ColorCount() { return count; } } Color aColor = Color.RED; if (anotherColor.equals(aColor) { … } 5.9 规则75. 用等效的方法替代重复发生的表达式 用一些公共的功能,重新包装一个类或者一个方法,尽量避免重复的写一些代码。 All rights reserved 版权所有 侵权必究 Page , Total 47 第页,共47页 Java编码规范 密级 5.10 规则76. 在流程控制语句中使用“块语句”而不是“表达式语句” 在控制流程的语句中,一定要使用“块语句”,以避免容易产生的错误。 下面的例子演示了这些错误: if (x >= 0) if (x > 0) positiveX(); else // Oops! Actually matches most recent if! negativeX(); for (int I = n; I >= 0; I--) for (int j = n; j >=0; j--) f(I,j); g(I,j); // Cannot add here! 正确的方法应该是: if (x >= 0) { if (x > 0) positiveX(); } else { negativeX(); // This is what we really wanted! } for (int I = n; I >= 0; I--) { for (int j = n; j >= 0; j--) { f(I,j); g(I,j); // Can add here! } } 5.11 规则77. 使用圆括号明确表达式中操作符的顺序 数学表达式中,操作符的顺序,并非总是很明确。即使你自己很清楚,也要假设其它的人不会明白。 下面的代码中使用了多余,但是有益的圆括号: int width = ( ( buffer * offset ) / pixelWidth ) + gap; 5.12 规则78. 在swith语句的case语句块的最后,总是使用break结束 下面这段代码,假设在“case Y”语句块后没有其他的case语句,因此你觉得,没必要在此使用break: swith ( … ) { case X: … break; case Y: … } 但是有一天,有人要在这段swith代码中,添加一段case语句。他没有注意到上面的case块中没有break语句。此时就会发生“fall-through”的错误: All rights reserved 版权所有 侵权必究 Page , Total 47 第页,共47页 Java编码规范 密级 swith (… ) { case X: … break; case Y: … // Oops! Unintended fall-through! case Z: … } 为了避免将来可能发生的错误,你应该,在swith语句的case语句块的最后,总是使用break结束,即使是在default语句中: switch (… ) { case X: … break; case Y: … break; // ok! No more fall-through! case Z: … break; default: … // Complain about value! break; } 如果确实需要“fall-through”,不要忘记添加“fall-through”注释(参见规则65)。 5.13 规则79. 判断两个对象的值是否相等时,使用equals()方法,而不是“==” 许多c++程序员在使用java处理数据和字符串时,会犯以下的错误: Date today = new Date(); while (date != today) { … } String name; … if (name == “Bob”) { hiBob(); } 在java中,“!=”和“==”两个操作符,比较的是对象的引用,而不是对象的值。你必须使用equals方法,去比较真正的字符串的值: Date today = new Date(); while (!date .equals(today)) { … } String name; … if (“Bob”.equals(name)) { hiBob(); } 备注:与表达式“name.equals(“Bob”)”不同的是“Bob”.equals(name)不会抛出异常。 All rights reserved 版权所有 侵权必究 Page , Total 47 第页,共47页 Java编码规范 密级 5.14 规则80. 总是构造有效的对象 决不要构造无效的对象。如果必须构造一个无效的对象,而且需要进一步的初始化后,才可将之转换为有效状态,则以一个静态方法,代替多级构造。 5.15 规则81. 不要在构造函数中呼叫未加final限制的方法 子类可以重栽未加final限制的方法,而且java在执行派生类的构造函数时,将会依据要构造的对象的具体类型,调用相应的方法。这就意味着构造函数调用派生方法时,派生对象可能尚在无效的状态。因此,不要在构造函数中呼叫未加final限制的方法,以避免这种情况的发生。 5.16 规则82. 使用嵌套的构造函数排除多余的代码 使用高级构造函数调用低级构造函数的方式,避免多余的构造代码。 下面的示例含有冗余的初始化代码: class Account { String name; double balance; final static double DEFAULT_BALANCE = 0.0d; Account (String name, double balance) { this.name = name; this.balance = balance; } Account(String name) { this.name = name; this.balance = DEFAULT_BALANCE; } } 下面是改进的代码: class Account { String name; double balance; final static double DEFAULT_BALANCE = 0.0d; Account (String name, double balance) { this.name = name; this.balance = balance; } Account(String name) { this(name, DEFAULT_BALANCE); } } 5.17 规则83. 通过抛出一个异常来指出程序逻辑中严重的无法预测的运行期错误 尽管运行期错误可以严重到使程序终止运行,但仍然可以捕获并处理它们。运行期错误,通常会因为程序错误而被抛出,比如一个失败的断言,索引范围超标,被零除,或者引用空的指针,等等; All rights reserved 版权所有 侵权必究 Page , Total 47 第页,共47页 Java编码规范 密级 5.18 规则84. 使用一个已经检查过的异常报告一个可能发生的错误;尽管在正常情况下这种可能性很小 在正常条件下,不需要用一个已被检查过的异常去指出一个严重的错误。调用者必须捕获这个异常。视具体的情况,程序可能从该异常处恢复。总而言之,这不是为了指明程序逻辑中的基本缺陷。 5.19 规则85. 使用返回值标明状态的改变 使用一个返回值,或设置一个标记,或是用一个返回一个状态的特别的方法,表明一种预期的状态的改变,这会增强代码的可读性,并使流程控制更加简洁有力。比如读一个文件的方法,用一个返回值标明已经读到文件的结尾。 5.20 规则86. 转换异常时仅仅增加新的信息 转换异常时,保留所有的异常信息,决不丢弃低级的说明: try { for (int I = v.size(); --I <= 0;) { ostream.println(v.elementAt(I)); } } catch (ArrayOutOfBounds e) { // should never get here throe new UnexpectedExceptionError(e); } 5.21 规则87. 不要在异常捕获语句中什么也不做 如果破坏了这条规则,将会增加调试的困难,因为异常信息丢失。比如: try { for (int I = v.size(); --I <= 0;) { ostream.println(v.elementAt(I)); } } catch (ArrayOutOfBounds e) { // Oops! We should never get here… // … but if we do, nobody will ever know! } 最起码,你应该输出“stack trace”,一旦发生问题时,好让别人知道发生了什么事: try { for (int I = v.size(); --I <= 0;) { ostream.println(v.elementAt(I)); } } catch (ArrayOutOfBounds e) { // Oops! We should never get here… // But print a stack trace just in case… e.printStackTrace(); } 5.22 规则88. 在finally语句块中释放资源 因为无论异常是否发生,finally语句都会被执行,因此,这里是释放资源的最好地方。 All rights reserved 版权所有 侵权必究 Page , Total 47 第页,共47页 Java编码规范 密级 比如下面的代码,如果发生了异常,资源就不会被释放: public void logSomeStuff() { OutputStream log = new FileOutputStream(“log”); … // could get exception here! … log.close(); } 修改以后,就不会发生这样的问题: public void logSomeStuff() { try { OutputStream log = new FileOutputStream(“log”); } finally { log.close(); } } 5.23 规则89. 编程约定 应该在程序中通过断言机制,约束方法的调用者必须满足方法的“先决条件”和“后续条件”。而且,如果派生类重栽了基类的方法,必须保证基类方法的先决、后续条件不变。可以运用某些设计模式来确保这一点。 比如说,在公共方法前加上final限制,同时创建一个未加final限制的受保护的方法,以实现该函数的主体。该公共的不可被扩展的方法,判断先决条件是否满足,并调用相关的受保护的方法完成函数功能,最后再判断后续条件是否被满足。这样以来,派生类就只能重栽受保护的方法,而不能重栽被final限制的公共方法,从而保证了必须被满足的条件。示例如下: class LinkedList { public final synchronized void prepend(Object object) { // Test pre-condition if (Assert.ENABLED) Assert.isTrue(object != null); doPrepend(object); // Test post-condition if (Assert.ENABLED) Assert.isTrue(first() == object); } protected void doPrepend(Object object) { Node node = new Node(object); if (this.head == null) this.head = node; else { node.next = this.head; this.head = node; } } } 5.24 规则90. 使用不会被执行的代码排除断言 All rights reserved 版权所有 侵权必究 Page , Total 47 第页,共47页 Java编码规范 密级 断言在开发阶段非常有用,它可以保证程序员之间正确的调用一些函数,但在程序发布时,它可能不会再被使用,为了程序的效率,我们需要排除这些断言代码。此时,可以利用java编译器,不会编译那些明显不会被执行的代码的这个特点。 比如下面的代码,java编译器知道,FALSE变量不可能为true,因此这段代码永远都不会被执行,所以,它不会编译这段代码: class DeadCode { static final boolean FALSE = false; public void example() { if (FALSE) { System.out.println(“Never to be seen.”); } } } 如此,我们就可以设计一个断言类,它能够让我们灵活地决定,是否将它编译进我们的程序: public class Assert { public static final boolean ENABLED = true; public static final void isTrue(boolean assertion) { if (Assert.ENABLED && !assertion) throw new RuntimeException(“Assertion Failed”); } } … if (Assert.ENABLED) Assert.isTrue(a > b); … 在不需要断言的时候,设置Assert类中的ENABLED变量为false即可。 5.25 规则91. 使用断言捕获代码中的逻辑错误 断言其实就是一个boolean表达式,如果条件为真,程序就可以正确的执行。利用这一点,可以校验程序中的假设是否成立。 5.26 规则92. 利用断言判断方法的先决条件和后续条件是否被满足 具体见规则89。 5.27 规则93. 仅在适当的时候使用多线程 多线程并非是改善应用程序执行效率的灵丹妙药。一个不适合多线程的应用程序,可能会运行的更慢。因为线程之间的转换增加了系统资源的开销。 l 如果你的程序有以下的需求,引入多线程,对改善你应用程序的效率是有帮助的: l 有许多的事件同时发生。如互联网浏览器或服务器。 l 提供高级的响应。如一个用户界面,在执行一个运算的同时,可以响应用户的其他的动作。 l 运行在更高级的多进程设备。比如你的程序运行在一个多 CPU的机器上。 5.28 规则94. 避免同步的发生 同步是非常昂贵的。因为对于代码中必须同步的部分,需要建立和释放一个同步对象。而且,同步使用串行方法访问一个对象,并发性很小。因此,要在的的确确需要使用同步的地方使用同步。 不要任意的同步每一个公共的方法。在同步一个方法前,要考虑这个方法访问的是否是,共享的、处于非同步状态的对象。如果一个方法仅仅访问本地的变量、参数,或者访问的是一个处在同步状态下的对象,就不需要同步。 All rights reserved 版权所有 侵权必究 Page , Total 47 第页,共47页 Java编码规范 密级 不要同步那些仅仅提供基本数据类型和结构的类。应该让使用它的用户决定,在哪里去同步它。 5.29 规则95. 使用同步包装提供一个同步接口 利用同步技术包装一个类的同步版本,这个类提供了与被包装类相同的接口,但这些方法已经被同步。而且,被包装的类提供一个静态方法访问这个同步的包装类。下面的例子示范一个栈,它有一些缺省的、未同步的方法,同时也有一个包装类,提供一些相同的,但却是同步的方法: public class Stack { public void push(Object o) { … } public Object pop() { … } public static Stack createSynchronizedStack() { return new SynchronizedStack(); } } Class SynchronizedStack extends Stack { public synchronized void push(Object o) { super.push(o); } public synchronized Object pop() { return super.pop(); } } 5.30 规则96. 不要因为一个方法包含了一个重要的操作就同步这个方法 因为一个同步方法告诉系统在特定的资源上加上一把锁,当某一线程在一个同步了的方法中执行的时候,其它所有企图调用同一对象实例的这个方法的线程和方法都必须等待,直到该方法执行结束,才会释放这把锁。所以,如果一个方法中,尽管只有很少的操作需要同步,但却同步了整个方法这是非常粗糙的做法。在这种情况下,应该用一个同步块来代替同步方法: protected void processRequest() { Request request = getNextRequest(); RequestId id = request.getId(); synchronize(this) { RequestHandler handler = this.handlerMap.get(id); } handler.handle(request); } 5.31 规则97. 读写变量时避免多余的同步 java语言已经确保读写一个对象的引用和所有的参数是原子性的(除long和double类型以外)。 所以,我们不需要在读写一个原子性的数据时,加上同步。但要注意的是,如果这个原子性的变量依赖于其他的变量,或同其他变量有关联,同步仍然是必须的。 例如下面的例子,为x和y符值必须同步,因为二者是相互依赖的: public void synchronized setCenter(int x, int y) { this.x = x; this.y = y; } 而下面的例子就不需要同步,因为为一个对象的引用赋值,是原子性的: All rights reserved 版权所有 侵权必究 Page , Total 47 第页,共47页 Java编码规范 密级 public void setCenter(Point p) { this.point = (point)p.clone(); } 5.32 规则98. 考虑用notify()取代notifyAll() java.lang.object包中的notify方法,唤醒一个处于等待状态的单个线程,而notifyAll方法唤醒所有等待状态的方法。尽可能使用notify而不是notifyAll方法,因为notify方法更有效率。 当只有一个线程在等待这个特定的条件,或者在这个时刻只有一个线程可以进行时,就使用notify方法。比如一个notify信号写入了一个队列,而只有一个线程可以从该队列中读取这个信号,此时唤醒所有线程就太浪费了。 只有当有许多线程在等待这个条件,或者需要很多线程运行时,才使用notifyAll方法。 5.33 规则99. 初始化需要同步时使用double-check模式 如果仅仅是初始化过程需要同步时,使用double-check模式。比如下面的例子,实例变量log,需要在该变量为空时进行初始化。为防止两个线程同时初始化该字段,函数getLog被宣告成同步方法: synchronized log getLog() { if (this.log == null) { this.log = new Log(); } return this.log; } 下面的代码也同样防止了同时初始化这个字段,但运用double-check模式,避免了同步整个函数,只是在需要同步的初始化部分进行了同步: log getLog() { if (this.log == null) { synchronized(this) { if if (this.log == null) { this.log = new Log(); } } } return this.log; } 5.34 规则100. 只在最需要的时候进行初始化 不要过早的构建一个对象,一定要在真真需要的时候才去构建。要建立对象的“存取器”,而且,一定要通过这些“存取器”去取得一个对象的引用: class PersonalFinance { LoanRateCalculator loanCalculator = null; LoanRateCalculator getLoanCalculator() { if (this.loanCalculator == null) this.loanCalculator = new LoanRateCalculator(); return this.loanCalculator; } } 5.35 规则101. 避免创建多余的对象 All rights reserved 版权所有 侵权必究 Page , Total 47 第页,共47页 Java编码规范 密级 这是非常重要的一个原则。因为,如果一个对象的生存周期很短,或者创建了一个对象,而不去释放它,这不仅浪费创建它的时间,还会耗费垃圾回收的时间。 在下面的例子中,多余的初始化是多么的庸俗和浪费: Color getTextColor() { Color c = new Color(…); if (this.state < 2) { c = new Color(…); } return c; } 要避免创建一个对象,直到你确定你需要它: Color getTextColor() { Color c = null; if (this.state < 2) { c = new Color(…); } else { c = new Color(…); } return c; } 5.36 规则102. 使用重用的对象而不是创建新的对象 储存并重复使用那些,因为生存期受限而需要频繁地被创建的对象 使用“存取器”的方法取代构造函数,去重新初始化一个对象。 要注意的是,使用一个不需创建它自身的对象去管理、储存对象,违背了这条原则的目的! 使用工厂模式的封装机制,达到储存和重用对象的目的。但要适当的运用这个机制,你必须确保,从对象工厂获得的对象,和需要返回的对象是一致的。这就意味着,必须维护一个对象和它的工厂之间的关系: 对于类来说,一个单独的静态的工厂被对象的类所关联,而且, 这个工厂管理着所有该类的对象。 对于对象来说,这个对象维护着一个管理它的工厂的引用。 对于对象的所有者来说,一个对象的“所有者”,维护着从中获得 对象的工厂的引用。 5.37 规则103. 将最佳化的工作放到最后 最佳化的第一规则是,不要做它,最佳化的第二规则是,还是不要做它。 —— Michael Jackson, Michael Jackson Systems Ltd. 不要浪费时间最佳化你的代码,直到你确信需要这样做。请记住80-20规则——平均来说,系统中百分之二十的代码使用了百分之八十的资源。如果你想优化你的代码,请确定这些代码是百分之二十的那一部分。 6 包 All rights reserved 版权所有 侵权必究 Page , Total 47 第页,共47页 Java编码规范 密级 6.1 规则104. 通常将一些被一起使用、交换和发布的类型,或者相互之间关联的类型,放置在一个包中 该规则包含如下几个相关的,包的设计原则: l 共享原则: 包中的类和接口有很强的耦合性,通常使用一个类,它会调用其他的类,它们之间有相互共享的关系,例如下面的类型: Ø Containes and iterators Ø Database tables, rows, and columns. Ø Calendars, dates, and times. Ø Points, lines, and polygons. l 同时闭合原则 有些类要求同时闭合,如果修改了其中的一个,就要修改其他的类。像这样的这些类要放在同一个包中。 l 发布和重用等价原则 将单独的类作为发布单元是很不明智的,因为一个应用程序中可能包含成百上千的类和接口,无论是发布还是调试,或者是版本的变更,都会是超乎想象的复杂。而用包包装这些类和接口,会使这些事情变得很简单。 l 非循环相关原则 包和包之间,如果有相互依存的关系结构,则必须是一个单向的非循环的关系结构。否则将使程序变得非常复杂。 6.2 规则105. 将容易变化的类和接口放置到不同的包中 避免将容易变化的类及接口,同已经稳定的类和接口放置在一个包中。因为,如果你以包的形式发布你的类和接口,这会使你更容易更新版本。 6.3 规则106. 避免让一个难以改变的包依赖于一个易变的包 该条规则派生出以下的设计原则: l 稳定关系原则: 相互依赖的两个包的依赖方向,应该是指向更加稳固的一方。如果一个包含难以改变类型的包,依赖于一个包含需要经常变化的类型的包,则这个包会妨碍那个易变包的更改。如果一定要这样做,就应考虑创建一个新的抽象,以改变这两者之间的关系。 6.4 规则107. 尽可能的抽象使稳定性更高 该规则派生自下面的设计原则: l 稳定抽象原则 一个包的稳定性,是与它的抽象程度成正比的。越抽象的包越稳定,越具体的包越不稳定。 6.5 规则108. 从高级别的设计和构架一个稳定的抽象入手,组织一个稳定的包 要使计划和管理一个软件开发工作取得完全的成功,高级别的设计必须是稳定的、快速的。如果一个软件系统的构架经常改变,那么,没有哪个开发的管理者能够准确的计划或估计时间表,也不能准确的分配资源。 7 文件和目录 All rights reserved 版权所有 侵权必究 Page , Total 47 第页,共47页 Java编码规范 密级 7.1 规则109. 遵循严格的文件和目录规定 文件类型 后缀 目录 Jsp文件 jsp // Jsp片断 inc // 样式表 css /css/ Javascript文件 js /js/ Html网页 html // 资源文件 gif,jpg etc /images/ 标签文件 tld /WEB-INF/tld/ Class文件 class /WEB-INF/classes 包 Zip、jar etc /WEB-INF/lib 如果特殊的应用服务器不支持上面的目录,可以将相关文件复制到指定的目录中。 7.2 规则110. 需要配置welcome页面 在web.xml文件中配置welcome-file内容。默认为index.jsp 7.3 规则111. 目录根据国别分开 如果是一个国际化的系统。需要根据不同的国家分开建目录,文件分开存放。 如:/en_US/index.jsp 7.4 规则112. Jsp/Xml文件结构 <%-- import attributes start here --%> <%@ page session="false" %> <%@ page import="java.util.*" %> <%@ page errorPage="/common/errorPage.jsp" %> <%@ page import="com.mycorp.bank.savings.*" %> <%@ page import="com.thirdpartycorp.cashmanagement.*" %> <%@ page import="com.mycorp.bank.foreignexchange.*" %> <%@ taglib uri="URI1" prefix="tagPrefix1" %> <%@ taglib uri="URI2" prefix="tagPrefix2" %> 7.5 规则113. CSS文件结构 H1.{.color: blue.} 7.6 规则114. Java代码和html语言混合。 可以参见规则5。 <% if ( tableHeaderRequired ) { %> All rights reserved 版权所有 侵权必究 Page , Total 47 第页,共47页 Java编码规范 密级 …. …….. …….. <% } %> …. ….
Last NameFirst Name
8 附录A 注释样例 8.1 类注释 /** * 用简短的语句描述class. *

* 用详细的语句描述class, 可以使用 HTML 等标签标记内容 * 如果你有“关键字”、“包”、“变量”、“代码样例”等等需要描述,使用‘code’标签标记 * 如String。 *

* * @author 姓名拼音 * @version 1.0 * @copyright (C) TOPCHEER Corp. 2000 * @security ***(暂时使用星号代替) * @date YYYY/MM/DD * @notes 备注 * * @see 关联的类 * @see 关联的类#方法 * * ****** modified****** * $Author: 姓名拼音 * $Modtime: YYYY/MM/DD * $Revision: 1.0.1 */ 8.2 方法描述 /** * 用简短的语句描述方法. *

* 用详细的语句描述方法, 可以使用 HTML 等标签标记内容 * 如果你有“关键字”、“包”、“变量”、“代码样例”等等需要描述,使用‘code’标签标记 * 如String。 *

* * @param 描述变量 * @param paramName String paramName:表示信息的状态 * @return returnName String returnName:表示操作是否成功 All rights reserved 版权所有 侵权必究 Page , Total 47 第页,共47页 Java编码规范 密级 * @exception 描述方法抛出何种错误 * @see 相关类 * @see 关联的类#方法 * @see #其它在本class中的方法 */ 8.3 页面描述 /** * 用简短的语句描述页面. *

* 用详细的语句描述页面。 *

* * @author 姓名拼音 * @version 1.0 * @copyright (C) TOPCHEER Corp. 2000 * @security ***(暂时使用星号代替) * @date YYYY/MM/DD * @notes 备注 * * @see 关联的类 * @see 关联的类#方法 * * ****** modified ****** * $Author: 姓名拼音 * $Modtime: YYYY/MM/DD * $Revision: 1.0.1 * * ****** request description ****** * @param 描述参数 * @param paramName String paramName:表示信息的状态 * @return returnName String returnName:表示操作是否成功 * @exception 描述方法抛出何种错误 * @see 相关类 * @see 关联的类#方法 * @see #其它在本class中的方法 */ 8.4 修改标记 /****** modified by拼音 Modtime: YYYY/MM/DD Revision: 1.0.1 beg ******/ 修改内容 /****** modified by拼音 Modtime: YYYY/MM/DD Revision: 1.0.1 end ******/ 9 附录B 结构样例 All rights reserved 版权所有 侵权必究 Page , Total 47 第页,共47页 Java编码规范 密级 9.1 类 package com.Topcheer; import com.Topcheer.info; /** * 简短描述. *

* 详细描述 *

* * @author 姓名拼音 * @version 1.0 * @copyright (C) TOPCHEER Corp. 2000 * @security ***(暂时使用星号代替) * @date YYYY/MM/DD * @notes 备注 * * @see 关联的类 * @see 关联的类#方法 */ public class Blah extends SomeClass { /** 变量的一行描述 */ public static int classVar1; /** * 变量的多行描述 * m */ private static Object classVar2; /** 变量的一行描述 */ public Object instanceVar1; /** 变量的一行描述 */ protected int instanceVar2; /** 变量的一行描述 */ private Object[] instanceVar3; /** * ...方法描述... */ public Blah() { // ...方法中需要的非必须描述... } /** * ...方法描述... */ public void doSomething() { // ...方法中需要的非必须描述... } /** * ...方法描述... * @param someParam description */ public void doSomethingElse(Object someParam) { // ...方法中需要的非必须描述... } } All rights reserved 版权所有 侵权必究 Page , Total 47 第页,共47页 Java编码规范 密级 9.2 页面 <%@ page contentType="text/html;charset=ISO8859_1" %> <% /** * 用简短的语句描述页面. *

* 用详细的语句描述页面。 *

* * @author 姓名拼音 * @version 1.0 * @copyright (C) TOPCHEER Corp. 2000 * @security ***(暂时使用星号代替) * @date YYYY/MM/DD * @notes 备注 * * @see 关联的类 * @see 关联的类#方法 * * ****** modified ****** * $Author: 姓名拼音 * $Modtime: YYYY/MM/DD * $Revision: 1.0.1 * * ****** request description ****** * @param 描述参数 * @param paramName String paramName:表示信息的状态 * @return returnName String returnName:表示操作是否成功 * @exception 描述方法抛出何种错误 * @see 相关类 * @see 关联的类#方法 * @see #其它在本class中的方法 */ %> <%@ page import="com.Topcheer.forum.*, com.Topcheer.forum.util.*" errorPage="error.jsp" %> <%@ include file="global.jsp" %> <% // 获取参数 boolean redirect = ParamUtils.getBooleanParameter(request,"x"); String where = ParamUtils.getParameter(request,"where"); if (redirect) { if (where == null) { where = "index.jsp"; } response.sendRedirect(where); return; } %> All rights reserved 版权所有 侵权必究 Page , Total 47 第页,共47页 Java编码规范 密级 <% String title = "论坛 - 创建帐号"; %> <%@ include file="header.jsp" %> <% out.flush(); %> <%-- Breadcrumbs --%> " color="<%= JiveGlobals.getJiveProperty("skin.default.linkColor") %>"> " >首页 » 论坛 » 创建帐号

"> 恭喜您!
您的帐号已经成功的创建.


<%@ include file="footer.jsp" %> All rights reserved 版权所有 侵权必究 Page , Total 47 第页,共47页

下载文档到电脑,查找使用更方便

文档的实际排版效果,会与网站的显示效果略有不同!!

需要 8 金币 [ 分享文档获得金币 ] 1 人已下载

下载文档