Java 语言编程规范

1.1    概述

1.1.1                                                                                                                                                标准

1.      简洁统一的缩进风格。尽管编译人员并不注意这一点,但这对于您编写的代码的未来读者来说尤其重要。本文将详细介绍缩进。

2.      连续缩进/换行的多行代码

3.      禁用差别不大(只有一个或少数几个字母不同)的名称。
例如:
hiThere hiThre

4.      在名称中禁用下划线字符('_'),类常量除外(见《类常量》一节)。

5.      使用完整的单词,避免使用缩略语,除非它们是大家非常熟悉的缩略语。

6.      如必须使用缩略语,则把它们作为单词使用。
例如:

tempVar 代替 temporaryVariable AmEx 代替 AMEX

7.      代码片断应符合如下标准,起始括号与其相对应的代码的同一行,代码在下一行缩进。结束括号应在与开始程序模块的最初代码的同一缩进位置。通过这一方式来标准化使用括号,您可以很容易地找到闭括号相对应的代码。

例如:
header {
   statement 1;
   statement 2;
}

例如:
try { 
   statement 1;
   statement 2;
}

例如:
for (int i=0; i < 10; i++) {
   statement 1;
   statement 2;
}

8.      复合语句应在前一语句的闭大括号的同一行开始。

例如:
if (this == that) {
   statement 1;
   statement 2;
} else {
   statement 3;
}

例如:
try {
   statement 1;
   statement 2;
} catch(Exception e) {
   statement 3;
}

9.      当您需要持续处理向量( Vector 中的元素时,使用以下代码以提高运行效率:

例如:
for (int i=0; i < vector.size(); i++) {
           // do something to vector.elementAt(i)
}

or

for (int i=vector.size()-1; i >= 0; i--) { // slightly more efficient
   // do something to vector.elementAt(i)
}

而不要用 :

Enumeration e = vector.elements();
while (e.hasMoreElements()) {
   // do something to e.nextElement()
}

10.   在方法的注释中记录作为异常处理的被忽略的情况 [YN1]  

例如:
// Something similar to the following would be in the
// method’s javadoc
// comment:
/**
   This method does something.  If such-and-such occurs, it
   is ignored.
 */
try {
   obj.doSomething;
} catch (Exception ignored) {}

11.   确保总有一个(catch)用于处理所有未捕获的异常。

1.1.2                                                                                                                                                指南

1.      保持代码的行长,以与编辑器的水平方向相符(大约75个字符)。

2.      当对基本数据类型(整型、长整型等)进行比较时,使用 ==;对对象数据类型 (字符串、整型、策略等)进行 比较 时,使用 .equals(object) 。与标准相比这仅是提醒而以 [YN2]   ,因为 ==用于 比较实际对象的引用信息,而 .equals(Object)用于校验两个对象是 “相等的”。如果您不想 或者如果不适用于 重载 equals(object)方法,则实现一个.isEqualTo(object)方法。

3.      当校验布尔型值时,不要采用如下方式
if (booleanValue == true) or
if (booleanValue == false)
建议使用
if (booleanValue) or
if (!booleanValue)  // For false condition

4.      避免在ifwhile 条件句中使用(“=”)(不要编码为: if (x = 0){} )。原因是 x将一直为0,条件将一直为

1.1.2.1       事件和异常―何时使用

事件- 用于“通知”全体用户(注册了收听的所有人)有事情发生。例如:库存达到最低值,策略已经改变。

异常- 当遇到“异常处理”时抛出(Throw)异常。例如:打开/读/写文件时出错,COBOL程序返回bad (非-0)返回代码,遇到无效指针等。

1.      当抛出异常时,如果抛出少量的异常(3种或低于3种),抛出和捕获(Catch)实际被抛出的异常。如果异常超过3种,设置ProjectException以显示可能要抛出的子类数。这将有助于更清楚地编写代码和文件。不同的捕获程序模块可以用于捕获各个异常。

 

例如:
someMethod()需要抛出3种以上的异常时public void someMethod()抛出ProjectException。

例如
public void anotherMethod() {
   try {
      this.someMethod()
   } catch(SpecificException1 exception) {
      // Do something
   } catch(SpecificException2 exception) {
      // Do something else
   } catch(ProjectException exception) {
      // Default case for handling other unexpected exceptions
   }
}

1.2   

1.2.1                                                                                                                                                标准

·         一般

1.      当创建包时确保它们使用一目了然的分级体系来进行管理。

 

例如,如果一个包的类引用第二个包的类,那么第二个包的类不应引用第一个包的类。(如果它们互相引用,它可能意味着任何一个包都不能单独使用。所有类最好在一个包中)。

 

1.      包将使用以下前缀,其中xxxxx是公司名或项目名的缩略语:com.xxxxx

2.      包名全部字母为小写

例如:
com.xxxxx.business
o bjects

1.      包的版本名应以这一包主要开发用于的项目阶段开始。

例如:

1阶段包版本:1.*

2阶段包版本: 2.*

2.      版本名中,包版本应跟在项目阶段编号之后。

3.      包的最后版本的版本名应在版本名尾包括“(FINAL)”。

1.2.2                                                                                                                                                指南

1.      为相关功能的各个单独项目或组创建新Java包。根据Java 包标准来创建和使用目录。应记录每个包的目的和有效范围。

2.      每个包中大约包括10 – 20个类。

 


1.3   

 

类可以用多种方式来声明。

如果未明确定义类的访问权限时,Friendly 为缺省访问权限。这意味着尽管类可以在同一包中的其它类访问,但不能被当前包外的其它类访问。人们常犯的一个错误是忘记声明类为 public,因此,如果您想要使用这一机制来限制类的有效使用范围,确保对其进行了描述。

正如它们名字的含义,所有对象都可以访问Public 类。这意味着它们可以被任何对象使用或继承,与包无关。

Final 类不能有任何子类。这一最终类修饰符的使用可能有些疑问,因为面向对象意味着能够继承类和它们的行为。但是,我们手边几乎没有可以继承类和行为的实例。例如,当写网络访问类时,您可能希望限制我们只能用一种方式来接入我们的网络,那么您可以创建final 类。

Abstract 允许我们定义类中至少有一种方法是抽象的。这迫使所有这一个类的子类都定义和实现抽象的方法。

1.3.1                                                                                                                                                标准

除非有非常明显的理由,否则要求提供构造函数(constructor)。这一构造函数可以有效调用父类构造函数,但子类仍需要提供该函数。务必在类的注释中说明不包括缺省构造函数的原因。

 

·        命名 标准

1.      所有类名都必须以大写字母开始,必须拼写出整个单词而且附加词的第一个字母必须为大写字母。

例如:
CapitalizedWithInternalWordsAlsoCapitalized, Customer

2.      不要为类添加前缀来标识类(除非某类名已存在)。 包名保证不要使用类名。

3.      特定类型的类后缀添加应适当。

例如:

对于每个业务对象来说(如Customer),可能有其它相关的对象-

·          服务器 上的Home对象 - 如CustomerHome

·          访问相 应服务器对象的客户stub的 对象- 如CustomerHomeStub

·          客户机 和服务器对象共享的接口 - 如CustomerHomeInterface

·          客户窗口界面 包含 代表业务对象的 视图- 如CustomerDetailsView

·          管理业务 流程客户控制器 - 如CreateCustomerManager

1.      使用javadoc注释来介绍每个类。当记录类的API时确保使用 /**    */ 标准。Javadoc包使用这一注释方法从文件夹中提取文件以创建HTML文件,从而记录我们的API。 适当地包括关于改进说明和样本使用的提示或免责声明。

类头的最小文件要求是:

·          类名

·          类的目的/说明

·          类创建者

·          创建日期

·          IBM所有权级别 (第I、II或III类)

·          修改信息:

·          修改的人员

·          修改日期

·          修改内容

 

例如:

/**  ClassName

Class description.

Author:  <Author’s name>

Date:  <Class creation date>

Ownership:  <Ownership Level>

 

Updates

       Name: <Author’s name>

       Date: <Class update date>

       Reason: <some descriptive reason>

*/

2.      注释应总是在类的引入语句之后。如果您在引入语句之前加了注释,当生成javadoc 时IBM VisualAge Java将忽略这些注释。

1.      类的版本命名应以阶段编号开始。

例如:

1阶段类版本: 1.*

2阶段类版本:2.*

2.      版本名中,类的版本应跟在阶段编号之后。

3.      类的上线版本的版本名应在结尾包括 “(FINAL)”。

1.3.2                                                                                                                                                指南

·        import

1.      仅在您从同一包中引入5种类型以上的文件时使用import的*格式,以便迅速编译。

2.      您可以在自己的代码中import所有类型文件。除非存在命名空间冲突,否则不要明确使用完整命名的文件。

3.      验证所有声明的import实际上都是为了帮助最大限度地减少编译时间。

4.      包和类名按字母顺序保存import语句以提高可读性。

1.      抽象类应至少有一个具体方法。如果抽象类只有抽象方法,实际上它应是一个接口。这是因为父类应用于共享一般行为,但接口支持执行所有行为。

内部类是在其它类中定义的类。只有外部类可以看到这些类。

注:写程序时,一些开发环境不能很好地处理内部类。

1.      内部类可用于稳藏“helper”类(如窗口的 EventListener )。这使用户可以更容易理解包中类的分层结构。

2.      保持内部类小而简单。如果类比较大或者复杂,适当时它应声明作为一个类。这将允许其它人使用这一类执行,并便于调试。

3.      内部类可以用于明确基本类的行为(如通过扩展 MouseAdapter )。但是,内部类应指定它想要的所有对外部类的调用。这意味着您可以通过了解外部类轻松了解两个类的操作。

4.      当使用内部类来监听事件时,在外部类的单一方法中初始化和注册该内部类(通常是 initialize()方法 )。

1.      当您创建类时,IBM VisualAge Java有些时候自动创建handleException()方法。这一方法开始不起任何作用。应对其进行修改以提供某些功能来报告未处理的异常。根据该方法注释中的建议,可以对那些向标准输出发送异常通知的(以下突出显示的)代码行取消注释符。

/**

 * Called whenever the part throws an exception.

 * @param exception java.lang.Throwable

 */

private void handleException(Throwable exception) {

   /* Uncomment the following lines to print uncaught exceptions to stdout */

   // System.out.println("--------- UNCAUGHT EXCEPTION ---------");

   // exception.printStackTrace(System.out);

}

1.4    属性(Attribute)

与其它一些面向对象的语言不同,Java是一种强类型语言。这意味着只能向属性(在C中也称为变量)分配一种类型的值。当声明成员变量时必须设置成员变量的类型。

 

我们可以用多种方式来声明成员变量:

·          如果未规定权限,那么Friendly为缺省访问权限。这意味着虽然它可以被在同一包中的其它类访问,但不能被当前类的子类或当前包外的其它类访问。请不要使用这一权限修饰符!

·          Public 允许其它类直接访问我们类中的成员变量。这违反了OO中的封装规则。请永远不要使用这一权限修饰符!

·          Private 不允许任何类,包括子类,访问您使用的类的成员变量。这是我们最常用的一种权限修饰符。

·          Protected 允许在同一包中的所有类和其它包中的子类访问您的类的成员变量。我们偶尔使用这一权限修饰符。

 

使用上面提到的权限修饰符,我们可以把属性按以下分类:

·          Static 使属性成为类变量。这意味着所有类实例和子类的实例将只有一个这一类变量的值。我们很少使用这一权限修饰符!

·          Final 将允许我们只能在声明成员变量时向变量分配一次值。

1.4.1                                                                                                                                                标准

1.      所有实例变量都必须以小写字母开始,而且必须全部拼写出整个单词,每个附加单词的第一个字母必须为大写。实例变量名也应能表明该属性指所代表的类的名称。

例如

theNameOfMyAttribute invoiceLineItem clientSearchController

2.      不要在成员变量名中包括类名。

例如:

不要使用 Client 类的 clientId ,只使用 id

3.      所有拥有一些集合 (如 Vector )的属性应为复数。

例如:

customers parts lineItems

1.      类中的所有成员变量应有访问方法 (settersgetters)。这取消了直接访问我们的类和子类中的成员变量的能力。优势是如果我们改变了设置或获取属性的值的方法,我们可以在访问方法中一次完成修改。

例如:

public void SomeClass {
   private String attributeName;

   //Getter method for attributeName
   public String getAttributeName() {
      return attributeName;
   }

   //Setter method for attributeName
   public void setAttributeName(String attributeName) {
      this.attributeName = attributeName;
   }
}

2.      当我们的成员变量是集合 ( Vector )时,我们将有4种访问方法:

setAttributes( Vector )   -  这一方法替换整个集合 ( 也就是设置 Vector ) 。通常这将声明为private或被构造方法调用。我们不希望外部类来设置我们的集合。

getAttributes()   -  这将返回集合。

addAttribute(object)   -  这一方法将用于向我们的集合添加一个对象。

removeAttribute( object ) 这一方法将用于从我们的集合删除一个对象。

3.      对象的Getter方法(与原始对象相反)应使用 lazy initialization (只有当它们需要时才初始化)。原始对象将自动设置为缺省值(boolean = false、int = 0等)。

1.      如果变量可用于其它文件,使用javadoc标准(/** */)来记录它。

2.      总是声明属性为private。 唯一异常将是 protected Protected 将只在我们完全需要我们的子类直接访问成员变量时使用。这一权限修饰符的负面影响如下:不仅仅是我们的子类能够访问成员变量,而且在同一包中的其它类也能访问成员变量。

3.      不要使用shadowing技术。Shadowing属性是指我们用父类中一个相同的名称来声明属性。阅读代码时它会导致混乱。

1.4.2                                                                                                                                                指南

1.      尽量使用有意义的名称 - 不要给成员变量名添加前缀 ( 如使用“ iv” “a” “my” “the” )

2.      定义算术型值时使用长整型而不是整型,双精度型而不是浮点型,循环计数器除外,它必须为整型。将来,整型浮点型数可能会变长,并且我们不必担心以后修改它们。

3.      使用访问方法来访问属性而不是直接访问属性。优势是如果我们改变了我们设置或获取成员变量值的方法,那么这些修改将通过调用这一方法自动拾取。如果您必须直接访问成员变量,使用 this.attributeName来解决有效范围模糊问题。 (如区别类的属性和方法参数/局部变量)

4.      声明数组为 Type[] arrayName 而不是 Type arrayName[] ,以保证统一,便于了解。

5.      当创建 Vectors Hashtables时声明预计的缺省大小(如果知道)。这将有助于减少为条目预留的内存空间,这些内存空间有可能永远不起作用。通过预防重新计算和多次分配 Vectors Hashtables的大小,它还可以提高程序运行。向量的缺省大小为7。如果您知道向量将有7个以上的元素,那么相应地初始化这一向量的大小。

例如:

Vector customers = new Vector(50)

6.      当声明实例变量时,设置原始变量为缺省值而不是假定它们使用Java的缺省值时(0代表整数,0.0代表浮点数,代表布尔型)。这将有助于未来开发人员更迅速、更简便和更正确地了解您的程序代码。

7.      当声明实例变量时,如果您不知道实际上是否将使用这一变量,设置对象变量为

(如 String faxNumber = null )。

8.      当声明实例变量时,如果您能确定将使用这一变量,设置对象变量为一缺省值 ( Vector policies = new Vector();)。

1.5    类常量

1.5.1                                                                                                                                                标准

1.      所有类常量名必须全部大写,必须全部拼写出整个单词并且必须使用下划线分开单词。

例如:

UPPER_CASE_WITH_UNDERSCORES_BETWEEN_WORDS

1.5.2                                                                                                                                                指南

1.      如果类定义了可能对其它类有用的常量,为该类创建一个接口(Interface),并把所有常量移到接口类中。这允许其它类来执行这一接口,并使用这些常量。用“Constants”作为接口名的后缀(如 SomeName Constants )。

2.      如果可能,使用 Constant 类型的原始数据类型而不是它们的值 [YN3]   本身。这有助于提高代码的运行效率,减少内存需求,因为很少有对象将需要初始化。

例如:

public static final int BY_ORG_NAME = 1;

而不是

public static final Integer BY_ORG_NAME = new Integer(1).

1.6    方法

我们使用多种方式来声明方法。

·          Public 允许其它类向我们的类发送消息。

·          Private 阻止所有类,包括子类,向我们的类发送消息。

·          Protected 允许在同一包中的所有类和其它包中的所有子类向我们的类发送消息。

 

使用以上提到的修饰符,可以另外把方法分类如下:

·          Static 把我们的方法转变成类方法。这意味着即使我们没有使用该类的对象时也可以调用这一方法。我们可以规定Static权限修饰符为:public static、private staticprotected static。

·          Final 方法将不能被重写。

·          Native 表示这一方法可以用其它语言在其它地方执行。它在我们需要执行一些与平台无关的代码时使用。在这声明作为这一方法的占位符。

·          Abstract 允许我们定义一个无缺省执行的方法。这迫使所有子类重新执行这一方法。

 

我们对方法声明无任何限制。我们使用何种方法权限修饰符取决于我们的使用目的。大多数时候我们将使用public。如果您不知道要使用哪一种方法权限修饰符,那么使用public。 所有为在类中方便使用而创造的消息都应被设为private

1.6.1                                                                                                                                                标准

1.      所有方法名都必须以小写字母开始、必须全部拼写出整个单词以及每个附加单词的第一个字母必须为大写。方法名也应作为方法目的的描述。

例如:

calculateTaxAmount, addLineItem, connectToServer

2.      如果一个方法获取 X 型变量,为这一方法使用以下署名: X getY()   ( Client getName() )

3.      确保方法名也可以作为这一方法目的的描述。

4.      不要在方法名中包括类名

例如:

不要使用 Client deleteClient() ,只使用 delete()

5.      使用 static getInstance () 来返回 singleton 实例。

6.      使用 static getInstance(someArguments)来返回一个类的一个具体实例。

·        方法说明(Method Arguments)

1.      所有方法说明名应遵循与类变量相同的命名标准。

2.      在方法的javadoc注释中记录每种说明。

·        局部变量

1.      方法内的所有局部变量应遵循与类变量相同的命名标准。

2.      不要在同一方法内重复使用(重新分配)临时变量 ,而是使用不同的临时变量,除非您确实需要这一临时变量,如下例所示:

例如:

for (int i=o ; i<vector.size(); i++) {
   String tempPartNumber = vector.elementAt(i);
   // do something with the part number
}

3.      除非您需要尽早定义局部变量(也就是在试运行程序模块之前),否则请在代码中准备使用局部变量时声明它。

1.      使用 javadoc 标准来说明所有方法的特性、目的、先决条件、效果、算法注释、使用说明、 reminders 等。类的成员变量的Getter和setter方法只需要在它们的javadoc文件中说明方法返回类型和参数,除非这一方法所起的作用远远不只是简单返回一个成员变量值。以下的最小文件要求仅适应于non-getters、non-setters、getters和setters,它们不仅仅是返回或设置一个成员变量值。

最小文件要求是:

·          方法的目的/说明

·          返回类型 (javadoc directive)

·          参数类型 (javadoc directive)

例如:

/**
 * Inserts element at the front of the sequence

 * @return int

 * @param element Object
 *
*/
public int addFirst(Object element) throws InvalidArgumentException {
    //Statements here...
}

2.      尽量使程序代码清楚明了。对于不清楚的代码,使用// 注释方法加以说明。

例如:

int index = -1;   // -1 serves as flag; the index isn't valid

覆盖通过以下两种方法之一完成:

·          当您覆盖一个父类方法以向其添加功能时,不要忘记首先调用父类方法。

·          当您覆盖一个父类方法以修改功能时,那么不要发送父类方法。

 

1.      如果您覆盖 Object.equals() ,那么您也要覆盖 Object.hashCode() ,反之亦然。

2.      如果 Serializable 类依靠在程序中可能不同的任何声明,包括(尤其是) hashCodes transient 字段时,覆盖 readObject writeObject

3.      如果与父类的执行无任何不同,不要覆盖一个方法。

1.6.2                                                                                                                                                指南

1.      方法通常只执行一个事件,它将有助于保持方法的小规模。这有助于提高可读性和减少变量混乱(说明和局部变量)以及鼓励重复使用。

2.      向任何局部变量分配空值,这些局部变量不再用于帮助最大限度地缩小范围,以及不能把其作为垃圾变量来收集。(这包括数组和其它collections的元素)。您可以选择是否使用这一项功能。

1.      仅在永远不覆盖这一方法时声明该方法为final。

2.      仅在方法不能被其子类访问时声明这一方法为private,因为其执行可能会随时更改。

3.      声明访问 ( 咨询 [YN4]   / 更新 / 增加 / 删除 )collections 的方法为 synchronized ,它们是实例或类变量。

4.      声明使用变量的方法为synchronized,这些变量依赖于可以使用另一种方法进行更新的其它变量(如Point类:x和y不应同时被多线程访问或更新)。

5.      在基本类中使用abstract方法,而不是no-op执行,以减少与实际使用的方法有关的混乱。

6.      声明提供一般缺省执行的父类方法为protected ,从而使子类编程人员可以覆盖或继承父类。

 

方法 重载 C++上运行。程序人员使用这一项技术与后台运行这一项技术不同。Smalltalk等纯面向对象的语言不支持这一点。当对代码进行维护时,尤其是当备选方法有少许不同的行为时,方法 重载 会引起某些混乱。(注:覆盖与 重载 不同。)

 

1.      尽可能避免 重载 方法。而是更改方法名,使其更容易进行描述。

例如:

public void someMethod() {
   do something…
}

public void someMethodWithAge(Integer age) {
   do something with age…
}

public void someMethodWithAgeAndName(Integer age, String name) {
   do something with age and name…
}

 

2                                              附件

2.1            附件A - Javadoc

2.1.1        标记

标记/ 使用

适应于类

适应于方法

适应于变量

@author originalAuthorName

X

 

 

@version versionNumber

X

 

 

@param argName argDescription

 

X

 

@return description

 

X

 

@exception classNamedescription

 

X

 

@see className

X

X

X

@see className#methodName

X

X

X

@deprecated explanation

X

X

X

@since version

X

X

X

 

此时您可能希望在您的文件中使用HTML标记,因为javadoc创建HTML。以下列出了一些有用的HTML标记:

 

<BR>

强制换行(一个空格)

<P>

强制分段(两个空格)

<B> </B>

加粗标记之前的文本

2.2            附件B – 运行指南

 

每个应用程序都有不同的运行要求,而且简单性和运行之间总是相对的。以下我们提供了一些简单指南,您可以选择应用它们以改进应用程序的运行。

2.2.1        同步

1.      同步成本时间(获取和检查锁(locks))。如果您知道应用程序的一部分为单线程,您不需要编写 synchronized 方法。您还可以考虑重新执行标准Java类(如向量)以取消同步。

2.2.2        缓冲

1.      创建对象的新实例是一项耗时的工作。考虑缓冲对象和执行 reinitialize()方法,从而可以重复使用缓冲的版本 (如:把对象添加到静态向量而不是把对象作为垃圾对象收集。当需要对象的新实例时,您可以从对象表中删除一个实例并重新初始化对象表。)

2.      在方法中,程序访问对象的成员变量的时间要超过访问静态变量和局部变量的时间。在方法开始时考虑把成员变量复制到局部变量中,然后在方法结束时更新变量值。

2.2.3        异常

1.      如果程序中的条件不太可能发生,把这一条件作为一个异常来执行是值得的。Java可以比条件测试更有效地处理试运行( try)和捕获程序模块 。但是,如果发生异常,它的成本要高于简单的条件测试成本。

2.3            附件 C----Java 编程建议

   本附录包含大量有用的建议,可帮助大家避免进行低级的程序设计,并提供了代码编写的一般性指导:

1 .若在定义中出现了常数初始化字符,则大写 static final 基本类型标识符中的所有字母这样便可标志出它们属于编译期的常数。

2 .为了常规用途而创建一个类时,请采取“经典形式”,并包含对下述元素的定义:
equals()
hashCode()
clone()(implement Cloneable)
implement Serializable

3 .对于自己创建的每一个类,都考虑置入一个 main() ,其中包含用于测试那个类的代码。为了使用一个项目中的类,我们没必要删除测试代码。若进行了任何形式的改动,可方便地返回测试。这些代码也可作为任何使用类的一个示例使用。

4 .应将方法设计成简要的功能性单元,用它描述和实现一个不连续的泪接口部分。理想情况下,方法应简明扼要。若长度很大,可考虑通过某种方式将其分割成较短的几个方法。这样做也方便于类内代码的重复使用(有些时候,方法必须非常大,但它们仍应只做同样的一件事情)。

5 .设计一个类时,请设身处地为需要调用你的类的其他程序员考虑一下(类的使用方法应该非常明确的)。然后,再设身处地为管理代码的人考虑一下(预计有可能进行哪些形式的修改,想想用什么方法可把它们变得更简单)。

6 .使类尽可能短小精悍,而且只解决一个特定的问题。下面是对类设计的一些建议:
■一个复杂的开关语句:考虑采用“多形”机制。
■数量众多的方法渋及到类型差别极大的操作:考虑用几个类来分别实现。
■许多成员变量在特征上有很大的差别:考虑使用几个类来分别实现。

7 .让一切东西都尽可能地“私用”—— private 。可使库的某一部分“公共化”(一个方法、类或一个字段等等),就永远不能把它拿出。若强行拿出,就可能破坏其他人现有的代码,使他们不得不重新编写和设计。若只公布自己必须公布的,就可放心大胆地改变其他任何东西。在多线程环境中,隐私是特别重要的一个因素——只有 private 字段才能在非同步使用的情况下受到保护。

8 .谨防“巨大对象综合症”。一些习惯于顺序编程思维且初渋 OOP 领域的新手,往往喜欢先写一个顺序执行的程序,再把它嵌入一个或两个巨大的对象里。根据编程原理,对象到应该是应用程序的概念,而不是应用程序本身。

9 .若不得已进行一些不太雅观的编程,至少应该把那些代码置于一个类的内部。

10 .任何时候只要发现类和类之间结合得非常紧密,就需考虑是否采用内部类,从而改善编码及维护工作 .

11 .尽可能细致地加上注释,并用 javadoc 注释文档语法生成自己的程序文档。

12 .避免使用“魔术数字”,这些数字很难与代码很好地配合。如以后需要修改它,无疑会成为一场噩梦,因为根本不知道“ 100 ”到底是指“数组大小”还是“其他全然不同的东西”。所以,我们应创建一个常数,并为其使用具有说服力的描述性名称,并在整个程序中都采用常数标识符。这样可使程序更易理解以及更易维护。

13 .渋及构建器和违例的时候,通常希望重新抛出在构建器中捕获得到的任何违例——如果它造成了那个对象的创建失败。这样一来,调用者就不会以为那个对象已正确地创建,从而盲目地继续。也就是说,构造函数需要能抛出例外。

14 .当你创建的对象被其他程序员用完以后,若你的类要求进行任何清除工作,可考虑将清除代码置于一个良好定义的方法里,采用类似于 cleanup() 这样的名字,明确表明自己的用途。除此以外,可在类内放置一个 boolean (布尔)标记,指出对象是否已被清除。在类的 finalize() 方法里请明确对象已被清除,并已抛出了从 RuntimeException 继承的一个类(如果没有的话),从而指出一个编程错误。在采取像这样的方案之前,请确定 finalize() 能够在自己的系统中工作(可能需要调用 System..runFinalizersOnExit(true), 从而确保这一行为)。

15 .在一个特定的作用域内,若一个对象必须清除(不由垃圾收集机制处理),请采用下述方法:初始化对对象;若成功,则立即进入一个含有 finally 从句的 try 块,开始清除工作。  

16 .若在初始化过程中需要覆盖(取消) finalize(), 请记住调用 super.finalize()( Object 属于我们的直接超类,则无此必要 ) 。在对 finalize() 进行覆盖的过程中,对 super.finalize() 的调用应属于最后一个行动,而不应是第一个行动,这样可确保在需要基础类组件的时候它们依然有效。

17 .创建大小固定的对象集合时,请将它们传输至一个数组(若准备从一个方法里返回这个集合,更应如此操作)。这样一来,我们就可享受到数组在编译期进行类型检查的好处。此外,为使用它们,数组的接受者也许并不需要将对象“造型 (Cast) ”到数组里。

 

18 .尽量使用 interfaces, 不要使用 abstract 类。若已知某样东西准备成为一个基础类,那么第一个选择应是将其变成一个 interfaces( 接口 ) 。只用在不得不使用方法定义或者成员变量的时候,才需要将其变成一个 abstract( 抽象 ) 类。接口主要描述了客户希望做什么事情,而一个类则致力于(或允许)具体的实施细节。

 

19 .在构建器内部,只进行那些将对象设为正确状态所需要的工作。尽可能地避免调用其他方法,因为那些方法可能被其他人覆盖或取消,从而在构建过程中产生不可预知道结果。

 

20 .对象不应只是简单地容纳一些数据;它们的行为也应得到良好的定义。

在现成类的基础上创建新类时,请首先选择“新建”或“创作”。只有自己的设计要求必须继承时,才考虑这方面的问题。若在本来允许新建的场合使用了继承,则整个设计会变得没有必要的复杂。

 

21 .用继承及方法覆盖来表示行为间的差异,而用字段表示状态间的区别。一个非常极端的例子是通过对不同类的继承来表示颜色,这是绝对应该避免的:应直接使用一个“颜色”字段。

 

22 .为避免编程时遇到麻烦,请保证在自己类路径指到的任何地方,每个名字都仅对应一个类。否则,编译器可能先找到同名的另一个类,并报告出错信息。若怀疑自己碰到了类路径问题,请试试在类路径的每一个起点,搜索一下同名的 .class 文件。

 

23 .在 Java 1.1  AWT 中使用事件“适配器”时,特别容易碰到一个陷阱。若覆盖了某个适配器方法,同时拼写方法没有特别讲究,最后结果就是新添加一个方法,而不是覆盖现成方法。然而,由于这样做事完全合法的,所以不会从编译器或运行期系统获得任何出错提示——只不过代码的工作就变得不正常了。

 

24 .用合理的设计方案消除“伪功能”。也就是说,假若只需要创建类的一个对象,就不要提前限制自己使用应用程序,并加上一条“只生成其中一个”注释。请考虑将其封装成一个“独生子 (Singleton) ”的形式。若在主程序里有大量散乱的代码,用于创建自己的对象,请考虑采纳一种创造性的方案,将这些代码封装起来。

 

25 .警惕“分析瘫痪”。请记住,无论如何都要提前了解整个项目的状况,再去考察其中的细节。由于把握了全局,可快速认识自己未知道一些因素,防止在考察细节的时候陷入“死逻辑”中。

 

26 .警惕“过早优化”。首先让它运行起来,在考虑变得更快——但只有自己必须这样做,而且经证实在某部分代码中的确存在一个性能瓶颈的时候,才应进行优化。除非用专门的工具分析瓶颈,否则很可能在浪费自己的时间。性能提升的隐含代价是自己的代码变得难于理解,而且难于维护。

 

27 .请记住。阅读代码的时间比写代码的时间多得多,因此注释一定要详尽。思路清晰的设计可导致易于理解的程序,但注释、细致都是相当重要的。如对此仍有怀疑,那么请试想自己试图从联机 Java 文档里找出有用信息时碰到的挫折,这样或许能将你说服。

 

28 .如认为自己已进行了良好的分析、设计或者实施,那么请稍微更换一下思维角度。试试邀请一些外来人士——并不一定是专家,但可以使来自本公司其他部门的人。请他们用完全新鲜的眼光考察你的工作,看看是否能找出你一度熟视无睹的问题。采取这种方式,往往能在最适合修改的阶段找出一些关键性问题,避免产品发行后再解决问题而造成的金钱及精力方面的损失。

良好的设计能带来最大的回报。简言之,对于一个特定的问题,通常会花较长的时间才能找到一种最恰当解决方案。但一旦找到了正确的方法,以后的工作就会轻松多了,再也不用经历数小时、数天或者数月的痛苦挣扎。我们的努力工作会带来最大的回报(甚至无可估量)。而且由于自己倾注了大量心血,最终获得一个出色的设计方案,成功的快感也是令人心动的。坚持抵制草草完工的诱惑——那样做往往得不偿失。

 

29 程序中,如果不涉及到兼容性的问题,则不要使用 Vector,HashTable Enumeration 这三种对象,取而代之的使用 ArrayList,HashMap Iterator 这三种对象。 Vector,HashTable Enumeration 这三种对象是 java1.0 版本出现的类,虽然他们有一些优点,但他们的效率实在是太低了!所以 SUN java1.2 版本中才又出现 ArrayList,HashMap Iterator 来取代他们的功能。详细情况可以参考《 Think in Java 》第一版。 http://www. bruceeckel.com/