文章目录
学习目标
- 理解继承的作用
- 必须掌握继承的语法
- 了解子类可以继承父类那些成员
- 掌握方法覆盖的判断规则和如何覆盖方法
- 掌握super关键字的含义
- 掌握抽象方法的定义和特点
- 掌握抽象类的定义和特点
继承思想
1、继承语法
在java程序中,如果一个类需要继承另一个类,此时使用extends关键字。
public class 父类名{
// 存放多个子类共同的字段和方法
}
public class 子类名 extends 父类名{
// 存放自己特有的(独有的)字段和方法
}
注意:Java中类只支持单继承,但是支持多重继承。也就是说一个子类只能有一个直接的父类,父类也可以再有父类。
- 下面是错误的写法! Java中的类只支持单继承。
class SuperClass1 { }
class SuperClass2 { }
// 错误用法
class SubClass extends SuperClass1,SuperClass2{
}
- 下面代码是正确的。一个父类可以有多个子类。
class SuperClass{}
class SubClass1 extends SuperClass{ }
class SubClass2 extends SuperClass{ }
- 下面代码是正确的,支持多重继承。
class SuperSuperClass{}
class SuperClass extends SuperSuperClass{}
class SubClass extends SuperClass
例如:大学生 extends 学生,学生 extends 人类,我们就说,大学生具有人类的特征和行为。
(Object类是Java语言的根类,任何类都是Object的子类,要么是直接子类,要么是间接子类)
public class Person {
}
等价于
public class Person extends Object{
}
1.1、继承操作
父类代码
public class Person {
private String name;
private int age;
public void rest() {
System.out.println("休息");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
子类代码:
public class Student extends Person{
private String sn; // 学号
public void study() {
System.out.println("学习");
}
public String getSn() {
return sn;
}
public void setSn(String sn) {
this.sn = sn;
}
}
测试代码:
public class ExtendsDemo {
public static void main(String[] args) {
// 创建学生对象
Student stu = new Student();
// 设置字段信息
stu.setName("will"); //继承了父类
stu.setAge(17); //继承了父类
stu.setSn("s_123");
// 调用方法
stu.study();
stu.rest(); //继承了父类
}
}
1.2、子类可以继承到父类的哪些成员
子类继承父类之后,可以拥有到父类的某一些成员(字段和方法),根据访问修饰符来判断:
- 如果父类中的成员使用public和protected修饰,子类都能继承.
- 如果父类和子类在同一个包中,使用缺省访问修饰的成员,此时子类可以继承到
- 如果父类中的成员使用private修饰,子类继承不到。private只能在本类中访问
- 父类的构造器,子类也不能继承,因为构造器必须和当前的类名相同
private: 私有的,本类可见。
默认: 同包可见,也经常把它成为包访问权限。
protected: 受保护的。1 同包可见 2 子类可见
public 公共的。任何地方可见
一句话:子类继承父类的非私有成员(字段和方法),但构造器除外。
2、方法覆盖
子类继承了父类,可以拥有父类的部分方法和成员变量。**可是当父类的某个方法不适合子类本身的特征时,此时怎么办?**比如鸵鸟(Ostrich)是鸟类(Bird)中的一个特殊品种,所以鸵鸟类是鸟类的一个子类,但是鸟类有飞翔的功能,但是对应鸵鸟,飞翔的行为显然不适合于它。
父类:
public class Bird {
public void fly() {
System.out.println("飞呀飞...");
}
}
子类:
public class Ostrich extends Bird{
}
测试类:
public class OverrideDemo {
public static void main(String[] args) {
//创建鸵鸟对象
Ostrich os = new Ostrich();
//调用飞翔功能
os.fly();
}
}
运行结果:
飞呀飞...
上述代码从语法是正确的,但从逻辑上是不合理的,因为鸵鸟不能飞翔,此时怎么办?——方法覆盖操作。
2.1、方法覆盖的操作
当子类存在一个和父类一模一样的方法时,我们就称之为子类覆盖了父类的方法,也称之为重写(override)。
提示:
当子类继承父类的方法时不能满足自身需要时,子类就可以根据自身需要对父类的同名方法进行重写。
那么我们就可以在子类方法体中,重写编写逻辑代码。
public class Ostrich extends Bird{
public void fly() {
System.out.println("扑扑翅膀,快速奔跑...");
}
}
运行测试代码:
扑扑翅膀,快速奔跑...
重写方法的调用顺序:
通过对象调用方法时,先在子类中查找有没有对应的方法,若存在就执行子类的,若子类不存在就执行父类的,如果父类也没有,报错。
方法覆盖的细节:(理解掌握)
- 实例方法签名必须相同 (方法签名= 方法名 + 方法的参数列表)
- 子类方法的返回值类型是和父类方法的返回类型相同或者是其子类
- 子类方法的访问权限 >= 父类方法访问权限
如果父类方法是private,子类方法不能重写。==> 重写建立在继承的基础上,没有继承,就不能重写。
- 子类方法中声明抛出的异常小于或等于父类方法声明抛出异常类型 (后续讲解)
上述的方法覆盖细节真多,记不住,那么记住下面这句话就万事OK了。
精华:直接拷贝父类中方法的定义粘贴到子类中,再重新编写子类方法体,打完收工!
2.2、覆盖中super关键字
问题,在子类中的某一个方法中需要去调用父类中被覆盖的方法,此时得使用super关键字。
public class Ostrich extends Bird{
public void fly() {
System.out.println("扑扑翅膀,快速奔跑...");
}
public void say() {
super.fly();//调用父类被覆盖的方法
fly();//调用本类中的方法
}
}
如果调用被覆盖的方法不使用super关键字,此时调用的是本类中的方法。
-
super关键字表示父类对象的意思
-
super.fly() 可以翻译成调用父类对象的fly方法。
3、抽象方法和抽象类
3.1、引出抽象方法
需求:求圆(Circle)和矩形(Rectangle)两种图形的面积。
分析:无论是圆形还是矩形,还是其他形状的图形,只要是图形,都有面积,也就说图形都有求面积的功能,那么我们就可以把定义一个图形(Graph)的父类,该类拥有求面积的方法,但是作为图形的概念,而并不是某种具体的图形,那么怎么求面积是不清楚的。
所以由上述需求的问题引出:
- 既然不同的图形求面积的算法是不同的,所以必须要求每一个图形子类去覆盖getArea方法,如果没有覆盖,应该以语法报错的形式做提示。
- 在Graph类中的getArea方法的方法体没有任何存在意义,因为不同图形求面积算法不一样,子类必须要覆盖getArea方法。
要满足上述对方法的要求,就得使用abstract来修饰方法,被abstract修饰的方法具备两个特征:
-
该方法没有方法体
-
要求子类必须覆盖该方法
这种方法,我们就称之为抽象方法。
3.2、抽象方法和抽象类
使用abstract修饰的方法,称为抽象方法。
public abstract 返回类型 方法名(参数);
抽象方法的特点:
-
使用abstract修饰,没有方法体,留给子类去覆盖
-
抽象方法必须定义在抽象类或接口中
注意:
- 如果要定义抽象方法,那么这个类就必须是抽象类
- 抽象类中可以同时存在抽象方法和普通方法
- 集成这个抽象类的子类必须实现抽象类中的抽象方法,或者这个子类也是一个抽象类
- 抽象方法没有方法体,方法体需要子类去写具体的实现
- 抽象类不能创建对象,只能继承他的子类创建对象,同时这个子类也不能是抽象类