多态不仅可以改善代码的组织结构和可读性,还可以创建可扩展程序
多态允许从同一基类导出的多种类型看成同一类型来处理,同一份代码可以无差别的运行在这些不同类型的之上了
向上转型
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的值为0
3 按照声明的顺序调用成员初始化的方法
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();
}
}
纯继承与扩展
继承可以确保所有的导出类都含有基类的方法,但是导出类中的扩展方法并不能别基类访问,所以一旦向上转型就不能调用这些新方法