从 Java 8 开始,接口也可以拥有默认的方法实现,这是因为不支持默认方法的接口的维护成本太高了。在 Java 8 之前,如果一个接口想要添加新的方法,那么要修改所有实现了该接口的类。
接口的成员(字段 + 方法)默认都是 public 的,并且不允许定义为 private 或者 protected。
接口的字段默认都是 static 和 final 的。
public interface InterfaceExample { void func1(); default void func2(){ System.out.println("func2"); } int x = 123; // int y; // Variable 'y' might not have been initialized public int z = 0; // Modifier 'public' is redundant for interface fields // private int k = 0; // Modifier 'private' not allowed here // protected int l = 0; // Modifier 'protected' not allowed here // private void fun3(); // Modifier 'private' not allowed here } public class InterfaceImplementExample implements InterfaceExample { @Override public void func1() { System.out.println("func1"); } } // InterfaceExample ie1 = new InterfaceExample(); // 'InterfaceExample' is abstract; cannot be instantiated InterfaceExample ie2 = new InterfaceImplementExample(); ie2.func1(); System.out.println(InterfaceExample.x);
比较
从设计层面来看,抽象类提供了一种IS-A关系,那么就必须满足里式替换原则,即子类对象必须能够替换掉所有父类对象。而接口更像是一种LIKE-A关系,他只是提供一个方法实现契约,并不要求接口和实现的类具有IS—A关系。
从使用上来看,一个类可以实现多个接口,但不能继承多个抽象类。
接口的字段只能是static和final类型的,而抽象类的字段没有这种限制。
接口的成员只能是public的,而抽象类的成员可以有多重访问权限。
使用选择
使用接口:
需要让不相关的类都实现一个方法,例如不相关的类都可以实现 Compareable 接口中的 compareTo() 方法;
需要使用多重继承。
使用抽象类:
需要在几个相关的类中共享代码。
需要能控制继承来的成员的访问权限,而不是都为 public。
需要继承非静态和非常量字段。
在很多情况下,接口优先于抽象类。因为接口没有抽象类严格的类层次结构要求,可以灵活地为一个类添加行为。并且从 Java 8 开始,接口也可以有默认的方法实现,使得修改接口的成本也变的很低。
深入理解 abstract class 和 interface
When to Use Abstract Class and Interface
super访问父类的构造函数:可以使用 super() 函数访问父类的构造函数,从而委托父类完成一些初始化的工作。
访问父类的成员:如果子类重写了父类的某个方法,可以通过使用 super 关键字来引用父类的方法实现。
public class SuperExample { protected int x; protected int y; public SuperExample(int x, int y) { this.x = x; this.y = y; } public void func() { System.out.println("SuperExample.func()"); } } public class SuperExtendExample extends SuperExample { private int z; public SuperExtendExample(int x, int y, int z) { super(x, y); this.z = z; } @Override public void func() { super.func(); System.out.println("SuperExtendExample.func()"); } } SuperExample e = new SuperExtendExample(1, 2, 3); e.func(); SuperExample.func() SuperExtendExample.func() 重写与重载 重写存在于继承体系中,指子类实现了一个与父类在方法声明上完全相同的一个方法。
为了满足里式替换原则,重写有有以下两个限制:
子类方法的访问权限必须大于等于父类方法;
子类方法的返回类型必须是父类方法返回类型或为其子类型。
使用 @Override 注解,可以让编译器帮忙检查是否满足上面的两个限制条件。
#### 重载
存在于同一个类中,指一个方法与已经存在的方法名称上相同,但是参数类型、个数、顺序至少有一个不同。
应该注意的是,返回值不同,其它都相同不算是重载。
实例 class A { public String show(D obj) { return ("A and D"); } public String show(A obj) { return ("A and A"); } } class B extends A { public String show(B obj) { return ("B and B"); } public String show(A obj) { return ("B and A"); } } class C extends B { } class D extends B { } public class Test { public static void main(String[] args) { A a1 = new A(); A a2 = new B(); B b = new B(); C c = new C(); D d = new D(); System.out.println(a1.show(b)); // A and A System.out.println(a1.show(c)); // A and A System.out.println(a1.show(d)); // A and D System.out.println(a2.show(b)); // B and A System.out.println(a2.show(c)); // B and A System.out.println(a2.show(d)); // A and D System.out.println(b.show(b)); // B and B System.out.println(b.show(c)); // B and B System.out.println(b.show(d)); // A and D } } public class Test { public static void main(String[] args) { A a1 = new A(); A a2 = new B(); B b = new B(); C c = new C(); D d = new D(); System.out.println(a1.show(b)); // A and A System.out.println(a1.show(c)); // A and A System.out.println(a1.show(d)); // A and D System.out.println(a2.show(b)); // B and A System.out.println(a2.show(c)); // B and A System.out.println(a2.show(d)); // A and D System.out.println(b.show(b)); // B and B System.out.println(b.show(c)); // B and B System.out.println(b.show(d)); // A and D } }涉及到重写时,方法调用的优先级为:
this.show(O)
super.show(O)
this.show((super)O)