02、定义接口的注意事项
由之前的例子我们就可以得出下面这些结论:
接口中允许定义变量
接口中允许定义抽象方法
接口中允许定义静态方法(Java 8 之后)
接口中允许定义默认方法(Java 8 之后)
除此之外,我们还应该知道:
1)接口不允许直接实例化。
需要定义一个类去实现接口,然后再实例化。
public class Computer implements Electronic {
public static void main(String[] args) {
new Computer();
}
@Override
public int getElectricityUse() {
return 0;
}
}
2)接口可以是空的,既不定义变量,也不定义方法。
public interface Serializable {
}
Serializable 是最典型的一个空的接口,我之前分享过一篇文章《Java Serializable:明明就一个空的接口嘛》,感兴趣的读者可以去我的个人博客看一看,你就明白了空接口的意义。
http://www.itwanger.com/java/2019/11/14/java-serializable.html
3)不要在定义接口的时候使用 final 关键字,否则会报编译错误,因为接口就是为了让子类实现的,而 final 阻止了这种行为。
4)接口的抽象方法不能是 private、protected 或者 final。
5)接口的变量是隐式 public static final,所以其值无法改变。
03、接口可以做什么
1)使某些实现类具有我们想要的功能,比如说,实现了 Cloneable 接口的类具有拷贝的功能,实现了 Comparable 或者 Comparator 的类具有比较功能。
Cloneable 和 Serializable 一样,都属于标记型接口,它们内部都是空的。实现了 Cloneable 接口的类可以使用 Object.clone() 方法,否则会抛出 CloneNotSupportedException。
public class CloneableTest implements Cloneable { @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } public static void main(String[] args) throws CloneNotSupportedException { CloneableTest c1 = new CloneableTest(); CloneableTest c2 = (CloneableTest) c1.clone(); } }
运行后没有报错。现在把 implements Cloneable 去掉。
public class CloneableTest { @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } public static void main(String[] args) throws CloneNotSupportedException { CloneableTest c1 = new CloneableTest(); CloneableTest c2 = (CloneableTest) c1.clone(); } }
运行后抛出 CloneNotSupportedException:
Exception in thread "main" java.lang.CloneNotSupportedException: com.cmower.baeldung.interface1.CloneableTest at java.base/java.lang.Object.clone(Native Method) at com.cmower.baeldung.interface1.CloneableTest.clone(CloneableTest.java:6) at com.cmower.baeldung.interface1.CloneableTest.main(CloneableTest.java:11)
至于 Comparable 和 Comparator 的用法,感兴趣的读者可以参照我之前写的另外一篇文章《来吧,一文彻底搞懂Java中的Comparable和Comparator》。
http://www.itwanger.com/java/2020/01/04/java-comparable-comparator.html
2)Java 原则上只支持单一继承,但通过接口可以实现多重继承的目的。
可能有些读者会问,“二哥,为什么 Java 只支持单一继承?”简单来解释一下。
如果有两个类共同继承(extends)一个有特定方法的父类,那么该方法会被两个子类重写。然后,如果你决定同时继承这两个子类,那么在你调用该重写方法时,编译器不能识别你要调用哪个子类的方法。这也正是著名的菱形问题,见下图。
ClassC 同时继承了 ClassA 和 ClassB,ClassC 的对象在调用 ClassA 和 ClassB 中重载的方法时,就不知道该调用 ClassA 的方法,还是 ClassB 的方法。
接口没有这方面的困扰。来定义两个接口,Fly 会飞,Run 会跑。
public interface Fly { void fly(); } public interface Run { void run(); }
然后让一个类同时实现这两个接口。
public class Pig implements Fly,Run{ @Override public void fly() { System.out.println("会飞的猪"); } @Override public void run() { System.out.println("会跑的猪"); } }
这就在某种形式上达到了多重继承的目的:现实世界里,猪的确只会跑,但在雷军的眼里,站在风口的猪就会飞,这就需要赋予这只猪更多的能力,通过抽象类是无法实现的,只能通过接口。