1.封装通过合并特征和行为来创建新的数据类型。实现隐藏则通过将细节“私有化”,把接口和实现分离。而多态的作用则是消除类型之间的耦合关系。 多态-动态绑定/后期绑定/运行时绑定2.将一个方法的调用同一个方法主体关联起来被称作绑定。 在前期绑定的情况下,即在程序执行前进行绑定,由编译器和连接程序实现。这样的话,编译器只有一个基类引用如Instrument的时候,它无法知道究竟调用哪个方法才对。解决的办法为后期绑定,它的含义就是在运行时根据对象的类型进行绑定。后期绑定也叫做动态绑定或者运行时绑定。如果一种语言想实现后期绑定,就必须具有某种机制,以便在运行时能动态判断对象的类型,从而调用恰当的方法。也就是说,编译器一直不知道对象的类型,但是方法调用机制能找到正确的方法体,并加以调用。后期绑定机制随编程语言的不同而有所不同,但是只要想一下就会得知,不管怎样都必须在对象中安置某种类型信息。 Java中除了static方法和final方法外(private方法也是final方法)之外,其他所有的方法都是后期绑定。这意味着,通常情况下,我们不必判断是否应该进行后期绑定,它会自动发生。为什么将某个方法声明为final,它可以防止其他人覆盖该方法。但也许更重要的一点是,它可以有效的“关闭”动态绑定,或者说告诉编译器不需要对其进行动态绑定。这样,编译器就可以为final方法调用生成更有效地代码。然而,大多是情况下,这样做对程序的整体性能不会有什么改观。所以,最好根据设计来决定是否使用final,而不是出于试图提高性能的目的来使用final。 注:去查看一下java动态绑定的实现原理3.可扩展性: 在一个设计良好的oop程序中,大多数或者所有都会遵循tune模型,只与基类接口通信。这样的程序是可扩展的,因为可以从通用的基类继承出新的数据类型,从而添加一些功能。那些操纵基类接口的方法是不需要任何改动,就可以应用到新类。 即tune接口不做任何修改,依旧正常运行,这就是我们期待的多态所具有的特性。我们所做的代码修改,不会对程序中其他不应该受到影响的部分产生破坏。换句话说,多态是一项让程序员“将改变的事物与未变的事物分开来”的重要技术。4. 1.private方法被自动认为是final方法,而且对导出类是屏蔽的。即只有非private/final方法才可以被覆盖,不过还是要密切关注覆盖private方法的现象,这时虽然编译器不会报错,但也不会按照我们期望的执行。所以,在导出类中,对于基类中的priavate方法,最好不要采用相同的名字。 2.多态缺陷之域与静态方法 1.只有普通的方法调用是多态的,例如你直接访问某个域,这个访问就在编译期进行解析。 2.如果某个方法是静态的,也不会具有多态性。因为静态方法是与类而不是与某单个对象关联的。 注:1.个人觉得静态方法的调用是直接和静态方法所属的类相关的,而不是你多态指定了一个导出类,就会调用导出类的方法;其永远属于其所属类的。 2.在你要覆盖的方法上加一个Override注解,就会都报错了,所以这是一个好习惯。 5.构造器实际上是static方法,只不过该static声明是隐式的。 基类的构造器总是在导出类的构造过程中被调用,而且按照继承层次逐渐向上链接,以使每个基类的构造器都能得到调用。这样做是有意义的,因为构造器具有一项特殊任务,即:检查对象是否被正确构造。导出类只能访问自己的成员,不能访问基类的成员,基类的成员通常是private的,只有基类的构造器才具有恰当的知识和权限来对自己的元素进行初始化。因此,必须令所有的构造器都能得到调用,否则就不可能构造完整对象。这正是编译器强制每个导出类部分都必须调用构造器的原因。6.调用构造器顺序: 1.调用基类构造器,这个步骤会不断的反复递归下去。首先是构造这种层次结构的根,然后是下一层导出类,等等,直至最低层的导出类。 2.按照声明顺序调用成员的初始化方法。 3.调用导出类构造函数的主体。 ->保证基类中可供我们访问的成员都已得到初始化。->保证所有基类成员以及当前对象的成员对象都被初始化了。 注:当基类中也有成员对象时,也按照相应顺序,先初始化成员对象,然后执行构造函数的主体7.继承与清理: 正常由垃圾回收器处理。 不过如果确实遇到清理的问题,那么必须用心为新类创建dipose方法。由于继承的缘故,如果我们有其他作为垃圾回收一部分的特殊清理动作,就必须在导出类中覆盖dispose方法。当覆盖被继承类的dispose方法时,务必记住调用基类版本的dispose方法,否则基类的清理动作就不会发生。 1.若某个子对象依赖于其他对象,则销毁的顺序应该和初始化顺序相反。对于字段,则意味着与声明的顺序相反。因为字段的初始化是按照声明的顺序进行的。对于基类,遵循C++析构函数的顺序,应该首先对其导出类进行清理,然后才是基类。这是因为导出类的清理可能会调用基类的某些方法,所以需要使基类的控件仍起作用而不是过早的销毁他们。 注:如果某些成员对象中存在与一个或多个对象共享的情况下,如果要清理的情况下,则需要使用引用计数来跟踪那些依旧访问着共享对象的对象数量了。 2.在一个构造器内部调用一个动态绑定方法->则会调用导出类的覆盖方法,而这个方法所操纵的成员可能还未进行初始化。这肯定会有很大问题. 注:初始化的实际过程: 1.在其他任何事物发生前,将分配给对象的存储空间初始化成二进制的0. 2.调用基类构造器,此时如果调用被覆盖的draw方法,在调用子类构造器之前调用,由于步骤1的原因,我们会发现radius的值为0. 3.按照声明顺序调用成员的初始化方法。 4.调用导出类的构造主体。 3.编写构造器准则:用尽可能简单的方法使对象进入正常状态;如果可以的话,避免调用其他方法。在构造器内唯一能够安全调用的方法是基类中的final方法,也适用于private方法,private方法自动输入final方法。这些方法不能被覆盖,所以不会出现上述令人惊讶的问题。你可能无法总是遵循这条准则,但是应该朝着它努力。8.协变返回类型: 1.JavaSE5增加了协变返回类型。它表示在导出类的被覆盖方法可以返回基类方法的返回类型的某种导出类型。9. 1.如果首先考虑继承技术,则会加重我们的设计负担,使事情变的不必要的复杂起来。更好的方式是优先选择”组合“,尤其是不能确定应该使用哪一种方式时。组合不会强制我们的程序设计进入继承的层次结构中,而且组合更加灵活,因为它可以动态选择类型,因此也就选择了行为。相反,继承在编译时就需要知道确切类型。 注:组合可以在运行期间获得动态灵活性--状态模式,而我们不能在运行期决定继承不同的对象,因为它要求在编译期间完全确定下来。见:Transmogrify.java 2.一条通用的准则:用继承表达行为间的差异,用字段表达状态的变化。 3.纯继承与扩展: 1.所谓的纯粹的继承指只有在基类中已经建立的方法才可以才导出类中被覆盖,继承可以确保所有的导出类具有基类的接口,且绝对不会少。->导出类和基类具有一样的接口。也可以认为是一种纯替代,因为导出类完全可以替代基类,而在使用他们时,完全不需要知道关于子类的任何额外信息。->基类可以发送给导出类任何消息,因为二者有着完全相同的接口。我们只需要从导出类向上转型,永远不需要知道正在处理的对象的确切类型。所有这一切,都是通过多态处理的。 2.扩展,可以称为is-like-a关系,因为导出类就像一个是一个基类,它有着相同的基本接口,但是它还具有由额外方法实现的其他特性。虽然这有一种有用且明智的办法(依赖于具体情况),但是它也有缺点。导出类中接口的扩展部分不能被基类访问,因此,一旦向上转型,就不能调用那些新方法。在这种情况下, 如果我们不进行向上转型,这样的问题就不会出现。但是通常情况下,我们需要重新查明对象的确切类型,以便能够访问该类型扩充的方法。 3.向下转型与运行时类型识别: 由于向上转型(在继承层次中向上移动)会丢失具体的类型信息。所以我们就想:通过向下转型,也就是在继承层次中向下移动,应该能够获取类型信息。然而我们知道向上转型是安全的,因为基类不会具有大于导出类的接口。因此我们通过基类接口发送的消息保证都能被接受。但是对于想下转型,如我们无法知道一个几何形状,它确实就是一个圆,它可以是一个三角形,正方形,或者其他一些类型。 要解决这个问题,必须有某种方法来确保向下转型的正确性,使我们不至于贸然转型到一种错误类型,进而发出该对象无法接受的消息。这样做是及其不安全的。 注:C++提供了dynamic_cast函数用于动态转型 但是在Java语言中,所有的转型都会得到检查。所有及时我们只是进行一次普通的加括弧形式的类型转换,在进入运行期时仍然会对其进行检查,以便保证他们的确是我们希望的那种类型。如果不是,就返回一个ClassCastException,类转型异常。这种在运行期间对类型进行检查的行为被称为"运行时类型识别".RTTI. 注:RTTI的内容不仅仅包括转型处理,例如它还提供一种方法,使你可以在试图向下转型之前,查看你所要处理的类型。10.部分源码: package com.book.chap8.multi;
/** *//**
*
*JavaSE5引入的协变返回类型
*
*返回更具体的谷物类型
*
*@author landon
*@since JDK1.6
*@version 1.0 2012-5-2
*
*输出结果:
*Grain
*Wheat
*/
public class CovariantReturn
{
public static void main(Stringargs)
{
Mill m = new Mill();
Grain g = m.process();
System.out.println(g);
m = new WheatMill();
g = m.process();
System.out.println(g);
}
}
//谷物
class Grain
{
@Override
public String toString()
{
return "Grain";
}
}
//小麦
class Wheat extends Grain
{
@Override
public String toString()
{
return "Wheat";
}
}
//磨坊
class Mill
{
//返回类型为Grain
Grain process()
{
return new Grain();
}
}
//磨面粉机
class WheatMill extends Mill
{
//注意,这里就用了协变返回类型,覆盖的process方法的返回类型Wheat为Grain的子类
@Override
Wheat process()
{
return new Wheat();
}
}
package com.book.chap8.multi;
/** *//**
*
*讨论多态的缺陷 : 域
*
*这只是讨论的例子,实际的代码中不应该这样写
*1.通常会将域设置为private,而不是此例的public
*2.你可不能不会对基类中的域和导出类中的域赋予相同的名字,因为这会让人感到混淆
*
*@author landon
*@since JDK1.6
*@version 1.0 2012-4-27
*
*/
public class FieldAccess
{
public static void main(Stringargs)
{
Super sub = new Sub();
//可以看到,这里直接访问sub.field,直接访问域,这是编译解析的;而sub.getField则是方法调用,会动态绑定
System.out.println("sub field:" + sub.field + " sub.getField:" + sub.getField());
Sub sub2 = new Sub();
System.out.println(sub2.field + " " + sub2.getField() + " " + sub2.getSuperField());
}
}
class Super
{
//public 域
public int field = 0;
public int getField()
{
return field;
}
}
class Sub extends Super
{
//1.public 2.与基类取得了相同的名字
public int field = 1;
@Override
public int getField()
{
return field;
}
public int getSuperField()
{
return super.field;
}
}
package com.book.chap8.multi;
import static java.lang.System.out;
/** *//**
*
*测试继承与清理,dispse 为清理方法
*<p>同C++析构函数的性质</p>
*
*@author landon
*@since JDK1.6
*@version 1.0 2012-4-28
*
*/
public class Frog extends Amphibian
{
private Characteristic c = new Characteristic("croaks");
private Description d = new Description("eat bugs");
public Frog()
{
out.println("Amphibian()");
}
@Override
protected void dispose()
{
out.println("frog dispose");
d.disopose();
c.disopose();
super.dispose();
}
public static void main(Stringargs)
{
Frog frog = new Frog();
out.println("Bye");
frog.dispose();
}
}
class Characteristic
{
private String s;
public Characteristic(String s)
{
this.s = s;
out.println("Creating Characteristic:" + s);
}
//protectd dispose
protected void disopose()
{
out.println("Disposing Characteristic:" + s);
}
}
class Description
{
private String s;
public Description(String s)
{
this.s = s;
out.println("Creating Description:" + s);
}
//protectd dispose
protected void disopose()
{
out.println("Disposing Description:" + s);
}
}
class LivingCreature
{
private Characteristic c = new Characteristic("is alive");
private Description d = new Description("basic living creature");
public LivingCreature()
{
out.println("Living Creature");
}
protected void dispose()
{
out.println("Living creature dispose");
//按照声明的相反顺序调用dispose方法
d.disopose();
c.disopose();
}
}
class Animal extends LivingCreature
{
private Characteristic c = new Characteristic("has heart");
private Description d = new Description("animal not vegetable");
public Animal()
{
out.println("Animal()");
}
//完全按照析构的形式,最后调用super.dispose
@Override
protected void dispose()
{
out.println("Animal dispose");
d.disopose();
c.disopose();
super.dispose();
}
}
class Amphibian extends LivingCreature
{
private Characteristic c = new Characteristic("can live in water");
private Description d = new Description("both water and land");
public Amphibian()
{
out.println("Amphibian()");
}
//完全按照析构的形式,最后调用super.dispose
@Override
protected void dispose()
{
out.println("Amphibian dispose");
d.disopose();
c.disopose();
super.dispose();
}
} package com.book.chap8.multi;
/** *//**
*
*经典的tune 多态
*
*@author landon
*@since JDK1.6
*@version 1.0 2012-4-27
*
*/
public class Music
{
public static void tune(Instrument instrument)
{
instrument.play();
}
public static void main(Stringargs)
{
Wind wind = new Wind();
tune(wind);
Brass brass = new Brass();
tune(brass);
}
}
class Instrument
{
public void play()
{
System.out.println("Instrument Play");
}
}
class Wind extends Instrument
{
@Override
public void play()
{
System.out.println("Wind Play");
}
}
class Brass extends Instrument
{
@Override
public void play()
{
System.out.println("Brass Play");
}
}
package com.book.chap8.multi;
import static java.lang.System.out;
/** *//**
*
*多态在构造内部的调用--
*<pre>
* 在一个构造器内部调用一个动态绑定方法->则会调用导出类的覆盖方法,而这个方法所操纵的成员可能还未进行初始化。
*这肯定会有很大问题.
*</pre>
*
*<pre>
* Glyph(),before draw
* RoundGlyph.draw,radius=0
* Glyph(),after draw
* RoundGlyph.RoundGlyph,radius=3
*
*从输出可以看到基类调用的draw方法的确是导出类的draw,不过比较可惜的是,结果输出的半径radius确实0,而不是默认的1,
*这肯定不是我们想要的结果
*</pre>
*
*@author landon
*@since JDK1.6
*@version 1.0 2012-5-2
*
*/
public class PolyConstructor
{
public static void main(Stringargs)
{
new RoundGlyph(3);
}
}
class Glyph
{
void draw()
{
out.println("Glyph.draw");
}
public Glyph()
{
out.println("Glyph(),before draw");
draw();
out.println("Glyph(),after draw");
}
}
//圆的Glyph
class RoundGlyph extends Glyph
{
//注意这个半径被初始化成了1
private int radius = 1;
public RoundGlyph(int r)
{
radius = r;
out.println("RoundGlyph.RoundGlyph,radius=" + radius);
}
//注意覆写了这个在基类构造函数中调用的动态绑定方法
@Override
void draw()
{
out.println("RoundGlyph.draw,radius=" + radius);
}
} package com.book.chap8.multi;
import static java.lang.System.out;
/** *//**
*
*private 方法被覆盖的多态情况
* 编译不会报错,注:其实用Override注解的时候就会报错了
*
* 输出:private f(),在PrivateOverride本类中调用private方法是完全正确的
*
*@author landon
*@since JDK1.6
*@version 1.0 2012-4-27
*
*/
public class PrivateOverride
{
private void f()
{
out.println("private f()");
}
public static void main(Stringargs)
{
PrivateOverride po = new Derived();
//注:我们可能期待的结果是因为多态,调用Derived的f方法,不过因为覆盖的private方法,不是真正的覆盖,也即不是真正的多态;Derived中的f方法其实是一个新方法
po.f();
}
}
class Derived extends PrivateOverride
{
//@Override ,加上该注解就可以解决这个问题了
public void f()
{
out.println("public f");
}
}
package com.book.chap8.multi;
import static java.lang.System.out;
/** *//**
*
*根据引用计数方法来清理共享的对象
*
*@author landon
*@since JDK1.6
*@version 1.0 2012-5-2
*
*/
public class ReferenceCounting
{
public static void main(Stringargs)
{
Shared shared = new Shared();
//shared对象被很多composing共享
Composing[] composings = {new Composing(shared),new Composing(shared),
new Composing(shared),new Composing(shared)};
for(Composing c : composings)
{
c.dispose();
}
}
}
//共享的shared对象
class Shared
{
private int refCount = 0;
//long,防止溢出
private static long counter = 0;
//对象id,final
private final long id = counter++;
public Shared()
{
out.println("Creating:" + this);
}
//增加对象引用
public void addRef()
{
refCount++;
}
//dipose方法,对象引用为0的时候,调用
protected void dispose()
{
if(--refCount == 0)
{
out.println("Disposing:" + this);
}
}
@Override
public String toString()
{
return "Shared " + id;
}
}
class Composing
{
//shared对象
private Shared shared;
private static long counter = 0;
private final long id = counter++;
public Composing(Shared shared)
{
out.println("Creating:" + this);
this.shared = shared;
//调用传入的shared的增加引用方法
this.shared.addRef();
}
protected void dispose()
{
out.println("diposing :" + this);
shared.dispose();
}
@Override
public String toString()
{
return "Composing " + id;
}
} package com.book.chap8.multi;
/** *//**
*
*向下转型之运行时类型识别
*
*@author landon
*@since JDK1.6
*@version 1.0 2012-5-2
*
*<pre>
*输出结果:
*Exception in thread "main" java.lang.ClassCastException: com.book.chap8.multi.Useful cannot be cast to com.book.chap8.multi.MoreUseful
* at com.book.chap8.multi.RTTI.main(RTTI.java:24)
*</pre>
*/
public class RTTI
{
public static void main(Stringargs)
{
Useful[] x = {new Useful(),new MoreUseful()};
x[0].f();
x[1].g();
//这句编译时就会报错
//x[1].u();
//注意:这两个类型转换编译不会报错,不过在运行后会检查类型,如果失败,则会抛出异常,如24L的语句
((MoreUseful)x[1]).u();//向下转型,RTTI
((MoreUseful)x[0]).u();
}
}
class Useful
{
public void f()
{
}
public void g()
{
}
}
//该接口继承了Useful接口,并扩展了三个方法u,v,w
class MoreUseful extends Useful
{
@Override
public void f()
{
}
@Override
public void g()
{
}
public void u()
{
}
public void v()
{
}
public void w()
{
}
} package com.book.chap8.multi;
import static java.lang.System.out;
/** *//**
*
*测试构造器的调用顺序
*<pre>
*1.调用基类构造器,这个步骤会不断的反复递归下去。首先是构造这种层次结构的根,然后是下一层导出类,等等,直至最低层的导出类。
* 2.按照声明顺序调用成员的初始化方法。
*3.调用导出类构造函数的主体。
*</pre>
*
*注:每个基类中也有相应的成员对象
*
*@author landon
*@since JDK1.6
*@version 1.0 2012-4-28
*
*/
public class Sandwich extends PortableLunch
{
private Bread b = new Bread();
private Cheese c = new Cheese();
private Lettuce l = new Lettuce();
public Sandwich()
{
out.println("Sandwich");
}
public static void main(Stringargs)
{
Sandwich sandwich = new Sandwich();
}
}
class Meal
{
private Bread bread = new Bread();
public Meal()
{
out.println("Meal");
}
}
class Bread
{
public Bread()
{
out.println("Bread");
}
}
class Cheese
{
public Cheese()
{
out.println("Cheese");
}
}
//生菜
class Lettuce
{
public Lettuce()
{
out.println("Lettuce");
}
}
class Lunch extends Meal
{
private Cheese cheese = new Cheese();
public Lunch()
{
out.println("Lunch");
}
}
class PortableLunch extends Lunch
{
private Lettuce lettuce = new Lettuce();
public PortableLunch()
{
out.println("Portable Lunch");
}
}
package com.book.chap8.multi;
/** *//**
*
*讨论静态 与多态
*因为static是与类相关的,而不是与单个对象相关联的
*
*@author landon
*@since JDK1.6
*@version 1.0 2012-4-27
*
*/
public class StaticPolymorphism
{
public static void main(Stringargs)
{
StaticSuper sup = new StaticSub();
//这里当然会调用StaticSuper类的静态方法,这早就绑定好了,因为其属于类
System.out.println(sup.staticGet());
System.out.println(sup.dynamicGet());
}
}
class StaticSuper
{
public static String staticGet()
{
return "Base StaticGet";
}
public String dynamicGet()
{
return "Base DynamicGet";
}
}
class StaticSub extends StaticSuper
{
//@Override,可以测试,加上Override注解肯定报错
public static String staticGet()
{
return "Derived StaticGet";
}
@Override
public String dynamicGet()
{
return "Derived DynamicGet";
}
}
package com.book.chap8.multi;
/** *//**
*
*使用组合的好处之一:动态灵活性
*一条通用的准则:用继承表达行为间的差异,用字段表达状态的变化。
*1.本例,通过继承得到了两个不同的类,用于表达act方法的差异
*2.Stage通过运用组合使自己的状态发生变化。这种情况下,这种状态的改变也就产生了行为的改变。
*
*@author landon
*@since JDK1.6
*@version 1.0 2012-5-2
*
*/
public class Transmogrify
{
public static void main(Stringargs)
{
Stage stage = new Stage();
stage.performPlay();
stage.change();
stage.performPlay();
}
}
class Actor
{
public void act()
{
}
}
class HappyActor extends Actor
{
@Override
public void act()
{
System.out.println("Happy Actor");
}
}
class SadActor extends Actor
{
@Override
public void act()
{
System.out.println("Sad Actor");
}
}
//注意该类使用了组合的方式,change方法可以动态改变行为,很灵活
class Stage
{
private Actor actor = new HappyActor();
public void change()
{
actor = new SadActor();
}
public void performPlay()
{
actor.act();
}
}
posted on 2013-01-08 18:06
landon 阅读(1465)
评论(0) 编辑 收藏 所属分类:
Program 、
Book