Java多态详解(2)

简介: Java多态详解(2)

向上转型和向下转型

向上转型

定义:实际就是创建一个子类对象,将其当作父类对象来使用

语法格式:父类类型 对象名 = new 子类类型()

Animal animal = new Cat("元宝", 2);

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

特点:

  1. 编译时多态性: 父类引用变量可以引用子类对象,编译器会在编译时检查引用变量的类型是否与对象的类型兼容。
  2. 运行时多态性: 在运行时,根据引用变量所指向的实际对象类型来调用对应的方法,实现方法的多态性。
  3. 限制方法访问: 向上转型后,只能调用父类中声明的方法,而不能直接调用子类新增的方法。

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

举例一:直接赋值:

class Animal {
    void makeSound() {
        System.out.println("Animal makes a sound");
    }
}
 
class Dog extends Animal {
    void makeSound() {
        System.out.println("Dog barks");
    }
}
 
public class Main {
    public static void main(String[] args) {
        Animal animal = new Dog(); // 直接赋值
        animal.makeSound(); // 调用的是 Dog 类的方法
    }
}

举例二:方法传参

class Shape {
    void draw() {
        System.out.println("draw a shape");
    }
}
 
class Circle extends Shape {
    @Override
    void draw() {
        System.out.println("draw a circle");
    }
}
 
class Triangle extends Shape {
    @Override
    void draw() {
        System.out.println("draw a triangle");
    }
}
public class Test {
    public static void shapeDrawing(Shape s) {
        s.draw();
    }
 
    public static void main(String[] args) {
        Shape s1 = new Circle();
        Shape s2 = new Triangle();
 
        shapeDrawing(s1);
        shapeDrawing(s2);//方法传参,传递不同子类对象
    }
}

举例三:做返回值

class Vehicle {
    String getType() {
        return "Vehicle";
    }
}
 
class Car extends Vehicle {
    @Override
    String getType() {
        return "Car";
    }
}
 
class Bike extends Vehicle {
    @Override
    String getType() {
        return "Bike";
    }
}
 
public class Test {
    public static Vehicle getType(String type){
        if(type.equals("car")){
            return new Car();
        } else if(type.equals("bike")) {
            return new Bike();
        } else {
            return new Vehicle();
        }
    }
 
    public static void main(String[] args) {
        Vehicle v1 = getType("car");
        Vehicle v2 = getType("bike");
        Vehicle v3 = getType("plane");
 
        System.out.println(v1.getType());
        System.out.println(v2.getType());//方法返回,返回的实际上可能是子类对象
        System.out.println(v3.getType());
    }
}

向上转型的优点:让代码实现更加简单灵活

向上转型的缺点:不能调用到子类特有的方法

向下转型

将一个子类对象经过向上转型之后当作父类方法使用,在无法调用子类的方法,但有时候可能需要调用子类特有的方法,此时:将父类引用还原为子类对象即可,即向下转换。

class Animal1 {
    void makeSound() {
        System.out.println("Animal makes a sound");
    }
}
 
class Dog1 extends Animal1 {
    @Override
    void makeSound() {
        System.out.println("Dog barks");
    }
 
    void fetch() {
        System.out.println("Dog fetch the ball");
    }
}
 
public class Test1 {
    public static void main(String[] args) {
        Animal1 animal1 = new Dog1();//向上转型
 
        //向下转型
        if(animal1 instanceof Dog1) {
            Dog1 dog = (Dog1)animal1;//实例类型检查和向下转型
            dog.makeSound();//调用子类的方法
            dog.fetch();//调用子类特有的方法
        }
    }
}

向下转型用的比较少,而且不安全,万一转换失败,运行时就会抛出异常。Java中为了提高向下转型的安全性,引入了instanceof(用于检查一个对象是否是指定类或其子类的实例),如果表达式的结果为true,则可以安全转换。

多态的优缺点

优点

假设有如下代码:

class Shape {
    //属性。。。
    public void draw() {
        System.out.println("画图形");
    }
}
 
class Rect extends Shape {
    @Override
    public void draw() {
        System.out.println("⎕");
    }
}
 
class Circle extends Shape {
    @Override
    public void draw() {
        System.out.println("◉");
    }
}
 
class Star extends Shape {
    @Override
    public void draw() {
        System.out.println("★");
    }
}

1.能够降低代码的“圈复杂度”,避免使用大量的if-else

圈复杂度:一种描述一段代码复杂程度的方式。一段代码如果平铺直叙,那么就比较简单容易理解,而如果有很多条件分支或者循环语句,就认为了解起来比较复杂。

因此我们可以简单粗暴的计算一段代码中条件语句和循环语句出现的个数,这个个数就称为“圈复杂度”,如果一个方法的圈复杂度太高,就需要考虑重构

不同公司对于圈复杂度的规范不一样,但一般不会超过10

例如我们现在需要的不只是打印一个形状了,而是多个形状,如果不基于多态,实现代码如下:

public static void drawShapes() {
    Rect rect = new Rect();
    Circle circle = new Circle();
    Star star = new Star();
    String[] shapes = {"circle", "rect", "circle", "rect", "star"};
    for(String shape : shapes) {
        if(shape.equals("circle")){
            circle.draw();
        } else if(shape.equals("rect")) {
            rect.draw();
        } else if(shape.equals("star")) {
            star.draw()
        }
    }
 
}

如果使用多态,则不必写这么多的if-else分支语句,代码更简单。

Shape[] shapes = {new Circle(), new Rect(), new Circle(), new Rect(), new Star()};
for(Shape shape : shapes) {
    shape.draw();
}

2.可扩展能力更强

如果要新增一种形状,使用多态的方式代码改动成本也比较低。

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

对于类的调用者(主函数),只要创建一个新类的实例就可以了,改动成本很低。

而对于不用多态的情况,就要把drawShapes中的if-else进行一定修改,改动成本更高

缺点

代码运行效率降低

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

2.构造方法没有多态性

相关文章
|
5天前
|
Java
Java中的继承和多态是什么?请举例说明。
Java中,继承让子类继承父类属性和方法,如`class Child extends Parent`,子类可重写父类方法。多态允许父类引用指向子类对象,如`Animal a = new Dog()`,调用`a.makeSound()`会根据实际对象类型动态绑定相应实现,增强了代码灵活性和可扩展性。
14 0
|
5天前
|
设计模式 Java
Java基础—笔记—多态、final、抽象类、接口篇
该文介绍了编程中的多态、final和抽象类、接口相关概念。多态允许子类重写父类方法,通过父类引用调用子类方法,实现解耦和提高代码灵活性,但也可能导致无法使用子类特有功能,需通过强制类型转换解决。final用于修饰不可变的类、方法或变量,防止继承、重写和多次赋值。抽象类是一种包含抽象方法的类,用于强制子类重写特定方法,实现多态,适用于模板方法设计模式,解决代码重复问题。
22 0
|
5天前
|
搜索推荐 Java
Java的面向对象特性主要包括封装、继承和多态
【4月更文挑战第5天】Java的面向对象特性主要包括封装、继承和多态
21 3
|
5天前
|
Java
【Java】一个简单的接口例子(帮助理解接口+多态)
【Java】一个简单的接口例子(帮助理解接口+多态)
20 0
|
5天前
|
Java
java多态详解
java多态详解
29 0
|
5天前
|
Java 程序员 编译器
Java继承与多态知识点详解
本文主要讲解的是Java中继承与多态的知识点
C4.
|
5天前
|
Java
Java多态
Java多态
C4.
15 0
|
5天前
|
安全 Java 数据库连接
【Java每日一题】——第四十三题:编程用多态实现打印机.。分为黑白打印机和彩色打印机,不同类型的打印机打印效果不同。
【Java每日一题】——第四十三题:编程用多态实现打印机.。分为黑白打印机和彩色打印机,不同类型的打印机打印效果不同。
60 0
|
5天前
|
Java 编译器
【Java开发指南 | 第二十六篇】Java多态
【Java开发指南 | 第二十六篇】Java多态
15 1
|
5天前
|
Java
【JAVA基础篇教学】第五篇:Java面向对象编程:类、对象、继承、多态
【JAVA基础篇教学】第五篇:Java面向对象编程:类、对象、继承、多态