本文部分内容摘自:https://www.runoob.com/java/java-inheritance.html
什么是继承
继承就是子类继承父类的特征和行为,使得子类对象(实例)具有父类的实例域和方法,或子类从父类继承方法,使得子类具有父类相同的行为。
例如人属于哺乳动物,鸡属于卵生动物,草属于植物,而哺乳动物、卵生动物、植物都属于生物。我们可以通过一张图来了解他们的继承结构:
通过图我们可以很清晰的看出,生物是哺乳动物的父类,哺乳动物是人的父类,鸡、草同理。
继承需要的关系是is-a的关系,以上图为例,人 is a 哺乳动物,哺乳动物 is a 生物。当满足这种is-a关系时便可以使用继承。像草is a 哺乳动物这一不成立的语句就不可以使用继承。
为什么要使用继承
使用继承的原因在于子类具有父类一些相同的特征(属性)和行为(方法)时,又与父类有一定的差异。在实际开发过程中,如果不使用继承,两个类之间容易有大量的代码重复,代码维护性不高。继承提取了两个类中共有的部分作为父类,通过继承的方式,子类不需要再重复写这部分共有的代码,只需要进行修改即可。
类的继承格式
Java中可以通过extends关键字申明一个类继承另一个类,一般形式如下:
class 父类{
//父类的属性和方法
}
class 子类 extends 父类{
//子类的属性和方法
}
Java除了继承类还有继承接口,我们可以使用implements关键字声明一个类继承一个接口,一般形式如下:
interface 接口A{
//接口中的方法
}
class 类B implements 接口A{
//重写接口中的方法
}
继承类型
还要注意,Java支持接口的多继承,但不支持类的多继承,原因是当两个父类具有相同的方法时,子类不知道需要调用哪一个方法。因此Java不支持多继承,但是Java支持多重继承。
继承的特性
- 子类拥有非private的属性、方法。
- 子类可以拥有自己的属性和方法,即子类可以对父类进行扩展
- 子类可以用自己的方式实现父类,即重写父类的方法
- Java是单继承,但可以多重继承。(这里主要讲的是类继承,接口支持多继承)
- 继承提高了类的耦合性(继承的缺点,耦合性越高代码独立性越差)。
super和this关键字
super关键字:我们可以通过super关键字来实现对父类成员的访问,用来应用当前对象的父类。
this关键字:指向当前类的引用。
实例:
class Animal {
void eat() {
System.out.println("animal : eat");
}
}
class Dog extends Animal {
void eat() {
System.out.println("dog : eat");
}
void eatTest() {
this.eat(); // this 调用自己的方法
super.eat(); // super 调用父类方法
}
}
public class Test {
public static void main(String[] args) {
Animal a = new Animal();
a.eat();
Dog d = new Dog();
d.eatTest();
}
}
继承的注意事项
- final关键字声明的类是不能继承的,若声明方法,则该方法无法被子类重写。变量也是如此。
- 子类是不继承父类的构造器的,它只是调用(隐式或者显示)。如果父类的构造器带有参数,则必须在子类的构造器中显示地通过super关键字来调用父类的构造器并配以适当的参数列表。若没有参数则不需要使用super调用父类构造器,系统会自动调用父类的无参构造器。
继承和组合
说到继承就必须要说到组合。类继承和对象组合是实现类复用的两种最常见的技术。
继承上边已经介绍过了,在此讲一下继承的缺点:
- 父类的内部细节对子类是可见的。
- 子类从父类继承的方法在编译时就确定下来,所以无法在运行期间改变从父类继承的方法的行为。
- 子类和父类是一种高耦合,违背了java的设计原则。(设计原则之后会在另一篇博客详细讲述)
- 继承最大的弱点是打破了封装,子类与父类高度耦合,子类缺乏独立性,从而影响了子类的可维护性。
- 不支持动态继承,在运行时子类无法选择不同的父类。
组合
上边讲过继承是is a的关系,而组合的关系可以用has a 来表示。话不多说,看看代码就懂了:
class Animal {
void eat() {
System.out.println("吃东西中");
}
}
class Dog {
private Animal a;
public Dog(Animal a){
this.a = a;
}
void eat() {
System.out.println("dog");
a.eat();
}
void fake(){
System.out.println("汪汪汪");
}
}
public class Test {
public static void main(String[] args) {
Animal a = new Animal();
Dog d = new Dog(a);
d.eat();
d.fake();
}
}
从上面代码可以清晰的看出,组合极大的降低了父类和子类的耦合性。组合简单来说就是通过拼装对象来实现类的复用,与继承相比,它更强调一个has a 的关系。
组合的优点:
- 不破坏封装,整体类和局部类之间松耦合,彼此相互独立。
- 具有较好的可扩展性。
- 支持动态组合,在运行时整体对象可以选择不同类型的局部对象。
组合的缺点:
- 创建整体类时,需要创建所有局部类对象,导致系统中对象的数量很多。
- 整体类不能自动获得和局部类同样的接口。
组合与继承的选择
既然组合能实现继承的作用,我们应该抛弃继承选择组合吗?答案是否定的,我们可以通过以下几点来决定选择组合还是继承:
- 两个类之间明显存在整体和部分的关系,则应该使用组合来实现复用,当我们需要对已有类做一番改造,从而得到新的符合需求的类,则应该使用继承。
- 当需要被复用的类一定不会改变时,应该使用组合,否则应该使用继承。