#今日内容
- 继承
- 方法的重写
- super
- 抽象类
##01. 继承的概述
- 什么是继承?
- 建立继承关系后, 有什么特点?
- 继承的好处是?
总结:
1.
让类与类之间产生了关系, 子父类关系
2.
子类就可以直接使用到父类中非私有的成员
3.
提高了代码的复用性
提高了代码的维护性
提高了代码的编写效率(注意不是提高了执行效率)
是 多态 的前提
弊端: 提高了代码的耦合性.
##02. 继承的格式
- 实现继承是通过哪个关键字?
总结:
1. extends
格式:
class 子类 extends 父类类名 {
}
示例代码:
class Animal {
String name;
int age;
String color;
}
class Cat extends Animal{
public void catchMouse(){
System.out.println("抓老鼠");
}
}
class Dog extends Animal{
public void lookHome(){
System.out.println("狗看家");
}
}
什么时候使用继承:
当发现多个类之间存在共性的内容, 就可以考虑将共性的数据 -> (向上抽取)
抽取之前需要考虑一点, 类与类之间是否产生了一种is..a的关系
##03. 继承中成员变量的访问特点
- 思考: 如果子父类中出现了重名的成员变量, 那么在调用的时候, 用子类的还是父类的?
总结: 子类的, 这里使用到了就近原则
- 在继承的关系中, 如果成员变量重名, 创建子类对象的时候, 访问有两种方式:
总结:
A : 直接通过子类对象访问成员变量
Zi z = new Zi();
System.out.println(z.num); // 就近原则
B : 间接通过成员方法访问成员变量
方法所属于谁, 使用的就是谁
public class Test2_Extends {
public static void main(String[] args) {
Zi z = new Zi();
z.methodZi(); // 方法所属于子类, 使用的是num20
z.methodFu(); // 方法所属于父类, 使用的是num10
}
}
class Fu{
int num = 10;
public void methodFu(){
System.out.println(num);
}
}
class Zi extends Fu {
int num = 20;
public void methodZi(){
System.out.println(num);
}
}
##04. 区分子类方法中重名的三种变量
- 局部变量和成员变量重名, 成员变量和父类成员变量重名.
class Dad {
String name = "建霖";
}
class Kid extends Dad {
String name = "四葱";
public void show() {
String name = "五葱";
System.out.println(super.name); // 建霖
System.out.println(name); // 五葱
System.out.println(this.name); // 四葱
}
}
##05. 继承中成员方法的访问特点
- 继承中如果出现了重名的成员方法, 调用的时候. 执行的是?
总结: 子类的成员方法
虽然是就近原则的效果, 但是这里有一个专业名字
方法的重写.
##06. 继承中方法的覆盖重写_概念与特点
- 重写和重载的概念:
总结:
1. 重载 : overload
在同一个类中, 方法名相同, 参数列表不同, 与返回值无关.
参数列表不同:
1. 类型不同
2. 个数不同
3. 顺序不同
2. 重写(覆盖 覆写) : override
在子父类当中, 出现方法声明一模一样的方法
方法声明 : 参数列表一致, 方法名也需要一致
##07. 继承中方法的覆盖重写_注意事项
- 注意事项:
总结:
1. 子类重写父类方法的时候, 需要保证方法声明一模一样
@Override: 注解
用来检测当前方法是否是一个重写的方法
2. 子类重写父类方法的时候, 返回值类型需要[ 小于等于 ]父类的类型
建议: 返回值类型一致
Object : 所有类的祖宗
所有的类都是直接或者间接的继承了Object
3. 子类重写父类方法的时候, 访问权限必须[ 大于等于 ] 父类的权限
从大到小依次为:
public -> protected -> 默认的 -> private
4. 子类不能重写父类中私有方法
子类无法继承父类的私有方法,连继承都做不到, 更不要说重写.
##08. 继承中方法的覆盖重写_应用场景.
- 什么情况下需要使用方法的重写?
总结:
当子类需要父类的功能, 而子类的方法又有自己特有的功能主体, 这时候就可以重写父类的方法
这样做既沿袭了父类的功能, 又定义了子类特有的内容.
大白话理解:
子类觉得父类的方法不好, 或者说是过于老旧, 那么就可以对父类的方法进行重写.
- 再次举例, 手机的例子
代码:
class Phone {
public void call(){
System.out.println("手机打电话");
}
}
class iOS6 extends Phone {
public void siri(){
System.out.println("speak English");
}
}
class iOS7 extends iOS6 {
@Override
public void siri(){
super.siri(); //既有了speak English的功能
System.out.println("说中文");//又添加了自己的新功能
}
}
##09. 继承中构造方法的访问特点
- 问题: 构造方法可以继承吗?
总结: 不可以 ! 构造方法要求方法名与类名相同,大小写也要一致
- 需要搞清楚的是子父类谁先完成初始化的问题
总结:
父类先完成初始化, 子类再完成初始化
初始化一个对象, 使用的是构造方法
子类在初始化的时候, 一定会先访问的到父类的构造方法! 从而完成父类的初始化.
问题: 如何访问的父类构造方法?
结论: 在每一个构造方法的第一行语句都默认隐藏了一句话 super();
- 访问构造方法的注意事项
总结:
1. super()可以访问父类的空参构造, 如果想要访问有参构造, 需要在super()内部传入参数
例如: super(10);
2. super()语句, 必须放在构造方法的第一行有效语句.
##10. super关键字的三种用法
总结:
1. 成员变量
2. 成员方法
3. 构造方法
格式记忆: 调用成员是super. 调用构造是super();
##11. this关键字的三种用法
总结:
1. 成员变量
2. 成员方法
3. 构造方法
格式记忆: 调用成员是this. 调用构造是this();
##12. super与this关键字图解
- 看图说话
- 案例演示继承的常见用法
需求:
猫类 : 毛的颜色, 腿的个数
吃饭, 抓老鼠
狗类 : 毛的颜色, 腿的个数
吃饭, 看家
##13. Java继承的三个特点
- 思考问题: 一个类是否可以拥有两个爹?
- 继承的特点
总结:
1: Java中只支持单继承, 不支持多继承, 但是可以多层继承
问题:
凭啥不支持多继承?
如果支持多继承的话, 两个父类中要是有相同的方法, 但是方法的功能主体不同.
这时候再创建对象调用方法, 就不知道该走那一段逻辑了.
例子:
class A {
public void method(){
System.out.println("A....");
}
}
class B {
public void method(){
System.out.println("B....");
}
}
class C extends A, B{
}
C c = new C();
c.method(); // 打印A...还是B... ? 懵了!
##14. 抽象的概念
- 什么是抽象类?
- 抽象类和普通的父类有什么区别?
- 抽象方法又是什么?
总结:
1.
2. 抽象类当中可以定义抽象方法;
普通的父类中不能定义抽象方法.因为抽象方法只存在于抽象类和接口中.
3. 当我们将共性的行为抽取到一个父类当中
结论:
当我们将事物的共性向上抽取之后, 发现某些行为描述不清了, 而且这些行为还是强制要求子类去重写的.
这时候就可以将该行为定义为抽象方法, 抽象方法一定要存活于抽象类或者是接口当中.
##15. 抽象方法和抽象类的格式
- 抽象方法定义格式
- 抽象类定义格式
总结:
1. public abstract void 方法名 ( 参数列表 ) { ... }
2. abstract 类名称 { ... }
[ 注意事项 ] 要和接口中的内容区分开来. 在接口中, abstract是可以省略的, 但是在类中, abstract是绝对不能省略的.需要特别注意.
##16. 抽象方法和抽象类的使用
- 重点: 抽象类不能创建对象, 所以使用的话, 主要关注的就是子类了.
- 作为抽象类的子类
总结:
1. 见17的总结
##17. 抽象方法和抽象类的注意事项
- 抽象类不能实例化( 创建对象 )
- 抽象类中有构造方法
- 抽象类中可以没有抽象方法
- 抽象类的子类
总结:
1. 抽象类不能创建对象,如果创建,编译无法通过而报错。只能创建其非抽象子类的对象。
理解:假设创建了抽象类的对象,调用抽象的方法,而抽象方法没有具体的方法体,没有意义。
2. 抽象类中,可以有构造方法,是供子类创建对象时,初始化父类成员使用的。
理解:子类的构造方法中,有默认的super(),需要访问父类构造方法。
3. 抽象类中,不一定包含抽象方法,但是有抽象方法的类必定是抽象类。
理解:未包含抽象方法的抽象类,目的就是不想让调用者创建该类对象,通常用于某些特殊的类结构设计。
4. 抽象类的子类,必须重写抽象父类中所有的抽象方法,否则,编译无法通过而报错。除非该子类也是抽象类。
理解:假设不重写所有抽象方法,则类中可能包含抽象方法。那么创建对象后,调用抽象的方法,没有 意 义。
##18. 发红包案例_分析
- 看图分析
##19. 发红包案例_实现
- 用户类
public class User {
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 count) {
// 首先需要一个集合,用来存储若干个红包的金额
ArrayList<Integer> redList = new ArrayList<>();
// 首先看一下群主自己有多少钱
int leftMoney = super.getMoney(); // 群主当前余额
if (totalMoney > leftMoney) {
System.out.println("余额不足");
return redList; // 返回空集合
}
// 扣钱,其实就是重新设置余额
super.setMoney(leftMoney - totalMoney);
// 发红包需要平均拆分成为count份
int avg = totalMoney / count;
int mod = totalMoney % count; // 余数,也就是甩下的零头
// 除不开的零头,包在最后一个红包当中
// 下面把红包一个一个放到集合当中
for (int i = 0; i < count - 1; i++) {
redList.add(avg);
}
// 最后一个红包
int last = avg + mod;
redList.add(last);
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 delta = list.remove(index);
// 当前成员自己本来有多少钱:
int money = super.getMoney();
// 加法,并且重新设置回去
super.setMoney(money + delta);
}
}
- 测试类
public class MainRedPacket {
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(); // 100
one.show(); // 0
two.show(); // 0
three.show(); // 0
System.out.println("===============");
// 群主总共发20块钱,分成3个红包
ArrayList<Integer> redList = manager.send(20, 3);
// 三个普通成员收红包
one.receive(redList);
two.receive(redList);
three.receive(redList);
manager.show(); // 100-20=80
// 6、6、8,随机分给三个人
one.show();
two.show();
three.show();
}
}