Java三大特性之多态

news/2024/7/3 13:12:07 标签: 多态, java, 面向对象编程,

在了解多态之前我们需要先了解一些相关的知识:向上转型,运行时绑定。

多态

  • 向上转型
    • 什么是向上转型?
    • 什么时候发生向上转型?
  • 向下转型
  • 重写(覆盖、覆写)
    • 注意事项
    • @Override
    • 在构造方法中调用重写方法的(一个坑)
  • 多态
  • 总结

向上转型

什么是向上转型?

向上转型:通俗点来说就引用引用“”对象(我是这样理解的:因为是把子给父,把小给大,所以是向上层转变呢)
注意:向上转型后,父只能访问自己的属性和方法,而不能访问子特有的属性字段和方法。
举例

java">class Animal{
    public  String name;
    private int age;
    public Animal(){
    }
    public Animal(String name,int age){
        this.name=name;
        this.age=age;
    }
    public void eat(){
        System.out.println("动物吃");
    }
}
class Dog extends Animal {
    public  int leg;
    public Dog(){
    }
    public Dog(String name,int age)
    {
        super(name,age);
    }
    public void wangwang(){
        System.out.println("汪汪叫!");
    }
}
public class Test{
    public static void main(String[] args) {
        Dog dog=new Dog();
        dog.wangwang();//可以正常访问
        Animal animal=new Dog();//父引用引用子对象(发生了向上转型)
        animal.name="dsda";//可以正常访问
        animal.leg=4;//error 报错(leg为Dog自己特有的属性)
        animal.wangwang();//error 报错(wangwang为Dog自己特有的方法)

    }
}

什么时候发生向上转型?

  • 直接赋值
java"> Animal animal=new Dog();
  • 方法传参
java"> public static void func(Animal animal){
        System.out.println("传参发生了向上转型");
    }

 func(new Dog());
  • 方法返回值
java">public static Animal func1(){
        return new Dog();
    }
 Animal animal2=func1();

向下转型

向下转型:就是将对象 引用 对象(但不安全,我们一般不使用)
例如下面的代码,并不是所有的动物都会飞。如果强行使用的话需要加上instanceof关键字,否则会报型转换的错误。

java">public class Test {
    public static void main(String[] args) {
        Animal animal1 = new Animal();
        Bird bird = (Bird) animal1;
        bird.fly();
         /* if (animal1 instanceof Bird) {
            Bird bird = (Bird) animal1;
            bird.fly();
        }*/
    }
}
error:ClassCastException: Animal cannot be cast to Bird

在这里插入图片描述

#运行时绑定(动态绑定)

当子和父中出现同名方法的时候, 再去调用会出现什么情况呢?
对前面的代码稍加修改, 给 Dog 也加上同名的 eat 方法, 并且在两个 eat 加以区分。

java">class Animal{
    public  String name;
    private int age;
    public Animal(){
    }
    public Animal(String name,int age){
        this.name=name;
        this.age=age;
    }
    public void eat(){
        System.out.println("动物吃");
    }
}
class Dog extends Animal {
    public  int leg;
    public Dog(){
        //super();
        //默认构造Animal的无参构造super()可以省略
    }
    public Dog(String name,int age)
    {
        //显示调用有参构造
        super(name,age);
    }
    public void eat(){
        System.out.println("狗吃");
    }
    public void wangwang(){
        System.out.println("汪汪叫!");
    }
}

public class Test{
    public static void main(String[] args) {
        Animal animal1=new Dog();
        Animal animal2=new Animal();
        animal1.eat();
        animal2.eat();
    }
}
运行结果为
狗吃
动物吃

此时, 我们发现:
animal1 和 animal2 虽然都是 Animal 型的引用, 但是animal1 指向Dog 型的实例. animal2 指向 Animal 型的实例, 针对 animal1 和 animal2 分别调用 eat 方法, 发现animal1.eat() 实际调用了子的方法.而 animal2.eat() 实际调用了父的方法,
因此, 在 Java 中, 调用某个的方法, 究竟执行了哪段代码 (是父方法的代码还是子方法的代码) , 要看究竟这个引用指向的是父对象还是子对象. 这个过程是程序运行时决定的(而不是编译期), 因此称为 动态绑定.我们可以看一下编译的代码,发现都是Animal的eat,所以说明是在运行时绑定的。
在这里插入图片描述

重写(覆盖、覆写)

针对刚才的eat方法来说:子实现父的同名方法,并且参数的型和个数完全相同,这种情况称为重写(覆盖、覆写)

注意事项

  • 重写重载完全不一样,不要混淆
  • 普通方法可以重写,static修饰的静态方法不能重写
  • 重写中子的方法的访问权限大于的访问权限(public可以相同
  • 重写的方法返回值不一定和父的方法相同(但建议写成相同的,不同时子和父的返回值要构成继承关系这种称为协同

@Override

  • 针对重写的方法, 可以使用 @Override 注解来显式指定.有了这个注解能帮我们进行一些合法性校验. 例如不小心将方法名字拼写错了 (比如写成 aet), 那么此时编译器就会发现父中没有 aet 方法, 就会编译报错, 提示无法构成重写.
  • 我们推荐在代码中进行重写方法时显式加上 @Override 注解.
java">class Bird extends Animal { 
@Override
private void eat(String food)
  {
  }
}

在构造方法中调用重写方法的(一个坑)

来看下面一段代码

java">class B {
    public B() {
       func();
    }
    public void func() {
        System.out.println("B.func()");
    }
}
class D extends B {
    private int num = 1;
    @Override
    public void func() {
        System.out.println("D.func() " + num);
    }
}
public class Test {
    public static void main(String[] args) {
        B b = new D();
    }
}
输出结果为 
D:func 0

为什么会这样呢,我们来分析一下

  • 构造 D 对象的同时, 会调用 B 的构造方法.
  • B 的构造方法中调用了 func 方法, 此时会触发动态绑定, 会调用到 D 中的 func
  • 此时 D 对象自身还没有构造, 此时 num 处在未初始化的状态, 值为 0.

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

多态

多态的含义

通俗点来说:多态就是同一个父引用可以引用不同的子对象,可以调用一个同名的方法,而产生不同的行为多态是一种思想)

举例

理解了向上转型、方法重写和运行时绑定之后我们来看看多态,我们用一段代码来理解。

java">//每一个class文件单独创建一个class
public class Shape {
    public void draw(){
        System.out.println("什么都不干");
    }
}

public class Circle extends Shape {
    @Override
    public void draw() {
        System.out.println("●");
    }
}

public class Rect extends Shape {
    @Override
    public  void draw(){
        System.out.println("♦");
    }
}
public class FLower extends Shape{
}

/我是分割线//

// Test.java
public class Test {
    public static void drawMap(Shape shape){
        shape.draw();
    }
    public static void main(String[] args) {
        Shape shape=new Shape();
        Rect rect=new Rect();
        Circle circle=new Circle();
        FLower flower=new FLower();
        drawMap(shape);
        drawMap(rect);
        drawMap(circle);
        drawMap(flower);
    }
}

运行结果:
在这里插入图片描述

  • 这段代码子Circle和Rect分别重写了父draw方法,在用方法传参时(drawMap)发生了向上转型,然后父引用(shape)分别前后引用了circle和rect,此时在调用draw时便会调用相应中重写的draw,而Flow虽然继承了Shape但没有重写其draw方法,所以调用的还是其父Shape的draw方法。
  • 在这个代码中, 分割线上方的代码是 的实现者 编写的, 分割线下方的代码是 的调用者 编写的.当的调用者在编写 drawMap 这个方法的时候, 参数型为 Shape (父), 此时在该方法内部并不知道, 也不关注当前的 shape 引用指向的是哪个型(哪个子)的实例. 此时 shape 这个引用调用 draw 方法可能会有**多种不同的表现(**和 shape 对应的实例相关rect或circle), 这种行为就称为 多态.

多态的好处

  1. 调用者对的使用成本降低多态可以让的调用者连这个型都不必知道是什么,只需要知道这个对象有某个方法即可)
  2. 能都降低代码的“圈复杂度”,避免大量的使用if-else
    如果我们打印的不是一个形状而是多个的话,如果不基于多态的话,代码如下
java">public static void drawShapes() { 
    Rect rect = new Rect();
    Cycle cycle = new Cycle(); 
    Flower flower = new Flower();
    String[] shapes = {"cycle", "rect", "cycle", "rect", "flower"};
        for (String shape : shapes)
        {
            if (shape.equals("cycle")) 
            { 
                cycle.draw();
            } 
            else if (shape.equals("rect")) 
            { 
                rect.draw();
            } 
            else if (shape.equals("flower")) 
            { 
                flower.draw();
            }
        }
} 

而使用多态的话便会简洁许多

java">public static void drawShapes() {
// 我们创建了一个 Shape 对象的数组.
        Shape[] shapes = {new Cycle(), new Rect(), new Cycle(),new Rect(), new Flower()};
       for(Shape shape:shapes)
        {
            shape.draw();
        }
}

圈复杂度是一种描述一段代码复杂程度的方式. 一段代码如果平铺直叙, 那么就比较简单容易理解. 而如果有很多的条件分支或者循环语句, 就认为理解起来更复杂.因此我们可以简单粗暴的计算一段代码中条件语句循环语句出现的个数, 这个个数就称为 “圈复杂度”. 如果一个方法的圈复杂度太高, 就需要考虑重构.

3. 可扩展性强
如果要新增一种新的形状,使用多态的方式代码改动的成本低。(比如新增一个画三角形的方法)

java">class Triangle extends Shape { @Override
public void draw() { 
    System.out.println("△");
   }
}

总结

多态面向对象程序设计中比较难理解的部分. 多态通常配合抽象、接口、继承使用,我们需要加深理解才能熟练使用。


http://www.niftyadmin.cn/n/1322758.html

相关文章

Java抽象类、接口知识总结

抽象类、接口抽象类什么是抽象类语法规则注意事项接口什么是接口语法规则扩展(extends) vs 实现(implements)注意事项接口的实例(常见的接口)CompareableCompareatorCloneable(实现深拷贝)总结抽象类 什么是抽象类 在之前多态的…

基于串口wifi:ESP8266的空中鼠标TCP/IP的java程序设计

文章目录前言一、ESP8266空中鼠标是什么?二、使用步骤1.运行代码前言 随着ESP8266的广泛使用,物联网方面很多都采用的ESP8266作为无线设备开发首选。 一、ESP8266空中鼠标是什么? 这次我用串口8266wifi模块设计的控制电脑鼠标的空中鼠标安卓…

ESP8266作为客户端连上网络调试助手服务器

文章目录前言一、说明二、使用步骤1.测试2.读入数据总结前言 daodanjishui物联网核心原创技术 一、说明 在上一期开源代码分享:基于串口wifi:ESP8266的空中鼠标TCP/IP的java程序设计 的基础上,现在用硬件来实现。 二、使用步骤 1.测试 使…

Java中异常知识点总结

异常什么是异常异常体系Throwable体系:Throwable中的常用方法:异常的分类编译时期异常运行时异常异常的用法捕获异常基本语法举例异常处理的流程关于 finally 的注意事项抛出异常异常说明自定义异常分类举例什么是异常 异常,就是不正常的意思…

Java中String知识总结

StringString的常见创建方式字符串比较比较equals比较字符常量池字符串不可变字符、字节与字符串字符与字符串字节与字符串字符串常见的操作字符串比较字符串查找字符串替换字符串拆分字符串截取其他常用的方法String、StringBuffer 和 StringBuilderString、StringBuffer 和 S…

ESP8266作为客户端上传DHT11温湿度给私人java服务器

系列文章目录 第一章ESP8266的java软件仿真测试 第二章ESP8266硬件与软件测试 第三章ESP8266客户端与Java后台服务器联调 文章目录系列文章目录前言一、这次要做的是什么?二、使用步骤1.下载源码2.读入DHT11数据总结前言 daodanjishui物联网核心原创技术之ESP8266…

Java泛型知识点总结

泛型的引入Generic泛型的引入泛型泛型类的定义泛型背后作用时期和背后的简单原理泛型的边界泛型类的使用总结泛型的引入 问题:我们之前实现过的顺序表,只能保存 int 类型的元素,如果现在需要保存 指向 Person 类型对象的引用的顺序表&#x…

基于51单片机的光照强度测量与调节的照明系统

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录概要一、51单片机照明系统是什么?二、使用步骤1.加载代码2.读入数据总结概要 51单片机用用IO口模拟I2C总线驱动GY-30 光照强度模块测量周围的光照强度&a…