继承
所有的OOP语言都会有三个特征:
为什么会有继承呢?可以先看下面的例子:
上面这两个类中的代码很相似因为它们只有最后一个方法不同其它的都相同,这样定义不但导致代码冗余而且非常麻烦。而在OOP语言中继承就是专门用来进行共性抽取,实现代码复用。
继承(inheritance)机制:是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特 性的基础上进行扩展,增加新功能,这样产生新的类,称派生类。继承呈现了面向对象程序设计的层次结构, 体现了由简单到复杂的认知过程。继承主要解决的问题是:共性的抽取,实现代码复用。
在JAVA中要实现继承需要借助extends关键字。
修饰符 class 子类 extends 父类 {
// ...
}
此时我们在看再面的例子老师和学生都属于人这个大类,那么我们就可以单独定义一个People类来将他们相同的属性和方法放在一起,然后由学生类和老师类分别继承人这个大类:
classPeople{ publicStringname; publicintage; publicvoidspeak(){ System.out.println(this.name+"正在说话"); } } classTeacherextendsPeople{ publicvoidwork(){ System.out.println(this.name+"正在教书"); } } classStudentextendsPeople{ publicvoidstudy(){ System.out.println(this.name+"正在学习"); } } publicclassTest { publicstaticvoidmain(String[] args) { Teacherteacher=newTeacher(); teacher.name="zhangsan"; teacher.age=24; teacher.speak(); teacher.work(); System.out.println("=========="); Studentstudent=newStudent(); student.name="xiaoming"; student.age=15; student.speak(); student.study(); } }
注意:子类会将父类中的成员变量或者成员方法继承到子类中。
那么现在又有一个新问题如果子类中有和父类相同的属性或方法那系统会怎么办?
classPeople{ publicStringname; publicintage=25; publicvoidspeak(){ System.out.println(this.name+"正在说话"); } } classTeacherextendsPeople{ publicintage=20; publicvoidwork(){ System.out.println(this.name+"正在教书"+"今年"+this.age); } } publicclassTest { publicstaticvoidmain(String[] args) { Teacherteacher=newTeacher(); teacher.name="zhangsan"; teacher.work(); } }
此时父类和子类都有age属性代码运行后可以看到使用的是子类的属性。
总结:
- 通过子类对象访问父类与子类中属性时,优先访问自己的,自己没有时再到父类中找,如果父类中也没有则报错。
- 通过子类对象访问父类与子类中不同名方法时,优先在子类中找,找到则访问,否则在父类中找,找到则访问,否则编译报错。
- 通过子类对象访问父类与子类同名方法时,如果父类和子类同名方法的参数列表不同(重载),根据调用方法适传递的参数选择合适的方法访问,如果没有则报错;
就算父类中有private修饰的属性或方法子类也会将它继承下来但是无法访问。
super
在上面的例子中出现了一个新的关键字super。那么super关键字是干什么的?
该关键字主要作用:在子类方法中访问父类的成员。
classPeople{ publicStringname; publicintage=25; publicvoidspeak(){ System.out.println(this.name+"正在说话"); } } classTeacherextendsPeople{ publicintage=20; publicvoidwork(){ System.out.println(this.name+"正在教书"+"今年"+super.age); } } publicclassTest { publicstaticvoidmain(String[] args) { Teacherteacher=newTeacher(); teacher.name="zhangsan"; teacher.work(); } }
因为父类和子类都有age属性此时如果想访问父类的属性就可以通过super关键字 ,super+.可以访问父类的属性和方法。(下文会为大家介绍super的第三种使用方法)。
子类的构造方法
此时如果父类有带参数的构造方法那么系统将会报错。
当父类中加入了含有两个参数构造方法之后代码就会报错。
接下来就为大家介绍一下super关键字的第三种使用方法:
super();
它可以调用父类的构造方法。
注:它必须放在构造方法的第一行。
因为实例化对象时有两个比较重要的步骤:
- 为对象分配内存;
- 调用合适的构造方法。
上面的代码在我们自己写了有参构造方法之后报错的原因就是子类的构造方法没有调用父类的构造方法。(当我们自己写了构造方法后系统就不会再为我们提供构造方法了,而子类对象构造时,需要先调用父类构造方法,然后再执行子类的构造方法。当我们没有写构造方法时系统会默认调用如下的构造方法:
我们只写了有参构造,所以系统就会因为找不到无参构造而报错,此时就需要我们自己进行调用:
子类对象中成员是有两部分组成的,基类继承下来的以及子类新增加的部分 。父子父子肯定是先有父再有子,所以在构造子类对象时候 ,先要调用基类的构造方法,将从基类继承下来的成员构造完整,然后再调用子类自己的构造方法,将子类自己新增加的成员初始化完整。
注意:
- 若父类显式定义无参或者默认的构造方法,在子类构造方法第一行默认有隐含的super()调用,即调用基类构造方法;
- 如果父类构造方法是带有参数的,此时需要用户为子类显式定义构造方法,并在子类构造方法中选择合适的父类构造方法调用,否则编译失败;
- 在子类构造方法中,super(...)调用父类构造时,必须是子类构造函数中第一条语句;
- super(...)只能在子类构造方法中出现一次,并且不能和this同时出现。
final
在JAVA中只支持以下几种继承方式:
- 单继承;
- 多层继承;
- 不同类继承同一个类。
一般我们不希望出现超过三层的继承关系继承层次如果继承层次太多太复杂, 就需要考虑对代码进行重构了。
那我们应该如何阻止别人不让他们进行继承呢?
JAVA中提供了final关键可以用来修饰变量、成员方法以及类。
- 修饰变量或字段,表示常量(即不能修改)
- 修饰类:表示此类不能被继承
- 修饰方法:表示该方法不能被重写
我们只要在不想被继承的类的前面用final修饰就可以了:
继承层次上代码的初始化顺序:
- 父类静态
- 子类静态
- 父类实例
- 父类构造
- 子类实例
- 子类构造