第七章读后感

第七章    多态

继承允许将对象视为它自己本身的类型或其基类型来加以处理。这种能力很重要,因为它允许将多种类型视为同一类型来处理,而同一份代码也就可以毫无差别地运行在这些不同类型之上了。

 

一个对象变量可以指向多种实际类型的现象被称为多态。而在运行时自动选择正确的方法进行调用的现象被称为“动态绑定”

 

这种把对某个对象的引用视为对其基类型的引用的做法被称为“向上转型”。

 

动态绑定:

1)编译器检查对象的声明类型和方法名

2)编译器检查方法调用中提供的参数类型

3若方法是privatefinalstatic的,或者是一个构造器,则编译器能准确判断应调用哪个方法,称为静态绑定;而对于其他方法,要调用哪个方法只有根据隐式参数的实际类型来决定,并在运行时使用动态绑定。

4当程序运行并且使用动态绑定调用方法时,虚拟机必须调用同隐式参数所指对象的实际类型相匹配的方法版本。

 

将某个方法声明为final有两个作用:防止别人覆盖该方法;可以有效关闭“动态绑定”,告诉编译器不需要对其进行动态绑定,可以生成更有效的代码。但是,最好根据设计来决定是否使用final,而不是出于试图提高性能的目的。

 

不能有这样的两个方法,它们具有相同的名字和参数类型,但是返回值却不同。这样会使得编译器不知该调用哪个方法。

 

class Haa{

private void f(){

        System.out.println("private");

}

}

 

public class Ha extends Haa{

public void f(){

        System.out.println("public");

}

public static void main(String[] args){

        Ha aHa=new Ha();

                     

        aHa.f();

}}//private方法被自动认为是final方法,而且对导出类是屏蔽的。Ha中的f方法是一

//个全新的方法。而且基类中的f方法在Ha中不可见,所以也不能被重载

 

仅有声明而没有方法体的是抽象方法。包含一个或多个抽象方法的类是抽象类,不需要所有的方法都是抽象的。如果从一个抽象类继承,并创建新类的对象,就要为新类中的所有抽象方法提供方法定义。

 

抽象类可以有具体方法,接口不可以。

 

构造器的工作顺序,如下

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 ();

   

  }

} ///:~   输出为

Meal()

Lunch()

PortableLunch()

Bread()

Cheese()

Lettuce()

Sandwich ()

从这个例子中可以看出,在复杂对象调用构造器时,要遵照的顺序为:

1)  调用基类构造器(并执行基类中的初始化语句

2)  按声明顺序调用成员的初始化方法

3)  调用导出类构造器的主体

但是,按照我的观察,如果把上述程序改为:

class a{

a(){System.out.println("Hello");}}

class Meal {

a aa=new a();

  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 ();

   

  }

} ///:~  输出为

Hello

Meal()

Lunch()

PortableLunch()

Bread()

Cheese()

Lettuce()

Sandwich ()

红色的代码是新添加的。由此我们可以看出,在调用基类构造器时,是将基类中的初始化语句也执行了的。所以在上面的总结中添加了括号中的红色说明。

 

一般来说,通过继承和组合来创建新类时,永远不必担心对象的清理问题。如果确实需要清理的问题,那么必须为新类创建dispose()方法,或者在导出类中覆盖dispose()方法。当覆盖基类的dispose()方法时,务必一定要调用基类版本的dispose()方法

对于子对象和字段,销毁的顺序应该和初始化(子对象)和声明(字段)的顺序相反。并且,先对导出类清理,后对基类清理。

 

看看下面这个例子:

abstract class Glyph {

  abstract void draw();

  Glyph() {

    System.out.println("Glyph() before draw()");

    draw();

    System.out.println("Glyph() after draw()");

  }

}

 

class RoundGlyph extends Glyph {

  private int radius = 1;

  RoundGlyph(int r) {

    radius = r;

    System.out.println(

      "RoundGlyph.RoundGlyph(), radius = " + radius);

  }

  void draw() {

    System.out.println(

      "RoundGlyph.draw(), radius = " + radius);

  }

}

 

public class PolyConstructors {

  public static void main(String[] args) {

    new RoundGlyph(5);

   

  }

} ///:~   输出为

Glyph() before draw()

RoundGlyph.draw(), radius = 0

Glyph() after draw()

RoundGlyph.RoundGlyph(), radius = 5

逻辑上看似可以,其实结果却不是我们想要的。所以,初始化的顺序为:

1)在其他任何事务发生之前,将分配给对象的存储空间初始化成二进制的零

2)调用基类构造器(并执行基类中的初始化语句

3)按声明顺序调用成员的初始化方法

4)  调用导出类构造器的主体

 

当不能十分确定应该使用继承还是组合时,更好的方式是首先选择组合。一条通用的准则是:用继承表达行为间的差异,并用字段表示状态上的变化。

 

Java中,所有类型转换都会得到检查。这一点对于向下转型尤其重要。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值