简介
继承作为面向对象的语言的一种特性,它能够让子类具有父类的方法及属性,很大程度的提高了代码的重用性,同时也让多态成为了一种可能。
为什么说继承具有两面性
继承会破坏封装
什么是封装:封装作为程序设计的一个原则,即就是隐藏程序的实现细节。让使用人员能简单的使用即可,不需要关注其内部实现。其中最常见的封装就是函数,类,封装对于程序设计的意义重大,如果没有封装,那么代码之间很可能存在着特别多的细节依赖,程序的构建和维护将会是灾难性的。
继承可能会对封装造成破坏是因为子类跟父类之间可能存在实现细节的依赖,这样就需要子类在继承父类的时候,必须关注父类的实现细节,父类在修改自己的内部实现的时候需要考虑到子类,不然会对子类构成影响。
继承是怎么样破坏封装的
具体是怎么样破坏封装的,举例说明:
我们先定义一个父类:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 calss Fater{
private static final int MAX_NUM=100;
private int [] arr=new int [MAX_NUM];
private int count;
public void add(int item){
if(count>MAX_NUM){
arr[count++]=item;
}
}
public void addItems(int [] arr){
for(int item:arr){
add(item);
}
}
}
子类继承:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 public class Child extends Father {
private long num;
public void add(int number) {
// TODO Auto-generated method stub
super.add(number);
num+=number;
}
public void addItems(int[] numberItems) {
// TODO Auto-generated method stub
for (int item : numberItems) {
add(item);
num+=item;
}
}
public long getNum() {
return num;
}
}
test类
1
2
3
4
5
6
7
8 class Test{
public static void main(String [] args){
int [] testArray={1,2,3};
Child child=new Child();
child.addItems(testArray);
System.out.println(child.getNum());
}
}
如果子类不知道父类的实现细节,那么很有可能产生错误的扩展。
注意:
- 父类不可以随意的增加公开方法,如果有子类不重写该方法,则不能保证该方法的正确性
- 子类通过继承是没有安全保障的,如果父类修改了实现细节,则子类的功能可能被破坏。如果被继承了,则父类不可以自由的修改内部实现
继承有时候不能反映出is-a的关系
继承关系本来是用来反映is-a关系,即就是子类是父类的一种,但是在is-a关系中,让程序设计完全的符合is-a关系是困难的。(鸵鸟不会飞
)
由上可知,并不是所有的子类都具有父类预期的属性或行为。
如何恰当的应对继承的两免性
避免使用继承
- 使用final关键字,可以保留父类的修改自由
- 优先使用组合
合理使用继承
- 重写方法不改变预期行为
- 了解方法之间的依赖关系
- 修改父类要相应的修改子类
- 只将真正的公共部分放到父类中
- 使用final修饰不希望被重写的方法