1. 多态说明
如下:
package com.zhangyue.learn;
public enum Note {
MIDDLE_C , C_SHARP , B_FLAT ;
}
package com.zhangyue.learn;
class Instruction{
public void play(Note n){
System.out.println("Instruction.play()");
}
}
class Wing extends Instruction{
public void play(Note n){
System.out.println("Wing.play()"+n);
}
}
package com.zhangyue.learn;
public class Music {
public static void tune(Instruction i){
i.play(Note.MIDDLE_C);
}
public static void main(String[] args) {
Wing flute = new Wing();
tune(flute);
}
}
/*output
Wing.play()MIDDLE_C
*/
Music.tune 接受一个Instruction 引用,同时也接收任何导出自Instrucdion 的类,当一个Wing引用传递到tune时,就会出现这种情况,并且不需要任何类型转换,是向上转型。如果让tune()方法直接接受一个Wing引用或许更直观,但就需要为Instruction 的每种类型都编写一个tune()方法。
只写一个简单方法,它仅接受基类作为参数,而不是导出类,编写的代码只与基类打交道,这就是多态。
- 缺陷:域和静态方法
package com.zhangyue.learn;
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 getSuperField(){return super.field;}
}
public class FiledAccess {
public static void main(String[] args) {
Super s = new Sub();
System.out.println(s.field+","+s.getField());
Sub s1 = new Sub();
System.out.println(s1.field+","+s1.getField()+","+s1.getSuperField());
}
}
当Sub对象转型为Super引用时,Sub实际上包含两个称为field的域,引用Sub中的field时默认产生的不是Super的field,为了得到Super的field必须显示的使用super.field()。这种混淆在实践中一般是不会发生的,因为通常会将所有的域设置成是private的。
如果某个方法是静态的,它的行为就不具有多态性,因为该方法是和类相关联的。
2. 构造器和多态
- 构造器的调用顺序
package com.zhangyue.learn;
class Meal{
public Meal() {
System.out.println("Meal()");
}
}
class Bread{
public Bread() {
System.out.println("Bread()");
}
}
class Cheese{
public Cheese() {
System.out.println("Cheese()");
}
}
class Lettuce{
public Lettuce() {
System.out.println("Lettuce()");
}
}
class Lunch extends Meal{
public Lunch() {
System.out.println("Lunch()");
}
}
public class Sandwich extends Lunch{
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) {
Sandwich s = new Sandwich();
}
}
/*
Meal()
Lunch()
Bread()
Chess()
Lettuce()
Sandwich()
*/
步骤是:调用基类构造器,按声明顺序调用成员初始化方法,调用导出类构造器的主体。
3. 继承与清理
当遇到清理问题时,要用心为新类创建dispose(),由于继承,要在导出类中覆盖dispose()方法,还要记住调用基类的dispose()方法,否则基类清理工作就不会发生。
package com.zhangyue.learn;
class Characteristic{
private String s;
Characteristic(String s) {
this.s = s;
System.out.println("Creating Characteristic "+s);
}
protected void dispose(){
System.out.println("disposing Characteristic "+s);
}
}
class Description{
private String s;
Description(String s) {
this.s = s;
System.out.println("Creating Description "+s);
}
protected void dispose(){
System.out.println("disposing Description "+s);
}
}
class LivingCreature{
private Characteristic p = new Characteristic("is alive");
private Description t = new Description("Basic Living Creature");
LivingCreature() {
System.out.println("LivingCreature()");
}
protected void dispose(){
System.out.println("LivingCreature dispose");
t.dispose();
p.dispose();
}
}
class Animal extends LivingCreature{
private Characteristic p = new Characteristic("has heart");
private Description t = new Description("Animal not Vegetable");
Animal() {
System.out.println("Animal()");
}
protected void dispose(){
System.out.println("Animal dispose");
t.dispose();
p.dispose();
super.dispose();
}
}
class Amphibian extends Animal{
private Characteristic p = new Characteristic("can live in water");
private Description t = new Description("Eats Bugs");
Amphibian() {
System.out.println("Amphibian()");
}
protected void dispose(){
System.out.println("Amphibian dispose");
t.dispose();
p.dispose();
super.dispose();
}
}
public class Frog extends Amphibian{
private Characteristic p = new Characteristic("Croaks");
private Description t = new Description("Eats Bugs");
public Frog(){System.out.println("Frog()");}
protected void dispose(){
System.out.println("Frog dispose");
t.dispose();
p.dispose();
// super.dispose();
}
public static void main(String[] args) {
Frog frog = new Frog();
System.out.println("Bye!");
frog.dispose();
}
}
/*
Creating Characteristic is alive
Creating Description Basic Living Creature
LivingCreature()
Creating Characteristic has heart
Creating Description Animal not Vegetable
Animal()
Creating Characteristic can live in water
Creating Description Eats Bugs
Amphibian()
Creating Characteristic Croaks
Creating Description Eats Bugs
Frog()
Bye!
Frog dispose
disposing Description Eats Bugs
disposing Characteristic Croaks
//
Amphibian dispose
disposing Description Eats Bugs
disposing Characteristic can live in water
Animal dispose
disposing Description Animal not Vegetable
disposing Characteristic has heart
LivingCreature dispose
disposing Description Basic Living Creature
disposing Characteristic is alive
*/
层次结构中,子类对象可能依赖于其他对象,所以销毁的顺序要与初始化的顺序相反,先对导出类清理,然后是基类,正如这个例子中看到的。清理必须要谨慎和小心。
如果成员对象存在于其他一个或多个对象共享的情况下,清理的时候就不能简单地调用dispose方法了,可以使用引用计数去跟踪使用者共享对象的对象数量。如下
class Shared{
private int refcount = 0; //引用数,创建的Shared实例的数量
private static long counter = 0;
private final long id = counter++;
public Shared(){
System.out.println("Creating "+this);
}
public void addRef(){refcount ++;}
protected void dispose(){ //跟踪引用数,决定何时执行清理
if (--refcount == 0) {
System.out.println("Disposing " + this);
}
}
public String toString(){
return "Shared "+id;
}
}
class Composing{
private Shared shared;
private static long counter = 0;
public final long id = counter++;
public Composing(Shared shared){
System.out.println("Creating " + this);
this.shared = shared;
this.shared.addRef();
}
protected void dispose(){
System.out.println("dispose "+ this);
shared.dispose();
}
public String toString(){
return "Composing " +id;
}
}
public class ReferenceCounting {
public static void main(String[] args) {
Shared shared = new Shared();
Composing[] composing = { new Composing(shared),new Composing(shared),new Composing(shared),
new Composing(shared),new Composing(shared)};
for (Composing c: composing) {
c.dispose();
}
}
};
/*output
Creating Shared 00
Creating Composing 0
Creating Composing 1
Creating Composing 2
Creating Composing 3
Creating Composing 4
dispose Composing 0
dispose Composing 1
dispose Composing 2
dispose Composing 3
dispose Composing 4
Disposing Shared 0
*/
- 构造器内部的多态方法的行为
在一般方法的内部,动态绑定的调用是在运行时才决定的,因为对象无法知道它是属于方法所在的类还是属于那个类的导出类;如果调用构造器内部的一个动态绑定方法,就要用到那个方法被覆盖后的定义。如下所示
package com.zhangyue.learn;
class Glyph{
void draw(){System.out.println("Glyph.draw");}
public Glyph() {
System.out.println("before Glyph.draw");
draw();
System.out.println("after Glyph.draw");
}
}
class RoundGlyph extends Glyph{
private int radius = 1;
public 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);
}
}
/*
before Glyph.draw
RoundGlyph.draw().radius = 0
after Glyph.draw
RoundGlyph.RoundGlyph().radius = 5
*/
Glyph.draw()要被RoundGlyph覆盖,但Glyph要调用draw(),导致调用了RoundGlyph.draw(),输出结果却不是1,而是0。
看初始化顺序就可以明白:
(1) 在其他任何事物发生之前,将分配给对象的存储空间初始化成二进制的0;
(2)调用基类构造器;
(3)按照声明的顺序调用成员的初始化方法;
(4)调用导出类的构造器主体。
- 多态意味着不同的形式,有从基类继承来的相同接口以及使用该接口的不同形式:不同版本的动态绑定方法。动态绑定即一个对象调用的是基类的方法还是导出类的重写方法,在运行时确定。