多态

多态

多态首先是建立在继承的基础上的,先有继承才能有多态。多态是指不同的子类在继承父类后分别都重写覆盖了父类的方法,即父类同一个方法,在继承的子类中表现出不同的形式。多态成立的另一个条件是在创建子类时候必须使用父类new子类的方式。

同一操作作用于不同的对象,可以产生不同的效果。这就是多态。

class Shape {
	public void draw() {
		// 啥都不用干
	}
}
class Cycle extends Shape {
	@Override
	public void draw() {
		 System.out.println("○");
	}
}
class Rect extends Shape {
	@Override
	public void draw() {
		System.out.println("□");
	}
}
class Flower extends Shape {
	@Override
	public void draw() {
		System.out.println("♣");
	}
}
/我是分割线//
// Test.java
public class Test {
	public static void main(String[] args) {
		Shape shape1 = new Flower();
		Shape shape2 = new Cycle();
		Shape shape3 = new Rect();
		drawMap(shape1);
		drawMap(shape2);
		drawMap(shape3);
	}
	// 打印单个图形
	public static void drawShape(Shape shape) {
		shape.draw();
	}
}

向上转型

上面代码中的

	Shape shape1 = new Flower();
	Shape shape2 = new Cycle();
	Shape shape3 = new Rect();

就是一个向上转型的例子

向上转型:父类引用指向子类的实例

向上转型发生的时机:
直接赋值
方法传参
方法返回

直接赋值

Shape shape1 = new Flower();

这个就是直接赋值

方法传参

public class Test {
	public static void main(String[] args) {
		Cycle cycle= new Cycle();
		drawMap(cycle);
	}
	public static void drawMap(Shape shape) {
		shape.draw();
	}
}

方法返回

public class Test {
	public static void main(String[] args) {
		Shape shape= drawMyShape();
	}
	public static Shape drawMyShape() {
		Cycle cycle= new Cycle();
		return cycle;
	}
}

动态绑定

调用某个类的方法, 究竟执行了哪段代码 (是父类方法的代码还是子类方法的代码) , 要看究竟这个引用指向的是父类对象还是子类对象. 这个过程是程序运行时决定的(而不是编译期), 因此称为动态绑定。
上面所示例的代码在调用方法时调用的是子类的方法。

重写

子类实现父类的同名方法, 并且参数的类型和个数完全相同, 这种情况称为 覆写/重写/覆盖(Override).

关于重写的注意事项

  1. 重写和重载完全不一样. 不要混淆
  2. 普通方法可以重写, static 修饰的静态方法不能重写.
  3. 重写中子类的方法的访问权限不能低于父类的方法访问权限.
  4. 重写的方法返回值类型不一定和父类的方法相同(但是建议最好写成相同, 特殊情况除外).
  5. 针对重写的方法, 可以使用 @Override 注解来显式指定.有了这个注解能帮我们进行一些合法性校验. 例如不小心将方法名字拼写错了 (比如写成 drow), 那么此时编译器就会发
    现父类中没有 drow 方法, 就会编译报错, 提示无法构成重写

重写和重载的区别

在这里插入图片描述

向下转型

// Animal.java
public class Animal {
	protected String name;
	public Animal(String name) {
		this.name = name;
	}
	public void eat(String food) {
		System.out.println("我是一只小动物");
		System.out.println(this.name + "正在吃" + food);
	}
}
// Bird.java
public class Bird extends Animal {
	public Bird(String name) {
		super(name);
	}
	public void eat(String food) {
		System.out.println("我是一只小鸟");
		System.out.println(this.name + "正在吃" + food);
	}
	public void fly() {
		System.out.println(this.name + "正在飞");
	}
}

首先发生向上转型,将父类引用指向子类对象,在判断向下转型时这个子类对象和要转型的对象是否是一个实例,若是,则将父类引用强制转换为这个子类对象。

Animal animal = new Cat("小猫");
if (animal instanceof Bird) {
Bird bird = (Bird)animal;
bird.fly();
}

先判定一下看看 animal 本质上是不是一个 Bird 实例, 再来转换
instanceof 可以判定一个引用是否是某个类的实例. 如果是, 则返回 true. 这时再进行向下转型就比较安全

多态的优点

  1. 类调用者对类的使用成本进一步降低.
    封装是让类的调用者不需要知道类的实现细节.
    多态能让类的调用者连这个类的类型是什么都不必知道, 只需要知道这个对象具有某个方法即可.
    因此, 多态可以理解成是封装的更进一步, 让类调用者对类的使用成本进一步降低.
    这也贴合了 <<代码大全>> 中关于 “管理代码复杂程度” 的初衷.
  2. 能够降低代码的 “圈复杂度”, 避免使用大量的 if - else
  3. 可扩展能力更强.
    如果要新增一种新的形状, 使用多态的方式代码改动成本也比较低

总结

对于Shape这个例子来说:发生继承,子类重写父类的方法,当发生向上转型时,调用的是子类对象重写父类对象的方法(此时发生了动态绑定)。
对于Animal这个例子来说:发生继承,当发生向下转型时,调用的是子类对象中的方法,不管父类中有没有这个方法。

如果抛开 Java, 多态其实是一个更广泛的概念, 和 “继承” 这样的语法并没有必然的联系.
C++ 中的 “动态多态” 和 Java 的多态类似. 但是 C++ 还有一种 “静态多态”(模板), 就和继承体系没有关系了.
Python 中的多态体现的是 “鸭子类型”, 也和继承体系没有关系.
Go 语言中没有 “继承” 这样的概念, 同样也能表示多态.
无论是哪种编程语言, 多态的核心都是让调用者不必关注对象的具体类型. 这是降低用户使用成本的一种重要方式.

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值