-
接口概念
在Java
程序设计语言中,接口不是类,二十对象的一组需求描述,这些类需要遵从接口描述统一格式进行定义。
接口中的所有方法自动地属于public
。因此,在接口中声明方法时,不必提供关键字public
。
接口中还可以定义常量,但是接口中决不能含有实例域,在Java SE 8
之前,也不能在接口中实现方法。
提供实例域和方法实现的任务应该由实现接口的那个类完成。因此,可以将接口看成是没有实例域的抽象类。但是这两个概念还是有一定区别的。(1)接口的特性
接口不是类,尤其不能使用new运算符实例化一个接口:x = new Comparable(...);//ERROR
然而,尽管不能构造接口的对象,却能声明接口变量:
Comparable x;
与接口中的方法都自动地被设置为
public
一样,接口中的域被自动的设置为public static final
有些接口只定义了常量,而没有定义方法。例如,在标准库中有一个SwingConstants
就是一个这样的接口,其中包含了NORTH
、SOUTH
等常量。任何实现SwingConstants
接口的类都自动地继承了这些常量,并可以在方法中直接引用NORTH
,而不必采用SwingConstants.NORTH
这样繁琐书写形式。
尽管每个类智能拥有一个超类,但却可以实现多个接口。这就为定义类的行为提供的极大的灵活性。
(2)接口与抽象类
为什么Java
程序设计语言还要不辞辛苦地引入接口的概念?为什么不将Comparable
直接设计成如下所示的抽象类。abstract class Comparable { public abstract int compareTo(Object other); }
然后,
Employee
类再直接扩展这个抽象类,并提供compareTo
方法的实现:class Employee extends Comparable { public int compareTo(Object other){...}; }
使用抽象类表示通用属性存在这样一个问题:每个类智能扩展于一个类。假设
Employee
类已经扩展于一个类,例如Person
,它就不能再扩展第二个类了。
但每个类可以实现多个接口。
(3)静态方法
在Java SE 8
,允许在接口中增加静态方法。理论上讲,没有任何理由任务这是不合法的。只是有违于将接口作为抽象规范的初衷。
(4)默认方法
可以为接口方法提供一个默认的实现。必须用default
修饰符标记这样一个方法。public interface Comparable<T> { default int compareTo(T other) {return 0}; }
子类不用必须实现
compareTo
方法。
(5)解决默认方法冲突
如果现在一个接口中将一个方法定义为默认方法,然后又在超类或另一个接口中定义了同样的方法:
1)超类优先。如果超类提供了一个具体方法,同名而且有相同参数类型的默认方法会被忽略。
2)接口冲突。如果一个超类接口提供了一个默认方法,另一个接口提供了一个同名而且参数类型相同的方法,必须覆盖这个方法来解决冲突。interface Named { default String getName() { return getClass().getName() + " _" + hashCode(); } } class Student implements Person, named { ... }
Student
类会继承Person
和Named
接口提供的两个不一致的getName
方法。并不是从中选择一个,Java
编译器会报告一个错误,让程序员来解决这个二义性。只需要在Student
类中提供一个getName
方法。在这个方法中,可以选择两个冲突方法中的一个:class Student implements Person, Named { public String getName() { return Person.super.getName(); } }
(6)接口与回调
回调是一种常见的程序设计模式。在这种模式中,可以指出某个特定时间发生时应该采取的动作。
(7)对象克隆
要了解克隆具体含义,先来回忆为一个包含对象引用的变量建立副本时会发生什么。原变量和副本都是同一个对象的引用。这说明任何一个变量改变都会影响另一个变量。Employee original = new Employee("John public", 50000); Employee copy = original; copy.raiseSalary(10); //如果希望copy是一个新对象,它的初始状态与original相同,但是之后它们各自会有自己不同的状态,这种情况下就可以使用clone方法。 Employee copy = original.clone(); copy.raiseSalary(10);
不过并没有这么简单。
clone
方法是Object
的一个protected
方法,这说明你的代码不能直接调用这个方法。只有Employee
类可以克隆Employee
对象。这个限制是有原因的。想想看Object
类如何实现clone
。它对于这个对象一无所知,所以只能逐个域地进行拷贝。如果对象中所有数据域都是数值或其他基本类型,拷贝这些域没有任何问题。但是如果对象包含子对象的引用,拷贝域就会得到相同子对象的另一个引用,这样一来,源对象和克隆对象仍然共享一些信息。