一篇文章带你爆杀Java-继承、重写、多态、抽象类、接口!

一、知识点

继承、重写、多态、抽象类、接口。

(Java语言三大特性:继承、封装、多态)

二、目标

  1. 深刻理解继承的作用。

  2. 掌握重写的规则。

  3. 深刻理解多态。

  4. 掌握抽象类与接口的区别。

三、内容分析

  1. 重点

    • 继承、重写、多态、抽象类、接口。

  2. 难点

    • 多态。

    • 抽象类与接口的区别。

    • 接口的作用。

四、内容

1、继承

继承是描述两个类的关系,如果两个类有重复的属性和方法,我们就可以使用继承的方式来实现/设计。

语法:

class 子类名称 extends 父类 {
    属性名
    方法名
}
  1. 继承中,子类会把父类的所有方法和属性都继承下来(除了private)。

  2. 子类继承父类后,还可以拥有自己的属性和方法。

  3. java中,只能单继承。

// 人
public class Person {

    public String name = "人";
    // 0未知,1男,2女
    public String sex = "0";

    public void eat() {
        System.out.println("吃饭");
    }

}

// 中国人
public class ChinesePerson extends Person {

    public String name = "中国人";
    // 生肖
    public String shengxiao;

    public void kungfu() {
        System.out.println("功夫");
    }
}

// 测试
public static void main(String[] args) {

    ChinesePerson chinesePerson = new ChinesePerson();
    // 子类没有定义sex属性,但是也可以使用,因为从父类继承了
    System.out.println(chinesePerson.sex); // 0
    // 子类、父类可以有同名属性
    System.out.println(chinesePerson.name); // 中国人
    // 子类可以有自己的属性
    System.out.println(chinesePerson.shengxiao); // null

    // 子类没有定义eat方法,但是可以使用,因为从父类继承了
    chinesePerson.eat();
    // 子类可以有自己的方法
    chinesePerson.kungfu();
}

 

2、super

super跟this比较

super所指向的是自身的父类

  1. 子类实例化的过程中,父类构造器先被调用,然后再调用子类的构造器(在子类构造器内部默认调用super())。

  2. 如果子类的构造器中调用了父类的有参构造方法,那么父类无参的构造器不会被调用。

  3. super()的调用要放在第一行。

  4. super可以表示父类的引用,我们可以使用super和this来区分父类和子类的同名属性和方法。

// 接着知识点1的案例
// Chinese添加方法test()
public void test() {
    // 优先找当前类的name属性
    System.out.println(name); // 中国人
    // 优先找当前类的sex属性,找不到就到父类找
    System.out.println(sex); // 0
    // 使用this关键字指定获取子类还是父类的同名属性
    System.out.println(this.name); // 中国人
    System.out.println(super.name); // 人
    // 方法和属性一样
    super.eat();
}

3、重写

  1. 定义

重写(override):也称覆盖。重写是子类对父类非静态非private非final方法的实现过程进行重新编写,返回值(JDK7以后,被重写的方法返回值类型可以不同,但是必须是具有父子关系的)和形参都不能改变。即外壳不变,核心重写。

重载(overload):同一个类中,有相同的方法名,但是参数的类型或者参数的个数不同,这两个方法就是重载。

  1. 为什么需要重写

原有的方法无法满足新的需求,需要对这个方法进行改良来满足新的需求。子类可以根据需要,定义特定于自己的行为。既沿袭了父类的功能名称,又根据子类的需要重新实现父类方法,从而进行扩展增强。

示例:

// 人类
public class Person {

    public String name = "人";

    // 人都会吃饭,具体怎么吃饭的不清楚,每个区域的吃饭方式都不一样
    public void eat() {
        System.out.println("吃饭");
    }

}

// 中国人
public class ChinesePerson extends Person{

    public String name = "中国人";

    // 对eat方法进行重写,延续了父类的动作,但是做了扩展,实现了具体的吃饭方式。
    @Override
    public void eat() {
        System.out.println("使用筷子吃饭");
    }
}

// 印度人
public class IndiaPerson extends Person{

    public String name = "印度人";

    @Override
    public void eat() {
        System.out.println("使用手吃饭");
    }
}

 

4、多态

  1. 定义

同一个行为具有不同的表现形式。执行一段代码,Java 在运行时能根据不同的对象产生不同的结果。

多态不是一种新技术,而是设计模式,基于已有的技术实现的一种场景

  1. 前提条件

  • 子类继承父类。

  • 子类覆盖父类的方法(重写)。

  • 父类引用指向子类对象。

  1. 示例

假设场景,学校食堂排队干饭。吃饭之前要先买饭,然后吃饭。

人都会吃饭,但是不同地区的人生活习惯不一样,吃饭方式也不一样。中国人使用筷子,英国人使用刀叉,印度人使用手。

  • 封装Person类,设置方法eat()和buyFood();

  • 再分别封装三个子类ChinesePerson、EnglishPerson、IndiaPerson;

  • 子类重写eat()方法,因为每个人吃饭的方式可能不一样;

  • buyFood()不用重写,因为每个人都得排队买饭,行为是一样的。

// 人
public class Person {

    public String name = "人";

    // 人都会吃饭,具体怎么吃还不清楚
    public void eat() {
        System.out.println("吃饭");
    }

    public void buyFood() {
        System.out.println("买饭");
    }
}
// 中国人
public class ChinesePerson extends Person{

    public String name = "中国人";

    // 重写父类eat方法,实现具体的吃饭方式
    @Override
    public void eat() {
        System.out.println(name + "用筷子吃饭");
        
    }
}
// 英国人
public class EnglishPerson extends Person{

    public String name = "英国人";

    @Override
    public void eat() {
        System.out.println(name + "用刀叉吃饭");
    }
}

// 用手吃饭
public class IndiaPerson extends Person{

    public String name = "印度人";

    @Override
    public void eat() {
        System.out.println(name + "用手吃饭");
    }
}
// main函数测试1。

public static void main(String[] args) {
    // 子类对象赋值给父类的引用(父类类型的变量)
    Person person1 = new ChinesePerson(); 
    Person person2 = new IndiaPerson();

    // 执行的是子类的方法
    person1.eat(); // 中国人用筷子吃饭
    person2.eat(); // 印度人用手吃饭
}
// main函数测试2。实现食堂排队买饭场景。
// 我们封装ganfan()方法,过程为 买饭 + 吃饭。
public static void main(String[] args) {
    // 使用数组模拟排队
    Person[] persons = { new ChinesePerson(), new IndiaPerson(), new EnglishPerson(), new IndiaPerson() };

    // 循环排队干饭
    for(int i = 0; i < persons.length; i++) {
        ganfan(persons[i]); // 运行过程中才确定是什么对象
        System.out.println("-----------");
    }
}

// 买饭 + 吃饭
// 干饭方法不需要具体知道是什么人吃饭
// 通过多态,实现不同的人使用不同的吃饭方式
public static void ganfan(Person person) {
    person.buyFood();
    person.eat();
}

注意:多态场景下,引用调用的方法是子类重写的方法,调用的属性是父类的属性

是什么?怎么用?有什么好处?

5、抽象类

5.1 定义

抽象类也是类,只是抽象类具备了一些特殊的性质。

我们以前编写一个类时,会为这个类编写具体的属性和方法,但有一些情况我们只知道一个类需要哪些属性方法,但不知道这些方法具体是什么,这时我们就要用到抽象类。

举个例子,有一位老师布置了一篇作文,要求以春天为题目写一篇字数不少于800字写景作文,题材不限(诗歌除外)。在这个例子中,这篇作文就是一个抽象类,这个抽象类有两个属性:以春天为题目和字数不少于800,还有一个抽象方法:写景。现在全班学生就会按照老师所给的要求,即抽象类,去完成作业。抽象类就像一个大纲,一种规范。

抽象类体现的是一种模板式设计。以抽象类作为其子类的模板,从而避免了子类设计的随意性。

5.2 语法

关键字:abstract。

// 抽象类
public abstract class 类名 {
    // 抽象方法
    public abstract 返回值 方法名(); 
}
  1. 抽象类不能实例化,即不能创建对象,只能被继承,然后实例化。

  2. 继承抽象类的非抽象类必须实现抽象方法。

  3. 抽象类中可以没有抽象方法,有抽象方法的类一定是抽象类。

  4. 抽象类可以继承抽象类,可以不用实现抽象方法。

  5. 抽象的方法不能private,final,static修饰。

6、接口

6.1 定义

接口这个概念在生活中并不陌生,比如计算机往往有多个USB接口,可以插各种USB设备,如键盘、鼠标、U盘、摄像头、手机等。

接口声明了一组能力,但它自己并没有实现这个能力,它只是一个约定。接口涉及交互两方对象,一方需要实现这个接口,另一方使用这个接口,但双方对象并不直接互相依赖,它们只是通过接口间接交互,上面的USB接口来说,USB协议约定了USB设备需要实现的能力,每个USB设备都需要实现这些能力,计算机使用USB协议与USB设备交互,计算机和USB设备互不依赖,但可以通过USB接口相互交互。

6.2 作用

Java中是单继承的,怎么才能让类实现多继承呢?

(比如中国人继承人,同时还会功夫)

比如燕子会飞,飞机也会飞,他们具有相同的行为,但是他们是毫不相关的两个类,如果让燕子和飞机都继承于同一个抽象类,显然不合适。而且燕子会吃东西,飞机可以加油,这时候也不适合在同一个抽象类中定义让他们继承。

接口主要解决的问题:

  1. 多继承

  2. 不同子类实现相同的行为

6.3 语法

关键字:interface

interface 接口名 {
   
}
  1. 接口不能实例化,需要有类使用implements来实现接口,并且实现接口的抽象方法。

  2. 接口中的方法默认是抽象的。

  3. 接口的方法不能使用private和final、static修饰。

  4. 接口可以多继承。

  5. 接口可以定义常量,常量默认会带public static final 。该常量不能修改,一般我们通过【接口名.常量】来访问。

6.4 接口与抽象类的区别
  1. 一个类只能继承一个抽象类,而一个类却可以实现多个接口。

  2. 抽象类是对一种事物的抽象,即对类抽象,而接口是对行为的抽象。

示例:假设场景,家里的门。门都有open( )和close( )两个动作,此时我们可以通过抽象类和接口来定义这个抽象概念。

public abstract class Door {
    public abstract void open();
    public abstract void close();
}
public interface Door {
    public abstract void open();
    public abstract void close();
}

但是现在如果我们需要门具有报警alarm( )的功能,那么该如何实现?下面提供两种思路:

  1. 将这三个功能都放在抽象类里面,但是这样一来所有继承于这个抽象类的子类都具备了报警功能,但是有的门并不一定具备报警功能;

  2. 将这三个功能都放在接口里面,需要用到什么功能我们就实现什么接口。

从这里可以看出, Door的open() 、close()和alarm()根本就属于两个不同范畴内的行为,open()和close()属于门本身固有的行为特性,而alarm()属于延伸的附加行为。因此最好的解决办法是单独将报警设计为一个接口,包含alarm()行为,Door设计为单独的一个抽象类,包含open和close两种行为。

// 接口强调行为,报警的动作
public interface Alram {
    void alarm();
}

// 抽象类强调事物,门具备开和关固有的行为特性
public abstract class Door {
    void open();
    void close();
}
 
// 如果是警报门。具备门基本的特征,所以继承Door。又具备报警的功能就实现Alarm接口进行扩展。
// 如果是普通门,把implements Alarm去掉即可。
class AlarmDoor extends Door implements Alarm {
    void oepn() {
      //....
    }
    void close() {
      //....
    }
    void alarm() {
      //....
    }
}

// 假如是一个火灾警报器,不具备门的特性,我们直接实现Alarm接口就好了x
class FireAlarm implements Alarm {
    void alarm() {
      //....
    }
}
6.5 JDK1.8之后(包括1.8)接口的变化

在jdk1.8之后接口可以定义static和default方法。

// 使用接口名.方法名调用
static void 方法名称() {
    
}
// 可以被子类继承,实例化子类之后调用
default void 方法名称() {
    
}

在1.8之前,接口与其实现类之间的耦合度太高,当需要为一个接口添加方法时,所有的实现类都必须随之修改。默认方法解决了这个问题,它可以为接口添加新的方法,而不会破坏已有的接口的实现。

五、小结

继续补充面向对象剩余知识点,重点理解多态的特性,以及普通类、抽象类、接口的区别。

六、练习

  1. 场景:公司给员工发工资。

  • Employee:所有员工总的父类。

    属性:员工的姓名(String), 员工的生日月份(int)。

    方法:double getSalary(int month),补贴工资,根据参数月份来确定工资,如果该月员工过生日,则公司会额外奖励100元。

  • SalariedEmployee:Employee的子类,拿固定工资的员工。

    属性:月薪。

  • HourlyEmployee:Employee的子类,按小时拿工资的员工,每月工作超出160小时的部分按照1.5倍工资发放。

    属性:每小时的工资、每月工作的小时数。

  • SalesEmployee:Employee的子类,销售人员,工资由月销售额和提成率决定。

    属性:月销售额、提成率。

测试:创建SalariedEmployee对象,姓名:张三,生日月份:6,月薪:8000。

​ 创建HourlyEmployee对象,姓名:李四,生日月份:12,每小时的工资:20,每月工作的小时数:180。

​ 创建SalesEmployee对象,姓名:王五,生日月份:8,月销售额:1000000,提成率:0.05。

​ 分别打印这三个员工的当前月工资(扩展:如何动态获取当前月份?不知道可以先假设当前月份是6月)。

2.moba游戏

假设正在开发一款moba游戏,设计一个抽象类 Hero 和一个接口 ISkill。

抽象类 Hero 包含通用属性如 name(名称)、level(等级)和 health(生命值),以及一个抽象方法 attack() 表示角色的攻击行为。

接口 ISkill定义了一个方法 useSkill(),表示角色使用技能的行为。

接下来,创建两个具体的英雄类 Houyi 和 Daji,它们都继承自 Hero 类并且实现 ISkill 接口。Houyi 类有一个射击技能 ShootSun,Daji类有一个魅惑术 Charm。

  • 12
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值