Java进阶之路_重温《java编程思想》篇(四)

1.多态

     理解多态

Java中多态实现的基础是后期绑定,或者叫做动态绑定或运行期绑定,编译器一直都不知道对象的具体类型,但是方法调用机制能够找到正确的方法体,并加以调用,所以后期绑定需要在对象中安置某种“类型信息”。最典型的例子:几何图形。

   缺陷:“覆盖私有方法

子类无法覆盖父类中的private方法,子类中与父类私有方法同名的的方法会被作为一个全新的方法来对待,下面程序的输出结果为:private f() 。

package duotai;

public class PrivateOverride {

	private void f(){System.out.println("private f()");}
	public static void main(String[] args) {
		PrivateOverride po = new Derived();
		po.f();
	}

}
class Derived extends PrivateOverride{
	public void f(){System.out.println("public f()");}
}

只有非private方法才可以被重载,但是还需要密切注意覆盖private()方法的现象,这时编译器虽然不会报错,但是也不会按照我们期望的来执行。确切的说,在导出类中,对于基类中的private方法,最好采用不同的名字。

缺陷:域与静态方法

只有普通的方法调用时可以多态的,域的访问则是在编译器就进行解析了的,示例程序如下:


package duotai;

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.getSuperField());
	}
	/* Output:
	 sup.field = 1. sup.getField() = 1
	 sub.field = 1. sub.getField() = 1. sub.getSuperField() = 1
	 */

}
class Super{
	public int field = 1;
	public int getField(){
		return field;
	}
}
class Sub extends Super{
	public int field = 1;
	public int getField(){
		return field;
	}
	public int getSuperField(){
		return super.field;
	}
}

当Sub对象转型为Super引用时,任何域访问操作都将由编译器解析,因此不是多态的。在上例中Super.field 和 Sub.field分配了不同的存储空间,这样,Sub实际上包含两个称为field的域;它自己和它从Super处得到的然而,在引用Sub中的field时所产生的默认域并非SuperSuper版本的field域,因此为了得到Super.field,必须显示地指明field。所以在实际开发过程当中,一般将域设置为private,而通过set和get方法来访问它们。

静态方法是与类而不是和对象相关联的,因此,静态方法肯定是不具有多态性的。


类的构造顺序问题

package duotai;

public class PolyConstructors{
	public static void main(String[] args) {
		new RoundGlyph(5);
	}

}/* Output:
Glyph() before drow()
RoundGlyph.drow(),radius = 0
Glyph() after drow()
RoundGlyph.RoundGlyph(),radius = 5
*///:~
class Glyph{
	void drow(){System.out.println("Glyph.drow()");}
	Glyph(){
		System.out.println("Glyph() before drow()");
		drow();
		System.out.println("Glyph() after drow()");
	}
}
class RoundGlyph extends Glyph{
	private int radius = 1;
	public RoundGlyph(int r) {
		radius = r;
		System.out.println("RoundGlyph.RoundGlyph(),radius = " + radius);
	}
	void drow(){
		System.out.println("RoundGlyph.drow(),radius = " + radius);
	}
}

上述代码结果解析:

子类覆盖了父类的drow()方法,而在父类中又要调用drow()方法,这会导致子类中drow()方法的调用,然而在构造子类之前,需要先构造父类,因此在调用drow()方法的时候,子类并没有开始构造,所以radius的值还没有被赋值1,依然是分配对象存储空间时的初始值0,然后按照顺序进行初始化。

因此,编写构造器时有一条有效的准则:“用尽可能简单的方法使对象进入正常的状态,如果可以的话,避免调用其他方法”。在构造器中唯一能够安全调用的那些方法是基类中的final方法(也适用于private方法,它们自动属于final方法)。这些方法不能被覆盖,也就不会出现上面那种奇怪的问题了。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值