抽象类和接口(Java)

抽象类和接口

父类中定义了相关子类中的共同行为。接口可以用于定义类的共同行为 (包括非相关的类) 。

可以使用 java.util.Arrays.sort 方法来对数值和字符串进行排序。

接口是为了定义多个类 (包括非相关的类) 的共同行为。接口与抽象类较为相似。

抽象类

抽象类不可以用于创建对象。抽象类可以包含抽象方法,这些方法将在具体的子类中实现。

在继承的层次结构中,每个新子类都使类变得越来越明确和具体。如果从一个子类追溯到父类,类就会变得更通用、更加不明确。类的设计应该确保父类包含它的子类的共同特征。有时候,一个父类设计得非常抽象,以至于它都没有任何具体的实例。这样的类称为抽象类 (abstract class) 。

抽象方法 (abstract method) ,在方法头中使用 abstract 修饰符表示。
在类头使用 abstract 修饰符表示该类为抽象类。在 UML 图形记号中,抽象类和抽象方法的名字用斜体表示。

抽象类和常规类很像,但是不能使用 new 操作符创建它的实例。抽象方法只有定义而没有实现,它的实现由子类提供。一个包含抽象方法的类必须声明为抽象类。

抽象类的构造方法定义为 protected ,因为它只被子类使用。创建一个具体子类的实例时,它的父类的构造方法被调用以初始化父类中定义的数据域。

抽象类的几点说明:

  • 抽象方法不能包含在非抽象类中。如果抽象父类的子类不能实现所有的抽象方法,那么子类也必须定义为抽象的。换句话说,在抽象类扩展的非抽象子类中,必须实现所有的抽象方法。还要注意到,抽象方法是非静态的。
  • 抽象类是不能使用 new 操作符来初始化的。但是,仍然可以定义它的构造方法,这个构造方法在它的子类的构造方法中调用。
  • 包含抽象方法的类必须是抽象的。但是,可以定义一个不包含抽象方法的抽象类。在这种情况下,不能使用 new 操作符创建该类的实例。这种类是用来定义新子类的基类的。
  • 子类可以覆盖父类的方法并将它定义为 abstract 。这是很少见的,但是它在当父类的方法实现在子类中变得无效时是很有用的。在这种情况下,子类必须定义为 abstract 。
  • 即使子类的父类是具体的,这个子类也可以是抽象的。
  • 不能使用 new 操作符从一个抽象类创建一个实例,但是抽象类可以用作一种数据类型。

Number 类

Number 类是教值包装类、Biglnteger 以及 BigDecimal 的抽象父类。

Number 类是 Double 、Float 、Long 、Integer 、Short 、Byte ,以及 Biglnteger 和 BigDecimal 类的抽象父类。

接口

接口是一种与类相似的结构,只包含常量和抽象方法。

接口在许多方面都与抽象类很相似,但是它的目的是指明相关或者不相关类的多个对象的共同行为。

为了区分接口和类,Java 采用下面的语法来定义接口:

修饰符 interface 接口名
{
	/** 常量声明 */
	/** 方法签名 */
}

在 Java 中,接口被看作是一种特殊的类。就像常规类一样,每个接口都被编译为独立的字节码文件。使用接口或多或少有点像使用抽象类。例如,可以使用接口作为引用变量的数据类型或类型转换的结果等。与抽象类相似,不能使用 new 操作符创建接口的实例。

需要使用 implements 关键字让对象的类实现这个接口来完成。

类和接口之间的关系称为接口继承 (interface inheritance) 。因为接口继承和类继承本质上是相同的,所以我们将它们都简称为继承。

当一个类实现接口时,该类用同样的签名和返冋值类型实现定义在接口中的所有方法。

注意: 由于接口中所有的数据域都是 public static final 而且所有的方法都是 public abstract ,所以 Java 允许忽略这些修饰符。

Comparable 接口

Comparable 接口定义了 compareTo 方法,用于比较对象。

为了实现这个方法,这两个对象必须是可比较的。因此,这两个对象都该有的共同方法就是 comparable (可比较的) 。为此,Java 提供了 Comparable 接口。接口的定义如下所示:

// Interface for comparing objects, defined in java.lang
package java.lang;
public interface Comparab1e<E> 
{
	public int compareTo(E o);
}

compareTo 方法判断这个对象相对于给定对象 o 的顺序,并且当这个对象小于、等于或大于给定对象 o 时,分别返回负整数、0 或正整数。

Comparable 接口是一个泛型接口。在实现该接口时,泛型类型 E 被替换成一种具体的类型。Java 类库中的许多类实现了 Comparable 接口以定义对象的自然顺序。

Byte 、Short 、Integer 、Long 、Float 、Double 、Character 、Biglnteger 、BigDecimalx Calendar 、String 以及 Date 类都实现了 Comparable 接 口。

由于所有 Comparable 对象都有 compareTo 方法,如果对象是 Comparable 接口类型的实例的话,Java API 中的 jaya.util.Arrays.sort(Object[]) 方法就可以使用 compareTo 方法来对数组中的对象进行比较和排序。

接口提供通用程序设计的另一种形式。如果不用接口,很难使用通用的 sort 方法来排序对象,因为必须使用多重继承才能同时继承 Comparable 和另一个类。

Object 类包含 equals 方法,它的目的就是为了让 Object 类的子类来覆盖它,以比较对象的内容是否相同。假设 Object 类包含一个类似于 Comparable 接口中所定义的 compareTo 方法,那么 sort 方法可以用来比较一组任意的对象。由于在 Object 类中没有定义 compareTo 方法,所以 Java 中定义了 Comparable 接口,以便能够对两个 Comparable 接口的实例对象进行比较。强烈建议 (尽管不要求) compareTo 应该与 equals 保持一致。也就是说,对于两个对象 ol和 o2, 应该确保当且仅当 ol.equals(o2) 为 true 时 ol.compareTo(o2)=0 成立。

Cloneable 接口

Cloneable 接口给出了一个可克隆的对象。

创建一个对象拷贝需要使用 clone 方法并理解 Cloneable 接口。

接口包括常量和抽象方法,但是 Cloneable 接口是一个特殊情况。在 java.lang 包中的 Cloneable 接口的定义如下所示:

package java.lang;
public interface Cloneable 
{
}

这个接口是空的。一个带空体的接口称为标记接口 (marker interface) 。一个标记接口既不包括常量也不包括方法。它用来表示一个类拥有某些特定的属性。实现 Cloneable 接口的类标记为可克隆的,而且它的对象可以使用在 Object 类中定义的 clone() 方法克隆。

Java 库中的很多类实现 Cloneable 。这样,这些类的实例可以被克隆。

为了定义一个自定义类来实现 Cloneable 接口,这个类必须覆盖 Object 类中的 clone() 方法。

关键字 native 表明这个方法不是用 Java 写的,但它是 JVM 针对自身平台实现的。关键字 protected限定方法只能在同一个包内或在其子类中访问。其它类必须覆盖该方法并将它的可见性修饰符改为 public, 这样,该方法就可以在任何一个包中使用。Object 类中针对自身平台实现的 clone 方法完成了克隆对象的任务,类中的 clone 方法只要简单调用 super.clone() 即可。在 Object 类中定义的 clone 方法会抛出 CloneNotSupportedException 异常。

Object 类中的 clone 方法将原始对象的每个数据域复制给目标对象。如果一个数据域是基本类型,复制的就是它的值。如果一个数据域是对象,复制的就是该域的引用。

如果数据域是对象类型,那么复制的是对象的引用,而不是它的内容。这称为浅复制 (shallow copy) 而不是深复制 (deep copy) 。

接口与抽象类

一个类可以实现多个接口,但是只能继承一个父类。

接口的使用和抽象类的使用基本类似,但是,定义一个接口与定义一个抽象类有所不同。

接口与抽象类:

变量构造方法方法
抽象类无限制子类通过构造方法链调用构造方法,抽象类不能用 new 操作符实例化无限制
接口所有的变量必须是 public static final没有构造方法。接口不能用 new 操作符实例化所有方法必须是公共的抽象实例方法

Java 只允许为类的扩展做单一继承,但是允许使用接口做多重扩展。
例如:

public class NewClass extends BasedClass implements Interface1 ,,InterfaceN
{
	...
}

利用关键字 extends,接口可以继承其他接口。这样的接口称为子接口 (subinterface) 。例如,在下面代码中,NewInterface 是 Interfacel ,… ,InterfaceN 的子接口。

public interface NewInterface extends Interface1 ,,InterfaceN
{
// constants and abstract methods
}

一个实现 Newlnterface 的类必须实现在 Newlnterface ,Interfacel , … ,InterfaceN 中定义的抽象方法。接口可以扩展其他接口而不是类。一个类可以扩展它的父类同时实现多个接口。

所有的类共享同一个根类 Object ,但是接口没有共同的根。与类相似,接口也可以定义一种类型。一个接口类型的变量可以引用任何实现该接口的类的实例。如果一个类实现了一个接口,那么这个接口就类似于该类的一个父类。可以将接口当作一种数据类型使用,将接口类型的变量转换为它的子类,反过来也可以。

注意: 类名是一个名词。接口名可以是形容词或名词。

设计指南: 抽象类和接口都是用来明确多个对象的共同特征的。那么该如何确定在什么情况下应该使用接口,什么情况下应该使用类呢??一般来说,清晰描述父。子关系的强的 “是一种” 的关系 (strong is-a relationship) 应该用类建模。弱的 “是一种” 的关系 (weak is-a relationship) 也称为类属关系 (is-kind-ofrelationship) ,它表明对象拥有某种属性,可以用接口来建模。

通常,推荐使用接口而非抽象类,因为接口可以定义非相关类共有的父类型。接口比类更加灵活。接口比类拥有更多的灵活性,因为不用使所有东西都属于同一个类型的类。

类的设计原则

类的设计原则有助于设计出合理的类。

内聚性

  • 类应该描述一个单一的实体,而所有的类操作应该在逻辑上相互配合,支持一个一致的目的。
  • 如果一个实体担负太多的职责,就应该按各自的职责分成几个类。

一致性

  • 遵循标准 Java 程序设计风格和命名习惯。为类、数据域和方法选取具有信息的名字。通常的风格是将数据声明置于构造方法之前,并且将构造方法置于方法之前。
  • 选择名字要保持一致。给类似的操作选择不同的名字并非良好的实践。
  • 般来说,应该具有一致性地提供一个公共无参构造方法,用于构建默认实例。如果一个类不支持无参的构造方法,要用文档写出原因。如果没有显式定义构造方法,即假定有一个空方法体的公共默认无参构造方法。
  • 如果不想让用户创建类的对象,可以在类中声明一个私有的构造方法,Math 类即是如此。

封装性

  • 一个类应该使用 private 修饰符隐藏其数据,以免用户直接访问它。这使得类更易于维护。
  • 只在希望数据域可读的情况下,才提供 get 方法;也只在希望数据域可更新的情况下,才提供 set 方法。

清晰性

  • 为使设计清晰,内聚性、一致性和封装性都是很好的设计原则。除此之外,类应该有一个很清晰的合约,从而易于解释和理解。
  • 用户可以以各种不同组合、顺序,以及在各种环境中结合使用多个类。因此,在设计一个类时,这个类不应该限制用户如何以及何时使用该类;以一种方式设计属性,以容许用户按值的任何顺序和任何组合来设置;设计方法应该使得实现的功能与它们出现的顺序无关。
  • 方法应在不产生混淆的情况下进行直观定义。
  • 不应该声明一个来自其它数据域的数据域。

完整性

  • 类是为许多不同用户的使用而设计的。为了能在一个广泛的应用中使用,一个类应该通过属性和方法提供多种方案以适应用户的不同需求。

实例和静态

  • 依赖于类的具体实例的变量或方法必须是一个实例变量或方法。如果一个变量被类的所有实例所共享,那就应该将它声明为静态的。如果方法不依赖于某个具体的实例,那就应该将它声明为静态方法。
  • 应该总是使用类名 (而不是引用变量) 引用静态变量和方法,以增强可读性并避免错误。
  • 不要从构造方法中传人参数来初始化静态数据域。最好使用 set 方法改变静态数据域。
  • 实例和静态是面向对象程序设计不可或缺的部分。数据域或方法要么是实例的,要么是静态的。不要错误地忽视了静态数据域或方法。常见的设计错误是将本应该声明为静态方法的方法声明为实例方法。
  • 构造方法永远都是实例方法,因为它是用来创建具体实例的。一个静态变量或方法可以从实例方法中调用,但是不能从静态方法中调用实例变量或方法。

继承与聚合

  • 继承和聚合之间的差异,就是 is-a (是一种)和 has-a (具有)之间的关系。

接口和抽象类

  • 接口和抽象类都可以用于为对象指定共同的行为。如何决定是采用接口还是类呢?通常,比较强的 is-a (是一种)关系清晰地描述了父子关系,应该采用类来建模。弱的 is-a 关系,也称为 is kind-of (是一类) 关系,表明一个对象拥有某种属性。弱的 is-a 关系可以使用接口建模。
  • 接口比抽象类更加灵活,因为一个子类只能继承一个父类,但是却可以实现任意个数的接口。然而,接口不能具有具体的方法。可以结合接口和抽象类的优点,创建一个接口,使用一个抽象类来实现它。可以视其方便使用接口或者抽象类。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值