父子关系
类的单继承 vs 接口的多实现
一个Java类只能继承一个类,原因主要是菱形继承问题:
假设支持多继承,D同时继承B,C。不巧的是B,C是同源的,各自重写了A中的方法。那么D中的方法有B,C两个来源,产生了混乱。
一个Java类可以实现多个接口,多个相同方法签名会合并
1 2 3 4 5 6 7 8 9 10 11 12 13 14 interface A { void add () ; } interface B { void add () ; } class Sample implements A , B { @Override public void add () { System.out.println("AB" ); } }
Java8接口支持default方法,但提供实现同样会遇到类似菱形继承的问题,被实现的多个接口如果包含同名方法,会报错。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 interface A { default void add () { System.out.println("A" ); } } interface B { default void add () { System.out.println("B" ); } } class Sample implements A , B {}
默认方法可以被覆盖
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 interface Size { public int getSize () ; }; interface SmallSize extends Size { @Override public default int getSize () { return 10 ; } }; interface BigSize extends Size { @Override public default int getSize () { return 100 ; } };
抽象类方法 vs 接口默认方法
Java8可以用default修饰方法,允许接口包含默认实现,接口不仅仅是方法签名,也具备了逻辑实现功能,可以进行基类实现,这符合traits
概念。在此之前公共的实现都是通过抽象类来共享,
坏处是如果实现了多个接口,那么这些接口的公共基础实现就都混合在抽象类中,造成类职责复杂,太重量级,破坏了接口的职责划分。
内聚性
有了默认实现,每个接口负责提供自身的公共基础实现,独立内聚,代码更加清晰。
可扩展性
接口default方法提升了扩展性,以前如果接口新增方法,那么所有实现类要改动。有了接口默认实现,就不必特殊处理所有实现类。
重写(Overriding) vs 隐藏(Hiding)
子类实例方法可以重写父类同名的方法,应该总是加上@Override标签,让编译器检查。
方法重写要求
名字和参数完全一致
返回值可以是子类(Java5 协变返回类型 covariant return types)
修饰符不能缩小范围
子类可以定义父类同名的静态方法。静态方法需要用类名调用,那个类调用就用哪个类的方法。
子类如果和父类有同名方法,但是一个是静态,一个是实例,将会报编译错误。
重写和隐藏的本质实际上是动态绑定和静态绑定的区别。
重写对应动态绑定,在运行时才能决定调用哪个类的方法,实现了多态性。
隐藏对应静态绑定,在编译时已决定调用方法。
静态域、静态方法都是静态绑定,另外实例域也不具备多态性,即所有域都不能被子类重写。
Sample 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 class Parent { public static String staticField = "Parent static field" ; public String instanceField = "Parent instance field" ; public static String staticMethod () { return "Parent static method" ; } public String instanceMethod () { return "Parent instance method" ; } } class Child extends Parent { public static String staticField = "Child static field" ; public String instanceField = "Child instance field" ; public static String staticMethod () { return "Child static method" ; } @Override public String instanceMethod () { return "Child instance method" ; } }
测试
Sample 1 2 3 4 5 6 7 8 9 10 11 12 13 System.out.println(Parent.staticField); System.out.println(Parent.staticMethod()); System.out.println(Child.staticField); System.out.println(Child.staticMethod()); Parent ins = new Child(); System.out.println(ins.instanceField); System.out.println(ins.instanceMethod()); System.out.println(((Parent) new Child()).instanceMethod());
父类变量容纳子类
虽然实际是子类对象,但是通过父类变量,只能调用父类方法
Parent p = new Child();
Sample 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 class Parent { public void method (Parent p) { System.out.println("Parent" ); } } class Child extends Parent { public void method (Child c) { System.out.println("Child" ); } @Override public void method (Parent p) { System.out.println("Parent-Child" ); } } public class Test { public static void main (String[] args) throws Exception { Parent p = new Child(); Child c = new Child(); p.method(c); } }