学习要点
- 继承的优点和实现
- 子类重写父类方法
- 继承下构造方法的执行过程
- 抽象类和抽象方法的使用
- final修饰属性、方法和类
继承的优点和实现
宠物管理系统优化设计
宠物管理系统中的类有什么问题?
使用继承优化后的类图:
子类和父类是is-a的关系。即子类是父类的一个特例,一个具体实现。例如苹果是水果,小学生是学生,大学生是学生……
继承的实现
第一步:编写父类
class Pet { //公共的属性和方法 }
第二步:编写子类
class Dog extends Pet { //注意:只能继承一个父类 //子类特有的属性和方法 }
子类访问父类成员
访问父类的构造方法
super(); super(name); // super调用构造方法时,只能是第一句
访问父类的成员变量
super.name;
访问父类的方法
super.print();
父类不能被继承的资源
- private成员
- 子类与父类不在同包,使用默认访问权限的成员
- 构造方法
访问控制符总结
|
private |
(默认)friendly |
protected |
public |
同一个类中 |
√ |
√ |
√ |
√ |
同一个包中 |
|
√ |
√ |
√ |
子类中 |
|
|
√ |
√ |
全局范围内 |
|
|
|
√ |
多重继承关系的初始化顺序
何时使用继承
- 继承和真实世界的描述一致:符合is-a的关系
- 继承实现代码的重用:将子类共有的属性和行为放到父类中
上机练习
练习1:阅读代码,说出控制台输出结果
class Car { private int site = 4; //座位数 Car(){ System.out.println ("载客量是"+site+"人); } public void setSite(int site){ this.site = site; } void print(){ System.out.print("载客量是"+site+"人"); } } class Bus extends Car { Bus(int site){ setSite(site); } } public static void main(String[] args) { Bus bus = new Bus(20); bus.print(); }
练习2:使用继承完成宠物管理系统的优化
子类重写父类方法
父类的方法的局限性
在宠物管理系统中,父类的print()方法如下:
问题:他能输出子类特有的属性信息吗?如何修改?
方法重写的相关注意事项
构造方法因为无法被继承,所以不能重写。
方法重写的规则:
- 发生在父类和子类之间
- 方法名相同
- 参数列表相同
- 返回值类型相同或者是其子类;
- 访问权限不能严于父类
方法重写和重载的区别
位置 |
方法名 |
参数表 |
返回值 |
访问修饰符 |
|
方法重写 |
子类 |
相同 |
相同 |
相同或是其子类 |
不能比父类更严格 |
方法重载 |
同类 |
相同 |
不相同 |
无关 |
无关 |
上机练习
优化电子宠物系统,重写父类方法,输出子类特有属性。
提示:尝试使用super调用父类已存在方法重写或者完全重写。
抽象类和抽象方法的使用
抽象类
问题:在宠物管理系统中,去实例化一个宠物有意义吗?
Pet pet = new Pet ("小土狗",20,40); pet.print();
pet是没有实际意义。所以使用abstract关键字去限制Pet类的实例化。
public abstract class Pet { //abstract修饰的类称为抽象类 }
抽象方法
在宠物管理系统中,父类Pet类中的print()方法是否存在问题?
public abstract class Pet { public void print() { //… } }
实际上每个子类的print()方法实现都不同。如何解决这个问题?父类不要实现这个方法,让子类去实现。
abstract也可用于方法——抽象方法
- 抽象方法没有方法体
- 抽象方法必须在抽象类里
- 抽象方法必须在子类中被实现,除非子类是抽象类
public abstract void print(); //没有方法体的抽象方法
上机练习
修改Pet类为抽象类
修改Pet类的print()方法为抽象方法
输出Dog和Penguin信息。
final修饰属性、方法和类
final类
Dog类不希望再被其他类继承?
public final class Dog extends Pet { //定义了一个终结版的类 //… }
final方法
方法不希望被重写?
public final void print () { //定义了一个终结版的方法 //… }
final成员变量
属性值不希望被修改?使用常量。
public class Penguin { final String home ="南极";// 居住地 public void setHome(String name){ this.home=home; //错误,不可再赋值 } }
使用final修饰引用型变量,变量的值是固定不变的,而变量所指向的对象的属性值是可变的。
class Dog { String name; public Dog(String name) { this.name = name; } } class Test { public static void main(String[] args) { final Dog dog = new Dog("旺旺"); dog.name = "小宝"; dog = new Dog("小土狗"); //错误:使用final修饰引用型变量,变量不可以再指向另外的对象。 } }
综合案例
需求描述
某汽车租赁公司出租多种车辆,车型及租金情况如下
轿车 |
客车(金杯、金龙) |
||||
车型 |
别克商务舱GL8 |
宝马550i |
别克林荫大道 |
<=16座 |
>16座 |
日租费 元/天 |
600 |
500 |
300 |
800 |
1500 |
编写程序实现计算租赁价。
分析
发现类
发现类的属性
发现类的方法
优化设计
编写程序入口
程序界面
根据分析,完成系统设计和编码实现
总结
继承
- 符合is-a关系
- 使用extends关键字
- 代码复用
方法重写的规则
- 方法名相同
- 参数列表相同
- 返回值类型相同或者是其子类
- 访问权限不能严于父类
super关键字来访问父类的成员
- super只能出现在子类的方法和构造方法中
- super调用构造方法时,只能是第一句
- super不能访问父类的private成员
抽象类和抽象方法
- 抽象类不能被实例化
- 可以有0~多个抽象方法
- 非抽象类必须重写父类的所有抽象方法
final修饰符
- 修饰的类,不能再被继承
- 修饰的方法,不能被子类重写
- 修饰的变量将变成常量,只能在初始化时进行赋值