第 5 章 对象设计的 Java规范
2012-3-7 Java面向对象程序设计教程 2
主要内容
? 5.1 好程序的基本条件
? 5.2 Java语言的 Object类及标准包
? 5.2.1 Object类
? 5.2.2 java.lang包
? 5.2.3 Java标准包
? 5.3 Java的异常处理机制
? 5.3.1 异常处理
? 5.3.2 核心代码与异常处理分离机制
? 5.3.3 引发异常及对异常处理的两条途径
? 5.3.4 Java的内置异常类
? 5.3.5 设计异常类
? 5.3.6 断言语句
? 5.3.7 对异常不作为与作为的区别
? 5.4 包、接口、类与方法设计
? 5.4.1 包设计
? 5.4.2 制作 JAR文件包
? 5.4.3 接口设计
? 5.4.4 抽象类设计
? 5.4.5 类设计
? 5.4.6 方法设计
? 5.5 Java编码的其它规范
? 5.5.1 命名规范
? 5.5.2 文件组织样式
? 5.5.3 增加程序可读性的一些建议
? 5.5.4 完整的例子
5.1 好程序的基本条件
2012-3-7 Java面向对象程序设计教程 4
程序可读性强的重要性
? 软件维护所占的成本约为一个软件生命周
期中总成本的 80%。
? 任何一个软件的维护工作都不可能始终由
最初的编程人员来执行。
? 编程规范提高了软件的可读性,使工程师
更快更彻底地读懂新的源代码。
? 如果我们的源代码是作为商品而开发的,
我们必须保证相关源代码可以与其它商品
清晰分开,并进行合理的打包。
2012-3-7 Java面向对象程序设计教程 5
一些良好的编程习惯
? 致命的异常终止决不允许
? 一条错误信息应该报告什么发生了,关于这个用
户能够做什么,程序下一步要做什么,以及哪一
行代码造成该问题?可能也要注意时间,用户名
和环境。
? 好的程序将自动地发送最近的错误信息给永久性
媒体
? 以这个次序编写:用户手册 ?说明书 ?帮助 ?源
代码
? 编码工作量应该不超过开发工作的 20%
? 测试应该至少要占工程的 30%
? 注释应该至少要占源代码的 20%
2012-3-7 Java面向对象程序设计教程 6
一些良好的编程习惯
? 带有清晰的变量名和实例名的文档代码
? 数据库应该是相关的
? 让简单的维护成为引导我们的灯光
? 总是采用最好的算法
? 永远不要隐藏一个我们仅用一次的实例,将其嵌
入到源代码中,然后,如果我们要多次使用一个
实例,则应将其变成一个方法调用。
? 首先编写更具有风险的模块
? 首先优化最慢的模块
? 遵守 30秒规则
5.2 Java语言的 Object类及标准包
2012-3-7 Java面向对象程序设计教程 8
Java语言的 Object类
? Object类是类层次的根。
? 所有类都直接或间接地继承了 Object类,
所以一个 Object类型的变量可以引用任何
对象,不论是类实例还是数组。
? Object类定义了 11个可供所有对象继承的
方法。这些方法分为两类:通用工具方法
和支持线程的方法。
2012-3-7 Java面向对象程序设计教程 9
Object类的工具方法
? public boolean equals(Object obj)
? public int hashCode()
? protected Object clone() throws
CloneNotSupportedException
? public final Class getClass()
? protected void finalize() throws Throwable
? public String toString()
? public final void wait(long timeout) throws
InterruptedException
? public final void wait(long timeout,int nanos) throws
InterruptedException
? public final void wait() throws InterruptedException
? public final void notify()
? public final void notifyAll()
2012-3-7 Java面向对象程序设计教程 10
Object类中 equals方法的性质
? 反身性( Reflexive) 对于任何引用 x,x.equals(x) 将
返回 true。
? 对称性( Symmetric) 对于引用 x和 y,当且仅当
y.equals(x) 返回 true时,x.equals(y)才会返回 true。
? 传递性( Transitive) 对于引用 x,y和 z,如果
x.equals(y)返回 true,y.equals(z)也返回 true,那么
x.equals(z)也将返回 true。
? 一致性( Consistent) 对于引用 x和 y,只要对象比较时
所有的信息并没有做任何的改动,那么多次调用
x.equals(y)要么都返回 true,要么都返回 false。
? 对于任何非空引用( non-null reference) x,
x.equals(null)将返回 false。
2012-3-7 Java面向对象程序设计教程 11
Object类中 hashCode方法的性质
? 在一次 Java应用中,只要对象比较时所有的信息并没有做
任何的改动,无论该对象的 hashCode方法被调用多少次,
每次返回的散列码都必须是同一个整数值。但在不同的
Java应用中,这个散列码的数值允许不一样。
? 如果有两个对象通过 equals方法比较返回 true,那么分
别调用这两个对象的 hashCode方法返回的散列码数值也
相等。
? 并不要求当通过 Object类的 equals方法比较结果不相等
时,调用这两个对象的 hashCode方法所得的散列码一定
不一样。但是,Java语言建议在这种情况下,最好赋给不
同的对象以不同的散列码数值,以提高散列表的性能。
? 举例,StringConvention.java
2012-3-7 Java面向对象程序设计教程 12
hashCode方法的建议覆盖方式
? Java语言建议直接使用 equals方法中的所有属性。
? 如果这些属性是基本类型或 equals方法已经转换
成对内容比较的类(如 String类),则取其包装
类或所属类的散列码值,然后将它返回;
? 当有多个属性用于 equals方法中,则将这几个属
性包装类或所属类的散列码做异或运算( ^,
XOR)后返回。
? 此外,则要在 hashCode方法中设计特殊的算法,
使之返回满足 Java规范的值。
? 举例,MyObjectConvention.java
MyObjectNonConvention.java
2012-3-7 Java面向对象程序设计教程 13
Object类中的 toString方法
? Object类中只提供了该对象的类名、一个
@字符和实例散列码的十六进制表示,通
常这些信息对于用户是远远不够的。
? 可以通过覆盖 toString方法,在其中提供
一些更加友好的信息。
? 举例,MyObjectConvention.java
2012-3-7 Java面向对象程序设计教程 14
java.lang包中的类
2012-3-7 Java面向对象程序设计教程 15
java.lang包中定义的接口
? CharSequence
? Cloneable
? Comparable
? Runnable
2012-3-7 Java面向对象程序设计教程 16
举例
? Process类演示,ProcessDemo.java
? System类演示,Now.java
PropertyDemo.java
? Class类演示,RTTIDemo.java
2012-3-7 Java面向对象程序设计教程 17
Java标准包
? java.lang
? java.io
? java.util
? java.security
? java.text
? java.awt
? java.applet
? java.beans
? java.math
? java.net
? java.rmi
? java.sql
? java.util.jar
? java.util.zip
5.3 Java的异常处理机制
2012-3-7 Java面向对象程序设计教程 19
异常
? Java语言的异常是一个描述在代码段中发生的异
常(也就是出错)情况的对象。
? 当异常情况发生,一个代表该异常的对象被创建
并且在导致该错误的方法中被引发。该方法可以
选择自己处理异常或传递该异常。
? 异常可能是由 JVM的运行时系统产生,或者是由
你的手工代码产生。被 Java语言引发的异常与违
反语言规范或超出 Java语言执行环境限制的基本
错误有关。手工编码产生的异常基本上用于报告
方法调用程序的出错状况。
2012-3-7 Java面向对象程序设计教程 20
讨论:异常处理的重要性
? 命题:编一段程序,使用户输入两个整型
数值,求出二数相除的结果。
? 程序,IgnoredException.java
2012-3-7 Java面向对象程序设计教程 21
讨论:核心代码与异常处理分离机制
? 命题:把文件内容读入内存的函数( Java
语言称方法)的伪代码,
readFile{
openFile;
determineSize;
allocateMemory;
readFileIntoMemory;
closeFile;
}
可能的问题,
文件无法打开?
文件长度无法判断?
内存不足?
读取失败?
关闭失败?
2012-3-7 Java面向对象程序设计教程 22
异常处理形式的不同解决方案
异常处理的一般形式
errorCodeType readFile{
initialize errorCode = 0
openFile;
if (fileIsOpen){
determineSize;
if(gotFileLength){
allocateEnoughMemory;
if (memoryAllocated){
readFileIntoMemory;
if(readFailed){
errorCode = -1;
}
}
else{
errorCode = -2;
}
}
else{
errorCode = -3;
}
closeFile;
if(fileDidn’tClose && errorCode == 0){
errorCode = -4;
}
else{
errorCode = -5;
}
return errorCode;
}
Java语言提供的异常处理形式的优雅解决方案
readFile{
// 核心代码部分
try{
openFile;
detemineSize;
allocateMemory;
readFileIntoMemory;
closefile;
}
//异常处理部分
catch(fileDidNotOpen){
handle_Error;
}
catch(sizeNotDetermined){
handle_Error;
}
catch(memoryAllocationFailed){
handle_Error;
}
catch(couldNotReadFile){
handle_Error;
}
catch(couldNotCloseFile){
handle_Error;
}
}
2012-3-7 Java面向对象程序设计教程 23
引发异常
? 异常通常是由以下两种情
况引起的:程序执行了不
合法或无效的动作(一般
是这种情况),或者通过
执行 throw语句程序显式
生成异常(比较少见)。
? 引发异常的通常形式,
throw MyException;
? MyException必须是
Throwable类型的一个对
象或者 Throwable的一个
子类。
? 在 catch子句中输入参数,
如
catch(MyException e){
throw e;
}
? 使用 new关键词创建类似
的对象,如
throw new MyException();
2012-3-7 Java面向对象程序设计教程 24
异常处理的途径
? 一种是显式告诉 JVM,我们想采用 try-catch-
finally方式对异常进行处理;
? 另一种是显式告诉 JVM我们不想对业已存在的异
常进行处理,这时同样要给出明确的声明,即在
方法定义时使用 throws关键字,并在其后写上业
已存在的异常类或者其超类的名称。
? Java编译器不能容忍已知一个异常(不过,运行
时异常编译器是管不上的!)存在而我们又不作
为,如果这样的话,它将报一个类似于下述的编
译错误,
unreported exception java.io.IOException;
must be caught or declared to be thrown
2012-3-7 Java面向对象程序设计教程 25
处理异常的 try-catch-finally结构
try{
// 希望进行异常检测的代码块
}
// 注意:此处不许插入其它语句,必须紧跟 catch或者 finally
catch(OneException e){
// 对异常 OneException.进行处理
}
catch(AnotherException e){
// 对异常 AnotherException进行处理
}
// …… 其它的 catch子句
finally{
// 执行完 try代码块以后需要执行的一些清除性代码,如最后关闭文件等
}
? 举例,ExceptionSolution.java
2012-3-7 Java面向对象程序设计教程 26
catch子句
? catch子句一般会给出与异常有关的提示性
信息,以及对该异常的反应(如控制转移
等)。
? 在 catch模块中应该忽略使用参数变量 e,
但可以利用这个参数变量来访问与异常类
有关的一些信息,这些信息对于编程调试
还是很有用的。
? 举例,ExceptionSolution.java
2012-3-7 Java面向对象程序设计教程 27
java.lang.Throwable类中与异常相关的方法
方 法 描 述
String getMessage() 用来得到和列出错误信息的文本
String toString() 输出类名及与类有关的详细信息
String getLocalizedMessage() 创建一个错误或者异常的描述符,子类可以
用来忽略消息并定制本方法使用的消息
void printStackTrace() 打印输出从错误点向上的堆栈信息
StackTraceElement[]
getStackTrace()
返回由 printStackTrace方法输出的堆栈信息,
为编程时访问这些信息提供途径
void printStackTrace(PrintStream) 打印输出与指定打印流相关的堆栈信息
void printStackTrace(PrintWriter) 打印输出相关的堆栈信息到指定位置
Throwable fillInStackTrace() 将该线程的堆栈状态插入到异常对象中
2012-3-7 Java面向对象程序设计教程 28
finally子句的两种主要编码风格
? finally子句提供了一种不
管是否引发异常都要执行
某一程序段的机制。
? 一种风格,
pre();
try{
//…… 其它操作
}finally{
post();
}
? 另二种风格,
Object val=null;
try{
val=pre();
//…… 其它操作
}finally{
if(val!=null)
post();
}
? 举例,FinallyTest.java
2012-3-7 Java面向对象程序设计教程 29
声明不处理异常的 throws子句
? 方法将异常抛出( throws)本身之外,从而由其
上一层方法捕获;
? 对于捕获到该方法可能弹出的异常而仍然没有兴
趣处理的上层方法同样要在 throws列表中注明。
? throws子句的方法声明的通用形式,
type methodName(parameterList) throws
ExceptionList{
//…… 方法体中的程序段
}
2012-3-7 Java面向对象程序设计教程 30
throws子句样例
? 假设如下三点为真,
? myMethodA调用 myMethodB,myMethodB调用
myMethodC,myMethodC调用 myMethodD;
? myMethodD引发异常 MyException且没有处理;
? myMethodC同样没有兴趣处理该异常,但是
myMethod B对 MyException非常有兴趣。
? 这种情况下,myMethodC必须在 throws列表中
引用 MyException,命令行如下,
public void myMethodC(int x,int y,int z) throws
MyException{
// …… 方法体中的程序段
}
2012-3-7 Java面向对象程序设计教程 31
Java的内置异常类
? java.lang包中定义的不作检查异常子类
? java.lang中定义的检查异常
? java.lang中定义的 Error类
2012-3-7 Java面向对象程序设计教程 32
进行异常处理的注意点
? 一是当一个异常一直传递到 main方法还没有被捕
获,那么 JVM将捕获该异常并给出相应的异常信
息并终止引发异常处之后的操作,这对于用户来
说实际上就是程序已经崩溃,因此自定义的异常
类名不宜放在 main方法声明时的 throws列表中。
? 二是 catch子句不能图方便,只写
catch(Exception e){},因为它将捕获所有的
异常,实际上无法给用户提供实质性的异常提示
信息,也不便于程序调试。
2012-3-7 Java面向对象程序设计教程 33
设计异常类的注意点
? 自定义类应从合适的类派生出来,一般选择
Exception类。
? 在自定义类中要给出构造方法原型,特别要设计
成可以传递用来描述异常的信息,即可以通过
Throwable.getMessage()方法访问到引发异常
的基本信息,参见 UsingMyException.java。
? 不要在自定义类中加入具体的异常信息,因为这
是一个类而不仅仅是一个对象。
? 举例,UsingMyException.java
2012-3-7 Java面向对象程序设计教程 34
断言语句
? 从 JDK1.4开始引入。
? 有助于调试代码,也有助于在程序部署之
后排除应用错误。
? 可以在程序的某一个位置编写断言语言,
表示开发人员相信某些重要的条件在该位
置应当是真的。如果这些条件不是真的,
断言语句可以发出一条错误信息,或者说
明这是一个不打算捕捉的异常。
2012-3-7 Java面向对象程序设计教程 35
断言语句的语法格式
assert booleanExpression;
或
assert booleanException,Expression2;
? 如果布尔值为假,则抛出 java.lang.AssertionError 。
? 第二种语法格式允许增加一个表达式,以便传递给
AssertionError的构造方法。表达式应是一个能够说
明发生什么错误的消息字符串,而且应当只用字符串
作为参数。当然也可以使用其他类型(如整数),表
示错误的数字编号或单个字符标记的错误信息。
? 举例,AssertionExample.java
2012-3-7 Java面向对象程序设计教程 36
四种使用断言功能的情况
? 检查流程的不变性。
? 内部运行的不变性。
? 私有方法结果的检查。
? 程序运行中的一致性。
2012-3-7 Java面向对象程序设计教程 37
使用断言语句时的两个关键问题
? 只应在程序中的少数几个关键位置布置断
言语句。
? 可以控制断言语句是否在运行时生效。
2012-3-7 Java面向对象程序设计教程 38
使用断言语句的两个限制
? 不要在 public方法中,用断言语句来检查参
数的正确性。
? 不要让断言语句去处理一些程序必需的流
程。
2012-3-7 Java面向对象程序设计教程 39
对异常的不作为与作为的区别
? 在于对我们编写的程序,我们是否能够控
制所有的操作。
? 举例,ProcessOrNot.java
5.4 包、接口、类与方法设计
2012-3-7 Java面向对象程序设计教程 41
包的概念
? 包( Package)定义了软件单元,它能够
独立发布,并能够与其它包组成应用程序,
包的成员是一些相关联的类、接口以及子
包,也可包含包中的类所使用的附加资源
文件。
? 定义包的语句形式,
package g3ds.joop.ch5;
? Package类举例,PackageDemo.java
2012-3-7 Java面向对象程序设计教程 42
包的主要作用
? 包将相互关联的接口和类结合成组。
? 包创建了命名空间来避免类型之间的命名
冲突。在一个包中的接口和类可以使用流
行的公共名字来命名,而这些命名只在一
个包内的上下文才有意义,在不同的包之
间则可以有相同的命名。
? 包机制为开发应用提供了保护域。包内的
代码通过访问标志可以相互协作,而这些
标志对外部代码是不可用的。
2012-3-7 Java面向对象程序设计教程 43
包的命名约定
? 采用 Internet域名反序,这种命名模式使
得包名变得很长,但却比较安全。
? 如上海交通大学的域名是 sjtu.edu.cn,那
么完整的 g3ds.joop.ch5包声明就应该是,
package cn.edu.sjtu.g3ds.joop.ch5;
2012-3-7 Java面向对象程序设计教程 44
程序设计时导入类型的方式
? 使用类型的完全限定名。
例如,String类的全限定名是
java.lang.String。
? 使用 import关键字将包的部分或全部内容
导入。
例如使用 g3ds.joop.ch5包的程序员可以
将下面这行代码放在源文件的首部,
import g3ds.joop.ch5.*;
2012-3-7 Java面向对象程序设计教程 45
编译器查找某一类型的顺序
? 包括子类型的当前类型。
? 当前类型中的嵌套类型。
? 显式命名的导入类型(单类型导入)。
? 在同一个包内声明的其它类型。
? 隐式命名的导入类型(按需导入)。
? 如果类型定义仍未找到,则会报错。
import g3ds.joop.ch5,MyObject;
import g3ds.joop.ch5,*;
2012-3-7 Java面向对象程序设计教程 46
讨论:如何解决潜在的冲突命名
? package机制和 import机制使程序员可以控制潜在的冲突命名。
? 例如在 javax.swing包和 java.util包都有一个名为 Timer的类,那么
想要同时在一个源文件中使用这两个类型的程序员可以有几种选择,
? 使用类型的完全限定名,如 javax.swing.Timer和 java.util.Timer。
? 仅导入 javax.swing.Timer和 javax.swing.*,那么对于
javax.swing.Timer就可以使用简单名 Timer来引用,而用完全限定
名来引用 java.util.Timer。
? 反过来:仅导入 java.util.Timer或 java.util.*,使用简单名 Timer来
表示 java.util.Timer,而使用完全限定名来表示
javax.swing.Timer。
? 将这两个包 javax.swing.*和 java.util.*都导入进来,并在自己的代
码中使用完全限定名 javax.swing.Timer和 java.util.Timer(如果
在按需导入的两个包中,有同名类型出现,对任何一个都不能使用简
单名)。
2012-3-7 Java面向对象程序设计教程 47
类成员的可见性
private ( default) protected public
同一类中可见 是 是 是 是
同一个包中对扩展类可
见
否 是 是 是
同一个包中对非扩展类
可见
否 是 是 是
不同包中对扩展类可见 否 否 是 是
不同包中对非扩展类可
见
否 否 否 是
2012-3-7 Java面向对象程序设计教程 48
类成员的可见性解析
? 任何声明为 public的内容可以被从任何地方访问。
? 如果一个成员不含有一个明确的访问说明,它对于子类或
该包中的其它类是可见的,而对于包外面的代码却是隐藏
的。换句话,标志符默认的访问修饰符相当于
,package”,唯一的例外是接口,其成员的默认的访问
修饰符是 public。这是默认访问。
? 如果你希望一个元素在当前包外可见,但仅仅是元素所在
类的子类直接可见,把元素定义成 protected。
? 被声明成 private的成员不能被该类外看到。
? 举例,Point.java, Point3d.java,
AccessibilityDemo.java
2012-3-7 Java面向对象程序设计教程 49
定义包时的内容组织
? 只包含功能上相关的类和接口。
? 应为程序员提供了对类和接口进行逻辑分组的方
法,以便使用者查找。
? 包应当有一定的中心,在某种, 主题, 或与指定
的类有一致性。
? 选择包就是要了解类是否放对了位置。
? 防止包中有太多功能不多的简单小类,使别人在
浏览包中的类时很难记住每个类的责任。
? 建立应用程序时最好维护两组包,一组包含可复
用代码,而另一组包含应用程序特定的类与接口。
2012-3-7 Java面向对象程序设计教程 50
练习:制作 JAR文件包
? 认识 jar 命令
? 一般的打包方法
? 制作可执行的 JAR文件
2012-3-7 Java面向对象程序设计教程 51
接口设计,
“一个接口,多个实现”的好处
? 接口是为支持运行时动态方法解决而设计
的。
? 一个接口定义了一套方法,任何类成员都
可以实现该接口。
? 不过,这些类必须完整地创建该接口所定
义的方法,但每个类可以自由决定它们自
己实现的细节。
2012-3-7 Java面向对象程序设计教程 52
讨论:在一个系统中建立机动运输
工具与油料补给站之间的关系
? 我们设计了运输工具类 Transport,在其中定义 license方法返回通行许可证以判断通
行是否合法,还定义 refuel方法以指定补给燃料的操作。然后在油料补给站类
SupplyDepot中定义一个 service(Transport obj)方法,并在该方法体中利用语句
public void service(Transport obj){
obj.refuel();
}
来实现对运输工具的补给服务。
2012-3-7 Java面向对象程序设计教程 53
改进策略之一:改变类层次结构
2012-3-7 Java面向对象程序设计教程 54
改进策略之二:方法下移
2012-3-7 Java面向对象程序设计教程 55
改进策略之三:设计接口方式
2012-3-7 Java面向对象程序设计教程 56
策略三的优势
? 接口指定了实现类必须严格遵循并提供的方法的签名,保
证了拥有该方法类型的接口同源。
? 使用接口类型作为方法的参数,在方法中调用接口参数承
诺的任何方法,这样在编译时为实现了接口的一组类与其
它类型交互提供了便利。
? 在运行时,实现了接口的对象拥有了接口承诺的所有方法,
并分别给出对该方法的具体实现,使程序能够运行。
? 程序,Refuellable.java, SupplyDepot.java,
Transport.java, RoadVehicle.java, Car.java,
Bicycle.java, MarineVehicle.java, Ship.java,
AirVehicle.java, Airplane.java
2012-3-7 Java面向对象程序设计教程 57
接口设计:利用接口存储常量
? 举例,Judge.java
2012-3-7 Java面向对象程序设计教程 58
设计抽象类的好处
? 避免了对一些确实没有实际意义的方法给
出模糊的解决方案。
? 可以通过这种机制保证从该类扩展出来的
非抽象子类(当然,抽象类的子类也有可
能还是抽象类)都必须覆盖所有的抽象方
法,给出具体的实现。
2012-3-7 Java面向对象程序设计教程 59
讨论:设计抽象类的好处
? 第一种 Shape类的定义
class Shape{
public void draw(){
System.err.println(“Shape类的 draw方法没有定义!, );
}
}
? 第二种 Shape类的定义
abstract class Shape{
//声明一个绘制几何形状的抽象方法
public abstract void draw();
}
不好!
较好!
2012-3-7 Java面向对象程序设计教程 60
抽象类的性质
? 抽象类没有对象。也就是说,一个抽象类
不能通过 new操作符直接实例化,这样的
对象是无用的,因为抽象类是不完全定义
的。
? 我们不能定义抽象构造方法或抽象静态方
法。
? 所有抽象类的子类都必须执行超类中的所
有抽象方法或者是它自己也声明成抽象类。
2012-3-7 Java面向对象程序设计教程 61
接口与抽象类的相似之处
? 定义一种必须具有的形式,但它实际上并
不提供真正的实现。
? 可以用来作为声明引用变量的类型,但决
不可直接用来创建实例 ——对于接口,
new后面的类型必须实现该接口;对于抽
象类,new后面的类型必须是该类的非抽
象子类。
举例,Callback.java
2012-3-7 Java面向对象程序设计教程 62
接口与抽象类的不同之处
? 抽象类是一个不完全的类,需要进一步具
体化。接口则只是一个行为的规范或规定。
? 接口基本上不具备继承的任何具体特点,
它仅仅承诺了能够调用的方法。
? 一个类一次可以实现若干个接口,但只能
扩展一个父类。
2012-3-7 Java面向对象程序设计教程 63
设计类的目的
? 类设计主要是指定责任,标识要实现的功
能并分配给最合适完成这个功能的类。
2012-3-7 Java面向对象程序设计教程 64
类的命名原则
? 类名通常都是名词,而且类名应该是一到
两个词,并能准确描述这个类。
? 类名通常是单数的,如, Student”而不是
,Students” 。
2012-3-7 Java面向对象程序设计教程 65
优化封装
? 应认真对类的方法和属性指定适当的访问
权限限定符。
? 通常要限制对属性的直接访问,同时对每
个字段生成一对访问( get)和修改( set)
方法,以便访问这些属性。
? 合理选择 protected访问权限限定符,这是
因为子类可以直接访问这些属性,这通常
是必需的。
2012-3-7 Java面向对象程序设计教程 66
具有代码复用性的类特征
? 松耦合( Loose coupling)
? 强聚合( Strong cohesion)
2012-3-7 Java面向对象程序设计教程 67
类继承的特征与优点
? 继承是建立类之间, is a”,,is like”或者
,is kind of”的关系,可以很容易地复用已
经存在的数据和代码。
? 容易进行新的实现,因为其大多数可继承
而来。
? 易于修改或扩展那些被复用的实现。
2012-3-7 Java面向对象程序设计教程 68
类继承的缺点
? 破坏了封装性,因为这会将父类的实现细
节暴露给子类。
?, 白盒, 复用,因为父类的内部细节对于
子类而言通常是可见的。
? 当父类的实现更改时,子类也不得不会随
之更改。
? 从父类继承来的实现将不能在运行时进行
改变。
2012-3-7 Java面向对象程序设计教程 69
应用继承的技巧
? 寻找相似之处
? 寻找已有的类
? 遵循单句规则 即类之间应该满足, is a”、
,is like”或者, is kind of”的关系
? 避免实现继承(也称便利继承) 即仅仅因
为方便而使用继承
? 继承所有的东西
2012-3-7 Java面向对象程序设计教程 70
对象聚合的特征与类型
? 表示两个类之间是, is part of”或者, has a”的关系。
? 例子,Person.java, Hobby.java ;
Point.java, Polygon.java, Circle.java ;
Car.java, Part.java 。
2012-3-7 Java面向对象程序设计教程 71
对象聚合的优点
? 容器类仅能通过被包含对象的接口来对其进行访
问。
? 属于, 黑盒( Black box), 复用,因为被包含
对象的内部细节对外是不可见的。
? 封装性好。
? 实现上的相互依赖性比较小。
? 每一个类只专注于一项任务。
? 通过获取指向其它的具有相同类型的对象引用,
可以在运行时动态地定义对象的聚合。
2012-3-7 Java面向对象程序设计教程 72
对象聚合的缺点
? 从而导致系统中的对象过多。
? 为了能将多个不同的对象作为聚合模块来
使用,必须仔细地对接口进行定义。
2012-3-7 Java面向对象程序设计教程 73
采用继承的一般判据
? 子类表达的, 是一个 … 的特殊类型,,而非, 是
一个由 … 所扮演的角色, 。
? 子类的一个实例永远不需要转化为其它类的一个
对象。
? 子类是对其父类的职责进行扩展,而非重写或废
弃。
? 子类没有对那些仅作为一个工具类的功能进行扩
展。
? 对于一个位于实际的问题域的类而言,其子类特
指一种角色,交易或设备。
2012-3-7 Java面向对象程序设计教程 74
讨论:采用继承还是聚合?
? 采用继承方式的人与角色类图
2012-3-7 Java面向对象程序设计教程 75
讨论:采用继承还是聚合?
? 采用聚合方式的人与角色类图
2012-3-7 Java面向对象程序设计教程 76
类设计的开放-封闭法则
? Open-Closed Principle, OCP
? 软件实体应对扩展是开放的,而对修改是封闭的。
( Software entities should be open for
extension,but closed for modification.)
? 可扩展,即, 对扩展是开放的, ( Open for
extension) 模块的行为可以被扩展,以满足新
的需求。
? 不可更改,即, 对更改是封闭的, ( Closed for
modification) 模块的源代码是不允许进行改
动的。
2012-3-7 Java面向对象程序设计教程 77
举例
? 程序,Car.java, PricePolicy.java, SalePrice.java
2012-3-7 Java面向对象程序设计教程 78
类设计的里氏替换法则
? Liskov Substitution Principle,LSP
? 由 Barbara Liskov提出的,是指使用指向基类(超类)
的引用的方法,必须能够在不知道具体扩展类对象类型的
情况下使用它们。
? 在所有根据 T定义的程序 P中,如果对于每个 S类型对象 o1
有一个 T类型对象 o2,那么,假如 S是 T的子类,则当把
o1替换成 o2时,P的行为不会发生改变。( If for each
object o1 of type S there is an object o2 of type T
such that for all programs P defined in terms of T,
the behavior of P is unchanged when o1 is
substituted for o2 then S is a subtype of T,)
? 举例,Rectangle.java, Square.java, TestLSP.java
2012-3-7 Java面向对象程序设计教程 79
方法设计的简单性
public void doSomething(int a,int b,int c,Object obj){
if(a<b){
if(obj instanceof Number){
for(int i=0; i<count; i++){
if(getSomeData(i)==null){
while(c<5){
if(c==0){
handleSpecialCase();
}
}
}
}
}
}
}
public void doSomething(int a,int b,int c,Object obj){
if(a<b){
if(obj instanceof Number){
for(int i=0; i<count; i++){
doPart(i,c);
}
}
}
}
public void doPart(int i,int c){
if(getSomeData(i)==null){
while(c<5){
if(c==0){
handleSpecialCase();
}
}
}
}
2012-3-7 Java面向对象程序设计教程 80
方法设计:传递参数与方法命名
? 当确定向一个方法传递参数时,应避免传
递, 标志, 或, 控制, 参数,告诉方法如
何完成其功能 。
? 举例,Roster.java, RosterUpdate.java,
RosterFinal.java
2012-3-7 Java面向对象程序设计教程 81
方法设计:减少代码重复
public class DuplicationSample {
protected int firstValue;
protected String secondValue;
protected Integer thirdValue;
public DuplicationSample(int first,
String second,Integer third) {
firstValue = first;
secondValue = second;
thirdValue = third;
}
public DuplicationSample(int first,
String second) {
firstValue = first;
secondValue = second;
thirdValue = new Integer(0);
}
}
public class DuplicationSample {
protected int firstValue;
protected String secondValue;
protected Integer thirdValue;
public DuplicationSample(int first,
String second,Integer third) {
firstValue = first;
secondValue = second;
thirdValue = third;
}
public DuplicationSample(int first,
String second) {
this(first,second,new
Integer(0));
}
}
2012-3-7 Java面向对象程序设计教程 82
学习设计模式
? 接受 首先接受设计模式是我们在学习工作
中一个很重要的前提。
? 确认 确认需要阅读设计模式,以便了解何
时加以使用。
? 掌握 最后,要充分掌握模式,明白哪些模
式可能有助于解决一个特定的设计问题。
5.5 Java编码的其它规范
2012-3-7 Java面向对象程序设计教程 84
文件类型的后缀
? Java源程序,.java
? Java字节码,.class
2012-3-7 Java面向对象程序设计教程 85
标识符类型的命名规则
标识符类型 命名规则 举例
包 包的名字应该都是由一个小写单词组成,并且以一个
顶级域名命名,如 com,edu,gov,mil,net,org等,
或者国家标识,如 cn,ca等;随后的包名可以根据自
己所在组织的内部规则命名,如部门、项目、登录名
等
com.sun.eng
cn.edu.sjtu.g3ds
类 类的名字必须是名词,由大写字母开头,而该词的其
他字母都小写;如果类名由多个单词组成的,则每个
单词的首字母也应大写,而且所使用的单词应该写全,
尽可能避免采用只取首字母或者采用缩写形式(除非
该缩写是众所周知的,如 URL或 HTML等);类名应
尽可能简单,又能有效表达设计意图
class Raster;
class ImageSprite;
接口 接口的命名法和类的要求相同 interface
RasterDelegate;
interface Storing;
2012-3-7 Java面向对象程序设计教程 86
标识符类型的命名规则
标识符类型 命名规则 举例
方法 方法名必须是动词,首字母应小写,而该词的其他
字母也都小写;如果方法名由多个单词组成的,则
每个单词的首字母也应大写
run( );
runFast( );
getBackground( );
变量 变量名首字母应小写,而该词的其他字母也都小写;
如果方法名由多个单词组成的,则每个单词的首字
母也应大写;变量名所首字符不应使用,_”或,$”
字符,虽然从标识符组成上来说也是可以的;变量
名应便于记忆,让使用者很容易明白设计该变量的
意图;对于临时变量名,通常用 i,j,k,m,n表示
int型变量,用 c,d,e表示 char型变量
char c;
float myWidth;
int[] myArray;
String userName;
常量 常量名全部采用大写字母表示,单词之间用,_”隔
开
static final int
MIN_WIDTH=4;
static final int
MAX_WIDTH=999;
static final int
GET_THE_CPU=1;
2012-3-7 Java面向对象程序设计教程 87
Java源代码的组织次序
? 开始的注释段
? 包定义和导入相关包的语句
? 类和接口的具体定义
2012-3-7 Java面向对象程序设计教程 88
开始的注释段
/*
* 类名
*
* 版本信息
*
* 日期
*
* 版权警告的注释
*/
2012-3-7 Java面向对象程序设计教程 89
包定义和导入相关包的语句
? 第一个非注释的语句必须是包定义,随后
导入文件中需要用到的包。
package cn.edu.sjtu.g3ds.joop,*;
import java.io,*;
2012-3-7 Java面向对象程序设计教程 90
类和接口的具体定义
类 /接口组成部分的声明 描述
1 类 /接口的文档注释
( /**… */)
用来说明该类 /接口设计的意图,如
/**
* The Example class provides,.,
*/
public class Example {,.,
2 class/interface语句 描述类及其继承关系或接口实现
3 如果有必要,可增加类 /接
口实现的注释( /**… */)
此处的注释用来放置与类 /接口有关的、却又不适合放在类 /接口文档注
释中的内容
4 类变量 类变量的声明顺序依次从 public,protected、( default)直到 private
5 实例变量 实例变量的声明顺序依次从 public,protected、( default)直到 private
6 构造方法 可以从一般的到特殊的
7 方法 方法的放置顺序应以功能组划分,而不应按作用域或者访问权限顺序
排列,以便增加程序的可读性
2012-3-7 Java面向对象程序设计教程 91
多行缩排
? 每行长度不超过 80个字符,而用于文档的例子每行长度应更短些,一般不超过 70个字符。
? 逗号之后,如
someMethod(longExpression1,longExpression2,longExpression3,
longExpression4,longExpression5);
? 运算符之前,如
longName1=longName2 *(longName3+longName4-longName5)
+4*longname6;
? 如果包含有多个级别,则应选择在高级别处换行。而且,新一行的起始位置应与前一行同级的位置 相同。如上面这个例子如下这样换行就不太合适,
longName1=longName2*(longName3+longName4
-longName5)+4*longname6;
? 如果以上的规则导致代码混乱或太挤向右侧,也可以仅用缩进 8个空格替代,如
private static synchronized horkingLongMethodName(int anArg,
Object anotherArg,String yetAnotherArg,Object andStillAnother){
…
}
? 比较特殊的是三元运算符,下面三种方式都可以,
alpha=(aLongBooleanExpression)? beta, gamma;
alpha=(aLongBooleanExpression)? beta
, gamma;
alpha=(aLongBooleanExpression)
beta
, gamma;
2012-3-7 Java面向对象程序设计教程 92
增加空行
? 增加两行空行的情况,
? 源代码文件之间。
? 类 /接口之间。
? 增加一行空行的情况,
? 方法代码块之间。
? 方法体中局部变量和第一个语句之间。
? 代码块或单行注释之前。
? 方法体中逻辑相关的代码块之间
2012-3-7 Java面向对象程序设计教程 93
增加空格
? 关键字和括号之间,如
while(true){
…
}
? 参数列表中逗号和参数变量之间,如
Sample(int i,int j){
…
}
? 除了,,”以外的所有二元运算符和操作数之间,但一元运算符和操作数之间不加空格,
如
a+=c+d;
a=(a+b)/(c*d);
while(d++=s++){
n++;
}
prints(″size is″+foo+″\n″);
? for语句的表达式之间,如
for(expr1; expr2; expr3)
? 类型转换之后,如
myMethod((byte)aNum,(Object)x);
2012-3-7 Java面向对象程序设计教程 94
应避免的一些做法
? 避免用一个对象访问类变量或类方法,如
classMethod(); //直接调用是允许的
AClass.classMethod(); //利用类名调用也是允许的
anObject.classMethod(); //应避免用实例对象调用类变量或类方法!
? 在一个语句中为多个变量赋值,如
fooBar.fChar=barFoo.lchar=′c′;
? 避免把关系运算符, ==”误写为赋值运算符, =”,如
if(c++=d++){
…
}
是错误的,应改写为类似于下式
if((c++=d++)!=0){
…
}
? 不要采用嵌套赋值语句,如
d=(a=b+c)+r;
是应该避免的,应表达为
a=b+c;
d=a+r;
2012-3-7 Java面向对象程序设计教程 95
推荐的一些做法
? 在易引起误解的地方使用括号,如
if(a==b && c==d)
虽然也是对的,但表达成下式更佳,
if((a==b)&&(c==d))
? 尽可能使语句结构更符合所要表达的意图,如
if(booleanExpression){
return true;
} else {
return false;
}
就应改写为
return booleanExpression;
同理,下式
if(condition){
return x;
}
return y;
的更好表达是,
return(condition? x, y);
? 在三元运算符,?,”中的条件判断如果含有二元运算符,则应用使用括号,如
(x>=0)? x,-x;
2012-3-7 Java面向对象程序设计教程 96
遵循规范的一个完整例子
/*
*/
package g3ds.joop.ch5;
import javax.swing,*;
/**
* ConventionExample类用来描述 Java语言中使用的一些规范。
*/
public class ConventionExample extends JFrame {
/* 可以在此添加有关类实现的一些相关的注释 */
/** 有关类变量 classVar1的文档注释 */
public static int classVar1;
/**
* 有关类变量 classVar2 的文档注释
* 注释可以多行表示,并可包含一些 HTML标识符号
*/
private static Object classVar2;
/** 有关实例变量 instanceVar1的文档注释 */
public Object instanceVar1;
/** 有关实例变量 instanceVar2的文档注释 */
protected int instanceVar2;
/** 有关实例变量 instanceVar3的文档注释 */
private Object[] instanceVar3;
/**
* 有关构造方法 ConventionExample的文档注释
*/
public ConventionExample(){
// 构造方法的具体实现 ……
}
/**
* 有关 doSomething方法 的文档注释
*/
public void doSomething(){
// 方法的具体实现 ……
}
/**
* 有关 doSomethingElse 方法的文档注释
* @param 行参 someParam的用途描述
*/
public void doSomethingElse(Object someParam){
// 方法的具体实现 ……
}
}
2012-3-7 Java面向对象程序设计教程 2
主要内容
? 5.1 好程序的基本条件
? 5.2 Java语言的 Object类及标准包
? 5.2.1 Object类
? 5.2.2 java.lang包
? 5.2.3 Java标准包
? 5.3 Java的异常处理机制
? 5.3.1 异常处理
? 5.3.2 核心代码与异常处理分离机制
? 5.3.3 引发异常及对异常处理的两条途径
? 5.3.4 Java的内置异常类
? 5.3.5 设计异常类
? 5.3.6 断言语句
? 5.3.7 对异常不作为与作为的区别
? 5.4 包、接口、类与方法设计
? 5.4.1 包设计
? 5.4.2 制作 JAR文件包
? 5.4.3 接口设计
? 5.4.4 抽象类设计
? 5.4.5 类设计
? 5.4.6 方法设计
? 5.5 Java编码的其它规范
? 5.5.1 命名规范
? 5.5.2 文件组织样式
? 5.5.3 增加程序可读性的一些建议
? 5.5.4 完整的例子
5.1 好程序的基本条件
2012-3-7 Java面向对象程序设计教程 4
程序可读性强的重要性
? 软件维护所占的成本约为一个软件生命周
期中总成本的 80%。
? 任何一个软件的维护工作都不可能始终由
最初的编程人员来执行。
? 编程规范提高了软件的可读性,使工程师
更快更彻底地读懂新的源代码。
? 如果我们的源代码是作为商品而开发的,
我们必须保证相关源代码可以与其它商品
清晰分开,并进行合理的打包。
2012-3-7 Java面向对象程序设计教程 5
一些良好的编程习惯
? 致命的异常终止决不允许
? 一条错误信息应该报告什么发生了,关于这个用
户能够做什么,程序下一步要做什么,以及哪一
行代码造成该问题?可能也要注意时间,用户名
和环境。
? 好的程序将自动地发送最近的错误信息给永久性
媒体
? 以这个次序编写:用户手册 ?说明书 ?帮助 ?源
代码
? 编码工作量应该不超过开发工作的 20%
? 测试应该至少要占工程的 30%
? 注释应该至少要占源代码的 20%
2012-3-7 Java面向对象程序设计教程 6
一些良好的编程习惯
? 带有清晰的变量名和实例名的文档代码
? 数据库应该是相关的
? 让简单的维护成为引导我们的灯光
? 总是采用最好的算法
? 永远不要隐藏一个我们仅用一次的实例,将其嵌
入到源代码中,然后,如果我们要多次使用一个
实例,则应将其变成一个方法调用。
? 首先编写更具有风险的模块
? 首先优化最慢的模块
? 遵守 30秒规则
5.2 Java语言的 Object类及标准包
2012-3-7 Java面向对象程序设计教程 8
Java语言的 Object类
? Object类是类层次的根。
? 所有类都直接或间接地继承了 Object类,
所以一个 Object类型的变量可以引用任何
对象,不论是类实例还是数组。
? Object类定义了 11个可供所有对象继承的
方法。这些方法分为两类:通用工具方法
和支持线程的方法。
2012-3-7 Java面向对象程序设计教程 9
Object类的工具方法
? public boolean equals(Object obj)
? public int hashCode()
? protected Object clone() throws
CloneNotSupportedException
? public final Class getClass()
? protected void finalize() throws Throwable
? public String toString()
? public final void wait(long timeout) throws
InterruptedException
? public final void wait(long timeout,int nanos) throws
InterruptedException
? public final void wait() throws InterruptedException
? public final void notify()
? public final void notifyAll()
2012-3-7 Java面向对象程序设计教程 10
Object类中 equals方法的性质
? 反身性( Reflexive) 对于任何引用 x,x.equals(x) 将
返回 true。
? 对称性( Symmetric) 对于引用 x和 y,当且仅当
y.equals(x) 返回 true时,x.equals(y)才会返回 true。
? 传递性( Transitive) 对于引用 x,y和 z,如果
x.equals(y)返回 true,y.equals(z)也返回 true,那么
x.equals(z)也将返回 true。
? 一致性( Consistent) 对于引用 x和 y,只要对象比较时
所有的信息并没有做任何的改动,那么多次调用
x.equals(y)要么都返回 true,要么都返回 false。
? 对于任何非空引用( non-null reference) x,
x.equals(null)将返回 false。
2012-3-7 Java面向对象程序设计教程 11
Object类中 hashCode方法的性质
? 在一次 Java应用中,只要对象比较时所有的信息并没有做
任何的改动,无论该对象的 hashCode方法被调用多少次,
每次返回的散列码都必须是同一个整数值。但在不同的
Java应用中,这个散列码的数值允许不一样。
? 如果有两个对象通过 equals方法比较返回 true,那么分
别调用这两个对象的 hashCode方法返回的散列码数值也
相等。
? 并不要求当通过 Object类的 equals方法比较结果不相等
时,调用这两个对象的 hashCode方法所得的散列码一定
不一样。但是,Java语言建议在这种情况下,最好赋给不
同的对象以不同的散列码数值,以提高散列表的性能。
? 举例,StringConvention.java
2012-3-7 Java面向对象程序设计教程 12
hashCode方法的建议覆盖方式
? Java语言建议直接使用 equals方法中的所有属性。
? 如果这些属性是基本类型或 equals方法已经转换
成对内容比较的类(如 String类),则取其包装
类或所属类的散列码值,然后将它返回;
? 当有多个属性用于 equals方法中,则将这几个属
性包装类或所属类的散列码做异或运算( ^,
XOR)后返回。
? 此外,则要在 hashCode方法中设计特殊的算法,
使之返回满足 Java规范的值。
? 举例,MyObjectConvention.java
MyObjectNonConvention.java
2012-3-7 Java面向对象程序设计教程 13
Object类中的 toString方法
? Object类中只提供了该对象的类名、一个
@字符和实例散列码的十六进制表示,通
常这些信息对于用户是远远不够的。
? 可以通过覆盖 toString方法,在其中提供
一些更加友好的信息。
? 举例,MyObjectConvention.java
2012-3-7 Java面向对象程序设计教程 14
java.lang包中的类
2012-3-7 Java面向对象程序设计教程 15
java.lang包中定义的接口
? CharSequence
? Cloneable
? Comparable
? Runnable
2012-3-7 Java面向对象程序设计教程 16
举例
? Process类演示,ProcessDemo.java
? System类演示,Now.java
PropertyDemo.java
? Class类演示,RTTIDemo.java
2012-3-7 Java面向对象程序设计教程 17
Java标准包
? java.lang
? java.io
? java.util
? java.security
? java.text
? java.awt
? java.applet
? java.beans
? java.math
? java.net
? java.rmi
? java.sql
? java.util.jar
? java.util.zip
5.3 Java的异常处理机制
2012-3-7 Java面向对象程序设计教程 19
异常
? Java语言的异常是一个描述在代码段中发生的异
常(也就是出错)情况的对象。
? 当异常情况发生,一个代表该异常的对象被创建
并且在导致该错误的方法中被引发。该方法可以
选择自己处理异常或传递该异常。
? 异常可能是由 JVM的运行时系统产生,或者是由
你的手工代码产生。被 Java语言引发的异常与违
反语言规范或超出 Java语言执行环境限制的基本
错误有关。手工编码产生的异常基本上用于报告
方法调用程序的出错状况。
2012-3-7 Java面向对象程序设计教程 20
讨论:异常处理的重要性
? 命题:编一段程序,使用户输入两个整型
数值,求出二数相除的结果。
? 程序,IgnoredException.java
2012-3-7 Java面向对象程序设计教程 21
讨论:核心代码与异常处理分离机制
? 命题:把文件内容读入内存的函数( Java
语言称方法)的伪代码,
readFile{
openFile;
determineSize;
allocateMemory;
readFileIntoMemory;
closeFile;
}
可能的问题,
文件无法打开?
文件长度无法判断?
内存不足?
读取失败?
关闭失败?
2012-3-7 Java面向对象程序设计教程 22
异常处理形式的不同解决方案
异常处理的一般形式
errorCodeType readFile{
initialize errorCode = 0
openFile;
if (fileIsOpen){
determineSize;
if(gotFileLength){
allocateEnoughMemory;
if (memoryAllocated){
readFileIntoMemory;
if(readFailed){
errorCode = -1;
}
}
else{
errorCode = -2;
}
}
else{
errorCode = -3;
}
closeFile;
if(fileDidn’tClose && errorCode == 0){
errorCode = -4;
}
else{
errorCode = -5;
}
return errorCode;
}
Java语言提供的异常处理形式的优雅解决方案
readFile{
// 核心代码部分
try{
openFile;
detemineSize;
allocateMemory;
readFileIntoMemory;
closefile;
}
//异常处理部分
catch(fileDidNotOpen){
handle_Error;
}
catch(sizeNotDetermined){
handle_Error;
}
catch(memoryAllocationFailed){
handle_Error;
}
catch(couldNotReadFile){
handle_Error;
}
catch(couldNotCloseFile){
handle_Error;
}
}
2012-3-7 Java面向对象程序设计教程 23
引发异常
? 异常通常是由以下两种情
况引起的:程序执行了不
合法或无效的动作(一般
是这种情况),或者通过
执行 throw语句程序显式
生成异常(比较少见)。
? 引发异常的通常形式,
throw MyException;
? MyException必须是
Throwable类型的一个对
象或者 Throwable的一个
子类。
? 在 catch子句中输入参数,
如
catch(MyException e){
throw e;
}
? 使用 new关键词创建类似
的对象,如
throw new MyException();
2012-3-7 Java面向对象程序设计教程 24
异常处理的途径
? 一种是显式告诉 JVM,我们想采用 try-catch-
finally方式对异常进行处理;
? 另一种是显式告诉 JVM我们不想对业已存在的异
常进行处理,这时同样要给出明确的声明,即在
方法定义时使用 throws关键字,并在其后写上业
已存在的异常类或者其超类的名称。
? Java编译器不能容忍已知一个异常(不过,运行
时异常编译器是管不上的!)存在而我们又不作
为,如果这样的话,它将报一个类似于下述的编
译错误,
unreported exception java.io.IOException;
must be caught or declared to be thrown
2012-3-7 Java面向对象程序设计教程 25
处理异常的 try-catch-finally结构
try{
// 希望进行异常检测的代码块
}
// 注意:此处不许插入其它语句,必须紧跟 catch或者 finally
catch(OneException e){
// 对异常 OneException.进行处理
}
catch(AnotherException e){
// 对异常 AnotherException进行处理
}
// …… 其它的 catch子句
finally{
// 执行完 try代码块以后需要执行的一些清除性代码,如最后关闭文件等
}
? 举例,ExceptionSolution.java
2012-3-7 Java面向对象程序设计教程 26
catch子句
? catch子句一般会给出与异常有关的提示性
信息,以及对该异常的反应(如控制转移
等)。
? 在 catch模块中应该忽略使用参数变量 e,
但可以利用这个参数变量来访问与异常类
有关的一些信息,这些信息对于编程调试
还是很有用的。
? 举例,ExceptionSolution.java
2012-3-7 Java面向对象程序设计教程 27
java.lang.Throwable类中与异常相关的方法
方 法 描 述
String getMessage() 用来得到和列出错误信息的文本
String toString() 输出类名及与类有关的详细信息
String getLocalizedMessage() 创建一个错误或者异常的描述符,子类可以
用来忽略消息并定制本方法使用的消息
void printStackTrace() 打印输出从错误点向上的堆栈信息
StackTraceElement[]
getStackTrace()
返回由 printStackTrace方法输出的堆栈信息,
为编程时访问这些信息提供途径
void printStackTrace(PrintStream) 打印输出与指定打印流相关的堆栈信息
void printStackTrace(PrintWriter) 打印输出相关的堆栈信息到指定位置
Throwable fillInStackTrace() 将该线程的堆栈状态插入到异常对象中
2012-3-7 Java面向对象程序设计教程 28
finally子句的两种主要编码风格
? finally子句提供了一种不
管是否引发异常都要执行
某一程序段的机制。
? 一种风格,
pre();
try{
//…… 其它操作
}finally{
post();
}
? 另二种风格,
Object val=null;
try{
val=pre();
//…… 其它操作
}finally{
if(val!=null)
post();
}
? 举例,FinallyTest.java
2012-3-7 Java面向对象程序设计教程 29
声明不处理异常的 throws子句
? 方法将异常抛出( throws)本身之外,从而由其
上一层方法捕获;
? 对于捕获到该方法可能弹出的异常而仍然没有兴
趣处理的上层方法同样要在 throws列表中注明。
? throws子句的方法声明的通用形式,
type methodName(parameterList) throws
ExceptionList{
//…… 方法体中的程序段
}
2012-3-7 Java面向对象程序设计教程 30
throws子句样例
? 假设如下三点为真,
? myMethodA调用 myMethodB,myMethodB调用
myMethodC,myMethodC调用 myMethodD;
? myMethodD引发异常 MyException且没有处理;
? myMethodC同样没有兴趣处理该异常,但是
myMethod B对 MyException非常有兴趣。
? 这种情况下,myMethodC必须在 throws列表中
引用 MyException,命令行如下,
public void myMethodC(int x,int y,int z) throws
MyException{
// …… 方法体中的程序段
}
2012-3-7 Java面向对象程序设计教程 31
Java的内置异常类
? java.lang包中定义的不作检查异常子类
? java.lang中定义的检查异常
? java.lang中定义的 Error类
2012-3-7 Java面向对象程序设计教程 32
进行异常处理的注意点
? 一是当一个异常一直传递到 main方法还没有被捕
获,那么 JVM将捕获该异常并给出相应的异常信
息并终止引发异常处之后的操作,这对于用户来
说实际上就是程序已经崩溃,因此自定义的异常
类名不宜放在 main方法声明时的 throws列表中。
? 二是 catch子句不能图方便,只写
catch(Exception e){},因为它将捕获所有的
异常,实际上无法给用户提供实质性的异常提示
信息,也不便于程序调试。
2012-3-7 Java面向对象程序设计教程 33
设计异常类的注意点
? 自定义类应从合适的类派生出来,一般选择
Exception类。
? 在自定义类中要给出构造方法原型,特别要设计
成可以传递用来描述异常的信息,即可以通过
Throwable.getMessage()方法访问到引发异常
的基本信息,参见 UsingMyException.java。
? 不要在自定义类中加入具体的异常信息,因为这
是一个类而不仅仅是一个对象。
? 举例,UsingMyException.java
2012-3-7 Java面向对象程序设计教程 34
断言语句
? 从 JDK1.4开始引入。
? 有助于调试代码,也有助于在程序部署之
后排除应用错误。
? 可以在程序的某一个位置编写断言语言,
表示开发人员相信某些重要的条件在该位
置应当是真的。如果这些条件不是真的,
断言语句可以发出一条错误信息,或者说
明这是一个不打算捕捉的异常。
2012-3-7 Java面向对象程序设计教程 35
断言语句的语法格式
assert booleanExpression;
或
assert booleanException,Expression2;
? 如果布尔值为假,则抛出 java.lang.AssertionError 。
? 第二种语法格式允许增加一个表达式,以便传递给
AssertionError的构造方法。表达式应是一个能够说
明发生什么错误的消息字符串,而且应当只用字符串
作为参数。当然也可以使用其他类型(如整数),表
示错误的数字编号或单个字符标记的错误信息。
? 举例,AssertionExample.java
2012-3-7 Java面向对象程序设计教程 36
四种使用断言功能的情况
? 检查流程的不变性。
? 内部运行的不变性。
? 私有方法结果的检查。
? 程序运行中的一致性。
2012-3-7 Java面向对象程序设计教程 37
使用断言语句时的两个关键问题
? 只应在程序中的少数几个关键位置布置断
言语句。
? 可以控制断言语句是否在运行时生效。
2012-3-7 Java面向对象程序设计教程 38
使用断言语句的两个限制
? 不要在 public方法中,用断言语句来检查参
数的正确性。
? 不要让断言语句去处理一些程序必需的流
程。
2012-3-7 Java面向对象程序设计教程 39
对异常的不作为与作为的区别
? 在于对我们编写的程序,我们是否能够控
制所有的操作。
? 举例,ProcessOrNot.java
5.4 包、接口、类与方法设计
2012-3-7 Java面向对象程序设计教程 41
包的概念
? 包( Package)定义了软件单元,它能够
独立发布,并能够与其它包组成应用程序,
包的成员是一些相关联的类、接口以及子
包,也可包含包中的类所使用的附加资源
文件。
? 定义包的语句形式,
package g3ds.joop.ch5;
? Package类举例,PackageDemo.java
2012-3-7 Java面向对象程序设计教程 42
包的主要作用
? 包将相互关联的接口和类结合成组。
? 包创建了命名空间来避免类型之间的命名
冲突。在一个包中的接口和类可以使用流
行的公共名字来命名,而这些命名只在一
个包内的上下文才有意义,在不同的包之
间则可以有相同的命名。
? 包机制为开发应用提供了保护域。包内的
代码通过访问标志可以相互协作,而这些
标志对外部代码是不可用的。
2012-3-7 Java面向对象程序设计教程 43
包的命名约定
? 采用 Internet域名反序,这种命名模式使
得包名变得很长,但却比较安全。
? 如上海交通大学的域名是 sjtu.edu.cn,那
么完整的 g3ds.joop.ch5包声明就应该是,
package cn.edu.sjtu.g3ds.joop.ch5;
2012-3-7 Java面向对象程序设计教程 44
程序设计时导入类型的方式
? 使用类型的完全限定名。
例如,String类的全限定名是
java.lang.String。
? 使用 import关键字将包的部分或全部内容
导入。
例如使用 g3ds.joop.ch5包的程序员可以
将下面这行代码放在源文件的首部,
import g3ds.joop.ch5.*;
2012-3-7 Java面向对象程序设计教程 45
编译器查找某一类型的顺序
? 包括子类型的当前类型。
? 当前类型中的嵌套类型。
? 显式命名的导入类型(单类型导入)。
? 在同一个包内声明的其它类型。
? 隐式命名的导入类型(按需导入)。
? 如果类型定义仍未找到,则会报错。
import g3ds.joop.ch5,MyObject;
import g3ds.joop.ch5,*;
2012-3-7 Java面向对象程序设计教程 46
讨论:如何解决潜在的冲突命名
? package机制和 import机制使程序员可以控制潜在的冲突命名。
? 例如在 javax.swing包和 java.util包都有一个名为 Timer的类,那么
想要同时在一个源文件中使用这两个类型的程序员可以有几种选择,
? 使用类型的完全限定名,如 javax.swing.Timer和 java.util.Timer。
? 仅导入 javax.swing.Timer和 javax.swing.*,那么对于
javax.swing.Timer就可以使用简单名 Timer来引用,而用完全限定
名来引用 java.util.Timer。
? 反过来:仅导入 java.util.Timer或 java.util.*,使用简单名 Timer来
表示 java.util.Timer,而使用完全限定名来表示
javax.swing.Timer。
? 将这两个包 javax.swing.*和 java.util.*都导入进来,并在自己的代
码中使用完全限定名 javax.swing.Timer和 java.util.Timer(如果
在按需导入的两个包中,有同名类型出现,对任何一个都不能使用简
单名)。
2012-3-7 Java面向对象程序设计教程 47
类成员的可见性
private ( default) protected public
同一类中可见 是 是 是 是
同一个包中对扩展类可
见
否 是 是 是
同一个包中对非扩展类
可见
否 是 是 是
不同包中对扩展类可见 否 否 是 是
不同包中对非扩展类可
见
否 否 否 是
2012-3-7 Java面向对象程序设计教程 48
类成员的可见性解析
? 任何声明为 public的内容可以被从任何地方访问。
? 如果一个成员不含有一个明确的访问说明,它对于子类或
该包中的其它类是可见的,而对于包外面的代码却是隐藏
的。换句话,标志符默认的访问修饰符相当于
,package”,唯一的例外是接口,其成员的默认的访问
修饰符是 public。这是默认访问。
? 如果你希望一个元素在当前包外可见,但仅仅是元素所在
类的子类直接可见,把元素定义成 protected。
? 被声明成 private的成员不能被该类外看到。
? 举例,Point.java, Point3d.java,
AccessibilityDemo.java
2012-3-7 Java面向对象程序设计教程 49
定义包时的内容组织
? 只包含功能上相关的类和接口。
? 应为程序员提供了对类和接口进行逻辑分组的方
法,以便使用者查找。
? 包应当有一定的中心,在某种, 主题, 或与指定
的类有一致性。
? 选择包就是要了解类是否放对了位置。
? 防止包中有太多功能不多的简单小类,使别人在
浏览包中的类时很难记住每个类的责任。
? 建立应用程序时最好维护两组包,一组包含可复
用代码,而另一组包含应用程序特定的类与接口。
2012-3-7 Java面向对象程序设计教程 50
练习:制作 JAR文件包
? 认识 jar 命令
? 一般的打包方法
? 制作可执行的 JAR文件
2012-3-7 Java面向对象程序设计教程 51
接口设计,
“一个接口,多个实现”的好处
? 接口是为支持运行时动态方法解决而设计
的。
? 一个接口定义了一套方法,任何类成员都
可以实现该接口。
? 不过,这些类必须完整地创建该接口所定
义的方法,但每个类可以自由决定它们自
己实现的细节。
2012-3-7 Java面向对象程序设计教程 52
讨论:在一个系统中建立机动运输
工具与油料补给站之间的关系
? 我们设计了运输工具类 Transport,在其中定义 license方法返回通行许可证以判断通
行是否合法,还定义 refuel方法以指定补给燃料的操作。然后在油料补给站类
SupplyDepot中定义一个 service(Transport obj)方法,并在该方法体中利用语句
public void service(Transport obj){
obj.refuel();
}
来实现对运输工具的补给服务。
2012-3-7 Java面向对象程序设计教程 53
改进策略之一:改变类层次结构
2012-3-7 Java面向对象程序设计教程 54
改进策略之二:方法下移
2012-3-7 Java面向对象程序设计教程 55
改进策略之三:设计接口方式
2012-3-7 Java面向对象程序设计教程 56
策略三的优势
? 接口指定了实现类必须严格遵循并提供的方法的签名,保
证了拥有该方法类型的接口同源。
? 使用接口类型作为方法的参数,在方法中调用接口参数承
诺的任何方法,这样在编译时为实现了接口的一组类与其
它类型交互提供了便利。
? 在运行时,实现了接口的对象拥有了接口承诺的所有方法,
并分别给出对该方法的具体实现,使程序能够运行。
? 程序,Refuellable.java, SupplyDepot.java,
Transport.java, RoadVehicle.java, Car.java,
Bicycle.java, MarineVehicle.java, Ship.java,
AirVehicle.java, Airplane.java
2012-3-7 Java面向对象程序设计教程 57
接口设计:利用接口存储常量
? 举例,Judge.java
2012-3-7 Java面向对象程序设计教程 58
设计抽象类的好处
? 避免了对一些确实没有实际意义的方法给
出模糊的解决方案。
? 可以通过这种机制保证从该类扩展出来的
非抽象子类(当然,抽象类的子类也有可
能还是抽象类)都必须覆盖所有的抽象方
法,给出具体的实现。
2012-3-7 Java面向对象程序设计教程 59
讨论:设计抽象类的好处
? 第一种 Shape类的定义
class Shape{
public void draw(){
System.err.println(“Shape类的 draw方法没有定义!, );
}
}
? 第二种 Shape类的定义
abstract class Shape{
//声明一个绘制几何形状的抽象方法
public abstract void draw();
}
不好!
较好!
2012-3-7 Java面向对象程序设计教程 60
抽象类的性质
? 抽象类没有对象。也就是说,一个抽象类
不能通过 new操作符直接实例化,这样的
对象是无用的,因为抽象类是不完全定义
的。
? 我们不能定义抽象构造方法或抽象静态方
法。
? 所有抽象类的子类都必须执行超类中的所
有抽象方法或者是它自己也声明成抽象类。
2012-3-7 Java面向对象程序设计教程 61
接口与抽象类的相似之处
? 定义一种必须具有的形式,但它实际上并
不提供真正的实现。
? 可以用来作为声明引用变量的类型,但决
不可直接用来创建实例 ——对于接口,
new后面的类型必须实现该接口;对于抽
象类,new后面的类型必须是该类的非抽
象子类。
举例,Callback.java
2012-3-7 Java面向对象程序设计教程 62
接口与抽象类的不同之处
? 抽象类是一个不完全的类,需要进一步具
体化。接口则只是一个行为的规范或规定。
? 接口基本上不具备继承的任何具体特点,
它仅仅承诺了能够调用的方法。
? 一个类一次可以实现若干个接口,但只能
扩展一个父类。
2012-3-7 Java面向对象程序设计教程 63
设计类的目的
? 类设计主要是指定责任,标识要实现的功
能并分配给最合适完成这个功能的类。
2012-3-7 Java面向对象程序设计教程 64
类的命名原则
? 类名通常都是名词,而且类名应该是一到
两个词,并能准确描述这个类。
? 类名通常是单数的,如, Student”而不是
,Students” 。
2012-3-7 Java面向对象程序设计教程 65
优化封装
? 应认真对类的方法和属性指定适当的访问
权限限定符。
? 通常要限制对属性的直接访问,同时对每
个字段生成一对访问( get)和修改( set)
方法,以便访问这些属性。
? 合理选择 protected访问权限限定符,这是
因为子类可以直接访问这些属性,这通常
是必需的。
2012-3-7 Java面向对象程序设计教程 66
具有代码复用性的类特征
? 松耦合( Loose coupling)
? 强聚合( Strong cohesion)
2012-3-7 Java面向对象程序设计教程 67
类继承的特征与优点
? 继承是建立类之间, is a”,,is like”或者
,is kind of”的关系,可以很容易地复用已
经存在的数据和代码。
? 容易进行新的实现,因为其大多数可继承
而来。
? 易于修改或扩展那些被复用的实现。
2012-3-7 Java面向对象程序设计教程 68
类继承的缺点
? 破坏了封装性,因为这会将父类的实现细
节暴露给子类。
?, 白盒, 复用,因为父类的内部细节对于
子类而言通常是可见的。
? 当父类的实现更改时,子类也不得不会随
之更改。
? 从父类继承来的实现将不能在运行时进行
改变。
2012-3-7 Java面向对象程序设计教程 69
应用继承的技巧
? 寻找相似之处
? 寻找已有的类
? 遵循单句规则 即类之间应该满足, is a”、
,is like”或者, is kind of”的关系
? 避免实现继承(也称便利继承) 即仅仅因
为方便而使用继承
? 继承所有的东西
2012-3-7 Java面向对象程序设计教程 70
对象聚合的特征与类型
? 表示两个类之间是, is part of”或者, has a”的关系。
? 例子,Person.java, Hobby.java ;
Point.java, Polygon.java, Circle.java ;
Car.java, Part.java 。
2012-3-7 Java面向对象程序设计教程 71
对象聚合的优点
? 容器类仅能通过被包含对象的接口来对其进行访
问。
? 属于, 黑盒( Black box), 复用,因为被包含
对象的内部细节对外是不可见的。
? 封装性好。
? 实现上的相互依赖性比较小。
? 每一个类只专注于一项任务。
? 通过获取指向其它的具有相同类型的对象引用,
可以在运行时动态地定义对象的聚合。
2012-3-7 Java面向对象程序设计教程 72
对象聚合的缺点
? 从而导致系统中的对象过多。
? 为了能将多个不同的对象作为聚合模块来
使用,必须仔细地对接口进行定义。
2012-3-7 Java面向对象程序设计教程 73
采用继承的一般判据
? 子类表达的, 是一个 … 的特殊类型,,而非, 是
一个由 … 所扮演的角色, 。
? 子类的一个实例永远不需要转化为其它类的一个
对象。
? 子类是对其父类的职责进行扩展,而非重写或废
弃。
? 子类没有对那些仅作为一个工具类的功能进行扩
展。
? 对于一个位于实际的问题域的类而言,其子类特
指一种角色,交易或设备。
2012-3-7 Java面向对象程序设计教程 74
讨论:采用继承还是聚合?
? 采用继承方式的人与角色类图
2012-3-7 Java面向对象程序设计教程 75
讨论:采用继承还是聚合?
? 采用聚合方式的人与角色类图
2012-3-7 Java面向对象程序设计教程 76
类设计的开放-封闭法则
? Open-Closed Principle, OCP
? 软件实体应对扩展是开放的,而对修改是封闭的。
( Software entities should be open for
extension,but closed for modification.)
? 可扩展,即, 对扩展是开放的, ( Open for
extension) 模块的行为可以被扩展,以满足新
的需求。
? 不可更改,即, 对更改是封闭的, ( Closed for
modification) 模块的源代码是不允许进行改
动的。
2012-3-7 Java面向对象程序设计教程 77
举例
? 程序,Car.java, PricePolicy.java, SalePrice.java
2012-3-7 Java面向对象程序设计教程 78
类设计的里氏替换法则
? Liskov Substitution Principle,LSP
? 由 Barbara Liskov提出的,是指使用指向基类(超类)
的引用的方法,必须能够在不知道具体扩展类对象类型的
情况下使用它们。
? 在所有根据 T定义的程序 P中,如果对于每个 S类型对象 o1
有一个 T类型对象 o2,那么,假如 S是 T的子类,则当把
o1替换成 o2时,P的行为不会发生改变。( If for each
object o1 of type S there is an object o2 of type T
such that for all programs P defined in terms of T,
the behavior of P is unchanged when o1 is
substituted for o2 then S is a subtype of T,)
? 举例,Rectangle.java, Square.java, TestLSP.java
2012-3-7 Java面向对象程序设计教程 79
方法设计的简单性
public void doSomething(int a,int b,int c,Object obj){
if(a<b){
if(obj instanceof Number){
for(int i=0; i<count; i++){
if(getSomeData(i)==null){
while(c<5){
if(c==0){
handleSpecialCase();
}
}
}
}
}
}
}
public void doSomething(int a,int b,int c,Object obj){
if(a<b){
if(obj instanceof Number){
for(int i=0; i<count; i++){
doPart(i,c);
}
}
}
}
public void doPart(int i,int c){
if(getSomeData(i)==null){
while(c<5){
if(c==0){
handleSpecialCase();
}
}
}
}
2012-3-7 Java面向对象程序设计教程 80
方法设计:传递参数与方法命名
? 当确定向一个方法传递参数时,应避免传
递, 标志, 或, 控制, 参数,告诉方法如
何完成其功能 。
? 举例,Roster.java, RosterUpdate.java,
RosterFinal.java
2012-3-7 Java面向对象程序设计教程 81
方法设计:减少代码重复
public class DuplicationSample {
protected int firstValue;
protected String secondValue;
protected Integer thirdValue;
public DuplicationSample(int first,
String second,Integer third) {
firstValue = first;
secondValue = second;
thirdValue = third;
}
public DuplicationSample(int first,
String second) {
firstValue = first;
secondValue = second;
thirdValue = new Integer(0);
}
}
public class DuplicationSample {
protected int firstValue;
protected String secondValue;
protected Integer thirdValue;
public DuplicationSample(int first,
String second,Integer third) {
firstValue = first;
secondValue = second;
thirdValue = third;
}
public DuplicationSample(int first,
String second) {
this(first,second,new
Integer(0));
}
}
2012-3-7 Java面向对象程序设计教程 82
学习设计模式
? 接受 首先接受设计模式是我们在学习工作
中一个很重要的前提。
? 确认 确认需要阅读设计模式,以便了解何
时加以使用。
? 掌握 最后,要充分掌握模式,明白哪些模
式可能有助于解决一个特定的设计问题。
5.5 Java编码的其它规范
2012-3-7 Java面向对象程序设计教程 84
文件类型的后缀
? Java源程序,.java
? Java字节码,.class
2012-3-7 Java面向对象程序设计教程 85
标识符类型的命名规则
标识符类型 命名规则 举例
包 包的名字应该都是由一个小写单词组成,并且以一个
顶级域名命名,如 com,edu,gov,mil,net,org等,
或者国家标识,如 cn,ca等;随后的包名可以根据自
己所在组织的内部规则命名,如部门、项目、登录名
等
com.sun.eng
cn.edu.sjtu.g3ds
类 类的名字必须是名词,由大写字母开头,而该词的其
他字母都小写;如果类名由多个单词组成的,则每个
单词的首字母也应大写,而且所使用的单词应该写全,
尽可能避免采用只取首字母或者采用缩写形式(除非
该缩写是众所周知的,如 URL或 HTML等);类名应
尽可能简单,又能有效表达设计意图
class Raster;
class ImageSprite;
接口 接口的命名法和类的要求相同 interface
RasterDelegate;
interface Storing;
2012-3-7 Java面向对象程序设计教程 86
标识符类型的命名规则
标识符类型 命名规则 举例
方法 方法名必须是动词,首字母应小写,而该词的其他
字母也都小写;如果方法名由多个单词组成的,则
每个单词的首字母也应大写
run( );
runFast( );
getBackground( );
变量 变量名首字母应小写,而该词的其他字母也都小写;
如果方法名由多个单词组成的,则每个单词的首字
母也应大写;变量名所首字符不应使用,_”或,$”
字符,虽然从标识符组成上来说也是可以的;变量
名应便于记忆,让使用者很容易明白设计该变量的
意图;对于临时变量名,通常用 i,j,k,m,n表示
int型变量,用 c,d,e表示 char型变量
char c;
float myWidth;
int[] myArray;
String userName;
常量 常量名全部采用大写字母表示,单词之间用,_”隔
开
static final int
MIN_WIDTH=4;
static final int
MAX_WIDTH=999;
static final int
GET_THE_CPU=1;
2012-3-7 Java面向对象程序设计教程 87
Java源代码的组织次序
? 开始的注释段
? 包定义和导入相关包的语句
? 类和接口的具体定义
2012-3-7 Java面向对象程序设计教程 88
开始的注释段
/*
* 类名
*
* 版本信息
*
* 日期
*
* 版权警告的注释
*/
2012-3-7 Java面向对象程序设计教程 89
包定义和导入相关包的语句
? 第一个非注释的语句必须是包定义,随后
导入文件中需要用到的包。
package cn.edu.sjtu.g3ds.joop,*;
import java.io,*;
2012-3-7 Java面向对象程序设计教程 90
类和接口的具体定义
类 /接口组成部分的声明 描述
1 类 /接口的文档注释
( /**… */)
用来说明该类 /接口设计的意图,如
/**
* The Example class provides,.,
*/
public class Example {,.,
2 class/interface语句 描述类及其继承关系或接口实现
3 如果有必要,可增加类 /接
口实现的注释( /**… */)
此处的注释用来放置与类 /接口有关的、却又不适合放在类 /接口文档注
释中的内容
4 类变量 类变量的声明顺序依次从 public,protected、( default)直到 private
5 实例变量 实例变量的声明顺序依次从 public,protected、( default)直到 private
6 构造方法 可以从一般的到特殊的
7 方法 方法的放置顺序应以功能组划分,而不应按作用域或者访问权限顺序
排列,以便增加程序的可读性
2012-3-7 Java面向对象程序设计教程 91
多行缩排
? 每行长度不超过 80个字符,而用于文档的例子每行长度应更短些,一般不超过 70个字符。
? 逗号之后,如
someMethod(longExpression1,longExpression2,longExpression3,
longExpression4,longExpression5);
? 运算符之前,如
longName1=longName2 *(longName3+longName4-longName5)
+4*longname6;
? 如果包含有多个级别,则应选择在高级别处换行。而且,新一行的起始位置应与前一行同级的位置 相同。如上面这个例子如下这样换行就不太合适,
longName1=longName2*(longName3+longName4
-longName5)+4*longname6;
? 如果以上的规则导致代码混乱或太挤向右侧,也可以仅用缩进 8个空格替代,如
private static synchronized horkingLongMethodName(int anArg,
Object anotherArg,String yetAnotherArg,Object andStillAnother){
…
}
? 比较特殊的是三元运算符,下面三种方式都可以,
alpha=(aLongBooleanExpression)? beta, gamma;
alpha=(aLongBooleanExpression)? beta
, gamma;
alpha=(aLongBooleanExpression)
beta
, gamma;
2012-3-7 Java面向对象程序设计教程 92
增加空行
? 增加两行空行的情况,
? 源代码文件之间。
? 类 /接口之间。
? 增加一行空行的情况,
? 方法代码块之间。
? 方法体中局部变量和第一个语句之间。
? 代码块或单行注释之前。
? 方法体中逻辑相关的代码块之间
2012-3-7 Java面向对象程序设计教程 93
增加空格
? 关键字和括号之间,如
while(true){
…
}
? 参数列表中逗号和参数变量之间,如
Sample(int i,int j){
…
}
? 除了,,”以外的所有二元运算符和操作数之间,但一元运算符和操作数之间不加空格,
如
a+=c+d;
a=(a+b)/(c*d);
while(d++=s++){
n++;
}
prints(″size is″+foo+″\n″);
? for语句的表达式之间,如
for(expr1; expr2; expr3)
? 类型转换之后,如
myMethod((byte)aNum,(Object)x);
2012-3-7 Java面向对象程序设计教程 94
应避免的一些做法
? 避免用一个对象访问类变量或类方法,如
classMethod(); //直接调用是允许的
AClass.classMethod(); //利用类名调用也是允许的
anObject.classMethod(); //应避免用实例对象调用类变量或类方法!
? 在一个语句中为多个变量赋值,如
fooBar.fChar=barFoo.lchar=′c′;
? 避免把关系运算符, ==”误写为赋值运算符, =”,如
if(c++=d++){
…
}
是错误的,应改写为类似于下式
if((c++=d++)!=0){
…
}
? 不要采用嵌套赋值语句,如
d=(a=b+c)+r;
是应该避免的,应表达为
a=b+c;
d=a+r;
2012-3-7 Java面向对象程序设计教程 95
推荐的一些做法
? 在易引起误解的地方使用括号,如
if(a==b && c==d)
虽然也是对的,但表达成下式更佳,
if((a==b)&&(c==d))
? 尽可能使语句结构更符合所要表达的意图,如
if(booleanExpression){
return true;
} else {
return false;
}
就应改写为
return booleanExpression;
同理,下式
if(condition){
return x;
}
return y;
的更好表达是,
return(condition? x, y);
? 在三元运算符,?,”中的条件判断如果含有二元运算符,则应用使用括号,如
(x>=0)? x,-x;
2012-3-7 Java面向对象程序设计教程 96
遵循规范的一个完整例子
/*
*/
package g3ds.joop.ch5;
import javax.swing,*;
/**
* ConventionExample类用来描述 Java语言中使用的一些规范。
*/
public class ConventionExample extends JFrame {
/* 可以在此添加有关类实现的一些相关的注释 */
/** 有关类变量 classVar1的文档注释 */
public static int classVar1;
/**
* 有关类变量 classVar2 的文档注释
* 注释可以多行表示,并可包含一些 HTML标识符号
*/
private static Object classVar2;
/** 有关实例变量 instanceVar1的文档注释 */
public Object instanceVar1;
/** 有关实例变量 instanceVar2的文档注释 */
protected int instanceVar2;
/** 有关实例变量 instanceVar3的文档注释 */
private Object[] instanceVar3;
/**
* 有关构造方法 ConventionExample的文档注释
*/
public ConventionExample(){
// 构造方法的具体实现 ……
}
/**
* 有关 doSomething方法 的文档注释
*/
public void doSomething(){
// 方法的具体实现 ……
}
/**
* 有关 doSomethingElse 方法的文档注释
* @param 行参 someParam的用途描述
*/
public void doSomethingElse(Object someParam){
// 方法的具体实现 ……
}
}