第十八章
一、继承
1.1 继承的概述
面向对象的三大特征:封装、继承、和多态,继承是多态的前提,如果没有继承,就没有多态。
1.2 继承的格式
在继承的关系中,子类就是一个父类,也就是说子类可以被当作父类看待
例如父类是员工,子类是讲师,那么“讲师就是一个员工”关系:is-a
定义父类格式:(一个普通的定义)
public class 父类名称{
}
定义子类的格式:
public class 子类名称 extends 父类名称{
}
**父类**
//定义一个父类
public class Employee {
public void method(){
System.out.println("方法执行");
}
}
子类1
//定义了一个子类
public class Teacher extends Employee {
}
子类2
public class Assistant extends Employee {
}
主方法
public class Demo01Extends {
public static void main(String[] args) {
//创建一个子类对象
Teacher teacher = new Teacher();
//Teacher类当中虽然什么都没写,但是会继承来自父类的method方法
teacher.method();
//创建另一个子类助教的对象
Assistant assistant = new Assistant();
assistant.method();
}
}
1.2 继承中成员变量的访问特点
在父子类的继承关系当中,如果成员变量重名,则创建子类对象时,访问有两种方式
直接通过子类对象访问成员变量:
等号左边是谁就优先用谁,没有则向上找
间接通过成员方法访问成员变量:
该方法属于谁就先用谁,没有则向上找
主方法
public class Demo01ExtendsField {
public static void main(String[] args) {
Fu fu = new Fu();//创建父类对象
System.out.println(fu.numFu);//只能使用父类的东西,没有任何子类的内容
Zi zi = new Zi();
System.out.println(zi.numFu);//10
System.out.println(zi.numZi);//20
System.out.println("------------------");
//等号左边是谁就优先用谁
System.out.println(zi.num);//200
// System.out.println(zi.abc);//到处都没有,编译报错
System.out.println("------------------");
//这个方法是子类的,优先用子类当中的
zi.methodZi();//200
//这个方法是在父类中定义的
zi.methodFu();//100
}
}
父类
public class Fu {
int numFu = 10;
int num = 100;
public void methodFu(){
//使用的是本类当中的num
System.out.println(num);
}
}
子类
public class Zi extends Fu{
int numZi = 20;
int num = 200;
public void methodZi(){
//因为本类当中有num,使用的是本类当中的num
System.out.println(num);
}
}
1.3 区分子类方法中重名的三种变量
局部变量: 直接写
本类成员变量: this.成员变量
父类成员变量: super.成员变量
子类
public class Zi extends Fu {
int num = 20;
public void method(){
int num = 30;
System.out.println(num);//30 局部变量
System.out.println(this.num);//20 本类的成员变量
System.out.println(super.num);//10父类的成员变量
}
}
父类
public class Fu {
int num = 10;
}
主方法
/*
局部变量: 直接写
本类成员变量: this.成员变量
父类成员变量: super.成员变量
*/
public class Demo01ExtendField {
public static void main(String[] args) {
Zi zi = new Zi();
zi.method();
}
}
1.4 继承中成员方法的访问特点
在子父类的继承关系当中,创建子类对象,访问成员方法的规则:
创建的对象是谁,就优先用谁,如果没有则向上找
注意事项:
无论是成员方法还是成员变量,都是向上找父类,绝对不会向下找子类
子类
public class Zi extends Fu {
public void methodZi(){
System.out.println("子类方法执行");
}
public void method(){
System.out.println("子类重名方法执行");
}
}
父类
public class Fu {
public void methodFu(){
System.out.println("父类方法执行");
}
public void method(){
System.out.println("父类重名方法执行");
}
}
主方法
public class Demo01ExtendsMethod {
public static void main(String[] args) {
Zi zi = new Zi();
zi.methodFu();
zi.methodZi();
//创建的是new了子类对象,所以优先用子类
zi.method();
}
}
1.5 继承中方法的覆盖重写的概念特点
重写(Override)
概念:在继承关系当中,方法的名称一样,参数列表也一样。
重写(Override):方法名称一样,参数列表【一样】:也叫覆盖,覆写
重载(Overload):方法的名称一样,参数列表【不一样】
方法的覆盖重写特点:创建的是子类对象,则优先用子类方法
1.6 继承中方法的覆盖重写—注意事项
方法重写的注意事项:
1.必须保证父子类之间的方法名称相同,参数列表也相同
@Override:写在方法前面,用来检测是不是有效的方法重写
这个注解就算不写也是正确的,只是安全检测手段。
2.子类方法的返回值必须[小于等于]父类方法的返回值范围
Object类是所有类的公共最高父类(祖宗类)。java.lang.String就是Object的子类
3.子类方法的权限必须【大于等于】父类方法的权限修饰符
public > protected > (default) > private
备注:(default) 不是关键字default,而是什么都不写,留空。
1.6 继承中方法的覆盖重写 —应用场景
子类
//新手机,老手机作为父类
public class NewPhone extends Phone {
@Override
public void show() {
super.show();//把父类的show方法拿来用
//子类添加的方法
System.out.println("显示姓名");
System.out.println("显示头像");
}
}
父类
//老款手机
public class Phone {
public void call(){
System.out.println("打电话");
}
public void send(){
System.out.println("发信息");
}
public void show(){
System.out.println("显示号码");
}
}
主方法
public class Demo01Phone {
public static void main(String[] args) {
Phone phone = new Phone();
phone.call();
phone.send();
phone.show();
System.out.println("---------");
NewPhone newPhone = new NewPhone();
newPhone.show();
newPhone.call();
newPhone.send();
}
}
1.7 继承中构造方法的访问特点
继承关系中,父子类构造方法的访问特点:
1.子类构造方法当中有一个默认隐含的"super()"调用,所以一定是先调用的父类构造,后执行的子类构造
2.子类构造可通过super关键字来调用父类重载构造
3.super的父类构造调用,必须是子类构造方法的第一个语句,不能一个子类构造调用多次super构造
总结:
子类必须掉用父类构造方法,不写则赠送super();写了则用写的指定的super调用, super只能有一个,还必须是第一个
主方法
public class Demo1Constructor {
public static void main(String[] args) {
Zi zi = new Zi();
}
}
父类
public class Fu {
public Fu(int num){
System.out.println("父类构造方法");
}
}
子类
public class Zi extends Fu{
public Zi(){
//super();//在调用父类构造方法
super(20);//调用父类重载的构造方法
System.out.println("子类构造方法");
}
public void method(){
//super();//错误写法只有子类构造方法才能调用父类构造方法
}
}
二、super关键字的三种用法
2.1 super关键字的用法有三种:
1.在子类的成员方法中,访问父类的成员变量
2.在子类的成员方法中,访问父类的成员方法
3.在子类构造方法中,访问父类的构造方法
子类
public class Zi extends Fu{
int num = 20;
public Zi() {
super();
}
public void methodZi(){
System.out.println(super.num);
}
public void method(){
super.method();//访问父类中的method
System.out.println("子类方法");
}
}
父类
public class Fu {
int num = 10;
public Fu() {
System.out.println("父类的构造方法");
}
public void method(){
System.out.println("父类方法");
}
}
2.2this关键字的三种用法
super 关键字用来访问父类内容,而this关键字用来访问本类内容。方法也有三种
1.在本类成员方法中,访问本类的成员变量
2.在本类的成员方法中,访问本类的另一个成员方法。
3.在本类的构造方法中,访问本类的另一个构造方法
在第三种用法中:this(...)必须是构造方法的第一个语句,唯一一个
super和this两种构造调用,不能同时使用
public class Zi extends Fu {
int num = 20;
public Zi(){
//super();//这里不再赠送
this(123);//本类的无参构造,调用本类的有参构造
}
public Zi(int n){
}
public void showNum(){
int num = 10;
System.out.println(num);//局部变量
System.out.println(this.num);//本类当中的成员变量
System.out.println(super.num);//父类当中的成员变量
}
public void methodA(){
System.out.println("AAA");
}
public void methodB(){
this.methodA();
System.out.println("BBB");
}
}
public class Fu {
int num = 20;
}
2.3 super与this关键字图解
三、Java继承的三个特点
Java语言是单继承的。一个类的直接父类只能有唯一的一个。
Java语言可以多级继承
一个父类可以有多个子类
四、抽象类
4.1 抽象的概念
抽象方法:在面向对象编程语言中抽象方法指一些只有方法声明,而没有具体方法体的方法。抽象方法一般存在于抽象类或接口中。也就是说抽象方法只需要声明而不需要实现。
4.1 抽象方法和抽象类的格式
- 抽象方法:就是加上abstract关键字,然后去掉大括号,直接分号结束
- 抽象类:抽象方法所在的类,必须是抽象类才行,在class之前写上abstract即可
public abstract class Animal {
//这是一个抽象方法,代表吃东西,但具体吃什么东西(大括号里的内容)不确定
public abstract void eat();
//这是普通的成员方法
public void normalMethod(){
}
}
4.1 如何使用抽象方法和抽象类
抽象方法;就是加上abstract关键字,然后去掉大括号,直接分号结束
抽象类:抽象方法所在的类,必须是抽象类才行,在class之前写上abstract即可
如何创建抽象类和抽象方法:
1.不能直接创建new抽象类对象
2.必须用一个子类来继承抽象父类
3.子类必须覆盖重写父类当中的所有的抽象方法。
覆盖重写(实现):子类去掉抽象方法的abstract关键字,然后补上方法体的大括号
4.创建子类对象进行使用。
抽象类
public abstract class Animal {
//这是一个抽象方法,代表吃东西,但具体吃什么东西(大括号里的内容)不确定
public abstract void eat();
//这是普通的成员方法
public void normalMethod(){
}
}
继承抽象类的子类
public class Cat extends Animal{
@Override
public void eat(){
System.out.println("猫吃鱼");
}
}
主方法
public class DemoMain {
public static void main(String[] args) {
//Animal animal = new Animal();//错误写法:不能直接创建抽象类对象
//用抽象类的子类进行方法创建
Cat cat = new Cat();
cat.eat();
}
}
4.1 抽象方法和抽象类的注意事项
一个抽象类不一定含有抽象方法:
只要保证抽象方法所在的类是抽象类。即可
这样的没有抽象方法的抽象类也不能直接创建对象
public class Zi extends Fu {
public Zi(){
//super();赠送的super调用的父类的构造方法,所以先输出了父类的构造方法
System.out.println("子类构造方法执行");
}
@Override
public void eat() {
System.out.println("吃饭");
}
}
抽象类
public abstract class Animal {
public abstract void sleep();
public abstract void eat();
}
继承的子类并未全部重写抽象父类的方法,所以子类也必须是一个抽象类
//子类也是一个抽象类
public abstract class Dog extends Animal {
@Override
public void eat() {
System.out.println("狗吃骨头");
}
// public abstract void sleep();
}
抽象子类的子类,抽象子类已经重写了eat()方法,这里只需要重写sleep()方法即可,如果不重写这个方法的话,会报错。
public class DogHa extends Dog {
@Override
public void sleep() {
System.out.println("嘿嘿黑");
}
@Override
public void eat() {
System.out.println("刺溜");
}
}
同样需要重写sleep()理由同上
public class GoldenDog extends Dog {
@Override
public void sleep() {
System.out.println("打呼噜");
}
}
主方法
public class DemoMain {
public static void main(String[] args) {
DogHa ha = new DogHa();//这是普通类,可以直接new对象
ha.sleep();
ha.eat();
System.out.println("------------");
GoldenDog golden = new GoldenDog();
golden.sleep();
golden.eat();
}
}
4.2 继承的综合案例-----发红包案例
分析:
用户类 :父类
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(delta + money);
}
}
主方法类
public class MainRedPacket {
public static void main(String[] args) {
Manager manager = new Manager("群主",100);
Member member1 = new Member("成员A",0);
Member member2 = new Member("成员B",0);
Member member3 = new Member("成员C",0);
manager.show();
member1.show();
member2.show();
member3.show();
System.out.println("-----------------");
//群主发红包,总共20块钱,分成三个红包
ArrayList<Integer> redList = manager.send(20,3);
//三个普通成员去收红包
member1.receive(redList);
member2.receive(redList);
member3.receive(redList);
manager.show();//100-20=80 群主还剩多少钱
//6.6.8随机分给三个人
member1.show();//6
member2.show();//6
member3.show();//8
}
}