多态不仅可以改善代码的组织结构和可读性,还可以创建可扩展程序
多态允许从同一基类导出的多种类型看成同一类型来处理,同一份代码可以无差别的运行在这些不同类型的之上了
向上转型
public enum Note {MIDDLE_C,C_SHARP,B_FLAT;}class Instrument{public void play(Note n){System.out.println("Instrument.play");}}class Flute extends Instrument{public void play(Note n){System.out.println("flute play " + n );}}public class Wind extends Instrument{public void play(Note n){System.out.println("Wind.play" + n);}}class Flute extends Instrument{public void play(Note n){System.out.println("flute play " + n );}}public class Wind extends Instrument{public void play(Note n){System.out.println("Wind.play" + n);}public class Music {public static void tune(Instrument i){i.play(Note.B_FLAT);//这个方法接受一个Instrument的引用,同时也能接受任何导出来自Instrument的类比如Wind,Flute如果想让tune的方法接受一个Wind引用作为自己的参数,就需要为Instrument中所有的导出类都编写一个新的tune方法}public static void main(String[] args) {Wind wind = new Wind();tune(wind);Flute flute = new Flute();tune(flute);}}
由于多态的机制,我们可以根据自己的需求对系统添加任意多的新类型,而且不需要更改tune方法
方法绑定
将一个方法调用和一个方法主体关联起来叫做绑定,如果在程序执行之前进行绑定,叫做前期绑定,但如果只有一个Instrument的话,编译器无法知道调用的是那个方法
后期绑定,在运行的时候根据对象的类型进行绑定
Java除了static和final方法(private是隐式的final)的时候,其他的方法都是后期绑定
将某个方法定义为final,可以防止其他人覆盖该方法,这样可以有效的关闭动态绑定
public class Shape {public void draw(){}public void erase(){} //为所有从这里继承而来的导出类建立了一个公共接口}public class Circle extends Shape{public void draw(){System.out.println("Circle draw() ---");}public void erase(){System.out.println("Circle erase() ---");}}public class Square extends Shape{public void draw(){System.out.println("Square draw() ---");}public void erase(){System.out.println("Square erase() ---");}}public class Triangle extends Shape{public void draw(){System.out.println("Triangle draw() ---");}public void erase(){System.out.println("Triangle erase() ---");}}public class RandomShapeGenerator {private Random rand = new Random(47);public Shape next(){switch (rand.nextInt(3)){default :case 0 : return new Circle();case 1 : return new Square();case 2 : return new Triangle();//每次调用next方法的时候,可以为随机选择的shape引用产生一个引用,这个时候只能通过运行的时候才能知道返回的具体类型是哪个类型}}}public class Shapes {private static RandomShapeGenerator r = new RandomShapeGenerator();public static void main(String[] args) {Shape[] s = new Shape[9];for (int i = 0 ; i < s.length ;i ++){s[i] = r.next();}for (Shape shape : s) {shape.draw();//在编译的时候,编译器不需要获得任何的特殊信息就可以进行正确的调用对draw方法的所有调用都是通过动态绑定进行的}}}
缺陷:覆盖私有方法
class Derived extends PrivateOverride{public void f(){System.out.println("Public f()");}}public class PrivateOverride {private void f(){System.out.println("Private f()");//由于private方法被自动的认为是final方法,并且对导出类来说是不可见的,所以Derived中的f方法是一个全新的方法}public static void main(String[] args) {PrivateOverride po = new Derived();po.f();//只有非Private方法才可以被覆盖,在导出类中对于基类中的private方法,最好采取不同的名字}}
缺陷:域与静态方法
class Super{public int field = 0;public int getField(){return field;}}class Sub extends Super{public int field = 1;public int getField(){return field;}public int gerSuperField(){return super.getField();}}public class FieldAccess {public static void main(String[] args) {Super sup = new Sub();System.out.println("sup.field = " + sup.field + " sup.getField = " + sup.getField());Sub sub = new Sub();System.out.println("sub.Field = " + sub.field + " sub.getField = " + sub.getField() + " sub.getSuperField = " +sub.gerSuperField());//想要的到基类的field的话,必须用super关键字来明确的指出}}
class StaticSuper{public static String staticGet(){return "Base staticGet()";}public String DynamicGet(){return "Base dynamicGet()";}}class StaticSub extends StaticSuper{public static String staticGet(){return "Derived staticGet()";}public String DynamicGet(){return "Derived staticGet()";}}public class StaticPolymorphism {public static void main(String[] args) {StaticSuper sup = new StaticSub();System.out.println(sup.staticGet());//得到的是父类的静态方法,静态方法继承不过来System.out.println(sup.DynamicGet());}//静态方法是和类相关联的,并不是和某个实例相关联的}
构造器的调用顺序
基类的构造器总是在导出类的构造过程中被调用,而且是按照继承的层次关系依次从上到下,让每一个基类的构造器都都能得到调用,确保在进入导出类的构造器的时候,在基类中可供我们访问的成员都已经得到了初始化
class Meal{Meal(){System.out.println("Meal()");}}class Bread{Bread(){System.out.println("Bread()");}}class Cheese{Cheese(){System.out.println("Cheese()");}}class Lettuce{Lettuce(){System.out.println("Lettuce()");}}class Lunch extends Meal{Lunch(){System.out.println("Lunch()");}}class PortableLunch extends Lunch{PortableLunch(){System.out.println("PortableLunch()");}}public class Sandwich extends PortableLunch{private Bread b = new Bread();private Cheese c = new Cheese();private Lettuce l = new Lettuce();public Sandwich(){System.out.println("Sandwich()");}public static void main(String[] args) {new Sandwich();}}//调用构造器的顺序:1 调用基类的构造器2 按照声明的顺序调用成员的初始化方法3 调用导出类构造器的主体
继承和清理
构造器内部的多态方法的行为
class Glyph{void draw(){System.out.println("Glyph draw()");}Glyph(){System.out.println("Before draw()");draw();//子类中重写了这个方法,调用的是子类中的方法System.out.println("After draw()");}}class RoundGlyph extends Glyph{private int radius = 1;RoundGlyph(int r){this.radius = r;System.out.println("RoundGlyph .radius = " + radius);}void draw(){System.out.println("RoundGlyph .radius = " + radius);}}public class PolyConstructors {public static void main(String[] args) {new RoundGlyph(5);}}//在基类中调用了draw方法中尚未初始化的radius初始化的实际过程1 在其他的任何事物发生之前,将分配给对象的存储空间初始化为0;2 调用基类的构造器,此时调用了被覆盖之后的draw方法,由于1,所以radius的值为03 按照声明的顺序调用成员初始化的方法4 调用导出类的构造器的主体
在构造器中唯一能够安全调用的方法是基类中的final方法(private方法),这些方法不能被覆盖
用继承进行设计
class Actor{public void act(){ }}class HappyActor extends Actor{public void act(){System.out.println("HappyActor");}}class SadActor extends Actor{public void act(){System.out.println("SadActor");}}class Stage{private Actor actor = new HappyActor();public void change(){actor = new SadActor(); //发生了改变}public void performPlay(){actor.act();}}public class Transmogrify {public static void main(String[] args) {Stage stage = new Stage();stage.performPlay();stage.change();stage.performPlay();}}
纯继承与扩展

继承可以确保所有的导出类都含有基类的方法,但是导出类中的扩展方法并不能别基类访问,所以一旦向上转型就不能调用这些新方法
