JavaSE 基础(九) 多态和抽象类

目录

前言

一.多态

1. 多态的概念

2.  多态实现条件 

3. 重写

3.1 方法重写的规则:

3.2 重写和重载的区别

3.3  静态/动态绑定

4. 向上转移和向下转型          

4.1 向上转型

4.2 向下转型

5. 多态的优缺点 

优点

缺点:代码的运行效率降低。

 6. 避免在构造方法中调用重写的方法

二. 抽象类

1.抽象类的概念  

2.抽象类语法                                   

 3. 抽象类特性

4 抽象类的作用

后记


前言

Hi~ 你好! 

欢迎点击我的博客 : )       

这是我的学习总结,希望也能帮助到你

我的博客地址:hi~,我在这里~,欢迎关注哦,

三连不迷路,优质内容持续更新!

一.多态

polymorphism

1. 多态的概念

有多种形态:
同一件事情,发生在不同对象身上,就会产生不同的结果

2.  多态实现条件 

实现多态必须同时满足以下条件:

1.必须在继承体系下

2.子类必须对父类中的方法进行重写

3.通过父类的引用调用重写的方法

public class TestAnimal2 {
    public static void main(String[] args) {
        new Animal("老虎",16).eat();//直接调用父类的方法
        
        Animal animal1 = new Cat("小橘",4);
        animal1.eat();//通过父类的引用调用重写的方法


        Animal animal2 = new Dog("大黄",7);
        animal2.eat();//通过父类的引用调用重写的方法
    }
}
老虎在淦饭
小橘在吃鱼
大黄在吃骨头

多态的体现 : 在运行代码时,当传递不同类对象的时,会调用对应类中的方法

public class Animal {//父类
    String name;
    int age;

    public Animal(String name, int age) {//Animal的构造方法
        this.name = name;
        this.age = age;
    }

    void eat(){
        System.out.println(name+"在淦饭");
    }
}
public class Cat extends Animal {
    public Cat(String name,int age){//cat的构造方法,来自传递父类的参数
        super(name,age); //在构造子类前,要先构造完父类
    }
    @Override
    void eat() {//重写父类的eat方法
        System.out.println(name+"在吃鱼");
    }
}
public class Dog extends Animal{
    public Dog(String name, int age) {
        super(name, age);
    }
    @Override
    void eat() {
        System.out.println(name+"在吃骨头");
    }
}
public class TestAnimal {
    public static void eatTest(Animal animal){//传参进行参数转型
        //通过父类的引用调用重写的方法
        animal.eat();
    }

    // 编译器在编译代码时,并不知道要调用Dog 还是 Cat 中eat的方法
    // 等程序运行起来后,形参animal引用的具体对象确定后,才知道调用哪个方法
    // 此处的形参类型必须时父类类型才可以

    public static void main(String[] args) {
        Cat cat = new Cat("Tom",5);
        Dog dog = new Dog("旺财",6);
        new Animal("狐狸",3).eat();//直接调用父类的est方法
        eatTest(new Animal("企鹅",8));//通过eatTest方法
        eatTest(cat);//通过父类的引用调用子类重写的方法
        eatTest(dog);
    }
}
    狐狸在淦饭
    企鹅在淦饭
    Tom在吃鱼
    旺财在吃骨头

 编译器在编译代码时,并不知道要调用Dog 还是 Cat 中eat的方法 等程序运行起来后,

 形参animal引用的具体对象确定后,才知道调用哪个方法

3. 重写

    重写是子类对父类非静态、非 private修饰,非final 修饰,非构造方法等的实现过程
进行重新编写 , 返回值和形参都不能改变
    重写的好处在于子类可以根据需要,定义特定于自己的行为
3.1 方法重写的规则:
1.子类重写父类方法时,必须与父类方法原型一致:返回值类型 方法名 (参数列表) 要完全一致

2.被重写的方法返回值类型可以不同,但是必须是具有父子关系的 (协变类型)

3.子类的访问权限要 >=  父类的访问权限                 private<默认<protected<public 

如果父类方法被public修饰,则子类中重写该方法就不能声明为 protected

4.父类中static(静态方法属于类)private修饰的方法、final修饰的方法(密封方法)、构造方法都不能被重写
5.重写的方法, 可以使用 @Override 注解, 能帮住校验拼写

3.2 重写和重载的区别
区别重写(override)重载(overload)
参数列表  不能修改不行修改
返回值类型不能修改(除非继承关系)可以修改
访问;限定符不能做更严格的限制可以修改
3.3  静态/动态绑定

静态绑定: 也称为前期绑定(早绑定),即在编译时,根据用户所传递实参类型就确定了具体调用那个方法。典型代表函数重载

动态绑定:  也称为后期绑定 ( 晚绑定 ),即在编译时,不能确定方法的行为,需要等到程序运行时,才能够确定具体调用哪个类的方法     

动态绑定的前提:

1.向上转型
2.方法重写
3.通过父类引用,调用重写的方法.

4. 向上转移和向下转型          

4.1 向上转型
创建一个子类对象,将其当成父类对象来使用
父类类型 对象名 = new 子类类型()
Animal animal1 = new Cat("Tom",5);

 animal是父类类型,但可以引用一个子类对象,因为是从小范围向大范围的转换。

使用:1.直接赋值        2.方法传参        3. 方法返回

作返回值:返回任意子类对象:

 public static Animal askAnimal(String var){
        if("狗".equals(var)){
            return new Dog("哮天犬",3000);
        } else if ("猫".equals(var)) {
            return new Cat("狸花猫",3);
        }else {
            return null;
        }
    }

    // 编译器在编译代码时,并不知道要调用Dog 还是 Cat 中eat的方法
    // 等程序运行起来后,形参animal引用的具体对象确定后,才知道调用哪个方法
    // 此处的形参类型必须时父类类型才可以

    public static void main(String[] args) {
        Animal animal1 = new Cat("Tom",5);
        Animal animal2= new Dog("旺财",6);
        new Animal("狐狸",3).eat();//直接调用父类的est方法
        eatTest(new Animal("企鹅",8));//通过eatTest方法
        eatTest(animal1);//通过父类的引用调用子类重写的方法
        eatTest(animal2);
        System.out.println("============");
        Animal animal3 = askAnimal("狗");
        Animal animal4 = askAnimal("猫");
        animal3.eat();
        animal4.eat();
        System.out.println("=============");
        eatTest(animal3);
        eatTest(animal4);

    }
}
狐狸在淦饭
企鹅在淦饭
Tom在吃鱼
旺财在吃骨头
============
哮天犬在吃骨头
狸花猫在吃鱼
=============
哮天犬在吃骨头
狸花猫在吃鱼
向上转型
     优点:让代码实现更简单灵活
     缺点:  不能调用到子类特有的方法
4.2 向下转型
        将一个子类对象经过向上转型之后当成父类方法使用,无法调用子类的方法,但有时可能需要调用子类特有的方法,此时:将父类引用再还原为子类对象即可,即向下转换

        向下转型用的比较少,而且不安全,万一转换失败,运行时就会抛异常。Java中为了提高向下转型的安全性,引入了 instanceof ,如果该表达式为true,则可以安全转换。

  public static void main(String[] args) {
        Cat cat = new Cat("红猫",8);

        cat.eat();
        //向上转型
        Animal animal1 =new Dog("史努比",50);//子类的类型给到父类
        //不管给的是什么,一定是动物
       // animal1.notsleep()//无法调用子类特有的方法

        //向下转型
        Dog dog = (Dog) animal1;//父类的类型给到了子类
        dog.notSleep();
        //不安全,能骗过编译器
        if (animal1 instanceof Cat){//instanceof关键字判断animal1是不是Cat类型
            Cat cat1 = (Cat) animal1;//这样不会报错,如果是Cat类型, 进行sleep方法
            cat1.sleep();
        }
  //      Cat cat1 = (Cat) animal1;//ClassCastException类型转换异常
    //    cat1.sleep();//animal本身引用的是狗,强转成猫,但之前给的是狗,报错
        //Dog cannot be cast to Cat

        System.out.println("=============");
        //向上转型
        Animal animal = cat;
        animal.eat();
        cat.sleep();
        System.out.println("========");
        animal = dog;
        animal.eat();

    }

}

红猫在吃鱼
史努比不想睡觉
=============
红猫在吃鱼
红猫想睡觉
========
史努比在吃骨头

5. 多态的优缺点 

优点
1. 能够降低代码的 " 圈复杂度 ", 避免使用大量的 if - else
2. 可扩展能力更强
缺点:代码的运行效率降低
1. 属性没有多态性
    当父类和子类都有同名属性的时候,通过父类引用,只能引用父类自己的成员属性
2. 构造方法没有多态性

 6. 避免在构造方法中调用重写的方法

class A{
    public A(){
        func();
    }
    public void func(){
        System.out.println("A.func");
    }
}
class B extends A{
    private  int num = 1;

    @Override
    public void func() {
        System.out.println("B.func"+num);
    }
}
public class Test {
    public static void main(String[] args) {
        B b = new B();
    }
}
B.func0

当在父类的构造方法当中, 去调用父类和子类重写的方法的时候,此时会调用子类

        用尽量简单的方式使对象进入可工作状态, 尽量不要在构造器中调用方法 ( 如果这个方法被子类重写 , 就会触发动态绑定, 但是此时子类对象还没构造完成 ), 可能会出现一些隐藏的但是又极难发现的问题 .

二. 抽象类

1.抽象类的概念  

        所有的对象都是通过类来描绘的,但是反过来,并不是所有的类都是用来描绘对象的
如果 一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类

2.抽象类语法                                   

abstract class Shape2{//抽象类
   abstract public void draw();//抽象方法
    public void func(){
    }
}
        一个类如果被 abstract 修饰称为抽象类,抽象类中被 abstract 修饰的方法称为抽象方法,抽象方法不用给出具体的实现体。

                抽象类也是类,内部可以包含普通方法和属性,甚至构造方法

 3. 抽象类特性

        1. 抽象类不能直接实例化对象

Shape shape = new Shape();
// 编译出错
Error:(30, 23) java: Shape是抽象的; 无法实例化

        2. 抽象方法不能是 private

abstract class Shape {
    abstract private void draw();
}
// 编译出错
Error:(4, 27) java: 非法的修饰符组合: abstract和private

        3. 抽象方法不能被finalstatic修饰,因为抽象方法要被子类重写

public abstract class Shape {
    abstract final void methodA();
    abstract public static void methodB();
}
     编译报错:
    Error:(20, 25) java: 非法的修饰符组合: abstract和final
    Error:(21, 33) java: 非法的修饰符组合: abstract和static

        4. 抽象类必须被继承,并且继承后子类要重写父类中的抽象方法,否则子类也是抽象类, 必须要使用 abstract修饰

        5. 抽象类中不一定包含抽象方法,但是有抽象方法的类一定是抽象类

        6. 抽象类中可以有构造方法,供子类创建对象时,初始化父类的成员变量

4 抽象类的作用

        抽象类本身不能被实例化, 要想使用, 只能创建该抽象类的子类. 然后让子类重写抽象类中的抽象方法 

        使用抽象类相当于多了一重编译器的校验,如果误用成父类了, 使用普通类编译器是不会报错的. 但是父类是抽象类就会在实例化的时候提示错误, 尽早发现问题

后记

看到这里,希望能帮到你~

您的点赞 ,收藏 ,关注 是我创作的最大动力!

同时也欢迎在评论区进行交流,共同进步~       

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值