Java OOP 继承
一、学习目标
- 掌握继承的优点和实现方法
- 掌握子类重写父类的方法
- 掌握继承条件下构造方法的执行过程
二、继承
2.0继承概念(Inheritance):面向对象的三大特性之一
- 一个类获取现有类的所有属性和行为的机制
- 创建基于现有类的新类,可以重用现有类的属性和方法
- 可以在新创建的子类中添加新属性和方法
作用:
- 有效的解决了代码的重用问题,使代码拓展更加灵活
- 从始至终完整的体现了一个应用系统,逻辑更加清晰
- 增加软件的可扩展性,以适应不同的业务需求
2.1.为什么使用继承
思考: 这两个类图有什么问题?
观察类图得出结论:
- 设计是存在缺陷
- 代码重复。如果需要修改,两个类都要修改
- 如果扩展新的类,重复的代码就会更多,修改量也会更大
如何解决这个问题?
- 使用继承优化设计
- 将重复代码抽取到父类中
2.2使用继承优化
2.3什么时候该使用继承?
- 继承与真实世界类似
- 只要说“苹果树是作物”,苹果树的很多属性、行为 就不言自明了
- 富士是一种苹果树
- 符合is-a关系的设计使用继承
- 继承是代码重用的一种方式
- 将子类共有的属性和行为放到父类中
2.4如何使用继承
语法:
2.4.1 编写父类
[ 访问修饰符 ] class <SuperClass> { //属性 //方法 }
2.4.2 编写子类,继承父类
[ 访问修饰符 ] class <SubClass> extends <SuperClass> { //子类特有的属性和方法 }
注意事项:只能继承一个父类
三、类的继承关系示例
示例要求:
- 使用继承重构星沐生态农场游戏 AppleTree类和 Corn类
示例分析:
- 提取公共属性和方法,编写Crop类
- 定义AppleTree类,继承自Crop类,并编写它的专有属性
- 定义Corn类,继承自Crop类,并编写它的专有属性
- 编写测试类Test,分别创建
//1.提取公共属性和方法,编写Crop类 public class Crop { private String name;//名称 private int growTime;//成长期 private int harvestTime;//采摘期 private int numsOfFruits;//果实数量 private String status;//生长状态 private boolean isHarvested;//果实是否已收割 public void print(){……} public printGrowReport(int day){……} public harvest(){……} //省略getter、get()方法等其他代码…… }
//2.定义AppleTree类,继承自Crop类,并编写它的专有属性 public class AppleTree extends Crop { private String brand; //专有属性:品种 public void setBrand(String brand) { this.brand = brand; } public AppleTree(String brand){ super("苹果树",10,2,100); //访问父类的构造方法 super.setStatus(Constants.GROW); //访问父类属性 this.brand = brand; } }
//3.定义Corn类,继承自Crop类,并编写它的专有属性 public class Corn extends Crop{ private double harvestCost;//专有属性:收割费用 //... …省略public方法 public Corn(double harvestCost) { super("玉米",5,2,120); //访问父类的构造方法 super.setStatus(Constants.GROW); //访问父类属性 this.harvestCost = harvestCost; } }
//4.编写测试类Test,分别创建 public class Test { public static void main(String[] args) { //1. 创建Crop对象并输出信息 Crop crop = new Crop(); crop.print(); //2. 创建AppleTree对象并输出信息 AppleTree appleTree = new AppleTree("富士"); appleTree.print(); } }
四、深入理解继承
问题:子类可以继承父类的所有些资源吗?
论证:…
super关键字
- 子类访问父类成员,使用super关键字,super代表父类对象
1.访问父类构造方法
//在子类构造方法中调用且必须是第一句 super(); super(参数);
public class AppleTree extends Crop { //… … 省略private属性定义 public AppleTree(String brand) { super("苹果树",10, 2,100); //访问直接父类的构造方法 this.brand = brand; } }
2.访问父类属性
super.<父类属性>; super.name;
3.访问父类方法
super.<父类方法>; super.print();
4.注意事项
super关键字必须出现在子类(子类的方法和构造方法)中,而不允许在其他位置 使用super关键字可以访问父类的成员(属性、方法、构造方法) 注意访问权限的限制,如无法通过super访问private成员
5.使用this/super关键字的注意事项
- 在构造方法中,如果出现this或super关键字,则只能是该方法的第一条语句
- 在一个构造方法中,不允许同时使用this和super关键字调用构造方法
- 在类方法(静态方法)中,不允许出现this或super关键字
- 在实例方法中,this和super语句不要求是第一条语句,可以共存。
6.访问修饰符protected
可以修饰属性和方法
本类、同包、子类可以访问7.访问修饰符总结
访问修饰符 本类 同包 子类 其他 private √ 默认(friendly) √ √ protected √ √ √ public √ √ √ √ 8.多级继承
一个类可以继承自某一个类,成为这个类的子类,同时,也可以在自身的基础上创建新的类,即成为它子类的父类。
如:新能源轿车类是轿车类的子类,同时,是无人驾驶轿车类的父类,如图所示:注意: 无人驾驶轿车类的直接父类是新能源轿车类,间接父类是轿车类、Object类
Java 中只支持单继承,即每个类只能有一个直接父类
9.多重继承关系的初始化顺序
1、初始化顺序 2、super()的用法 3、继承条件下构造方法的调用规则如下: 如果子类的构造方法中没有通过super显式调用父类的有参构造方法,也没有通过this显式调用自身的其他构造方法,则系统会默认先调用父类的无参构造方法。在这种情况下,写不写“super();”语句,效果是一样的。 如果子类的构造方法中通过super显式调用父类的有参构造方法,那将执行父类相应构造方法,而不执行父类无参构造方法。 如果子类的构造方法中通过this显式调用自身的其他构造方法,在相应构造方法中应用以上两条规则。 特别注意的是,如果存在多级继承关系,在创建一个子类对象时,以上规则会多次向更高一级父类应用,一直到执行顶级父类Object类的无参构造方法为止。
继承关系中的构造方法
Java 虚拟机按照先父类后子类的顺序执行一系列的构造方法
五、Object类
是所有 Java 类的祖先
- 所有的 Java 类都直接或间接地继承了Object类
位于java.lang包中
在定义一个类时,如果没有使用extends关键字,即没有显式地继承某个类,那么这个类直接继承Object类
常用方法:
方法 说明 toString() 返回当前对象本身的有关信息,返回字符串对象 equals() 比较两个对象是否是同一个对象。若是,返回true clone() 生成当前对象的一个副本,并返回 hashCode() 返回该对象的哈希代码值 getClass() 获取当前对象所属的类信息,返回Class对象
- 注意: java.lang.String类重写了Object类中的equals()方法,用于比较两个字符串的值是否相等
六、方法重写
问题:
分析: 使用方法重写实现
- 方法的重写或方法的覆盖(Overriding)
- 在子类中,根据需求对从父类继承的方法体进行重新编写,以实现子类需求
- 必须遵守的规则
- 重写方法和被重写方法必须具有相同的方法名
- 重写方法和被重写方法必须具有相同的参数列表
- 重写方法返回值类型必须和被重写方法的返回值类型相同或为其子类
- 重写方法不能缩小被重写方法的访问权限
方法重写示例:
- 在AppleTree类中重写继承自父类Crop的print()方法
/** * 重写父类的print()方法 */ public void print() { super.print(); //调用父类的方法 System.out.println("苹果树品种:"+this.brand); }
- 在Corn类中重写继承自父类Crop的print()方法
/** * 重写父类的print()方法 */ public void print() { super.print(); //调用父类的方法 System.out.println(super.getName()+"的收割费用:"+this.harvestCost+"元。"); }
IntelliJ IDEA对父类方法的重写操作
操作步骤 1.在AppleTree类代码中右键单击,在打开的列表中选择Generate选项 2.选择Override Methods选项,打开面板 选择重写Crop类的print()方法,默认勾选Insert @Override,点击OK按钮 AppleTree类中自动生成print()方法框架 3.在自动生成的方法框架中编写具体的方法体
方法重载与方法重写
位置 方法名 参数表 返回值 访问修饰符 方法重写 子类 相同 相同 相同或是其子类 不能比父类更严格 方法重载 同类 相同 不相同 无关 无关 方法重写示例
- 查看苹果树和玉米生长状态,并输出以及收获果实方法
- 不同作物的生长特性不同,各具有不同的生长过程
- 根据具体生长特性,子类中重写Crop类的方法
@Override public void printGrowReport(int day) { System.out.println("您种植了"+super.getName()+",品种:“ +this.brand); super.printGrowReport(day); //调用父类printGrowReport()方法 } @Override public void harvest() { super.harvest(); }
@Override public void printGrowReport(int day) { super.printGrowReport(day); } @Override public void harvest() { super.harvest(); System.out.println("使用收割机收割"+super.getName() + ",费用:“ +this.harvestCost+"元。"); }