面向对象三大特征:封装
、继承
、多态
。
继承是多态的前提,没有继承,就没有多态。
继承
- 继承主要是为了达到:共性抽取。
定义一个父类,父类就是一个普普通通的类,格式没有特殊之处。
定义子类的格式:
public class 子类名称 extends 父类名称 {
// ...
}
父类定义:(基类、超类)
public class Employee {}
// 员工
子类定义:(派生类)
public class Teacher extends Employee {}
// 讲师
称谓:
Teacher
讲师类,是Employee
员工类的子类。
Employee是Teacher
的父类。
在父子类继承关系当中,成员变量、成员方法的访问特点:
直接通过对象名称访问成员变量:等号左边是谁,优先用谁
;如果没有,向上找父类。
间接通过成员方法访问成员变量: 该方法属于谁,优先用谁
;如果没有,向上找父类。
继承关系当中可能出现下面三种重名:
1. 访问局部变量 直接写
2. 访问本类成员变量 this.成员变量
3. 访问父类成员变量 super.成员变量
super关键字代表访问父类的内容。
重载(Overload):方法的名称相同,参数列表不同
。
重写(Override):方法的名称相同,参数列表【也相同】
。也叫做覆写、覆盖。
访问特点:创建的是子类对象,就优先调用子类方法。
@Override是一种注解,可以帮助我们检测方法是不是正确的覆盖重写。如果报错,就不是正确的覆盖重写。
但是要注意:就算没有注解,只要符合要求,也照样是方法的覆盖重写。注解只是一种可选的检测手段而已。
在方法覆盖重写的时候,注意事项:
- 方法名称完全一样,参数列表也是完全一样。
- 子类方法的返回值范围,必须【小于等于】父类方法的返回值范围。
- 子类方法的访问权限,必须【大于等于】父类方法的访问权限。
四种访问权限的大小规则:
public > protected > (default) > private
【注意】不能继承的东西:
1. private私有
2. 构造方法
继承的构造方法:
在继承关系当中,父类构造方法,在子类构造方法之前完成。
子类对象在创建的时候,必须调用父类的构造方法。
子类构造方法的第一行,默认会赠送一个:super();代表调用父类的无参构造方法。
Super关键字
super关键字有下面三种典型用法:
- 在子类的成员方法中,访问父类的成员变量:
super.父类成员变量名
- 在子类的成员方法中,访问父类的成员方法:
super.父类成员方法名(参数)
- 在子类的构造方法中,访问父类的构造方法:
super(参数)
注意事项:
1. 只有子类构造方法
,才可以super调用父类构造方法。
2. super的构造调用必须是子类构造方法的第一行,注释不算。
3. 正因如此,所以super调用只能调用一次。
public void methodZi() {
// super(); // 错误!只有构造方法才能super调用构造方法
System.out.println(super.numFu);
super.methodFu();
}
this关键字
this关键字也有下面的三种用法:
- 在本类的成员方法中,访问本类的成员变量 :
this.本类成员变量名
- 在本类的成员方法中,访问本类的另一个成员方法:
this.本类另一个成员方法名(参数)
- 在本类的构造方法中,调用本类的重载构造方法 :
this(参数)
注意事项:
1. 只有构造方法才能this调用另一个构造方法。
2. this构造调用,必须是构造方法当中的第一行。
3. 正因如此,this构造调用只能调用一次。
4. this和super二者不能并存,这并不想影响“子类必须调用父类构造”的要求。
5. 不能出现“僵持循环调用”。
抽象(abstract)
抽象方法定义格式:
public abstract 返回值类型 方法名称(参数类型 参数名称);
抽象类定义格式:
public abstract class 类名称 {
// ...
}
注意:抽象方法所在的类,必须是抽象类。
使用步骤:
1. 抽象类不能直接new对象。
2. 必须用一个子类来继承抽象类。
3. 子类必须覆盖重写所有的抽象方法。
4. 创建子类对象,使用。
子类必须覆盖重写抽象父类当中所有的抽象方法,除非子类也是一个抽象类。
抽象的概念:
抽象方法存在的意义:
群主发红包,成员收红包案例分析:
定义一个抽象的用户类:
private String name; // 姓名
private int money; // 余额
public User() {
}
public User(String name, int money) {
this.name = name;
this.money = money;
}
public void show() {
System.out.println("我叫:" + name + ",我的余额:" + 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 class Manager extends User {
public Manager() {
}
public Manager(String name, int money) {
super(name, money);
}
public ArrayList<Integer> send(int totalMoney, int totalCount) {
// 首先准备一个集合,用来存储拆分之后的好几份红包
ArrayList<Integer> redList = new ArrayList<>();
// 首先看看群主有没有那么多钱,够不够
int leftMoney = super.getMoney(); // 先拿到群主本来的钱
if (totalMoney > leftMoney) { // 如果钱不够
System.out.println("余额不足");
return redList;
}
// 如果够,余额减去红包金额
int money = leftMoney - totalMoney;
super.setMoney(money);
int avg = totalMoney / totalCount; // 平均数
int mod = totalMoney % totalCount; // 余数
// 前n-1个红包,都是平均数
for (int i = 0; i < totalCount - 1; i++) {
redList.add(avg);
}
redList.add(avg + mod); // 最后一个红包包含平均数+余额
return redList;
}
}
定义一个普通成员类继承用户类:
public class Member extends User {
public Member() {
}
public Member(String name, int money) {
super(name, money);
}
public void receive(ArrayList<Integer> list) {
// 从多个红包里面,随机抽取出来一个,给我自己
// 产生一个随机数字,代表索引值
int index = new Random().nextInt(list.size());
// 根据索引值“取出”一个红包
// 取出:拿走一个,就应该少一个。
int money = list.remove(index);
// 我看看自己当前余额是多少钱
int leftMoney = super.getMoney();
super.setMoney(leftMoney + money);
}
}
调用测试类:
public static void main(String[] args) {
Manager manager = new Manager("群主", 100);
Member one = new Member("成员A", 0);
Member two = new Member("成员B", 0);
Member three = new Member("成员C", 0);
manager.show();
one.show();
two.show();
three.show();
System.out.println("===============");
ArrayList<Integer> list = manager.send(80, 3);
one.receive(list);
two.receive(list);
three.receive(list);
manager.show();
one.show();
two.show();
three.show();
}