深入了解java中的多态

多态

向上转型

Animal animal = new Bird();

像这种形式,父类的引用指向一个子类的实,这种称之为向上转型

父类的引用只能访问自己的东西,不能访问子类特有的属性或者方法

一句话概括:父类引用 引用 子类对象


向上转型发生的时机:
共三种:

  1. 直接赋值
 Animal animal = new Bird();
  1. 方法传参
public static void func(Animal animal) {
    
    
	
}
public static void main(String[] args) {
    
    
	Dog dog = new Dog();
	func(dog);
}
  1. 方法返回值
public static Animal func() {
    
    
	Dog dog = new Dog();
	return dog;
}

向下转型

向下转型会不安全,最好不要用或者少用

Animal animal = new Dog();
Dog dog = (Dog)animal;	//需要强制类型转换
dog.wangwang();	//此时可以调用Dog特有的方法
Animal animal = new Dog();
//这里需要判断一下animal是不是Bird的一个实例,是的话才能向下转型
if(animal instanceof Bird) {
    
    
	Bird bird = (Bird)animal;
	bird.fly();	//此时可以访问Bird的特有方法
}

A instanceof B
A是否是B的实例
A之前是否引用了B这个对象

运行时绑定

class Animal {
    
    
    public String name;
    public int age;
    public void eat() {
    
    
        System.out.println("Animal::eat()");
    }
}
class Dog extends Animal1{
    
    

}
public class Test {
    
    
	public static void main(String[] args) {
    
    
		Animal animal = new Dog();
		animal.eat();
	}
}

此时运行会打印Animal::eat()

而我们给Dog重写一下eat方法

class Animal {
    
    
    public String name;
    public int age;
    public void eat() {
    
    
        System.out.println("Animal::eat()");
    }
}
class Dog extends Animal1{
    
    
	public void eat() {
    
    
		System.out.println("Dog::eat()");
	}
}
public class Test {
    
    
	public static void main(String[] args) {
    
    
		Animal animal = new Dog();
		animal.eat();
	}
}

此时就会打印Dog::eat()

这就是因为在编译时期发生了运行时绑定,也称作动态绑定


发生动态绑定时的条件

  1. 父类引用 引用子类对象
  2. 通过父类的引用 调用父类和子类的同名重写方法

此时就会发生动态绑定,这也时多态的前提

重写

这里说到了重写,那就又不得不回顾一下重载了。

重载(overload):[在同一个类当中]

  1. 方法名相同
  2. 参数列表不同
  3. 返回值不做要求

重写(override): [在继承关系中]
也叫做覆盖,覆写

  1. 方法名相同
  2. 参数列表相同
  3. 返回值相同

注意:

  1. 子类的访问权限 不能低于 父类的访问权限
  2. 要重写的方法一定不可以是static方法
  3. 要重写的方法一定不可以被final修饰

理解多态

先举个例子

class Shape {
    
    
    public void draw() {
    
    

    }
}

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

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

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

//===============================================================
public class TestDemo {
    
    

    public static void drawMap(Shape shape) {
    
    
        shape.draw();;
    }

    public static void main(String[] args) {
    
    
        Rect rect = new Rect();
        drawMap(rect);

        Cricle cricle = new Cricle();
        drawMap(cricle);

        Flower flower = new Flower();
        drawMap(flower);
    }

}

在这个代码中, 分割线上方的代码是 类的实现者 编写的, 分割线下方的代码是 类的调用者 编写的.

当类的调用者在编写 drawMap 这个方法的时候, 参数类型为 Shape (父类),此时在该方法内部并不知道, 也不关注当前的 shape 引用指向的是哪个类型(哪个子类)的实例. 此时 shape 这个引用调用 draw 方法可能会有多种不同的表现(和 shape 对应的实例相关), 这种行为就称为 多态

多态的好处

  1. 类调用者对类的使用成本进一步降低
  • 封装是让类的调用者不需要知道类的实现细节
  • 多态能让类的调用者连这个类的类型是什么都不必知道, 只需要知道这个对象具有某个方法即可
  1. 能够降低代码的 “圈复杂度”, 避免使用大量的 if - else
    基于刚才的代码,如果不基于多态,代码就会很长很复杂
	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();
            }
        }
    }

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

	public static void drawShapes() {
    
    
        // 我们创建了一个 Shape 对象的数组.
        Shape[] shapes = {
    
    new Cycle(), new Rect(), new Cycle(),
                new Rect(), new Flower()};
        for (Shape shape : shapes) {
    
    
            shape.draw();
        }
    }
  1. 可扩展能力更强
    如果要新增一种新的形状, 使用多态的方式代码改动成本也比较低
class Triangle extends Shape {
    
    
	@Override
	public void draw() {
    
    
		System.out.println("△");
	}
}

核心

多态的核心都是让调用者不必关注对象的具体类型

猜你喜欢

转载自blog.csdn.net/starry1441/article/details/113896343