16. 面向对象三大特性之继承性
面向对象的三大特征:封装、继承、多态
继承是多态的前提,如果没有继承,就没有多态。
在继承的关系中,”子类就是一个父类“。也就是说,子类可以被当作父类看待。
例如父类是员工,子类是讲师,那么”讲师就是一个员工“。
Java语言是单继承的。一个类的直接父类只能由唯一一个。
但是,Java语言可以多级继承。
一个子类的直接父类是唯一的,但是一个父类可以由多个子类。
16.1 继承的基本格式
定义父类格式:(一个普通的类定义)
public class 父类名称{
// ...
}
定义子类的格式:
public class 子类名称 extends 父类名称{
// ...
}
Employee类
package com.tipdm.demo01;
// 定义一个父类
public class Employee {
public void method(){
System.out.println("方法执行!");
}
}
Teacher类
package com.tipdm.demo01;
// 定义了一个员工的子类:讲师
public class Teacher extends Employee{
}
Assistant类
package com.tipdm.demo01;
// 定义一个子类:助教
public class Assistant extends Employee{
}
主类:
package com.tipdm.demo01;
/**
* 面向对象的三大特征:封装、继承、多态
* 继承是多态的前提,如果没有继承,就没有多态。
*
* 在继承的关系中,”子类就是一个父类“。也就是说,子类可以被当作父类看待。
* 例如父类是员工,子类是讲师,那么”讲师就是一个员工“。关系:is-a。
*
* 定义父类格式:(一个普通的类定义)
* public class 父类名称{
* // ...
* }
*
* 定义子类的格式:
* public class 子类名称 extends 父类名称{
* // ...
* }
*/
public class demo1 {
public static void main(String[] args) {
// 创建一个子类对象:讲师
Teacher teacher1 = new Teacher();
// Teacher类当中虽然什么都没写,但是会继承来自Employee的方法。
teacher1.method();
// 创建一个子类对象:助教
Assistant assistant1 = new Assistant();
// Assistant类当中虽然什么都没写,但是会继承来自Employee的方法。
assistant1.method();
}
}
16.2 继承后的特点
父类
package com.tipdm.demo02;
public class Fu {
int numFu = 10;
int num = 100;
public void methodFu(){
// 使用的是本类当中的,不会向下找子类的。
System.out.println(num);
}
}
子类
package com.tipdm.demo02;
public class Zi extends Fu{
int numZi = 20;
int num = 200;
public void methodZi(){
// 因为本类当中有num,所以这里用的是本类的num
System.out.println(num);
}
}
主类:
package com.tipdm.demo02;
/**
* 在父子类的继承关系当中,如果成员变量重名,则创建子类对象时,访问有两种方式:
* 直接通过子类对象访问成员变量:
* 等号左边是谁,就有限用谁,没有则向上找。
* 间接通过成员方法访问成员变量:
* 该方法属于谁,就优先用谁,没有则向上找。
*/
public class demo1 {
public static void main(String[] args) {
Fu fu = new Fu(); // 创建父类对象
System.out.println(fu.numFu); // 10 只能使用父类的东西,没有任何子类的内容
System.out.println("================");
Zi zi = new Zi();
System.out.println(zi.numFu); // 10
System.out.println(zi.numZi); // 20
System.out.println("================");
System.out.println(zi.num); // 如果父类中出现同名变量,以子类为主
// System.out.println(zi.abc); // 不存在则编译报错
System.out.println("================");
// 这个方法是子类的,优先用子类的,没有再向上找
zi.methodZi(); // 200
// 这个方法是在父类中定义的,优先使用父类中的num
zi.methodFu();
}
}
16.3 继承后变量的访问规则
父类
package com.tipdm.demo03;
public class Fu {
int num = 10;
}
子类
package com.tipdm.demo03;
public class Zi extends Fu{
int num = 20;
public void method(){
int num = 30;
System.out.println(num); // 局部变量
System.out.println(this.num); // 本类的成员变量
System.out.println(super.num); // 父类的成员变量
}
}
主类
package com.tipdm.demo03;
/**
* 局部变量:直接写
* 本类的成员变量:this.成员变量名
* 父类的成员变量:super.成员变量名
*/
public class demo1 {
public static void main(String[] args) {
Zi zi = new Zi();
zi.method();
}
}
16.4 继承后方法的访问规则
父类
package com.tipdm.demo04;
public class Fu {
public void methodFu(){
System.out.println("父类方法执行!");
}
public void method(){
System.out.println("父类重名方法执行!");
}
}
子类
package com.tipdm.demo04;
public class Zi extends Fu{
public void methodZi(){
System.out.println("子类方法执行!");
}
public void method(){
System.out.println("子类重名方法执行!");
}
}
主类:
package com.tipdm.demo04;
/**
* 在父子类的继承关系中,创建子类对象,访问成员方法的规则:
* 创建的对象是谁,就优先用谁,如果没有则向上找
*
* 注意事项:
* 无论是成员方法还是成员变量,如果没有都是向上找父类,绝不会向下找子类的。
* 重写(Override)
* 概念:在继承关系中,当方法的名称一样,参数列表也一样。
* 重写(Overrie): 方法的名称【一样】,参数列表【也一样】。覆盖、覆写。
* 重载(Overload): 法的名称【一样】,参数列表【不一样】。
*
* 方法的覆盖重写特点:创建的是子类对象,则优先用子类方法。
*/
public class demo1 {
public static void main(String[] args) {
Zi zi = new Zi();
zi.methodFu();
zi.methodZi();
// 创建的是new了子类对象,所以优先用子类方法
zi.method();
}
}
16.5 继承之重写(Override)
父类
package com.tipdm.demo05;
public class Fu {
public void method(){
System.out.println("父类方法!");
}
}
子类
package com.tipdm.demo05;
public class Zi extends Fu{
@Override
public void method(){
System.out.println("子类方法!");
}
}
主类
package com.tipdm.demo05;
/**
* 方法覆盖重写的注意事项:
* 1. 必须保证父子类之间方法相同,参数列表也相同。
* @Override:写在方法前面,用来检验是不是有效的正确覆盖重写,如果写在方法前面没有报错说明是有效的覆盖重写。
* 这个注解就算不写,只要满足要求,也是正确的方法覆盖重写。
* 2. 子类方法的返回值必须小于等于父类的返回值范围。
* java.lang.Object类是所有类的公共最高父类(祖宗类),java.lang.String就算Object的子类
* 父类返回Object,子类返回String是正确写法。
* 父类返回String,子类返回Object是错误写法。
* 3. 子类方法的权限必须大于等于父类方法的权限修饰符。
* 小扩展提示:public > protected > (default) > private
* 备注:(default)不是关键字default,而是什么都不写,留空
*/
public class demo1 {
public static void main(String[] args) {
}
}
复用父类的方法
父类
package com.tipdm.demo06;
// 本来的老款手机
public class Phone {
public void call(){
System.out.println("打电话!");
}
public void send(){
System.out.println("发短信!");
}
public void show(){
System.out.println("显示号码");
}
}
子类
package com.tipdm.demo06;
public class NewPhone extends Phone{
@Override
public void show() {
super.show(); // 把父类的show方法拿过来重复利用
// 自己子类再添加
System.out.println("显示姓名");
System.out.println("显示头像");
}
}
主类
package com.tipdm.demo06;
public class demo1 {
public static void main(String[] args) {
Phone phone = new Phone();
phone.call();
phone.send();
phone.show();
System.out.println("==============");
NewPhone newPhone = new NewPhone();
newPhone.call();
newPhone.send();
newPhone.show();
}
}
16.6 父子类构造方法访问特点
父类
package com.tipdm.demo07;
public class Fu {
public Fu(){
System.out.println("父类无参构造方法");
}
public Fu(int num){
System.out.println("父类有参构造方法");
}
}
子类
package com.tipdm.demo07;
public class Zi extends Fu{
public Zi(){
// super(); // 在调用父类无参构造方法
super(10); // 调用父类重载的构造方法
System.out.println("子类构造方法!");
}
// public void method(){
// super(); // 错误写法!只有子类构造方法,才能调用父类构造方法!。
// }
}
主类
package com.tipdm.demo07;
/**
* 继承关系中,父子类构造方法访问特点:
* 1. 子类构造方法中,有一个默认隐含的“super()”调用,所以一定是先调用父类的构造方法,再调用子类的构造方法。
* 2. 可以通过super关键字来子类构造调用父类重载构造。
* 3. super的父类构造调用,必须是自来构造方法的第一个语句。不能一个子类构造调用多次super构造。
*
* 总结:
* 子类必须调用父类构造方法,不写则赠送super(); 写了则用写的指定的super调用,super只能有一个,还必须是第一个。
*/
public class demo1 {
public static void main(String[] args) {
Zi zi = new Zi();
}
}
16.7 super关键字的三种用法
父类
package com.tipdm.demo08;
public class Fu {
int num = 10;
public Fu(){
System.out.println("父类的无参构造方法!");
}
public Fu(int num){
System.out.println("父类的有参构造方法!");
}
public void methodFu(){
System.out.println(num);
}
}
子类
package com.tipdm.demo08;
public class Zi extends Fu{
int num = 20;
public Zi(){
super(12);
}
public void methodZi(){
super.methodFu(); // 访问父类的成员方法
System.out.println(super.num); // 访问父类的成员变量
}
}
主类
package com.tipdm.demo08;
/**
* super关键字的用法有三种:
* 1. 在子类的成员方法中,访问父类的成员变量
* 2. 在子类的成员方法中,访问父类的成员方法
* 3. 在子类的构造方法中,访问父类的构造方法
*/
public class demo1 {
public static void main(String[] args) {
Zi zi = new Zi();
System.out.println("============");
zi.methodZi();
}
}
16.8 this关键字的三种用法
父类
package com.tipdm.demo09;
public class Fu {
int num = 30;
}
子类
package com.tipdm.demo09;
public class Zi extends Fu{
int num = 20;
public Zi(){
// super(); // 错误写法
this(123); // 本类的无参构造,调用本类的有参构造
// this(12, 123); // 错误写法
}
public Zi(int n){
this(12, 13);
}
public Zi(int m, int n){}
public void method(){
int num = 10;
System.out.println(num); // 局部变量
System.out.println(this.num); // 本类中的成员变量
System.out.println(super.num); // 父类中的成员变量
this.showMessage();
}
public void showMessage(){
System.out.println("本类中的另外一个成员方法!");
}
}
主类
package com.tipdm.demo09;
/**
* super关键字用来访问父类内容,而this关键字用来访问本类内容。用法也有三种:
* 1. 在本类的成员方法中,访问本类的成员变量。
* 2. 在本类的成员方法中,访问本类中的另一个成员方法。
* 3. 在本类的构造方法中,访问本类中的另一个构造方法。
* 在第三种用法当中要注意:
* A. this(...)调用也必须是构造方法的第一个语句,唯一一个。
* B. this和super只能一个放在狗仔方法的第一个语句。
*/
public class demo1 {
public static void main(String[] args) {
Zi zi = new Zi();
zi.method();
}
}
16.9 抽象方法和抽象类
抽象父类
package com.tipdm.demo10;
/**
* 抽象方法:就算加上abstract关键字,然后去掉大括号,直接分号结束
* 抽象类:抽象方法所在的类,必须是抽象类才行.在class之前写上abstract即可.
*
* 如何使用抽象类和抽线方法:
* 1. 不能直接创建new抽象对象.
* 2. 必须用一个子类继承抽象类.
* 3. 子类必须覆盖重写抽象父类中所有的抽象方法.
* 覆盖重写(实现):子类去掉抽象方法的abstract关键字,然后补上方法体大括号.
* 4. 创建子类对象进行使用
*
* 注意事项:
* 1.
*/
public abstract class Animal {
public abstract void eat();
}
子类
package com.tipdm.demo10;
public class Cat extends Animal{
@Override
public void eat() {
System.out.println("吃鱼!");
}
}
主类
package com.tipdm.demo10;
public class demo1 {
public static void main(String[] args) {
Cat cat1 = new Cat();
cat1.eat();
}
}
16.10 抽象类的构造方法
父类
package com.tipdm.demo11;
public abstract class Fu {
public Fu(){
System.out.println("抽象父类构造方法执行!");
}
public abstract void eat();
}
子类
package com.tipdm.demo11;
public class Zi extends Fu{
public Zi(){
// super();
System.out.println("子类构造方法执行!");
}
@Override
public void eat() {
System.out.println("吃饭饭");
}
}
主类
package com.tipdm.demo11;
public class demo1 {
public static void main(String[] args) {
Zi zi = new Zi();
zi.eat();
}
}
注意事项:
- 一个抽象类不一定含有抽象方法, 只要保证抽象方法所在的类是抽象类,即可.
- 这样没有抽象方法的抽象类,也不能直接创建对象,在一些特殊场景下有用途.
public abstract class MuAbstract {
}
16.11 抽象类的继承
父类
package com.tipdm.demo12;
// 最高的抽象父类
public abstract class Animal {
public abstract void sleep();
public abstract void eat();
}
二级父类
package com.tipdm.demo12;
public abstract class Dog extends Animal{
@Override
public void eat() {
System.out.println("狗吃骨头");
}
// public abstract void sleep(); 这个抽象方法没有重写导致子类也应该为抽象类
}
子类1
package com.tipdm.demo12;
public class Dog2ha extends Dog{
@Override
public void sleep() {
System.out.println("hehehehe~~");
}
}
子类2
package com.tipdm.demo12;
public class DogGolden extends Dog{
@Override
public void sleep() {
System.out.println("呼呼呼....");
}
}
主类
package com.tipdm.demo12;
public class demo {
public static void main(String[] args) {
// Animal animal = new Animal(); // 错误写法,抽象类无法直接new创建
// Dog dog = new Dog(); // 由于Dog类中仍然存在抽象方法,故Dog类也为抽象类,同样无法直接创建
Dog2ha dog2ha = new Dog2ha();
dog2ha.eat();
dog2ha.sleep();
System.out.println("================");
DogGolden dogGolden = new DogGolden();
dogGolden.eat();
dogGolden.sleep();
}
}
16.12 题目:群主发红包
群主发普通红包.某群有多名成员,群主给成员发普通红包.
- 普通红包规则:
-
- 群主的一笔金额,从群主余额中扣除,平均分成n等份,让成员领取.
-
- 成员领取红包后,保存到成员余额中.
User类
package com.tipdm.demo13;
public class User {
private String name;
private int money;
public void show(){
System.out.println("我是:" + this.name + ",我有多少钱:" + this.money);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getMoney() {
return money;
}
public void setMoney(int money) {
this.money = money;
}
public User(String name, int money) {
this.name = name;
this.money = money;
}
public User() {
}
}
Manager类
package com.tipdm.demo13;
import java.util.ArrayList;
public class Manager extends User{
public Manager(){
}
public Manager(String name, int money) {
super(name, money);
}
// 发红包
public ArrayList<Integer> send(int totalMoney, int count){
// 设置一个数据用来存储发出去的红包
ArrayList<Integer> RPList = new ArrayList<>();
// 获取用户余额
int restMoney = super.getMoney();
// 判断一下,剩下的钱是否够发红包
if(totalMoney > restMoney){
System.out.println("余额不足!");
return RPList;
}
// 开始发红包
// 扣钱
super.setMoney(restMoney - totalMoney);
// 计算每个红包应该发多少钱,有可能除不尽,把余数也拿上
int RP = totalMoney / count;
int rest = totalMoney % count;
// 装红包
for (int i = 0; i < count - 1; i++) {
RPList.add(RP);
}
RPList.add(RP + rest); // 如果有余数就把剩下的钱全部放入最后一个红包
return RPList;
}
}
member类
package com.tipdm.demo13;
import java.util.ArrayList;
import java.util.Random;
public class Member extends User{
public Member(){
}
public Member(String name, int money) {
super(name, money);
}
public void get(ArrayList<Integer> RPList){
// 判断一下红包池中是否还有红包
if (RPList.size() == 0){
System.out.println("*红包数量不够,[" + super.getName() + "]未抢到红包!");
}else {
// 抽取一个红包编号
int index = new Random().nextInt(RPList.size());
// 获取红包,并将该红包从红包池中移除
Integer rp = RPList.remove(index);
// 将获取到的红包放入余额
int restMoney = super.getMoney();
super.setMoney(restMoney + rp);
}
}
}
主类
package com.tipdm.demo13;
import java.util.ArrayList;
/**
* 群主发普通红包.某群有多名成员,群主给成员发普通红包.
* 普通红包规则:
* 1. 群主的一笔金额,从群主余额中扣除,平均分成n等份,让成员领取.
* 2. 成员领取红包后,保存到成员余额中.
*/
public class demo1 {
public static void main(String[] args) {
Manager manager = new Manager("群主", 100);
Member member1 = new Member("员工1", 0);
Member member2 = new Member("员工2", 0);
Member member3 = new Member("员工3", 0);
Member member4 = new Member("员工4", 0);
// 展示各用户余额
manager.show();
member1.show();
member2.show();
member3.show();
member4.show();
System.out.println("=========== 开始发送红包 ===========");
// 开始发红包
ArrayList<Integer> RPList = manager.send(50, 4);
member1.get(RPList);
member2.get(RPList);
member3.get(RPList);
member4.get(RPList);
System.out.println("=========== 红包发送结束 ===========");
manager.show();
member1.show();
member2.show();
member3.show();
member4.show();
}
}