JAVA编程思想学习总结:第八章——多态

(1)转型后方法执行的规则

若在程序执行前进行绑定(如果有的话,由编译器和连接程序实现),路段前期绑定。这是面向过程的语言中不需要选择就默认的绑定方式。后期绑定就是在运行时根据对象的类型进行绑定。后期绑定也叫做动态绑定或运行时绑定。后期绑定必须具有某种机制,以便在运行时能判断对象的类型,从而调用恰当的方法。
Java中除了static方法和final方法(private方法属于final方法)之外,其他所有方法都是后期绑定。final方法可以有效的关闭动态绑定,为final方法调用生成更有效的代码(整体性能不会有什么改观)。
程序是可扩展的是指可以从通用的基类继承出新的数据类型,从而新添一些功能。那些操纵基类接口的方法不需要任何发动就可以应用于新类。
/*
 * 整合148-157的所有示例,需理解所有输出的原因
 */
class Superclass{
	public int overrideField=0;
	private void Method(){
		System.out.println("private Method");
	}
	public void formalMethod(){
		System.out.println("super formal Method");
	}
	public void showField(){
		System.out.println("overrideField:"+overrideField);
	}
	public void shownooverField(){
		System.out.println("shownooverrideField:"+overrideField);
	}
	public static void staticMethod(){
		System.out.println("super static method");
	}
	public static void main(String[] args){
		Superclass sup=new Subclass();
		sup.Method();
	}
}
class Subclass extends Superclass{
	public int overrideField=1;
	public void showField(){
		System.out.println("overrideField:"+overrideField);
	}
	public void formalMethod(){
		System.out.println("sub formal Method");
	}
	public static void staticMethod(){
		System.out.println("sub static method");
	}
	public void showSuperField(){
		System.out.println("super.overrideField"+super.overrideField);
	}
	public void Mehtod(){
		System.out.println("public Method");
	}
}
public class PolymorphismTest {
	public static void main(String[] args){
		Superclass sup=new Subclass();
		System.out.println("sup.overrideField:"+sup.overrideField);
		sup.showField();
		sup.shownooverField();
		sup.staticMethod();
		sup.formalMethod();
		Subclass subclass=new Subclass();
		System.out.println("subclass.overrideField:"+subclass.overrideField);
		subclass.showField();
		subclass.shownooverField();
		subclass.staticMethod();
		subclass.showSuperField();
		subclass.Mehtod();
		subclass.formalMethod();
	}
}
在运行Superclass对象的main方法时,输出:private Method
这是因为private方法被自动认为是final方法,对导出类屏蔽。所有Method方法能够访问,纯粹是因为现在调用的该对象下的main方法,该main方法直接调用了该对象的私有方法。
在运行PolymorphismTest对象的main方法时,输出:
sup.overrideField:0//对象在转型时,任何域访问操作都将由编译器解析,因此不是多态的,因此直接访问的是父类的方法
overrideField:1//对象在转型后,方法已经重载,访问的是子类的方法
shownooverrideField:0//对象在转型后,方法如果没有被重载,该方法访问的域只会是父类的
super static method//静态方法不具有多态性
sub formal Method
subclass.overrideField:1
overrideField:1
shownooverrideField:0//对象在转型后,方法如果没有被重载,该方法访问的域只会是父类的
sub static method
super.overrideField0
public Method
sub formal Method
需要明确的一点是重载的只会是同名的方法,而不是同名的域。
为了避免以上输出造成的困扰,在实践中,我们应该要注意以下两点:(1)子类中的方法最好不要与基类中的私有方法重名。(2)最好将所有的域设置为private。
在java中,所有转型都会得到检查,所以即使中是进行一次普通的加括弧形式的类型转换,在进入运行期时仍然会对其进行检查,以便保证它的确是我们希望的那种类型。如果不是,就会返回一个ClassCastException(类转型异常)(不必为这个异常编写任何特殊的代码,因为它指出的是程序员在程序中的任何地方都可能会犯的错误)。这种在运行期间对类型进行检查的行为称为“运行时类型识别”(RTTI)。

(2)构造器与多态

在一般情况下,构造器遵照下面的顺序:
1)调用基类构造器。这个步骤会次断地反复递归下去,首先是构造这种层次结构的根,然后是下一层导出类,等等,直到最低层的导出类。
2)按声明顺序调用成员的初始化方法。
3)调用导出类构造器的主体。
但如果构造器存在某个动态绑定方法,则会看到一些不一样的效果。
package polymorphism;
/*
 * P163
 */
class Glyph{
	void draw(){
		System.out.println("Glyph.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("UoundGlyph.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()
UoundGlyph.RoundGlyph(), radius = 5
这个例子向我们展示了初始化更细致的过程:
1)在其他任何事物发生之前,将分配给对象的存储空间初始化为二进制的0。
2)如前所述调用基类构造器,如果存在动态绑定方法,则调用导出类中的方法。
3)按照声明顺序调用成员的初始化方法。
4)调用导出类的构造器主体。
编写构造器时有一条有效的准则:“用尽可能简单的方法使对象进入正常状态,如果可以的话,避免调用其他方法。在构造器内唯一能够安全调用的那些方法是基类中的final方法(或者private方法)。这些方法不能被覆盖,因此也就有会出现上述令人惊讶的问题。你可能无法总是能够遵循这条准则,但是应该朝着它努力。

(3)协变返回类型

Java SE5中添加了协变返回类型,它表示在导同类的被覆盖方法可以返回基类方法的返回类型的某种导出类型。
package polymorphism;
class Grain{
	public String toString(){
		return "Grain";
	}
}
class Wheat extends Grain{
	public String toString(){
		return "Whead";
	}
}
class Mill{
	Grain process(){
		return new Grain();
	}
}
class WheatMill extends Mill{
	Wheat process(){
		return new Wheat();
	}
}
public class CovarianReturn {
	public static void main(String[] args){
		Mill m =new Mill();
		Grain g =m.process();
		System.out.println(g);
		m = new WheatMill();
		g = m.process();
		System.out.println(g);
	}
}
输出结果:
Grain
Whead
(4)用继承进行设计——状态模式
/*
 * P165
 */
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();
	}
}
通过在Stage对象中包含一个Actor的引用,并将Actor指向不同的子类使得performPlay()产生的行为随之改变。这样一来,在运行期间获得了动态灵活性(这也称作状态模式)。
一条通用的准则是:用继承表达行为间的差异,并用字段表达状态上的变化。上述例子中,两者都用到了,通过继承得到了两个不同的类,用于表达act()方法的差异;而Stage通过运用组合使自己的状态发生变化。



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值