java核心技术卷I-接口

本文详细阐述了接口的概念及其在类实现中的作用,包括接口的声明、特性、与抽象类的区别与共性,并介绍了Java SE 8中新引入的静态方法和默认方法特性。

接口

接口不是类,而是对类的一组需求描述,这些类要遵从接口描述的统一格式进行定义。接口可能包含多个方法。在接口中还可以定义常量,然而, 更为重要的是要知道接口不能提供哪些功能。接口绝不能含有实例域,在 JavaSE 8之前,也不能在接口中实现方法。提供实例域和方法实现的任务应该由实现接口的那个类来完成。因此,可以将接口看成是没有实例域的抽象类,但是这两个概念还是有一定区别的。
为了让类实现一个接口,通常需要下面两个步骤:
1 ) 将类声明为实现给定的接口。
2 ) 对接口中的所有方法进行定义。
要将类声明为实现某个接口, 需要使用关键字 implements:

class Employee implements Comparable

接口的特性

接口不是类,尤其不能使用 new 运算符实例化一个接口:

x = new Comparable(. . .); // ERROR

然而, 尽管不能构造接口的对象,却能声明接口的变量:

Comparable x; // OK

接口变量必须弓I用实现了接口的类对象:

x = new Employee(. . .); // OK provided Employee implements Comparable

可以使用instance 检查一个对象是否实现了某个特定的接口

if (anObject instanceof Comparable) { . . . }

与可以建立类的继承关系一样,接口也可以被扩展。这里允许存在多条从具有较高通用
性的接口到较高专用性的接口的链

public interface Powered extends Moveable
{
double milesPerCallon();
double SPEED.LIHIT = 95; // a public static final constant
}

与接口中的方法都自动地被设置为 public—样,接口中的域将被自动设为 public static final。
尽管每个类只能够拥有一个超类,但却可以实现多个接口。如果希望自己设计的类拥有克隆和比较的能力,只要实现这两个接口就可以了,使用逗号将实现的各个接口分隔开

class Employee implements Cloneable, Comparable

接口和抽象类的区别

(1)抽象类可以有构造方法,接口中不能有构造方法。
(2)抽象类中可以有普通成员变量,接口中没有普通成员变量
(3)抽象类中可以包含静态方法,接口中不能包含静态方法
(4) 一个类可以实现多个接口,但只能继承一个抽象类。
(5)接口可以被多重实现,抽象类只能被单一继承
(6)如果抽象类实现接口,则可以把接口中方法映射到抽象类中作为抽象方法而不必实现,而在抽象类的子类中实现接口中方法

接口和抽象类的共性

(1) 都可以被继承
(2) 都不能被实例化
(3) 都可以包含方法声明
(4) 派生类必须实现未实现的方法

静态方法

在 Java SE 8 中,允许在接口中增加静态方法。

默认方法

可以为接口方法提供一个默认实现。 必须用 default 修饰符标记这样一个方法。当然, 这并没有太大用处, 因为 Comparable 的每一个实际实现都要覆盖这个方法。不过有些情况下, 默认方法可能很有用。
默认方法的一"t•重要用法是“‘ 接口演化” (interface evolution。) 以 Collection 接口为例,这个接口作为 Java 的一部分已经有很多年了。假设很久以前你提供了这样一个类:

public class Bag implements Collection

后来,在 JavaSE 8 中,又为这个接口增加了一个 stream 方法。假设 stream 方法不是一个默认方法。那么 Bag 类将不能编译,因为它没有实现这个新方法。为接口增加一个非默认方法不能保证“源代码兼容”
不过,假设不重新编译这个类,而只是使用原先的一个包含这个类的 JAR 文件。这个类
仍能正常加载,尽管没有这个新方法。程序仍然可以正常构造 Bag 实例,不会有意外发生。 ( 为接口增加方法可以保证“ 二进制兼容”)。不过,如果程序在一个 Bag 实例上调用 stream方法,就会出现一个 AbstractMethodError。
将方法实现为一个默认方法就可以解决这两个问题。Bag 类又能正常编译了。另外如果
没有重新编译而直接加载这个类,并在一个 Bag 实例上调用 stream 方法,将调用 Collection.stream 方法。

解决默认方法冲突

如果先在一个接口中将一个方法定义为默认方法,然后又在超类或另一个接口中定义了
同样的方法,会发生什么情况? 诸如 Scala 和 C++ 等语言对于解决这种二义性有一些复杂的规则。幸运的是,Java 的相应规则要简单得多。规则如下:
1 ) 超类优先。如果超类提供了一个具体方法,同名而且有相同参数类型的默认方法会
被忽略。
2 ) 接口冲突。 如果一个超接口提供了一个默认方法,另一个接口提供了一个同名而且
参数类型(不论是否是默认参数)相同的方法,必须覆盖这个方法来解决冲突。
一个类扩展了一个超类,同时实现了一个接口,并从超类和接口继承了相同的方法,“ 类优先” 规则,优先使用超类的方法

对象克隆

一个包含对象引用的变量建立副本时会发生什么。原变量和副本都是同一个对象的引用。这说明, 任何一个变量改变都会影响另一个变量。

Employee original = new Employee("John Public", 50000);
Employee copy = original;
copy.raiseSalary(10); // oops-also changed original

如果希望 copy 是一个新对象,它的初始状态与 original 相同,但是之后它们各自会有自己不同的状态,这种情况下就可以使用 clone 方法。

Employee copy = original,clone();
copy.raiseSalary(10); // OK original unchanged

拷贝和克隆示意图
对于每一个类,需要确定:
1 ) 默认的 clone 方法是否满足要求;
2 ) 是否可以在可变的子对象上调用 clone 来修补默认的 clone 方法;
3 ) 是否不该使用 clone。
实际上第 3 个选项是默认选项。如果选择第 1 项或第 2 项,类必须:
1 ) 实现 Cloneable 接口;
2 ) 重新定义 clone 方法,并指定 public 访问修饰符。
即使 clone 的默认(浅拷贝)实现能够满足要求,还是需要实现 Cloneable 接口, 将 clone重新定义为 public,再调用 super.clone()。

class Employee implements Cloneable{
// raise visibility level to public, change return type
	public Employee clone() throws CloneNotSupportedException{
	return (Employee) super.clone();
	}
}

与 Object.clone提供的浅拷贝相比,前面看到的 clone 方法并没有为它增加任何功能。这里只是让这个方法是公有的。要建立深拷贝,还需要做更多工作,克隆对象中可变的实例域。

class Employee implements Cloneable{
	public Employee cloneO throws CloneNotSupportedException
	{
	    // call Object,clone0
		Employee cloned = (Employee) super.clone() ; 
		// clone mutable fields
	    cloned.hireDay = (Date) hireDay.clone();
	    return cloned;
	}
}

所有数组类型都有一个 public 的 clone 方法,而不是 protected: 可以用这个方法
建立一个新数组,包含原数组所有元素的副本

int[] luckyNumbers = { 2, 3, 5, 7, 11, 13 };
int[] cloned = luckyNumbers.clone();
cloned[5] = 12; // doesn't change luckyNumbers[5]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

局外人一枚

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值