继承指的是子类对父类中的属性以及方法的继承。即申明继承后,父类中定义过的属性和方法子类默认全部拥有。可以实现代码的复用减小工作量和源码体积。以下是一个子类SunClass对父类SuperClass的继承的例子:
Public classSubClass extends SuperClass(){
......
}
如果要解释多态,就必须先来说一下什么是is-a。先观察下面一段代码:
//在这里,Cat是Pet的子类
Cat mimi=new Cat();
Pet cat=new Pet();
第一行定义了一个Pet类的新对象,第二行定义了一个Cat类的新对象,一切正常。那么这样呢?
Cat mimi=new Cat();
Pet cat=mimi;
试一下就可以知道,上面的两行都能通过编译,但是下面的却不行了:
Pet cat=new Pet();
Cat mimi=cat;
原因就是,编译器认为,宠物不是一种猫,而猫是一种宠物,所以将宠物类的对象赋值给猫类的对象是允许的,但是把猫类的对象赋值给宠物却是非法的。
因为 Cat is a Pet 所以 Pet cat=mimi 成立。编译器会将子类自动“提升为父类”,但是被提升为父类后,仍然拥有子类中的属性。这有什么用呢?来看下面一个要求。
要求设置一个动物类,动物类中有weight和getWeight()方法,然后有两个子类分别是猫和鸟,现在请设计static方法返回所有动物的体重,比较好的一个办法是重载,即在main()方法中定义2个重名方法:
Public static void getWeight(Cat cat){
cat.getWeight();
}
Public static void getWeight(Bird bird){
bird.getWeight();
}
然后在main()中调用getWeight(cat)或者getWeight(bird),这样根据参数的不同,就可以自动得到和对象对应的体重了。但是如果有很多种动物的情况下,还是会造成大量的重复代码。
但是如果鸟和狗都继承自Animal类的话,就可以用如下方法避免代码的重复
//在main()方法中定义
getWeight(Animal animal){
animal.getWeight();//getWeight()为Animal类中的方法
}
然后
建立getWeight(Animal animal)函数调用之:
getWeight(bird);
getWeight(cat);
因为鸟is a Animal,狗 is a Animal,所以调用getWeight(Animal animal)时,会自动参考传入的是bird还是cat来返回对应的体重值,这样,即使有100动物类的子类,也能用一个getWeight()函数解决,而不需要重载100个函数。
现在就可以解释什么是多态了。上面的getWeight()函数能够通过一个接收animal类的方法来实现对animal下子类多个对象的操作。通过同一个方法既可以操作cat的特有动作,又可以操作bird的特有动作,这就是多态。抽象的说法是,通过单一的方法操作多种类型的对象。
现在有个需求,要求定义一个static方法操作bird和cat,使它们eat(),你可能想到了上面提到的多态,public static void eat(Animal animal)但是由于不同动物的吃饭方式的实现方式是不同的等原因,Animal类中并没有eat()方法,因此会导致编译报错。
但是如果cat和bird中都有eat()方法,即方法时同名的,只是方法操作内容不同,就可以把eat()提升到Animal类中定义:
Public void eat(){
//方法体是空的}
因为只有动物的子类中才知道eat()应该如何操作,所以Animal类中的方法体是空的,当Cat继承Animal类后,在对其内容进行定义:
Public void eat(){
System.out.println(“喵呜喵呜。。。”);
}
在继承父类后,定义和父类名字相同的方法,但执行内容不同,这就叫做方法的重定义(Override)。由于Animal现在定义了eat(),所以编译就能通过了。那编译器是否能够正确无误的执行期望的对象的方法呢?如果传入的参数是Cat 的mimi对象,animal参数参考的就是mimi实例,即如果public static void eat(cat),则会执行mimi对象中的eat().这样就能使用上面提到的多态来解决这个问题了。
但是子类中重新定义的方法名必须和父类中相同,对于父类中的eat()来说,如果在子类Cat中定义为aet()则属于另一个方法了,虽然并不会报错,但是在运行时你会发现并没有执行到cat.eat()。为了避免这种错误,可以使用标注@Override来让编译器帮你检查是否真的对父类中的方法进行了重定义。标注使用方法为:
@Override
Public void eat(){
......
}
这样当方法名打错时,编译器会在提示你必须重定义或者实现一个父类的方法。
上一个例子中,为了实现多态,而把子类中的方法提升到父类中进行定义,但是由于父类中并不知道执行何种动作,所以留空,而让子类自动执行相应的动作。但是这依赖于程序的自动执行,但是并没有显式的告诉别人你的目的。这种情况下,如果方法中真的没有仍和代码需要执行,就可以使用抽象类关键词abstract来表示该方法为抽象方法:
Public abstract void eat();//注意,没有{},只是在()后加了一个分号表示方法声明结束。
一个类中如果有抽象方法,则说明该类没有定义完全,所以不能用来生成对象。所以该类也是一个抽象类,必须用abstract关键字来修饰:public abstract class Animal(){......}。当有子类继承一个抽象类时,必须在子类中对抽象方法进行处理:要么继续用abstract关键字修饰之,要么写出该方法的实现方式。如果没有对抽象类进行处理,则会引发编译器报错。
下面是一个简单的综合应用了以上知识的小练习:
//父类——Role
package com.wmr.bigwar;
public abstract class Role{
//属性
private String name;
protected int blood;
private int atk;
private int level;
//构造方法
public Role(){}
//普通方法
public abstract boolean fight(Role beAtkRole);
public abstract boolean beAtk(int atk);
public String getName(){
return this.name;
}
public boolean setName(String name){
this.name=name;
return true;
}
public int getBlood(){
return this.blood;
}
public boolean setBlood(int blood){
this.blood=blood;
return true;
}
public int getAtk(){
return this.atk;
}
public boolean setAtk(int atk){
this.atk=atk;
return true;
}
public int getLevel(){
return level;
}
public boolean setLevel(int level){
this.level=level;
return true;
}
}
//Role类的继承类——MagicMan
package com.wmr.bigwar;
public class MagicMan extends Role{
//属性
//构造方法
public MagicMan(){
this.setName("卡尔");
this.setBlood(1000);
this.setAtk(200);
this.setLevel(1);
}
//普通方法
@Override
public boolean fight(Role beAtkRole){
System.out.println(this.getName()+"向"+beAtkRole.getName()+"施展了陨石术!");
return beAtkRole.beAtk(this.getAtk());
}
public boolean beAtk(int atk){
super.blood-=atk;
if(super.blood>0){
System.out.println(super.getName()+"受到了攻击,还剩"+super.blood+"血");
return false;}
else{
System.out.println(super.getName()+"受到了致命攻击,死了");
return true;}
}
}
//Role类的继承类——SwordsMan
package com.wmr.bigwar;
public class SwordsMan extends Role{
//属性
//构造方法
public SwordsMan() {
this.setName("剑圣");
this.setBlood(1600);
this.setAtk(100);
this.setLevel(1);
}
//普通方法
@Override
public boolean fight(Role beAtkRole){
System.out.println(this.getName()+"向"+beAtkRole.getName()+"施展了剑气!");
return beAtkRole.beAtk(this.getAtk());
}
public boolean beAtk(int atk){
super.blood-=atk;
if(super.blood>0){
System.out.println(super.getName()+"受到了攻击,还剩"+super.blood+"血");
return false;}
else{
System.out.println(super.getName()+"受到了致命攻击,死了");
return true;}
}
}
//主类
package com.wmr.bigwar;
public class TestFight {
//必须为static方法,因为main()函数是static,
//而static方法在本类中只能调用其他static方法
public static boolean doFight(Role atkRole,Role beAtkRole){
return atkRole.fight(beAtkRole);
}
//
public static void main(String[] args) {
// TODO Auto-generated method stub
SwordsMan sM = new SwordsMan();
MagicMan mM=new MagicMan();
System.out.println("卡尔的血量为"+mM.getBlood());
System.out.println("剑圣的血量为"+sM.getBlood());
boolean die=false;
while(!die){
die=doFight(mM,sM);
if(die)
break;
else {
die=doFight(sM,mM);
}
}
}
}
//执行结果:
卡尔的血量为1000
剑圣的血量为1600
卡尔向剑圣施展了陨石术!
剑圣受到了攻击,还剩1400血
剑圣向卡尔施展了剑气!
卡尔受到了攻击,还剩900血
卡尔向剑圣施展了陨石术!
剑圣受到了攻击,还剩1200血
剑圣向卡尔施展了剑气!
卡尔受到了攻击,还剩800血
卡尔向剑圣施展了陨石术!
剑圣受到了攻击,还剩1000血
剑圣向卡尔施展了剑气!
卡尔受到了攻击,还剩700血
卡尔向剑圣施展了陨石术!
剑圣受到了攻击,还剩800血
剑圣向卡尔施展了剑气!
卡尔受到了攻击,还剩600血
卡尔向剑圣施展了陨石术!
剑圣受到了攻击,还剩600血
剑圣向卡尔施展了剑气!
卡尔受到了攻击,还剩500血
卡尔向剑圣施展了陨石术!
剑圣受到了攻击,还剩400血
剑圣向卡尔施展了剑气!
卡尔受到了攻击,还剩400血
卡尔向剑圣施展了陨石术!
剑圣受到了攻击,还剩200血
剑圣向卡尔施展了剑气!
卡尔受到了攻击,还剩300血
卡尔向剑圣施展了陨石术!
剑圣受到了致命攻击,死了