码出高效:Java开发手册-第2章(3)

2.3 类

2.3.1 类的定义

类的定义由访问级别、类型、类名、是否抽象、是否静态、泛型标识、继承或实现关键字、父类或接口名称等组成。类的访问级别有public 和无访问控制符,类型分为class、interface、enum。

Java 类主要由两部分组成:成员和方法。在定义Java 类时,推荐首先定义变量,然后定义方法。由于公有方法是类的调用者和维护者最关心的方法,因此最好首屏展示;保护方法虽然只被子类关心,但也可能是模板设计模式下的核心方法,因此重要性仅次于公有方法;而私有方法对外部来说是一个黑盒实现,因此一般不需要被特别关注;最后是getter/setter 方法,虽然它们也是公有方法,但是因为承载的信息价值较低,一般不包含业务逻辑,所以所有getter/setter 方法须放在类最后。

2.3.2 接口与抽象类

正如面向对象四大特性(抽象、封装、继承、多态)所述,定义类的过程就是抽象和封装的过程,而接口与抽象类则是对实体类进行更高层次的抽象,仅定义公共行为和特征。接口与抽象类的共同点是都不能被实例化,但可以定义引用变量指向实例对象。本节主要分析两者的不同之处,首先从语法上进行区分,如表2-1 所示。

表2-1 接口与抽象类的语法区别

码出高效:Java开发手册-第2章(3)

抽象类在被继承时体现的是is-a 关系,接口在被实现时体现的是can-do 关系。与接口相比,抽象类通常是对同类事物相对具体的抽象,通常包含抽象方法、实体方法、属性变量。如果一个抽象类只有一个抽象方法,那么它就等同于一个接口。is-a关系需要符合里氏代换原则,例如Eagle is a Bird. Bird is an Object。 can-do 关系要符合接口隔离原则,实现类要有能力去实现并执行接口中定义的行为,例如Plane can fly. Bird can fly. 中应该把fly 定义成一个接口,而不是把fly() 放在某个抽象类中,再由Plane 和Bird 利用is-a 关系去继承此抽象类。因为严格意义上讲,除fly 这个行为外,在Plane 和Bird 之间很难找到其他共同特征。

抽象类是模板式设计,而接口是契约式设计。抽象类包含一组相对具体的特征,性格偏内向,比如某品牌特定型号的汽车,底盘架构、控制电路、刹车系统等是抽象出来的共同特征,但根据动感型、舒适型、豪华型的区分,内饰、车头灯、显示屏等可以存在不同版本的实现。接口是开放的,性格偏外向,它就像一份合同,定义了方法名、参数、返回值,甚至抛出异常的类型。谁都可以来实现它,但如果想实现它的类就必须遵守这份接口约定合同,比如,任何类型的车辆都必须实现如下接口:

public interface VehicleSafe {

/**

* @param initSpeed 刹车时的初始速度

* @param brakeTime 从initSpeed 开始刹车到停止行驶的时间,单位是毫秒

* @return 从initSpeed 开始刹车到停止行驶的距离

*/

double brake(int initSpeed, int brakeTime);

}

刹车是一个开放式的强制行为规范,任何车辆都必须具有刹车的能力,要明确在特定初速度的情况下,刹车时间多久,刹车距离多长。此规范对任何车辆都是强约束的,这就是契约。

接口是*的“类”,虽然关键字是interface,但是编译之后的字节码扩展名还是.class。抽象类是二当家,接口位于顶层,而抽象类对各个接口进行了组合,然后实现部分接口行为,其中AbstractCollection 是最典型的抽象类:

public abstract class AbstractCollection<E> implements Collection<E> {

// Collection 定义的抽象方法,但本类没有实现

// Collection 接口定义的方法,size() 这个方法对于链表和顺序表有不同的实现方式

public abstract int size();

// 实现Collection 接口的这个方法,因为对AbstractCollection 的子类

// 它们判空的方式是一致的,这就是模板式设计,对于所有它的子类,实现共同的方法体,

// 通过多态调用到子类的具体size() 实现

public boolean isEmpty() {

// 实现Collection 的方法

return size() == 0;

}

// 其他属性和部分方法实现……

}

Java 语言中类的继承采用单继承形式,避免继承泛滥、菱形继承、循环继承,甚至“四不像”实现类的出现。在JVM 中,一个类如果有多个直接父类,那么方法的绑定机制会变得非常复杂。接口继承接口,关键字是extends,而不是implements,允许多重继承,是因为接口有契约式的行为约定,没有任何具体实现和属性,某个实体类在实现多重继承后的接口时,只是说明“can do many things”。当纠结定义接口还是抽象类时,优先推荐定义为接口,遵循接口隔离原则,按某个维度划分成多个接口,然后再用抽象类去implements 某些接口,这样做可方便后续的扩展和重构。


上一篇:码出高效:Java开发手册-第1章(9)


下一篇:Java邮件发送