第八章:多态

第八章:多态

基类 base = new 子类 ==> base引用指向子类对象,时基类类型

class Instrument {
    
    
  public void play(Note n) {
    
    
    print("Instrument.play()");
  }
}

public class Wind extends Instrument {
    
    
  // Redefine interface method:
  public void play(Note n) {
    
    
    System.out.println("Wind.play() " + n);
  }
}

public class Music {
    
    
  public static void tune(Instrument i) {
    
    
    // ...
    i.play(Note.MIDDLE_C);
  }
  public static void main(String[] args) {
    
    
    Wind flute = new Wind();
    tune(flute); // Upcasting 向上转型
  }
} /* Output:
Wind.play() MIDDLE_C
*///:~

一、转机

如上例子,多态情况下编译器如何知道调用哪个方法?

Instrument引用调用,通过后期绑定实现,利用方法调用机制找到正确的方法。

1、方法绑定

将一个方法调用同方法主体关联起来叫做绑定

前期绑定:①程序执行前绑定(编译器和连接器实现) ②private(类私有)、static(随类加载)、final(关闭动态绑定)修饰的方法属于前期绑定,其他属于后期绑定,它自动发生。

后期绑定(动态绑定):①根据某种方法调用机制,判断对象的类型执行相应的方法;编译器不知道对象的类型,方法调用机制找到正确的方法

class A {
    
    
	protected void f() {
    
      //调用第二个方法
		System.out.println("A.f()"); 
		this.g(); 
	}
	protected void g() {
    
    
		System.out.println("A.g()"); 
	}
}

class B extends A {
    
    
	@Override protected void g() {
    
    
		System.out.println("B.g()");	//子类覆盖父类方法
	}
}

public class Ex10 {
    
    
	public static void main(String[] args) {
    
    
		B b = new B();
		//自动升级为基类以调用基类方法A.f()
		//通过多态性,它将调用派生类方法B.g():
		b.f();
	}
}
结果:
A.f()
B.g()

2、扩展性
3、缺陷,覆盖私有方法:private

基类私有方法private修饰(可认为final修饰,不可覆盖,也有隐士的final意识),对于导出类是屏蔽的。所以子类的public同名方法是新方法并不是覆盖

public class PrivateOverride {
    
    
  private void f() {
    
     print("private f()"); }
  public static void main(String[] args) {
    
    
    PrivateOverride po = new Derived();
    po.f();
  }
}

class Derived extends PrivateOverride {
    
    
  public void f() {
    
     print("public f()"); }
} /* Output:
private f()
*///:~
4、缺陷:域或静态方法

静态是随着类的加载,与类相关联,不具有多态性;而多态是动态绑定

class Super {
    
    
  public int field = 0;//相同名字的成员变量,在各自对象的域中
  public int getField() {
    
     return field; }
  public static String dynamicGet() {
    
    
    return "Super dynamicGet()";
  }
}

class Sub extends Super {
    
    
  public int field = 1; //相同名字的成员变量,在各自对象的域中
  public int getField() {
    
     return field; }
  public int getSuperField() {
    
     return super.field; }
  public static String dynamicGet() {
    
    
    return "Sub dynamicGet()";
  }
}

public class FieldAccess {
    
    
  public static void main(String[] args) {
    
    
    Super sup = new Sub(); // Upcast 向上转型
    System.out.println(sup.dynamicGet()); //  静态方法,调用基类中
    System.out.println("sup.field = " + sup.field +", sup.getField() = " + sup.getField());
    Sub sub = new Sub();
    System.out.println(sub.dynamicGet()); //  静态方法,调用本类
    System.out.println("sub.field = " +sub.field + ", sub.getField() = " +
      sub.getField() +", sub.getSuperField() = " +sub.getSuperField());
      
  }
} /* Output:
Super dynamicGet()
sup.field = 0, sup.getField() = 1
Sub dynamicGet()
sub.field = 1, sub.getField() = 1, sub.getSuperField() = 0
*///:~

二、构造器与多态

1、构造器的调用顺序

1、导出类按照继承体系树向上链接,找到基类,然后逆序初始化构造直到导出类构造,这样也是为了保证导出类调用基类的数据完整性;

2、导出类构造中有句默认的super(); ,如果基类中没有默认的构造将会报错

3、构造其实是一个static方法,只不过是隐式的

class Meal {
    
    
  private static String s = "Meal_test成员变量";
  Meal() {
    
     print("Meal()构造方法"+s); }
  static{
    
    
    System.out.println("Meal()_静态代码块"+s);
  }
  {
    
    
    System.out.println("Meal()_构造代码块"+s);
  }
}

class Bread {
    
    
  Bread() {
    
     print("Bread()"); }
}

class Cheese {
    
    
  Cheese() {
    
     print("Cheese()"); }
}

class Lettuce {
    
    
  Lettuce() {
    
     print("Lettuce()"); }
}

class Lunch extends Meal {
    
    
  Lunch() {
    
     print("Lunch()"); }
}

class PortableLunch extends Lunch {
    
    
  PortableLunch() {
    
     print("PortableLunch()");}
}

public class Sandwich extends PortableLunch {
    
    
  private Bread b = new Bread();
  private Cheese c = new Cheese();
  private Lettuce l = new Lettuce();
  public Sandwich() {
    
     print("Sandwich()"); }
  public static void main(String[] args) {
    
    
    new Sandwich();
  }
} /* Output:
Meal()_静态代码块Meal_test成员变量
Meal()_构造代码块Meal_test成员变量
Meal()构造方法Meal_test成员变量
Lunch()
PortableLunch()
Bread()
Cheese()
Lettuce()
Sandwich()
*///:~
总结:执行顺序是先静态域、静态代码块、基类成员变量初始化、构造代码块、基类构造初始化、子类成员变量初始化、构造代码块、子类构造初始化
2、继承与清理

引用计数 清理垃圾

class Shared {
    
    
  private int refcount = 0;  // refcount=5,由于同一对象成员变量调用自增方法
  private static long counter = 0;//追中Shared实例数量
  private final long id = counter++; //counter=0,使用final修饰,不希望生命周期内改变
  public Shared() {
    
    
    print("Creating " + this);
  }
  public void addRef() {
    
     refcount++; }//追踪引用数,清理时方便知道没有引用
  protected void dispose() {
    
    
    if(--refcount == 0)
      print("Disposing " + this);
  }
  public String toString() {
    
     return "Shared " + id; }
}

class Composing {
    
    
  private Shared shared;
  private static long counter = 0;
  private final long id = counter++;
  public Composing(Shared shared) {
    
    
    print("Creating " + this);
    this.shared = shared;
    this.shared.addRef();
  }
  protected void dispose() {
    
    
    print("disposing " + this);
    shared.dispose();
  }
  public String toString() {
    
     return "Composing " + id; }
}

public class ReferenceCounting {
    
    
  public static void main(String[] args) {
    
    
    Shared shared = new Shared(); // refcount=5,由于同一对象成员变量调用自增方法
    Composing[] composing = {
    
     new Composing(shared),
      new Composing(shared), new Composing(shared),
      new Composing(shared), new Composing(shared) };
    for(Composing c : composing)	//按照数组顺序输出,所以id是从0开始
      c.dispose();
  }
} /* Output:
Creating Shared 0
Creating Composing 0
Creating Composing 1
Creating Composing 2
Creating Composing 3
Creating Composing 4
disposing Composing 0
disposing Composing 1
disposing Composing 2
disposing Composing 3
disposing Composing 4
Disposing Shared 0
*///:~

3、构造器内部的多态行为

基类构造器内部调用一个动态绑定的方法(被覆盖),导致导出类未初始化,出现意外

class Glyph {
    
    
  void draw() {
    
     print("Glyph.draw()"); }
  Glyph() {
    
    
    print("Glyph() before draw()");
    draw();
    print("Glyph() after draw()");
  }
}	

class RoundGlyph extends Glyph {
    
    
  private int radius = 1;
  RoundGlyph(int r) {
    
    
    radius = r;
    print("RoundGlyph.RoundGlyph(), radius = " + radius);
  }
  void draw() {
    
    
    print("RoundGlyph.draw(), radius = " + radius);//被覆盖方法
  }
}	

public class PolyConstructors {
    
    
  public static void main(String[] args) {
    
    
    new RoundGlyph(5);
  }
} /* Output:
Glyph() before draw()
RoundGlyph.draw(), radius = 0
Glyph() after draw()
RoundGlyph.RoundGlyph(), radius = 5
*///:~

三、关于继承和组合设计

1、如果向上转型使用继承,不然使用组合

2、由于继承的多态性,需要动态确定类型是基类还是导出类,方法调用机制才好正确调用方法。(动态绑定)

1、类型转换

编译时父类有该方法,才能调用子类覆盖的方法,不然编译时异常

类型转换必须确定类型(instanceof),不然出现类型转换异常(ClassCastException)

class Useful {
    
    
  public void f() {
    
    }
  public void g() {
    
    }
}

class MoreUseful extends Useful {
    
    
  public void f() {
    
    }
  public void g() {
    
    }
  public void u() {
    
    }
  public void v() {
    
    }
  public void w() {
    
    }
}	

public class RTTI {
    
    
  public static void main(String[] args) {
    
    
    Useful[] x = {
    
    
      new Useful(),
      new MoreUseful()
    };
    x[0].f();
    x[1].g();
    // Compile time: method not found in Useful:  编译时错误
    //! x[1].u();
    ((MoreUseful)x[1]).u(); // Downcast/RTTI  没有确定类型就强制转换,类型转换异常
    ((MoreUseful)x[0]).u(); // Exception thrown
  }
} ///:~

猜你喜欢

转载自blog.csdn.net/jue6628/article/details/104223686