Java——继承与多态、抽象类、抽象方法、接口

目录

1. 继承的应用场景

2. 继承的格式

3.继承的特点

4.子类究竟能继承父类的什么东西?

5. 继承中:成员变量的访问特点

6.重写

7. 多态

8. 抽象类和抽象方法

9. 接口


1. 继承的应用场景

        当类与类之间有共同的属性时,就可以把这些属性拎出来放到另外一个类中。

2. 继承的格式

public class 子类 extends 父类{ }

3.继承的特点

  • Java只支持单继承:一个类只能继承一个父类。(类比生活中一个孩子只能有一个爸爸)
  • Java不支持多继承,但是支持多层继承:父类还可以继承其父类。(类比生活中我的爸爸也有爸爸,但Java里面不叫做“爷爷”,而叫做“间接父类”)
  • Java中所有类的最顶层类都是Object类,即所有类都继承Object类。
  • 子类只能继承父类非私有的成员。

4.子类究竟能继承父类的什么东西?

     从3个方面来分析:

  • 构造方法:不管是私有的还是非私有的,都不能被继承。(构造方法与类名一致性)
  • 成员变量:不管是私有的还是非私有的,都被继承。(私有的不能用)
  • 成员方法:只有在虚方法表里的,才能被继承。

    ps:什么是虚方法表

           非private、非static、非final

5. 继承中:成员变量的访问特点

    重名变量,采用就近原则

【说明】

 name:直接从局部中找;

 this.name:从当前类的成员变量开始找;

 super.name:从父类开始找。

 ps:当然,如果没有局部变量的name和类中的name,只有父类中有name的话,也是可以通过层层查找找到的。

6.重写

 应用场景:当父类提供的满足不了子类的需求时,子类自己重写适合自己的方法。

 注意事项:①重写的方法格式和父类一致;②只有被添加到虚方法表中的方法才能被重写。

                                                                      (忘记虚方法表的盆友请看第4点的ps)

7. 多态

(1)多态的定义:对象的多种形态。(类比一头母猪生出各种颜色的小花猪,各有各的形态,但共同点是猪【继承】,特点是花色各异【方法重写】)

(2)多态的前提

  • 有继承关系
  • 有父类引用指向子类对象
  • 有方法的重写

举例:设计一个信息系统,用来存取学生和老师的信息。

1. 抓取学生和老师的共同特性——人,创建Person作为其父类

public class Person {
    String name;
    int tel;

    public Person() {
    }

    public Person(String name, int tel) {
        this.name = name;
        this.tel = tel;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getTel() {
        return tel;
    }

    public void setTel(int tel) {
        this.tel = tel;
    }

    public void show(){
    }
}

 2. 创建子类Student、Teacher,继承Person类,注意要进行方法的重写

public class Student extends Person{
    @Override
    //必须要有方法的重写
    public void show() {
        super.show();
        System.out.println("学生的信息:"+ getName() +","+getTel());
    }
}

public class Teacher extends Person{
    @Override
    //必须要有方法的重写
    public void show() {
        super.show();
        System.out.println("老师的信息:"+ getName() +","+getTel());
    }
}

 3. 测试类种创建学生实例和老师实例,创建函数message()用来接收Student和Teacher的信息,必须使用它们的父类作为参数(别忘了调用函数哦)

public class Test {
    public static void main(String[] args) {
        Student s = new Student();
        Teacher t = new Teacher();

        s.setName("学生");
        s.setTel(111);

        t.setName("老师");
        t.setTel(222);

        //调用函数,实现多态
        message(s);
        message(t);
    }

    //这个方法既能接收学生,也能接收老师
    //只需把参数写成它们的父类Person即可
    public static void message(Person p){
        p.show();
    }
}

 运行结果:

(3)多态的好处

        使用父类作为参数,可以接收所有子类对象,代码复用性高。

(4)多态调用成员的规则

        形如:Animal a = new Dog();   //多态

       ①调用成员变量:看父类。父类有这个成员变量则编译成功,否则失败。

       ②调用成员方法:看子类。子类对父类的成员方法进行重写,将父类覆盖,故调用的是子类。

例:Animal 和 Dog 的继承与多态关系:

class Animal{
    String type;

    /*此处省略构造方法*/

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

class Dog extends Animal{
    public void eat{
        System.out.println("小狗会吃饭");
    }
}

public class Test{
    public void static main(){
        Animal a = new Dog(); //多态

        //多态调用成员变量
        system.out.println(a.name);  //检索父类是否有该成员变量,若有则正常编译;若无,编译失败

        //多态调用成员方法
        a.eat(); //若子类有重写,子类覆盖父类的成员方法,故调用的是子类的成员方法
    }
}

结果:编译成功(父类有name成员变量);返回结果是“小狗会吃饭”(方法重写)。

结论:若一个方法中的参数是一个类名,那我们就可以传入这个类的所有子类。

(5)多态的综合练习

代码如下:

public class Animal {
    int age;
    String color;
    public Animal() {
    }

    public Animal(int age, String color) {
        this.age = age;
        this.color = color;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }

    public void eat(String something){

    }
}

//狗继承动物
public class Dog extends Animal{
    public Dog() {
    }

    public Dog(int age, String color) {
        super(age, color);
    }

    public void eat(String something){
        System.out.println(getAge()+"岁的"+getColor()+"的狗用两只前腿保住"+something+"一顿猛吃");
    }

    public void lookHome(){
        System.out.println("狗在看家");
    }
}


//猫继承动物
public class Cat extends Animal{
    public Cat() {
    }

    public Cat(int age, String color) {
        super(age, color);
    }

    @Override
    public void eat(String something) {
        System.out.println(getAge()+"岁的"+getColor()+"猫正趴在地上歪着头吃"+something);
    }

    void catchMouse(){
        System.out.println("猫在抓老鼠");
    }
}


//饲养员单独开来
public class Person {
    String name;
    int age;

    public Person() {
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public void keepPet(Dog dog,String something){
        System.out.println(age +"岁的" + name +"在喂狗");
        dog.eat(something);
    }

    public void keepPet(Cat cat,String something){
        System.out.println(age +"岁的" + name +"在喂猫");
        cat.eat(something);
    }
}

//测试类
public class Test {
    public static void main(String[] args) {
        Person wang = new Person("老王",40);
        Dog dog = new Dog(2,"黑色");
        wang.keepPet(dog,"骨头");

        Person li = new Person("老李",45);
        Cat cat = new Cat(3,"黄色");
        li.keepPet(cat,"鱼刺");
    }
}

利用多态改写代码:

Person中keepPet(),传入的参数是父类Animal类型,子类都可以传参进来。

    public void keepPet(Animal a,String something){
        //强转
        if(a instanceof Dog){  //如果传入的a是狗类型,将其强转为Dog,实例是d
            Dog d = (Dog) a;
            System.out.println(age +"岁的" + name +"在喂狗");
            d.eat(something);
        }else if(a instanceof Cat){  //如果传入的a是猫类型,将其强转为Cat,实例是c
            Cat c = (Cat) a;
            System.out.println(age +"岁的" + name +"在喂猫");
            c.eat(something);
        }else {
            System.out.println("没有这种动物");
        }
    }

在测试类中:

public class Test {
    public static void main(String[] args) {
        Person wang = new Person("老王",40);
        Animal dog = new Dog(2,"黑色");

        //第一个参数是Animal,当方法的参数是一个类时,可以传入它的所有子类
        //这里就可以传入Animal的子类:Dog和Cat ,注意:传入的是实例dog而非类型Dog
        wang.keepPet(dog,"骨头");

        Person li = new Person("老李",45);
        Animal cat = new Cat(3,"黄色");
        li.keepPet(cat,"鱼刺");
    }
}

运行结果:

注意:

该代码在Java 16以上版本中使用了instanceof模式匹配操作符,但在低版本的Java中这不可用,因此会导致编译错误。

Java 16以上版本:

if(a instanceof Dog d){

Java 16以下版本:

if(a instanceof Dog){  
    Dog d = (Dog) a;
}

8. 抽象类和抽象方法

  • 抽象方法的定义格式:

public abstract void eat();   //注意:没有方法体!

  • 抽象类的定义格式:

public abstract class dog{ }

例: 青蛙、狗、山羊

 【思路】方法中共性但又独特的拿去父类做抽象方法,然后子类重写所有抽象方法。

比如该例中:三种动物都会吃,但是吃的东西又不一样,所以用abstract来修饰该方法。现在请自己动手尝试一下吧。

代码如下:

public abstract class Animals {
    String name;
    int age;

    public Animals() {
    }

    public Animals(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public abstract void eat();
    public void drink(){
        System.out.println("我会喝水");
    }
}

public class Dog extends Animals{
    public Dog() {
    }

    public Dog(String name, int age) {
        super(name, age);
    }

    @Override
    public void eat() {
        System.out.println("狗吃骨头");
    }
}

public class Sheep extends Animals{
    public Sheep() {
    }

    public Sheep(String name, int age) {
        super(name, age);
    }

    @Override
    public void eat() {
        System.out.println("山羊吃草");
    }
}

public class frog extends Animals{
    public frog() {
    }

    public frog(String name, int age) {
        super(name, age);
    }

    @Override
    public void eat() {
        System.out.println("青蛙吃虫子");
    }
}

public class Test {
    public static void main(String[] args) {
        Dog d = new Dog("小黄",5);
        System.out.println("狗:"+ d.getName() + "," + d.getAge());
        d.drink();
        d.eat();

        frog f = new frog("小绿",1);
        System.out.println("青蛙:"+ f.getName() + "," + f.getAge());
        f.drink();
        f.eat();

        Sheep s = new Sheep("小白",50);
        System.out.println("山羊:"+ s.getName() + "," + s.getAge());
        s.drink();
        s.eat();
    }
}

【注】别忘了子类中要写构造方法哦!

运行结果如下:

 

9. 接口

(1)如何理解接口?

接口就是一种规则,是对行为的抽象。

【应用场景】多个但非全部类的共同行为,将该行为作为接口,接口中写入抽象方法,类实现该接口,并对抽象方法进行重写。

//定义接口
public interface Swimming {
    public abstract void Swim();  //抽象方法
}

//狗会游泳(狗刨)
public class Dog extends Animals implements Swimming{

    //重写抽象方法

    public void Swim() {
        System.out.println("狗刨");
    }

}

//青蛙会游泳(蛙泳)
public class frog extends Animals implements Swimming{

    //重写抽象方法

    public void Swim() {
        System.out.println("蛙泳");
    }
}

注意:

1. 类可以实现多个接口:

public class Student implements Eating,Drinking,Studing{}

2. 类可以在继承一个类的同时实现多个接口:

public class Student extends Person implements Eating,Drinking,Studing{}

(2)如何区分父类中的抽象方法与接口?

如:青蛙、狗和山羊

青蛙        属性:名字,年龄        行为:吃虫子,蛙泳

狗            属性:名字,年龄        行为:吃骨头,狗刨

山羊        属性:名字,年龄        行为:吃草

青蛙、狗和兔子都有“吃”的动作 ——> 父类中的抽象方法 eat()

青蛙和狗有“游泳”的动作,但兔子没有 ——> 接口 swimming

【总结】抽象方法是全部子类的共性,接口是部分子类的共性(倾向于动作)。

【注】它们的共性是:方法都是抽象的。

(3)例子:该例建立在第8点抽象类之上,多了游泳接口:

注意:是在接口下写抽象方法,让需要的类去重写接口的抽象方法。

//父类
public abstract class Animals {
    String name;
    int age;

    public Animals() {
    }

    public Animals(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
    
    //抽象方法
    public abstract void eat();

    public void drink(){
        System.out.println("我会喝水");
    }
}


//定义接口
public interface Swimming {
    public abstract void Swim();  //抽象方法
}

//狗:吃(父类的eat()抽象方法)、游泳(swimming接口的抽象方法Swim)
public class Dog extends Animals implements Swimming{
    public Dog() {
    }

    public Dog(String name, int age) {
        super(name, age);
    }

    @Override
    public void eat() {
        System.out.println("狗吃骨头");
    }


    @Override
    public void Swim() {
        System.out.println("狗刨");
    }
}

//青蛙:吃(父类的eat()抽象方法)、游泳(swimming接口的抽象方法Swim)
public class frog extends Animals implements Swimming{
    public frog() {
    }

    public frog(String name, int age) {
        super(name, age);
    }

    @Override
    public void eat() {
        System.out.println("青蛙吃虫子");
    }

    @Override
    public void Swim() {
        System.out.println("蛙泳");
    }
}

//山羊:吃(父类的eat()抽象方法)
public class Sheep extends Animals{
    public Sheep() {
    }

    public Sheep(String name, int age) {
        super(name, age);
    }

    @Override
    public void eat() {
        System.out.println("山羊吃草");
    }
}

//测试类
public class Test {
    public static void main(String[] args) {
        Dog d = new Dog("小黄",5);
        System.out.println("狗:"+ d.getName() + "," + d.getAge());
        d.drink();
        d.eat();
        d.Swim();

        frog f = new frog("小绿",1);
        System.out.println("青蛙:"+ f.getName() + "," + f.getAge());
        f.drink();
        f.eat();
        f.Swim();

        Sheep s = new Sheep("小白",50);
        System.out.println("山羊:"+ s.getName() + "," + s.getAge());
        s.drink();
        s.eat();
    }
}

(4)7-1 定义接口(Biology、Animal)、类(Person)、子类(Pupil)

代码:

public interface Biology {
    public abstract void breathe();
}

public interface Animal {
    public abstract void eat();
    public abstract void sleep();
}

public class Person implements Animal,Biology{

    @Override
    public void eat() {
        System.out.println("我会按时吃饭");
    }

    @Override
    public void sleep() {
        System.out.println("早睡早起身体好");
    }

    @Override
    public void breathe() {
        System.out.println("我喜欢呼吸新鲜空气");
    }

    public void think(){
        System.out.println("我喜欢思考");
    }
}

public class Pupil extends Person{
    private  String school;

    public Pupil() {
    }

    public Pupil(String school) {
        this.school = school;
    }

    public String getSchool() {
        return school;
    }

    public void setSchool(String school) {
        this.school = school;
    }


}


public class Main {
    public static void main(String[] args) {
        Pupil zhangsan = new Pupil("福建理工大学");
        zhangsan.breathe();
        zhangsan.eat();
        zhangsan.sleep();
        zhangsan.think();
        System.out.println("我的学校是" + zhangsan.getSchool());

    }
}

运行结果:

 发现:若父类实现了接口,那么子类也会实现该接口。

(5)抽象类和接口的综合运用

【思路】

1.  运动员和教练属于人,所以创建父类Person(从中抽取共性:姓名,年龄);

2.  运动员又分为乒乓球运动员和篮球运动员,故将运动员作为父类;教练同理。

3. 说英语是部分类(乒乓球运动员和乒乓球教练)的动作,将其作为接口。

                                            Person(name,age)            SpeakEnglish接口

             Athlete(study())                                           Coach(teach())

   PingpongAth        BlankAth                           PingpongCoa        BlankCoa

 (学乒乓球,说英语)   (学篮球)                   (教乒乓球,说英语)        (教篮球)

好啦,现在请尝试自己写写代码叭~

//人
public class People {
    String name;
    int age;

    public People() {
    }

    public People(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

//说英语
public interface SpeakEnglish {
    public abstract void speak();
}

//运动员
public abstract class Athlete extends People{
    public abstract void study();
}

//教练
public abstract class Coach extends People{
    public abstract void teach();
}

//乒乓球运动员
public class PingpongAlth extends Athlete implements SpeakEnglish{

    @Override
    public void study() {
        System.out.println("乒乓球运动员学打乒乓球");
    }

    @Override
    public void speak() {
        System.out.println("乒乓球运动员要会说英语");
    }
}

//篮球运动员
public class BlankBallAlth extends Athlete{
    @Override
    public void study() {
        System.out.println("篮球运动员学打篮球");
    }
}

//乒乓球教练
public class PingpongCoa extends Coach implements SpeakEnglish{

    @Override
    public void teach() {
        System.out.println("乒乓球教练教打乒乓球");
    }


    @Override
    public void speak() {
        System.out.println("乒乓球教练要会说英语");
    }
}

//篮球教练
public class BlankBallCoa extends Coach{
    @Override
    public void teach() {
        System.out.println("篮球运动员教打篮球");
    }
}

//测试类
public class Test {
    public static void main(String[] args) {
        PingpongAlth pingpongAlth = new PingpongAlth();
        pingpongAlth.setName("乒乓球运动员");
        pingpongAlth.setAge(20);
        System.out.println(pingpongAlth.getName()+ "," + pingpongAlth.getAge());
        pingpongAlth.study();
        pingpongAlth.speak();

        PingpongCoa pingpongCoa = new PingpongCoa();
        pingpongCoa.setName("乒乓球教练");
        pingpongCoa.setAge(30);
        System.out.println(pingpongCoa.getName()+ "," + pingpongCoa.getAge());
        pingpongCoa.teach();
        pingpongCoa.speak();

        BlankBallAlth blankBallAlth = new BlankBallAlth();
        blankBallAlth.setName("篮球运动员");
        System.out.println(blankBallAlth.getName()+ "," + blankBallAlth.getAge());
        blankBallAlth.setAge(22);
        blankBallAlth.study();

        BlankBallCoa blankBallCoa = new BlankBallCoa();
        blankBallCoa.setName("篮球教练");
        blankBallCoa.setAge(28);
        System.out.println(blankBallCoa.getName()+ "," + blankBallCoa.getAge());
        blankBallCoa.teach();
    }
}

好啦,今天的分享就到这啦~ 希望对你有所帮助

前路漫漫,修行漫漫,蓦然回首,共勉前行⎛ -᷄ ᴥ -᷅ ⎞೯

ps:本文根据黑马程序员的视频学习,喜欢的小伙伴可以前往观看视频哦ꉂꉂ꒰•̤▿•̤*ૢ꒱

黑马程序员Java零基础视频教程_上部(Java入门,含斯坦福大学练习题+力扣算法题和大厂java面试题)_哔哩哔哩_bilibili 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值