夯实Java基础(三)——面向对象之继承
1、继承概述
继承是Java面向对象的三大特征之一,是比较重要的一部分,与后面的多态有着直接的关系。继承就是子类继承父类的特征和行为,使得子类对象(实例)具有父类的实例域和方法,或子类从父类继承方法,使得子类具有父类相同的行为。
①、Java继承的特点:
Java只支持单继承,不支持多继承(如A继承B,A继承C),但支持多层继承(如A继承B,B继承C)。
子类拥有父类非private的属性,方法。(其实子类继承父类后,仍然认为获取了父类的private结构,只是因为封装的影响,使得子类不能直接调用父类的结构而已)
子类可以拥有自己的属性和方法,即子类可以对父类进行扩展。
子类可以用自己的方式实现父类的方法。
提高了类之间的耦合性(继承的缺点,耦合度高就会造成代码之间的联系)。
②、继承的好处:
减少代码的冗余,提高代码的复用性。
便于功能的扩展。
为后面多态的使用提供了前提。
③、类的继承格式:
在Java中通过 extends 关键字来实现继承关系,形式如下:
class 父类 { 属性、方法 } class 子类 extends 父类 { 属性、方法 }
提到Java的继承,肯定离不开this,super关键字和构造器的使用,下面来介绍一下:
2、this、super关键字
this关键字:表示引用当前对象或正在创建的对象。可用于调用本类的属性、方法、构造器。
public class Test { public static void main(String[] args) { Father father=new Father("tang_hao",20); father.show(); } } class Father{ String name; int age; public Father() { } public Father(String name) { //调用本类的无参构造器 this(); this.name = name; } public Father(String name, int age) { //调用本类的有参构造器 this(name); this.age = age; } public void show(){ System.out.println("name"+this.name+",age"+this.age); } }
super关键字:表示引用当前对象的父类。可用于调用父类的属性、方法、构造器。
public class Test { public static void main(String[] args) { Son son=new Son(); son.show(); } } class Father{ String name="Father"; int age=40; public Father() { System.out.println("调用了父类的无参构造器"); } public void show(){ System.out.println("父类的Show方法"); } } class Son extends Father{ String name="Son"; int age=20; public Son() { //调用父类构造器 super(); } public void show(){ System.out.println("子类的Show方法"); System.out.println("本类属性:"+this.name+","+this.age); //调用父类方法 super.show(); System.out.println("父类属性:"+super.name+","+super.age); } }
注意:在使用this、super调用构造器的时候,this、super语句必须放在构造方法的第一行,否则编译会报错。不能在子类中使用父类构造方法名来调用父类构造方法。 父类的构造方法不被子类继承。调用父类的构造方法的唯一途径是使用 super 关键字,如果子类中没显式调用,则编译器自动将 super();也就是说会一直调到Object类,因为Object是所有类的父类。静态方法中不能使用 super 关键字。
3、构造器
前面讲到子类可以继承父类的非private修饰的属性和方法,那么我们思考一下?父类的构造器能够被子类继承吗?答案是:不能!对于构造器而言,它只能够被调用,而不能被继承。 调用父类的构造方法我们使用super()即可。
我们先来看一个示例:
public class Father { protected String name;//姓名 protected int age;//年龄 public Father(){ System.out.println("Father constructor"); } public void eat(){ System.out.println("Father eat..."); } public void sleep(){ System.out.println("Father sleep..."); } } class Son extends Father{ protected String name;//姓名 protected int age;//年龄 public Son() { System.out.println("Son constructor"); } public void eat() { System.out.println("Son eat..."); } public void sleep() { System.out.println("Son sleep..."); } public static void main(String[] args) { Son son=new Son(); son.eat(); son.sleep(); } }
运行结果:
从运行的结果来看,我们只new了Son的实例,但是却调用父类的无参构造器,而且先输出了父类构造器的语句。这是因为:如果子类中没显式调用父类构造器,则编译器会自动在构造器方法第一行加上super()。
通过这个示例可以看出,构建过程是从父类“向外”扩散的,也就是从父类开始向子类一级一级地完成构建。而且我们并没有显示的引用父类的构造器,这就是java的聪明之处:编译器会默认给子类调用父类的构造器。
但是,这个默认调用父类的构造器是有前提的:父类有默认构造器。如果父类没有默认构造器,我们就要必须显示的使用super()来调用父类构造器,否则编译器会报错:无法找到符合父类形式的构造器。
public class Father { protected String name;//姓名 protected int age;//年龄 public Father(String name, int age) { this.name = name; this.age = age; } public void eat(){ System.out.println("Father eat..."); } public void sleep(){ System.out.println("Father sleep..."); } } class Son extends Father{ protected String name;//姓名 protected int age;//年龄 public Son(String name, int age) { //这里父类没有默认构造器,所以必须显式的使用super() super("tang_hao",22); this.name = name; this.age = age; } public void eat() { System.out.println("Son eat..."); } public void sleep() { System.out.println("Son sleep..."); } public static void main(String[] args) { Son son=new Son("tang_hao",20); son.eat(); son.sleep(); } }
小结:子类会默认使用super()调用父类默认构造器,如果父类没有默认构造器,则子类必须显式的使用super()来调用父类构造器,而且super()必须要放在子类构造方法的第一行。
4、继承带来的问题
- 子类与父类存在严重的耦合关系。
- 继承破坏了父类的封装性。
- 子类继承父类的属性和方法,也就说明可以从子类中恶意修改父类的属性和方法。
所以能不使用继承关系就尽量不要使用继承。
5、何时使用继承
- 子类需要额外增加属性,而不仅仅是属性值的改变。
- 子类需要增加自己独有的行为方式(包括增加新的方法或重写父类的方法)。
相关文章