1. 多态

将一个方法调用同一个方法主体关联起来被称作绑定。若在程序执行前进行绑定,叫做前期绑定,比如C只有一种方法调用,就是前期绑定。而运行时根据对象的类型进行绑定,叫后期绑定。后期绑定也称做动态绑定或运行时绑定。Java中除了static方法和final方法之外,其他所有的方法都是后期绑定,这意味着它会自动发生,例如:

class Shape {
  void draw() {
  }
  void erase() {
  }
}
class Circle extends Shape {
  void draw() {
  }
}
class Square extends Shape {
  void draw() {
  }
}
public class Main {
  public static void main(String[] args) {
    Shape s = new Circle();
    s.draw();
  }
}

这里创建了一个Circle对象,并把得到的引用赋值给Shape,这是可以的,因为通过继承,Circle就是一种Shape。这时调用方法draw(),由于后期绑定(多态),还是正确调用了Circle.draw()方法。只有非private方法才可以被覆盖,因为private方法被自动认为是final方法,而且对导出类是屏蔽的,例如:

public class Shape {
  private void draw() {
    System.out.println("private");
  }
  public static void main(String[] args) {
    Shape s = new Circle();
    s.draw(); // private
  }
}
class Circle extends Shape {
  public void draw() {
    System.out.println("public");
  }
}

只有普通的方法调用是可以是多态的,如果直接访问某个域,这个访问将在编译期进行解析,例如:

class Super {
  public int field = 0;
  public void getField() {
    System.out.println(field);
  }
}
class Sub extends Super {
  public int field = 1;
  public void getField() {
    System.out.println(field);
  }
  public void getSuperField() {
    System.out.println(super.field);
  }
}
public class Main {
  public static void main(String[] args) {
    Super sup = new Sub();
    System.out.println(sup.field); // 0
    sup.getField(); // 1
    Sub sub = new Sub();
    System.out.println(sub.field); // 1
    sub.getField(); // 1
    sub.getSuperField(); // 0
  }
}


2. 抽象

Java提供了一个叫做抽象方法的机制,仅有声明而没有方法体,采用的语法如下:

abstract void f();

包含抽象方法的类叫抽象类,如果一个类包含一个或多个抽象方法,类必须被限定为抽象的。如果从一个抽象类继承,并想创建该类的对象,那么就必须为基类中的所有抽象方法提供方法定义,否则导出类也是抽象类,且编译器会强制要求用abstract关键字来限定这个类,例如:

abstract class Test1 {
  public abstract void f();
  public void print() {
    System.out.println("Hello World!");
  }
}
class Test2 extends Test1 {
  public void f() {
    System.out.println("Description");
  }
}
public class Main {
  public static void main(String[] args) {
    Test2 t = new Test2();
    t.print();
    t.f();
  }
}


3. 接口

interface关键字使抽象的概念更向前迈进了一步,abstract关键字允许在类中创建一个或多个没有任何定义的方法,interface关键字产生一个完全抽象的类,它没有任何相应的具体实现,它允许创建者确定方法名、参数列表和返回类型,但是没有任何方法体。

要想创建一个接口,需要用interface关键字来替代class关键字,可以在interface关键字前添加public关键字,如果不添加public关键字,则它只具有包访问权限。接口也可以包含域,但是这些域隐式地是static和final的。要让一个类遵循某个特定接口,需要使用implements关键字,除此之外,看起来很像继承。可以选择在接口中显式地将方法声明为public的,但即使不这么做,它们也是public的,因此当要实现一个接口时,在接口中被定义的方法必须被定义为public,例如:

interface Test1 {
  public void f();
}
class Test2 implements Test1 {
  public void f() {
    System.out.println("Hello World!");
  }
}
public class Main {
  public static void main(String[] args) {
    Test2 t = new Test2();
    t.f();
  }
}


4. 多重继承

在C++中,组合多个类的接口的行为被称作多重继承,它可能会带来沉重的包袱,因为每个类都有一个具体实现,在Java中,可以执行相同的行为,但是只有一个类可以有具体实现。在导出类中,不强制要求必须有一个是抽象的或没有任何抽象方法的基类,如果要从一个非接口的类继承,那么只能从一个类去继承,其余的基元素都必须是接口。需要将所有的接口名都置于implements关键字之后,用逗号将它们一一隔开,例如:

interface CanFight {
  void fight();
}
interface CanSwim {
  void swim();
}
interface CanFly {
  void fly();
}
class ActionCharacter {
  public void fight() {}
}
class Hero extends ActionCharacter implements CanFight, CanSwim, CanFly {
  public void swim() {}
  public void fly() {}
}
public class Main {
  public static void main(String[] args) {
    Hero h = new Hero();
    h.fight();
    h.swim();
    h.fly();
  }
}


5. 嵌套接口

接口可以嵌套在类或其他接口中,例如:

class Test {
  interface I {
    void f();
  }
  public class A implements I {
    public void f() {}
  }
  public class B implements I {
    public void f() {}
  }
}

在类中嵌套接口的语法就像非嵌套接口一样,可以拥有public和包访问两种可视性,另外作为一种新添加的方式,接口也可以被实现为private(相同的语法即适用于嵌套接口,也适用于嵌套类)。