首页 > 编程学习 > 多态的讲解

多态的讲解

发布时间:2022/11/15 4:46:46

hi,大家好,今天为大家带来多态的讲解

💚💚💚💚1.对于引用的理解

💚💚💚💚2.多态的定义

💚💚💚💚3.多态的实现条件

💚💚💚💚4.重写

💚💚💚💚5.向上转型

💚💚💚💚6.动态绑定

💚💚💚💚7.静态绑定

💚💚💚💚8.向下转型

💚💚💚💚9.多态优缺点

💚💚💚💚9.重写的小点

今天的知识比较多,也会有代码帮助理解

1.为了加深对引用的理解,用一个swap交换函数来帮助理解

class MyValue {
    public int value;

}



public class TestDemo {
    public static void swap(MyValue my1,MyValue my2){
        int tmp= my1.value;
        my1.value=my2.value;
        my2.value=tmp;
    }


    public static void main(String[] args) {
            MyValue myValue1=new MyValue();
        MyValue myValue2=new MyValue();
        myValue1.value  =20;
        myValue2.value=30;
        swap(myValue1,myValue2);
        System.out.println( myValue1.value);
        System.out.println(myValue2.value);
    }
}
运行结果如下

 这个代码实现了交换,下面来画图理解一下引用

 创建变量,在栈上开辟栈帧,new对象时在堆上

2  什么是多态呢???

多态就是多种形态,完成某个行为,最后的结果不尽相同。这个概念比较抽象,下面来举一个例子,相同的食材,给两个不同的人做,两个人做出来的味道是不一样的,比如做番茄炒蛋,有甜口的,还有咸口的,那么这就可以叫做多态,土豆可以做成炸土豆条,炸土豆片,这也是多态

3.多态的实现条件

实现多态需要三个条件

1.继承

2.重写

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

继承在上一篇已经介绍过了,我们现在直接说重写,说到重写,就得说一说重写的条件了

4.重写

1.方法名,返回值,参数列表必须完全一致,返回类型可一样也可不一样,但必须具有父子关系

2.重写又可以称作覆盖,是子类对父类的非静态,非构造,非fianl修饰,非private修饰的方法的重写

3.重写的方法访问修饰符的访问权限要大于等于父类的

4.父类被staticprivate修饰的方法、构造方法都不能被重写。

5.重写的时候,加上一个@Override注解,帮助我们检查重写的番薯方法是否有问题

下面来一段代码

6.被final 修饰的方法不能被重写,称作密封方法

class Animal{
    public String name;
    public  int age;
    public void eat(){
        System.out.println(name+"正在吃饭");
    }
}
class Dog extends Animal{
    public void wangwang(){
        System.out.println(name+"正在汪汪叫");
    }
    @Override
    public void eat(){
        System.out.println(name+"正在吃狗粮");
    }
}
class Bird extends Animal{
    public String wing;
    public void fly(){
        System.out.println(name+"正在飞");

    }
    public void eat(){
        System.out.println(name+"正在吃鸟粮");
    }
}




public class TestDemo2 {
    public static void main(String[] args) {
        Animal animal1=new Dog();
        animal1.name="贝贝";
        animal1.eat();
        Animal animal2=new Bird();
        animal2.name="喳喳";
        animal2.eat();

    }

}

在这段代码中我们可以看到子类重写了父类的eat方法,那么疑问就来了,我们看到引用是父类引用,那么运行结果应该是父类的name,但是父类并没有起名字,所以是null+正在吃饭,那么这就要提到向上转型和动态绑定了

 5.向上转型

在重写过程中,我们需要用到动态绑定,因为在第二点提到了是实现多态的第三点是,通过父类引用去调用重写的方法,所以要用到向上转型,向上转型就是从子类到父类,父类的引用指向子类的对象

 

 如蓝笔画出来的那个一样,就是向上转型,父类引用指向子类对象

6.动态绑定

什么叫做动态绑定

就是在代码运行的时候很智能的帮我们调用子类中重写的父类方法,在编译的时候它并不能识别出调用哪个方法,但是在运行的时候就可以了,就像上面的代码运行结果一样,new  的是谁,就会调用哪个子类的重写方法

7.静态绑定

与动态绑定相对应得就是静态绑定,静态绑定又叫做早绑定,一般用于重载中,在编译的时候就知道应该执行哪一个方法,当然,在多态中一般是不用得,但是也能用,但是它不安全,一般不敢用,下面来写举个例子

class Animal{
    public String name;
    public  int age;
    public void eat(){
        System.out.println(name+"正在吃饭");
    }
}
class Dog extends Animal{
    public void wangwang(){
        System.out.println(name+"正在汪汪叫");
    }
    @Override
    public void eat(){
        System.out.println(name+"正在吃狗粮");
    }
}
class Bird extends Animal{
    public String wing;
    public void fly(){
        System.out.println(name+"正在飞");

    }
   @Override
    public void eat(){
        System.out.println(name+"正在吃鸟粮");
    }
}
class Cat extends Animal{
    public void miaomiao(){
        System.out.println(name+"正在喵喵叫");
    }

    @Override
    public void eat() {
        System.out.println(name+"正在吃猫粮");
    }
}
public class TestDemo2 {
    public static void main(String[] args) {
        Animal animal=new Dog();
        Dog dog=(Dog)animal;
        dog.name="贝贝";
        dog.eat();
        dog.wangwang();
        System.out.println("=======");
        Cat  cat=(Cat)animal;
        cat.eat();
        cat.miaomiao();

    }
    public static void mai1(String[] args) {
        Animal animal1=new Dog();
        animal1.name="贝贝";
        animal1.eat();
        Animal animal2=new Bird();
        animal2.name="喳喳";
        animal2.eat();
        Animal  animal3=new Cat();
        animal3.name="小花";
        animal3.eat();


    }


}

这个就是向下转型,很不安全,如下图,子类引用指向父类对象,报错了 

 但是有方法防止报错,使用库提供的instanceof函数,,现在对这个代码进行改良

 

这句话的意思是判断子类对象指向的引用是否包含Cat类,这样就不会报错了,但我们还是不提倡这样写

9.多态的优缺点

优点:能够降低代码的 "圈复杂度", 避免使用大量的 if - else

多态缺点

父类的属性不能重写,父类的构造方法不能重写

当父类和子类都有同名属性的时候,通过父类引用,只能引用父类自己的成员属性

10.重写的小点

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

class B {
    public B() {
        // do nothing
        func();
}
    public void func() {
        System.out.println("B.func()");
    }
}
class D extends B {
    private int num = 1;
    public D () {
        super();
    }
    @Override
    public void func() {
        //System.out.println("fafdadssa!!!!!");
        System.out.println("D.func() " + num+" 因为父类此时还没有走完!");
    }
}
public class Test4 {
    public static void main(String[] args) {
        D d = new D();
    }
}

 

当大家看到这段代码的时候大家觉得结果是几,一定有人以为是1,大漏特漏!!!

运行他看看

看,结果是0,为啥呢,因为父类都没有构造完成,所以不能到子类那一步,不要写这种代码,有坑,大家避雷!!!

 下面写一个图形类的代码加深大家对多态的理解

class Shape {

    public void draw() {
        System.out.println("画图形!");
    }
}
class Rect extends Shape {
    @Override
    public void draw() {
        System.out.println("画矩形!");
    }
}
class Cycle extends Shape {
    @Override
    public void draw() {
        System.out.println("画圆!");
    }
}
class Flower extends Shape {
    @Override
    public void draw() {
        System.out.println("❀!");
    }
}

public class Test3 {
    public static void drawMap(Shape shape) {
        shape.draw();
    }
    public static void drawMap() {
        Rect rect = new Rect();
        Cycle cycle = new Cycle();
        Flower flower = new Flower();

        Shape[] shapes = {cycle,rect,cycle,rect,flower};
        //int[] array = {1,2,3,4};
        for(Shape shape : shapes) {
            shape.draw();
        }
    }

  /*  public static void drawMap3() {
        Shape rect = new Rect();
        Shape cycle = new Cycle();
        Shape flower = new Flower();

        Shape[] shapes = {cycle,rect,cycle,rect,flower};//这里完成了向上转型!!!,和上面的写法其实是一样的
        //int[] array = {1,2,3,4};
        for(Shape shape : shapes) {//for  each 遍历
            shape.draw();
        }
    }
*/

    public static void main(String[] args) {
        drawMap();
        //drawMap2() ;
        /*Rect rect = new Rect();
        Cycle cycle = new Cycle();

        drawMap(rect);
        drawMap(cycle);
        drawMap(new Flower());*/
    }
}

 

 向上转型有三种方式

1.直接赋值

2.方法传参

3.方法返回值

显然上述代码用的是方法传参

好了,今天的讲解就到此结束,下一期为大家带来抽象类和接口!!!

886!!!🎉🎉🎉👀👀👀

备注:抽象类小部分知识在11.13日笔记

Copyright © 2010-2022 dgrt.cn 版权所有 |关于我们| 联系方式