Effective Java(第二版)-复合优于继承

继承是java面向对象思想里非常重要的一个概念,是实现代码重用的重要手段,但在Effective Java的第17条中却告诫到,需谨慎的使用继承,这是为什么呢?另外如果不使用继承,用什么方式才能更好的实现多态呢?下文中,首先从一道关于覆盖比较容易出错的面试题讲起,如果覆盖继承的作用仅限于对付面试题的话,不去了解也罢,然而事实并非如此,因此,接下来,将通过Effective Java上的例子说明在具体项目中,如果对覆盖使用不当,将会造成怎样不当的后果,继而引出一种解决方法。

1. 一道覆盖面试题

public class TestPolysim {


	public static void main(String[] args) {
		Super aSuper = new Sub();
		aSuper.print();
	}


}
class Super {
	public void print() {
		System.out.println(" I am in " + getContent());
	}
	
	public String getContent() {
		return "Super";
	}
}


class Sub extends Super{
	
	@Override
	public void print() {
		System.out.println("I am in " +getContent());
		super.print();
	}
	
	@Override
	public String getContent() {
		return "Sub";
	}
}

上述代码的输出是:I am in Sub  \n I am in Sub 对于这个输出,也许有些朋友会觉得奇怪,为什么会这样?正如文 http://www.iteye.com/topic/42811解释的那样,只要我们清楚java方法调用的方式,这个问题就迎刃而解了。在Java虚拟机中,每启动一个新的线程,虚拟机都会为它分配一个Java栈,而每当线程调用一个java方法时,虚拟机就会在该线程的java栈中压入一个新帧,用以存储参数,局部变量等数据。我们将这个正在被执行的方法称为该线程的当前方法,其相应的栈帧为当前帧。
    好了,当我们调用一个方法时,我们需要往当前帧中压入哪些参数呢?简单,方法的参数列表中不是都说得清清楚楚的吗?嗯,对于C语言来说,这个说法是正确的,但是对于诸如C++,Java,Python等面向对象语言来说,却是不对的。大家还记得那个"this"指针吗?!不错,在Java中,所有的实例方法(Instance Method)调用的时候都会把当前对象压入当前帧中,Java虚拟机正是通过这个参数来决定当前所使用的类(通过判断该对象的类型)。
    在上面的例子中,main中调用aSuper.print()时,压入的当前对象自然是Sub类对象,我们记为sub。在Sub的print()中调用super.print()时,压入的就是刚刚压入的对象,也就是sub了。同样,在Super的print中调用print()时,压入的也是sub。因此,在使用 invokevirtual指令调用print()时,找的就是Sub的方法表(当前对象sub的类型为Sub),也就执行了类Sub的print了。

    这种现象在构造函数中特别常见,因为构造函数中会隐含使用调用父类的构造函数的,如果在父类的构造函数中调用了实例方法(如 A的fa),而在子类中又覆盖了这个实例方法(如 B的fa),那么得到的结果往往不是我们所要的。因此,我们最好不要在构造函数中使用多态方法,不然,Debug会很痛苦的:)(上面的解释出自上文提到的连接)

2.

当然,上面的例子更像一道面试题,以此来说明谨慎使用继承可能还不是很有说服力,下面这个来自Effective Java的例子,应该有更具说服力:

import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;

public class TestOverLoad {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		InstrumentHashSet<String> s = new InstrumentHashSet<String>();
		s.addAll(Arrays.asList("Snap","Crackle","Pop"));
		System.out.println(s.getAddCount());
	}

}

class InstrumentHashSet<E> extends HashSet<E> {
	private int addCount =0;

	public InstrumentHashSet(int addCount) {
		this.addCount = addCount;
	}
	
	public InstrumentHashSet() {
	
	}
	
	@Override public boolean add(E e) {
		addCount++;
		return super.add(e);
	}
	
	@Override public boolean addAll(Collection<? extends E> c) {
		addCount += c.size();
		return super.addAll(c);
	}
	
	public int getAddCount() {
		return addCount;
	}
}




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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值