文章目录
前言
在前篇文章中我们讲解了Java三大特性之一的封装性,本篇文章中我们将继续对面向对象编程的学习,了解并学习剩下的两大特性之一的继承性。
一、继承
**继承(inherit)**也是面向对象三大特性之一。
生活中的继承:某某继承了父亲百亿资产;某某继承了家族产业等等。
简单来看,继承就是获得了原本不属于自己的东西。在Java中亦是如此。
举个栗子:
某公司正在开发一款高校教学、教务管理系统。其中涉及到教师(Teacher)、辅导员(Assistant)、学生(Student)的信息管理。抛开教师、辅导员、学生的增删改查,如何用Java类表示教师、辅导员、学生?
通过分析:我们需要3个类Teacher、Assistant、Student。
Teacher类有姓名、性别、年龄、职称,能授课、能管理学生。
Assistant类有姓名、性别、年龄,能管理学生。
Student类有姓名、性别、年龄,能学习。
通过观察,你会发现这3个类有部分特征和行为是相同的。都有姓名、性别、年龄属性,都有对应的setter、getter方法。如果要完整的表示这3个类,会更多的属性,更多的setter、getter方法。
如果一个项目比较大,有几百个类,要写多少代码?能不能优化?
保留这个疑问我们继续往下看。
二、使用继承
Java提供了继承(inherit)功能。继承可以解决我们上述的问题。
父类:在继承关系里,被继承的类叫做父类,也叫基类或超类。
子类:在继承关系里,继承别的类的类叫子类,也叫派生类。
而父类和子类是相对而言的。
1.继承的语法
public class 类名 extends 父类名{
//属性
//方法
}
通过继承,我们可以将上述案例中相同的特征和行为进行抽取,放到一个单独的类中,把这个类作为父类,再让现有的类继承于这个单独的类,这样就可以继承父类中的特征和行为。
上代码:
公共父类Person
public class Person {
//属性
private String name; //姓名
private String sex; //性别
private int age; //年龄
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
Teacher类
public class Teacher extends Person{
//属性
private String title; //职称
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
//构造方法
public Teacher() {
}
public Teacher(String name, String sex, int age, String title) {
this.setName(name);
this.setSex(sex);
this.setAge(age);
this.title = title;
}
//方法
public void teach() {
System.out.println(this.getName() + "老师正在认真的讲课");
}
public void manageStudents() {
System.out.println("管理学生");
}
public void showInfo() {
System.out.println(this.getName() + "," + this.getSex() + "," + this.getAge() + "," + title);
}
}
Assistant类:
public class Assistant extends Person{
//属性
//构造方法
public Assistant() {
}
public Assistant(String name, String sex, int age) {
this.setName(name);
this.setSex(sex);
this.setAge(age);
}
//方法
public void manageStudents() {
System.out.println("管理学生");
}
public void showInfo() {
System.out.println(this.getName() + "," + this.getSex() + "," + this.getAge());
}
}
Student类
public class Student extends Person{
//属性
//构造方法
public Student() {
}
public Student(String name, String sex, int age) {
this.setName(name);
this.setSex(sex);
this.setAge(age);
}
//方法
public void study() {
System.out.println("Good good study, day day up");
}
public void showInfo() {
System.out.println(this.getName() + "," + this.getSex() + "," + this.getAge());
}
}
测试代码自编即可。
通过继承,我们发现代码明显少了很多,而且能实现继承前全部的功能。但美中不足的地方是,构造方法赋值比较麻烦,那能不能优化呢?
2.父类、子类构造方法
子类可以从父类中继承属性和方法,无法继承构造方法,但可以调用父类中的构造方法为我们继承过来的属性赋值。
比如在父类Person中添加构造方法如下:
//父类构造方法
public Person() {
}
public Person(String name, String sex, int age) {
this.name = name;
this.sex = sex;
this.age = age;
}
在不同的子类中就可以按照如下的格式修改构造方法:
Teacher类:
//构造方法
public Teacher() {
}
public Teacher(String name, String sex, int age, String title) {
super(name, sex, age);
this.title = title;
}
Assistant类
public Assistant() {
}
public Assistant(String name, String sex, int age) {
super(name, sex, age);
}
显然,修改之后,代码仍然能正确的运行。我们可以发现在子类构造方法中有一个新的关键字——super;
3.super关键字
super是一个特殊的关键字,这个关键字用于访问父类中的方法和属性(super相当于父类类名)。
注:private修饰的属性和方法无法直接访问(super.属性名或者super.方法名())。因为private修饰的属性和方法只能在本类中访问。
虽然不能访问,但肯定是继承了。
如果理解了可以直接跳过下面内容,没有的话我们一起写一个样例练习来加深了解。
定义2个类,Son类和Father类,Son继承于Father。
Father类代码:
public class Father {
//属性
int a;
private int b;
//getter、setter
public int getB() {
return b;
}
public void setB(int b) {
this.b = b;
}
//构造方法
public Father() {
}
public Father(int a, int b) {
this.a = a;
this.b = b;
}
//打印对象信息
public void showInfo() {
System.out.println("a = " + a + ",b = " + b);
}
//私有方法--私有方法只能在类内部供类中别的方法调用,例如在publicMethod方法中访问
private void privateMethod() {
System.out.println("这是私有方法");
}
//公开方法,在这个方法中调用私有方法。
public void publicMethod() {
privateMethod();//等价于this.privateMethod();
}
}
Son类代码:
public class Son extends Father {
//属性
private int a;
private int c;
//setter、getter
public int getA() {
return a;
}
public void setA(int a) {
this.a = a;
}
public int getC() {
return c;
}
public void setC(int c) {
this.c = c;
}
//构造方法
public Son() {
super();
}
public Son(int a, int b) {
super(a, b);
}
public Son(int a, int b, int c) {
super(a, b);
this.c = c;
}
//打印自身信息
public void showInfo() {
System.out.println("a = " + a + ",b = " + this.getB() + ",c = " + c);
}
//测试super调用父类方法
public void testSuperMethod() {
super.showInfo();
}
//测试super调用父类属性
public void testSuperProperty() {
System.out.println(a);
System.out.println(this.a);
System.out.println(super.a);
//私有属性不能直接访问,因为private修饰的属性,只能在本类中直接访问。我们可以通过方法间接访问。
//System.out.println(super.b);//报错
System.out.println(super.getB());
}
//测试调用父类中的 私有方法
public void testSuperPrivateMthod() {
//私有方法不能直接访问,因为private修饰的方法,只能在本类中直接方法。我们可以通过方法间接访问。
//super.privateMethod();//报错
super.publicMethod();
}
}
测试类代码:
public static void main(String[] args) {
Son son = new Son(10,20,30);
son.showInfo();//如果子类和父类有同名方法,调用的是自身的方法,不是父类继承过来的方法。
son.testSuperMethod();//测试super调用父类方法
son.testSuperPrivateMthod();//测试super调用父类私有方法
son.testSuperProperty();//测试super调用父类属性
son.publicMethod();//调用继承过来的方法
son.setB(100);//调用继承过来的setter方法
son.showInfo();
}
总结一下:
- 通过super访问父类构造方法。语法:super(参数列表); 主要作用:给继承过来的属性赋初始值。通过super调用父类构造方法时,代码必须写在第一行。
- 通过super访问父类中的其他方法(非私有)。语法:super.方法名(参数列表);
- 通过super访问父类中的属性(非私有)。语法:super.属性名;
4. this关键字
前面的文章,我们讲了this关键是一个特殊的对象,代指当前对象,即随调用方法,this就是谁。this可以用于区分实例变量和局部变量。
this除了用于区分实例变量和局部变量之外,还有以下2个功能:
- this调用本类的其他构造方法。语法:this(参数列表); 通过this调用本类构造方法时,代码必须写在第一行。
- this调用本类中其他的方法。(this可省略)
5. this和super的区别
比较事项 | this | super |
---|---|---|
访问实例变量 | this.实例变量 访问本类中的实例变量 | super.实例变量 访问父类中的实例变量(非私有) |
访问实例方法 | this.实例方法(…) 访问本类中的实例方法 | super.实例方法(…) 访问父类中的实例方法(非私有) |
访问构造方法 | this(…) 调用本类中的构造方法,写在构造方法的第一行 | super(…) 调用父类中的构造方法,写在构造方法的第一行 |
是否是对象 | 是对象,代表当前对象 | 不是对象,只是关键字 |
继承中构造方法的访问特点:如果子类构造方法中没有明确写出调用哪个父类构造方法,会默认调用父类的无参构造方法,即super(); -----即使你没有写super(),系统也会默认在第一行代码中调用super()。如果父类不提供无参构造方法,提供有参数的构造方法,子类会报错。
继承中实例方法的访问特点:1.首先在子类中查找要调用的方法,如果有直接调用;2.如果没有,在父类中查找要调用的方法,如果有就执行;3.如果没有,继续向上找父类,如果更上一级父类中有要调用的方法,就执行,如果没有继续向上找,直到根类Object,如果Object中也没有,就报错。
6. 方法重写
方法重写:在继承关系里,如果子类中的某个方法和父类中的方法相同(方法名和参数也要相同),称为子类重写了父类的方法。子类重写父类方法往往是因为父类的方法满足不了子类的需求,所以子类才需要自己实现这个方法。
方法重写、方法重载的区别:
方法重载:在同一个类中,如果多个方法具有相同的方法名,但是参数个数或者参数类型不同(或都不同),这称为方法的重载。
方法重写:在继承关系里,父类的实现满足不了子类的需求,子类可以重新实现父类中定义的方法,这是方法重写。
如果子类重写了父类的方法,我们习惯上在重写的方法上面添加@Override注解。—注解是我们后面要学习的内容。@Override注解的作用是检测方法是否和父类中的方法相同。
总结
1. Java中继承的特点
- 一个类只能有一个父类。----不允许多继承
- 子类会继承父类全部的属性和方法(私有的也能继承,只是不能直接访问),无法继承构造方法。
- 如果要访问父类中的属性、方法、构造方法使用super关键字。
- 子类可以重写父类的方法。调用时默认调用子类的方法。
- 可以多层继承。A extends B,B extends C,这样A将拥有B和C中的内容
- 所有类的根类是Object。如果一个类没有继承任何类,默认继承Object类。
- 通常在子类的构造方法中调用父类的构造方法。
- 根类:没有父类的类。已经是最顶层的类了。
2. 继承的好处和弊端
继承的好处:
- 提高了代码的复用性(多个类相同的内容可以放到同一个类中)。
- 提高了代码的维护性(如果方法的代码需要修改,修改一处即可)。
继承弊端:
- 继承让类与类之间产生了关联,类的耦合性增强了,当父类发生变化时,子类也不得不跟着变化,削弱了子类的独立性。
什么时候使用继承?
- 继承体现的关系是: is a (是一个)
- 如果两个类A和B,如果他们在逻辑上满足A是B的一种,或者B是A的一种,就说明他们是继承关系,这个时候可以使用继承来体现,否则就是滥用继承。
- 例如:苹果和水果,苹果属于水果,可以让苹果类继承于水果类。再例如:苹果手机和手机,苹果手机是手机的一种,可以让苹果手机类继承于手机类。再例如:猫和狗,不能让猫继承于狗,也不能让狗继承于猫,而是定义一个动物类,他们都继承于动物。
本篇文章我们讲解了三大特性之一的继承性,继承的存在极大的方便了多个类时的代码书写,并且帮助我们理清类于类之间的关系(可以参考UML类图)。下一篇文章我们将学习最后一大特性——多态性,感谢大家的观看。