继承与多态

来源:互联网 发布:淘宝店铺企业认证 编辑:程序博客网 时间:2024/06/01 17:55

继承与多态

多态与is-a

在Java中。子类只能继承一个父类,子类与父类之间会有is-a(是一种)的关系。要理解多态要知道自己操作的是哪一种东西。

如下一种继承关系:

image

对象种类:
第一种:

SwordsMan swordsMan = new SwordsMan();Magician magician = new Magician();

第二种:

Role role1 = new SwordsMan();Role role2 = new Magician();

第三种:

SwordsMan swordsMan = new Role();Magician magician = new Role();

要知道以上程序片段为何可以通过编译,为何无法通过编
译,就是将自己当作编译程序,检查语法的逻辑是否正确,方式是从=号右边往左读:右边
是不是一种左边呢(右边类是不是左边类的子类)?

        role si a →Role role = new SwordsMan();

从右往左读!!
SwordsMan 是一种Role所以正确。同理判断第二种第三种方式。

Role不一定是一种SwordsMan 编译失败!

  • 编译成功,执行成功
SwordsMan swordsMan = new SwordsMan();Role role = swordsMan; // SwordsMan 是一种Role
  • 编译失败
SwordsMan swordsMan = new SwordsMan();Role role = swordsMan; // SwordsMan 是一种Role,这行通过编译SwordsMan swordsMan = role; // Role 不一定是一种SwordsMan,编译失败
  • 编译成功,执行成功
SwordsMan swordsMan = new SwordsMan();Role role = swordsMan; // SwordsMan 是一种Role,这行通过编译// 你告诉编译程序要让Role 扮演SwordsMan,以下这行通过编译SwordsMan swordsMan = (SwordsMan) role; // role 参考SwordsMan 实例,执行成功
  • 编译成功,执行报错ClassCastException:
SwordsMan swordsMan = new SwordsMan();Role role = swordsMan; // SwordsMan 是一种Role,这行通过编译// 你告诉编译程序要让Role 扮演Magician,以下这行通过编译Magician magician = (Magician) role; // role 参考Magician 实例,执行失败

多态

设定两个类都有血量输出:想输入血量代码如下

public static void showBlood(SwordsMan swordsMan) {System.out.printf("%s 血量 %d%n",swordsMan.getName(), swordsMan.getBlood());}public static void showBlood(Magician magician) {System.out.printf("%s 血量 %d%n",magician.getName(), magician.getBlood());}

分别为SwordsMan 与Magician 设计showBlood()同名方法,这是重载方法的运用,如此
就可以如下调用:
···
showBlood(swordsMan); // swordsMan 是SwordsMan 类型
showBlood(magician); // magician 是Magician 类型
···

问题:目前只有SwordsMan 与Magician 两个角色,如果有100
个角色呢?重载出100 个方法?这种方式显然不可能。

解决办法:

package cc.openhome;public class RPG {public static void showBlood(Role role) {System.out.printf("%s 血量 %d%n",role.getName(), role.getBlood());}public static void main(String[] args) {SwordsMan swordsMan = new SwordsMan();swordsMan.setName("Justin");swordsMan.setLevel(1);swordsMan.setBlood(200);Magician magician = new Magician();magician.setName("Monica");magician.setLevel(1);magician.setBlood(100);showBlood(swordsMan);showBlood(magician);}}

在这里仅定义了一个showBlood()方法,参数声明为Role 类型。第一次调用showBlood()
时传入了SwordsMan 实例,这是合法的语法,因为SwordsMan 是一种Role。第一次调用
showBlood()时传入了Magician 实例也是可行,因为Magician 是一种Role 。执行的结果如下:

Justin 血量 200Monica 血量 100

这样的写法好处为何?就算有100 种角色,只要它们都是继承Role,都可以使用这个
方法显示角色的血量,而不需要像前面重载的方式,为不同角色写100 个方法,多态的写
法显然具有更高的可维护性。

构造函数

如果类有继承关系,在创建子类实例后,会先进行父类定义的初始流程,再进行子类
中定义的初始流程,也就是创建子类实例后,会先执行父类构造函数定义的流程,再执行
子类构造函数定义的流程。
构造函数可以重载,父类中可重载多个构造函数,如果子类构造函数中没有指定执行
父类中哪个构造函数,默认会调用父类中无参数构造函数。如果这样撰写程序:

class Some {Some() {System.out.println("调用Some()");}}class Other extends Some {Other() {System.out.println("调用Other()");}}

如果尝试new Other(),先执行Some()中的流程,再执行Other()中的流程,
也就是先显示”调用Some()”,再显示”调用Other()”。很奇怪是吧!先继续往下看,就知道
为什么了。如果想执行父类中某构造函数,可以使用super()指定。例如:

class Some {Some() {System.out.println("调用Some()");}Some(int i) {System.out.println("调用Some(int i)");}}class Other extends Some {Other() {super(10);System.out.println("调用Other()");}}执行结果:调用Some10调用Other

在这个例子中,new Other()时,先调用了Other()版本的构造函数,super(10)表示调用
父类构造函数时传入int 数值10,因此就是调用了父类中Some(int i)版本的构造函数,而
后再继续Other()中super(10)之后的流程。

注意:,编译程序会在你没有撰写任何构造函数时,自动加入没有参数的默认构
造函数(Default constructor),如果自行定义了构造函数,就不会自动加入任何构造函数了。Other
的构造函数中没有指定调用父类中哪个构造函数,而且父类中还没有无参构造函数会编译失败。

原创粉丝点击