继承
继承是面向对象的三大特征之一(封装,继承,多态),是实现软件复用的一种重要手段,是"is-a"的关系,在java具有单继承的特点,只能有一个直接父类,但是可以有多个间接父类。
优点:
- 代码复用
- 子类可重写父类方法
- 子类在父类的继承上可根据自己的业务需求扩展
- 创建子类对象时,无需创建父类对象,子类自动继承父类的的成员变量和方法,如果权限允许,子类可直接访问
缺点:
-
不支持动态继承,在编译阶段就确定了子类的父类
-
破坏封装性
封装性指出每个类都应该封装它内容信息和实现细节,而只暴露必要的方法给其他类使用。但在继承关系中,子类可以直接访问父类的成员变量和方法,如下例子中父类Fruit中有成员变量weight。Apple继承了Fruit之后,Apple可直接操作Fruit类的成员变量,因此破坏了封装性!// 父类 public class Fruit { //成员变量 public double weight; public void info(){ System.out.println("我是一个水果!重" + weight + "g!"); } } //子类 public class Apple extends Fruit { public static void main(String[] args){ Apple a = new Apple(); // 子类可直接访问父类的成员变量 a.weight = 10; a.info(); } }
-
紧耦合
当父类的实现做了修改时,父类也不得不修改(比如修改了父类某个接口名,子类也必须作相应修改);子类必须依赖父类存在
组合
组合也是实现软件复用的重要方式,并且更好的实现了封装性,是一种"has-a"的关系。
代码体现:
public class Animal {
private void beat(){
System.out.println("心脏跳动...");
}
public void breath(){
beat();
System.out.println("呼吸中...");
}
}
public class Bird {
//将Animal作为Bird的成员变量
private Animal a;
public Bird(Animal a){
this.a = a;
}
public void breath(){
a.breath();
}
public void fly(){
System.out.println("我在飞..");
}
public static void main(String[] args){
Animal animal = new Animal();
Bird b = new Bird(animal);
b.breath();
b.fly();
}
}
优点:
- 支持动态扩展,可在运行时根据具体对象选择不同类型的组合对象(扩展性比继承好)
- 不破坏封装性
- 松耦合
缺点:
- 整体类不能自动获取局部类的接口
- 没有实现多态
如何选择?
- 想实现复用,并且复用部分可能改变,用继承;复用不会改变,用组合
- 当两个类之间明显存在整体与部分的关系时,用组合关系