- 文件构成
- 文件名
一个类对应一个.java文件,如:public class Point是在Point.java文件中。
一个包内的非public方法,可以包含在经常被调用的public类中(这种情况下,.java与.class可以不对应——如内部类(笔者注))。
-
文件的位置
对于myProject.framework包,其文件全路径为<ProjectRoot>/myProject/framework。
对于JP.co.esm.wiki.extremedomo包,其文件全路径为。<ProjectRoot>/JP/co/esm/wiki/extremedomo
-
测试类名
类Point的测试类:命名为PointTest.java。
包JP.co.esm.wiki.extremedomo的测试类:命名为ExtremeDomoTest.java。
其它规则——ClassName类的单元测试类,命名为ClassNameUt(Ut是UnitTest的简写)。
-
测试类的位置
测试类一般位于被测试类的同一目录下,或该目录的名为“test”的子目录下。
原因:如果测试类不与被测试的对象在一起,其存储路径容易被忘记。而测试类与产品的分离工作可以交给一些测试工具来进行(如:makefile, Ant的build.xml 等)。
其它规则——把测试类的子目录命名为“ut ”。
其它规则——分离测试类,如包JP.co.esm.wiki.extremedomo的测试类放在包test.JP.co.esm.wiki.extremedomo中。
- 文件名
- 命名规则
- 包名
以“.”进行区分。如:JP.co.your.domainname.projectname - 文件名
根据编译程序的规则,公有类名必须与文件字相同(包括大小写)。 - 类名
以大写字母开头,并以大写字母区分单词。如:HelloWorld - 异常类名(例外类名)
类名以Exception结尾。如:IllegalArgumentException - 接口名
与类的命名规则相同。如:Attributed
如果有必要与类相区别,则以“I ”开头。如:IAttributed。
再有,如果这个接口表示的是一种可归属于各种不同类的不同对象的属性,这些属性是根据对象“有能力”做的事情来定义的,则命名以-able结尾表示该接口具有某种功能。如:Runnable,Clonabe, Serializable, … - 封装类名
特别指出的是,如果有必要与接口进行区分,实现类名可以以Impl结束。如:AttributedImpl - 抽象类名
当抽象类找不到合适的名字的时候,可以把Abstract 放在子类的开头。
如:AbstractBeforeSubClassName - 常量(static final)
全为大写字母,并以“_”区分单词。如:UPPER_CASE_WITH_UNDERSCORES - 方法名
以小写字母开头,以大写字母区分单词。如:HelloWorld - 工厂方法(factory method)名(用于创建对象的方法)
X newX()
X createX() - 转换方法(把一个对象类型转换成另一个对象类型的方法)
X toX() - 取得属性的方法
X x()
X getX() // 处理JavaBeans 中的属性(推荐)
boolean isEnabled() // 处理JavaBeans 中的属性(推荐) - 设置属性的方法
void setX(X value) // 处理JavaBeans 中的属性(推荐) - 返回boolean值的方法
is + 形容词,can + 动词,has + 过去分词,动词的第三人称单数形式(简称“动词三单”或“三单元动词”),动词三单+ 名词。
boolean isEmpty() // 处理JavaBeans 中的属性(推荐)
boolean empty() // 不行!这个动词不能很好地表达‘空’这个意义
boolean canGet()
boolean hasChanged()
boolean contains(Object)
boolean containsKey(Key) - boolean变量
形容词,is + 形容词,can + 动词,has + 过去分词,动词三单,动词三单+ 名词。
boolean isEmpty
boolean dirty
boolean containsMoreElements - 英语和日语
基本上所有的识别子使用英文来表示,另外做成日英对应用字典,在整个工程生命周期中做维持(维护,保护)
-
名称的对称性
在给类、方法命名时,注意使用诸如下面的那些具有对称性的词汇。
add/remove
insert/delete
get/set
start/stop
begin/end
send/receive
first/last
get/release
put/get
up/down
show/hide
source/target
open/close
sorece/ destination
increment/ destination
lock/ unlock
old/ new
next/ previous
-
循环计数
小范围内进行的循环计数,在迭代程序中按顺序使用i, j, k(作为控制循环的参数)。
-
使用域小的名字
对于使用域小的变量名,可以采用类型的简化形式。
如:ServeletContext sc = getServletContext();
-
省略意义的名字
可以根据变量参数名来领会意义的参数。
反例:copy(s1, s2)
正例:copy(from, to) 或 copy(source, destination)
-
无意义的名字
谨慎使用如Info, Data, Temp, Str, Buf这样的名字。
反例:double temp = Math.sqrt(b*b - 4*a*c);
正例:double determinant = Math.sqrt(b*b - 4*a*c);
-
大写和小写
虽然(java语言中)区分大小写,但不可只以大小写来区分名字。
-
其它
另外,有时候也根据项目的要求来使用以下的的命名规则。
局部(local)变量:lower_case_with_underscore
private/protected变量:_prefixUnderscore 或 suffixUnderscore_
static private/protected变量:__twoPrefixUnderscores 或 twoSuffixUnderscores__
- 包名
-
使用原则
-
编码方式
编码方式是以Sun Microsystems, Inc 的 JDK为标准的。基本方式与K&R 的C语言编码方式相同,类和方法的定义开始的“{”不另起一行写。
/**/ /* COPYRIGHT ... =====⇒版权信息写的文件开头。注意这里不是“/**”,而是“/*”* ...
*/
package myProject.util; =====⇒接下来是package,空一行列出import
import java.util.Stack;
import java.util.Vector;
/** */ /** =====⇒ |在类定义之前(紧挨着类定义),从“/**”开始写注释。第一* Stack を表現するクラス. |行简要地介绍类,用半角句号结束。下面进行详细的说* オブジェクトのpush, pop が可能. |明。另起一行时,以“*”开始,与前面的第一个“*”相呼应。
*
* @author Kenji Hiranabe =====⇒@author部分必须写
*/
public class Stack ... { =====⇒类定义开始,“{”不换行
/** *//** =====⇒|方法的定义与类相同。如果有@param, @return, * 要素を追加する. |@exception,必须写。?--按照必要的@see等。--?
* @param item 追加する要素
*/ |定义开始,“{”不换行;
public void push(Object item) ...{ =====⇒|缩进,1TAB=4 SPACE。if (itemCapacity <= itemCount) ...{ =====⇒if, while等的关键字与“(”间空一格。(方法名之后的“(”没有空格)。“(”之后没有空格,演算子前后各一个空格。“)”之后加一个空格,与“}”连接。
// ...
} else ...{ =====⇒注意if/ else的“{”与“}”的位置
// ...
}
}
/** *//**
* 先頭要素を取得する.先頭要素は取り除かれる.
* @return 先頭要素
*/
public Object pop() ...{
// ...
return top; =====⇒return值不用“()”括起来
}
}
-
长的行
一行最多为80个字符,如果超过了就需要断行。断行的方法有:1)利用局部变量;2)根据逗号换行;3)在优先级低的运算符前断行。
如:
double length = Math.sqrt(Math.pow(Math.random(), 2.0 ) +
Math.pow(Math.random(), 2.0 ));
//方法1
double xSquared = Math.pow(Math.random(), 2.0 );
double ySquared = Math.pow(Math.random(), 2.0 );
double length = Math.sqrt(xSquared + ySquared);
//方法2
double length = Math.sqrt(Math.pow(Math.random(), 2.0 ,
Math.pow(Math.random(), 2.0 );
//方法3
return this == obj
|| ( this .obj instanceof MyClass
&& this .field == obj.field);
-
长的声明行
如果类或方法的声明很长,1)根据extends/implements/throws换行;2)根据逗号换行。
如:
public class LongNameClassImplemenation
extends AbstractImplementation,
implements Serializable, Cloneable ... {
private void longNameInternalIOMethod(int a, int b)
throws IOException ...{
// …
}
public void longMethodSignature(int a, int b, int c,
int d, int e, int f) ...{
// …
}
// …
}
-
import
在import中,尽量不要使用“*”;从同一个包中引入三个以上的类时,用“*”。
理由:增强可读性。
-
abstract class vs. interface
尽量不使用抽象类(abstract class),而使用接口(interface)。抽象类仅在有一部分被封装,一部分是抽象方法时使用。
理由:接口可以有多重继承,但类只能是单一继承。
-
public variable
实例变量(笔者理解为“类字段”)不推荐设为public,应对它们设置合适的访问方法。
理由:对象指向的标准。类的内部状态被随意访问不好。
但是,如果满足下面的条件,把实例变量设为public,直接对它们进行访问也是可以的:-
这个实例变量与别的实例变量相独立,他单独的变更不会影响内部的整合性。
-
对任一个都有getX()/ set()方法。
- インスタンス変数の実装が将来に渡って変更されないことが根拠付けられる.
另外,如果不满足上面的条件,但要十分注意速度的情况下,不受这个限制。(但还是要慎重使用)
如:在Stack类中,不能将itemCount属性设为public,在Point类中,将x,y设为public也可以(十分注意速度的时候,ex.Java3D 的 Vector/Point类)。
Stack s = new Stack();
s.itemCount = 79 ; // 这种情况会使内部状态瓦解
Point p = new Point();
p.x = 30 ; // 不会使内部状态瓦解
-
- 初始化
不以初始化为目的(参数没有被null初始化)。还有,不能两次初始化。
反例:
class PoorInitialization ... {
private String name = "initial_name";
public void Sample() ...{
name = "initial_name";
}
} - 避免static变量
极力避免使用static变量(类变量)。(static final常量除外)
理由:static变量叫Semi-Global比较好,より文脈依存なコードを招き,副作用を覆いかくしてしまう.(static变量,也可以称为准全局变量。更由于上下文的依赖关系编码的引用,会产生未预料到的效果。-- 网友 更正于2008/04/07) - private vs. protected
与其使用private,不如使用protected。
理由:private は確実にそのクラス外からの使用をシャットアウトできるが,クライアン
トが,より細かいチューニングをsubclass 化によって行うことを出来なくしてしまう.
另:有时候需要使用private。如果使用的是protected,会对之后继承它的所有子类产生影响。 - get/set方法
避免胡乱生成用于访问实例变量的public getX()/setX()方法。有必要进行讨论,作成更有意义的方法。
理由:多数实例变量与其它的实例变量有着依赖关系。不能破坏类内部的整合性。 - 变量隐藏
避免使用与超级类变量相同的变量名。
理由:一般来说那种情况都存在bug。如果有这方面的意图,要对其进行注释。 - 数组声明
数据声明为Type[] arrayName。
由于:Type arrayName[]不过是C语言留下来的习惯。
如:
static void main(String[] args); --- ○
static void main(String args[]); --- × -
public方法
类的public方法是以“自动贩卖机的接口”为目标的。设计成易懂,就算使用方法错了,也不会影响内部的整合性。还有,可能的话,实行按合同进行的设计,把类的不变条件和方法的事前事后条件一起用代码表现出来。 -
状态取得和状态变更的分离
方法是被设计成(只)做“一件事”。特别的,状态变更和状态取得这两服务不能用一个方法来实现。负责状态变更的方法return值为void。Stack的例子中,top()和removeTop()这两步要比pop()要好。
理由1:只做一件事的方法容易懂。(Stack的例子中,惯用pop()方法)。
理由2:并行性的控制,容易做到对异常安全的保护(参考:在C++中,pop()方法不能做到异常安全,所以pop()被作成了不能返回值的式样)。
理由3:容易做子类化地扩展。 -
this的return
即使假装考虑了方便客户,也应该避免return this。
理由:叫做 a.meth1().meth2().meth3() 的连锁一般会成为synchronization上的问题之源。 -
方法的多重定义
避免过多使用根据引数类型来区分的方法(引数数量不同的可以)。特别是和继承一起使用的话较麻烦。如:
× : draw(Line), draw(Rectangle)
○ : drawLine(Line), drawRectangle(Rectangle)
○ : draw(Shape) - equals()和hashCode()
由于重载Object.equals()方法,同是hashCode()方法也能重载。反之亦然。
理由:因为对应Container类(Hashtable)等。 - clone()
如果使用clone()方法,需要封装Cloneable并清楚标明。
如:
class Foo implements Cloneable ... {
// ...
public Object clone() ...{
try ...{
Foo foo = (Foo) super.clone();
// Foo 类属性的克隆
// ...
} catch (CloneNotSupportedException e) ...{
// 因为implements Cloneable, 所以不能发生
throw new InternalError();
}
}
}
理由:shallow copy中不好的例子很多。 - 缺省构造方法
如果有可能,无论什么时候都要准备缺省的构造函数(没有参数的那种)。
理由:在Class.newInstance()里,可以根据类名的字符串来创建类。 - abstract method in abstract classes
在abstract类中写no-op的方法,明确声明为abstract方法。并且,如果可以准备可公用的缺省的封装,将其声明为protected,使子类可以在一行写处理。
理由:java编译器能检查出没有被封装的abstract方法,可以避免所谓忘记单个被封装的bug。 - Object的同值比较
使用equals()方法对Object进行比较,而不使用“==”。特别的,String 的比较中必须使用“==”。
理由:如果封装者准备了equals()这个方法,就是希望使用这个封装。equals()的缺省封装,仅仅是“==”而已。
理由:单元测试使用的是assertEquals中的equals(),所以可以简单地写同值测试。 - 声明和初始化
局部变量与初始值一起声明。
理由:最小化变量的假定值。
反例:
void f( int start) ... {
int i, j; // 无初始值声明
// 更多的代码
// ...
i = start + 1;
j = i + 1;
// 使用i, j
}
正例:
void f( int start) ... {
// 更多的代码
// ...
// 使用前进行声明和初始化
int i = start + 1;
int j = i + 1;
// 使用i, j
} - 局部变量的重复利用不好
与其重复印使用某局部变量,不如新声明并初始化一个。
理由:最小化变量的假定值。
理由:有利于编译程序的最优化。
反例:
void f( int N, int delta) ... {
int i; // 无初始值声明
for (i = 0; i < N; i++) ...{
// 使用 i
}
for (i = 0; i < N; i++) ...{// 还使用i
if (...) ...{
break;
}
}
if (i != N) ...{ // 判断是否回到最后时使用了i
// ...
}
i = N – delta*2; // 再使 用
// ...
}
正例:
void f( int N, int delta) ... {
for (int i = 0; i < N; i++) ...{
// 使用i
}
for (int i = 0; i < N; i++) ...{
// 使用其他的iif (...) ...{
found = true;
break;
}
}
if (found) ...{
// ...
}
int total = N – delta*2; // 有其他含义的变量
// ...
} - if/ while条件中的“=”
if, while的条件中,必须使用代入符号“=” 。
理由:一般情况下都是bug。只要不是boolean类型的,java编译都能捕捉这种bug。 - 比较大小的运算符
尽量使用“<”,“<=”,尽量避免使用“>”,“>=”。
理由:统一大小方向,右边的值大于左边的值,以避免混乱。 - Cast
Cast尽可能用instanceof的条件文包围。
C cx = null ;
if (x instanceof C)
cx = (C) x;
else
evasiveAction();
理由:在这里,经常会习惯性地考虑:“如果不是这个对象的实例呢?”。不过,如果可以判断不能进行类型转换(Cast)的时候就有bug,这种情况不在此限制以内。 - 异常类
?--异常类有使用泛围大的特点,难以读取多次使用的程序流。--?
对于异常类,如果可以使用JDK标准包中已有的内容,尽量加以利用,而不是新创建一个异常类。
如:IOException, NoSuchFileException, IllegalArgumentException,等,都是常用的异常类。
如果要创建新的异常类,要把它作为对应包的全体接口来讨论。 - 方法参数的变更是不好的
原则上方法参数需要输入, 不用返回。即在方法内部不调用改变参数状态的方法。不在返回参数中代入新的对象(如果可能的话设为final)。
反例:
void moveX(Point p, int dx) ... {
p.setX(p.getX() + dx); // 参数变更(尽量避免)
}
void moveX(Point p, int dx) ... {
p = new Point(p.getX() + dx, p.getY());
// 这里不传递给调用程序
}
例外:需要注意性参的情况下 - 方法参数的名字
应使方法参数容易读取。特别是在与实例变量重复时,活用this,可以使参数的读取较为容易。
反例:
void reset( int x_, int y_) ... {
x = x_;
y = y_;
}
正例:
void reset( int x, int y) ... { // 不要将参数名取为 x_, y_ 等
this.x = x;
this.y = y;
} - toString()
toString()方法如果可能要随时封装。
理由1:用System.out.println(object)可随时打印。
理由2:如果在单元测试等(过程)中失败了,其显示容易被区分。 - switch, if/else的循环处理是不好的
在用switch文进行分支处理的时候,要考虑到这是否为不良设计的征兆,同时还要考虑是否可以用多态性来实现。特别是有2个以上相同的switch时,一定要用多态性、FactoryMethod(工厂方法)、Prototype(原型)等来实现。if/else也是一样的。并且,如果同样的用于检查null的if很多,考虑采用NullObject类型。 - String和基本类型的变换
从int到String的互逆变换,如下(其它基本类型也一样)
String s = String.valueOf(i);
int i = Integer.parseInt(s);
理由:虽然有其它的方法,但上面的方法最易懂最有效。
其它方法:(不推荐)
String s = “” + i;
String s = new Integer(i).toString();
String s = Integer.toString(i); // 这样不好
int i = new Integer(s).intValue();
int i = Integer.valueOf(s).intValue(); - collection
如果环境允许,请使用JDK1.2以后的collection类。也就是说,不用Vector、Hashtable,Enumeration,而用List(ArrayList),Map(HashMap),Iterator。
理由1:可以简洁地使用有逻辑、有连贯性的方法名。
理由2:通运List,Set,Map接口,或不改变接口就能实现替换。
理由3:由于有同步化的操作,可以写成高速的代码(有这样的可能性)。
参考:JDK1.2 collection使用指南 http://www.objectclub.jp/technicaldoc/java/jdk
-
- 注释
- javadoc的活用
多用“/**注释*/”的注释。这样的注释,由与javadoc同样的工具可以变为HTML形式的文档。
java的注释有3种类型。
/** ... */ javadoc注释。输出为html形式的文档
/* ... */ 一般的注释。内部的
// 一般的注释。内部的
public类,方法,域必须要加“/** ... */ ”的注释。 - 长注释
注释要跨多行时,应在最初用一句话概括,然后后面加长注释。 - javadoc标签(tag)
“/** ... */ ”注释中,从“@”开始是关键字(javadoc标签)。
@author author-name
@param paramName description
@return description of return value
@exception exceptionName description
@see string
@see URL
@see classname#methodname
param, return有特别要注意之处,要进行主旨注释。如,用于输出的参数,被变更时。
例1:
/** */ /**
* 取得边界框.
*
* @param b 边界框(用于内存与速度效率,输出参数)
*/
void getBBox(BBox b) ... { b.min = this.min; b.max = this.max; } - 类注释
“/** ... */ ”用于功能概要,也就是外部规格的描述,写在类方法的定义开始之前。这个注释的第一行有特别处理。即,被html的Method Index(方法目录)使用。因此,最开始的第一行是注释对象的外部功能的简短说明。这行以半角“.”或<br>HTML标签结束。接着第一行进行功能说明。
如:
/** */ /**
* 表现堆的类. 堆为先进后出的数据结构.
* <p>
* 元素总数保存到count中,元素保存到Vector中.
*
* @see util.Vector
* @author yourNameHere
*/
public class Stack ... {
/** *//**
* 当前的元素总数. 非负且小于capacity .
*/
protected int count;
/** *//**
* 取出上面的一个元素. 元素总数减1.
*
* <pre>
* 使用例子:
* Stack s = new Stack(10);
* s.push(99);
* int i = pop(); // i必须是99
* </pre>
*
* @return 上面的元素
*/
public int pop() ...{ ... }
}
在注释中,写“使用例子”等的时候,也可用<pre> </pre>围住自动进行缩排,以避免改行。 - // 和 /* */
方法和类的内部的注释,是用“ /* */ ”还是“ // ”,可以根据注释的长度来判断。
一行注释最好用“ // ”。
例1:
/**/ /*
* 戦略:
* 1. まずnode を探す
* 2. clone する
* 3. inserter にclone を追加要請
* 4. 成功したら,node を削除
*/
例2:
int index = - 1 ; // -1 表示不合法
参考:这个方法最好。
static final int INVALID = - 1 ;
int index = INVALID; - Design by Contract (根据合同进行设计)
因为要按照合同进行设计,所以在工程中添加Assert类。用Assert类来表现合同。
如:
class Stack ... {
private int capacity;
private int itemCount;
public void push(Object o) ...{
Assert.require(o != null); // 事前条件
// ...
// ...
Assert.ensure(this.contains(o)); // 事后条件
}
public boolean invariant() ...{ // 不变条件
Assert.invariant(0 <= capacity);
Assert.invariant(0 <= itemCount);
Assert.invariant(itemCount <= capacity);
return true;
}
}
补充:在J2SE1.4以后,assert为关键字。
- javadoc的活用
- 性能
- 首先进行检测
性能的改善从检测开始。不能乱猜。 - new
在java中new费时间。多重循环中调用new的时候,如需要,使用输出参数。
X getX() ... {
return new X(this.value);
}
上面代码比较慢时,让调用程序new,如下,
void getX(X x) ... {
x.setValue(this.value);
} - synchronized
synchronized(同步的)费时间。不要同步化所有类,而只对必要的部分进行同步。再有,Vector, Hashtable有默认的同步化重载。最好使用ArrayList,HashMap对必要的部分进行同步。(用Collections.synchronizedCollection进行外部同步) - 为变量赋null值
当有大量不使用的变量时,积极把它们设为null。特别是,数组的元素(对性能有严格要求的情况下)。
理由:有利于垃圾回收处理(ガベージコレクション)。
- 首先进行检测
- 其它
- 在自己重新编制前进行讨论
别人创建的类中有时需要新的方法,若要自己继承(extends)该类来创创建新的类,或将该类作为实例变量作成类时,首先要与该类的作者商谈。如通用形式满足其要求,则可使用原来的类。 - 复杂的设计不好
设计有迷惑时,多数情况是注重‘Simplicyty’方法,或要与java语言的特性良好一致。java语言的设计原理是KISS(Keep It Small and Simple)。同时,在后面的维护中,“Simplicyty”也很重要。 - 性能调整应在测试后
没有编码从一开始就注意性能的。要优先保证易读、易维护。性能应在测试后进行改善。 - 过于精巧的代码不好
要写一般的java程序员都能理解的代码。要假定对于运算符的顺序、初始化的规则等,谁都不能肯定有自信,要用()明确运算顺序,进行明确的初始化,这样才易读。
反例:return cond == 0 ? a < b && b < c : d == 1;
正例:return (cond == 0) ? ((a < b) && (b < c)) : (d == 1);
反例:
// 写成单位行列,但费时间,谁也不好读。
for (int i = 1; i <= N; i++)
for (int j = 1; j <= N; j++)
M[i-1][j-1] = (i/j)* (j/i); - 没有100%的正确
这里所写的,并是100%作为标准。如有困惑要进行整理、讨论。常会有有充分理由不用规则的情况。本编码标准的目的是希望能对团结协作的团对有所帮助。
- 在自己重新编制前进行讨论
- 謝辞
このコーディング標準をまとめるにあたって,太田健一郎さん,栗原哲也さん,高橋徹さ
ん,小藪隆史さん,牛尾剛さん,井芹義博,山崎貴弘さんから有用なコメントを頂きまし
た.ありがとうございました. - 参考資料
Kenji Hiranabe, Java コーディング標準(オリジナル)
http://www.objectclub.jp/community/codingstandard/CodingStd.doc
Jeff Langr, Essential JAVA STYLE – Patterns for Implementation
1999, Prentice Hall
Doug Lea’s Draft Java Coding Standard
http://gee.cs.oswego.edu/dl/html/javaCodingStd.html
Mark Fussell’s Java Development Standards
http://www.chimu.com/publications/javaStandards/index.html
Macadamian Technology coding conventions for C++ and Java
http://www.macadamian.com/codingconventions.htm
AmbySoft Inc. Java Coding Standards, Elements of Java Style
http://www.ambysoft.com/essays/javaCodingStandards.html
http://www.ambysoft.com/books/elementsJavaStyle.html
Javasoft coding standards
http://java.sun.com/docs/codeconv/html/CodeConvTOC.doc.html
以上
------------------------------------
以上为自己学习时译,仅供参考学习。如有译得不妥或解释不妥的地方,望您指出,以学习。其中红色部分为不理解或不会译的地方,有待学习。
谢谢!
——xiaohuaidan717