我们来写一个pk类小游戏深刻理解一下继承:
public class LiuBei {
// 属性
String name;
int ad;
int hp;
// PK 攻击的是一种类型
public void ack_1(LvBu lvbu) {
lvbu.hp = lvbu.hp - this.ad;
System.out.println(this.name + "攻击了" + lvbu.name);
System.out.println(lvbu.name + "失去了" + this.ad + "点HP");
System.out.println(lvbu.name + "还剩下" + lvbu.hp + "点HP");
}
}
public class LvBu{
String name;
int ad;
int hp;
// PK 攻击的是一种类型
public void ack_1(LiuBei liubei){
liubei.hp = liubei.hp - (this.ad*2);
System.out.println (this.name + "攻击了" + liubei.name);
System.out.println (liubei.name+"失去了"+(this.ad*2)+"点HP");
System.out.println (liubei.name+"还剩下"+liubei.hp+"点HP");
}
}
public class Entrance{
public static void main(String[] args){
// 先创建双方对象
LvBu lvbu = new LvBu ();
LiuBei liubei = new LiuBei ();
// 给对象的属性赋值
lvbu.name="吕布";
lvbu.hp=2000;
lvbu.ad=130;
liubei.name="刘备";
liubei.hp=1800;
liubei.ad=150;
int count=0;
while(lvbu.hp>0&&liubei.hp>0){
System.out.println ("-----第"+(++count)+"回合----");
// 互相攻击了一次
lvbu.ack_1 (liubei);
if(liubei.hp>0){// 被攻击之后 再判断一次生命值
liubei.ack_1(lvbu);
}
}
}
}
这几段代码出现后,我们可以很明显的看到,在吕布,刘备这两个类中,有代码冗余。这仅仅是两个简单的人物,我们想一想,假如我们一个人物需要写1000行代码,我们的游戏中有108个人物,那么总计就有108000行代码。
于是我们就要思考一个问题,能否将这些人物共同的功能写在一个类中,让每个人物都来“继承”这个类中的功能?假如我们每个人物之间重复的代码大约是400行,那么我们每个人物只需要编写600行,用刚提到的策略,代码行数总共为400+600*108=65200,少了42800行代码,一下子使代码行数少了一大半,并且可以有更好的维护性。
从这个例子可以看出,人物之间共同的代码越多,继承效果越好,实际上,两个任务的共同的功能很可能超过了一半。上述策略就叫做继承,继承是面向对象的重要特征。在java中,被继承的类叫父类、基类或者超类,与之对应的叫子类或派生类。
我们就以这个pk游戏为例,他们都有攻击功能,且属性均为姓名、攻击值、生命力,因此将两个类共同的部分写成一个类——Hero,让两个人物刘备和吕布继承它即可。
public class Hero {
String name;
int ad;
int hp;
public void ack(Hero h){
h.hp=h.hp-this.ad;
System.out.println(this.name+"攻击了"+h.name);
System.out.println(h.name+"失去了"+this.ad+"点HP");
System.out.println(h.name+"还剩下"+h.hp+"点HP");
}
public void move(){
System.out.println(this.name+"每秒移动1米");
}
}
两个子类如下:
public class LiuBei extends Hero{
//重写父类的方法
@ Override //重写方法的检查注解
public void move(){
System.out.println(this.name+"每秒移动2米");
}
}
public class LvBu extends Hero{
//重写父类的方法
@ Override //重写方法的检查注解
public void ack(Hero h){
h.hp = h.hp - (this.ad*2);
System.out.println (this.name + "攻击了" + h.name);
System.out.println (h.name+"失去了"+(this.ad*2)+"点HP");
System.out.println (h.name+"还剩下"+h.hp+"点HP");
}
}
public class Entrance{
public static void main(String[] args){
// 先创建双方对象
LvBu lvbu = new LvBu ();
LiuBei liubei = new LiuBei ();
// 给对象的属性赋值
liubei.name="吕布";
liubei.hp=2000;
liubei.ad=130;
lvbu.name="刘备";
lvbu.hp=1800;
lvbu.ad=150;
int count=0;
while(lvbu.hp>0&&liubei.hp>0){
System.out.println ("-----第"+(count++)+"回合----");
// 互相攻击了一次
lvbu.ack (liubei);
lvbu.move();
if(liubei.hp>0){// 被攻击之后 再判断一次生命值
liubei.ack(lvbu);
liubei.move();
}
}
}
}
从表面看,虽然刘备和吕布类都没有定义成员变量,但他们继承了父类的成员变量,可以i当作自己的变量一样使用。他们各自也继承或修改了父类的方法,具体不再阐述。
为了便于理解,我们再写一个书上的例子。以字体对话框和段落对话框为例,为了简化起见,假如字体对话框特征是标题、字体名称,该对话库具有显示的功能;段落对话框特征是标题、高度,该对话框具有显示的功能。
显然,这两个对话框都有标题和显示功能,因此首先将两个类共同的部分写成一个类——Dialog。
public class Dialog {
protected String title;
public void show(){
System.out.println(title+"对话框显示");
}
}
接下来编写字体对话框类FontDialog来继承Dialog类。
public class FontDialog extends Dialog {
private String fontName;
public FontDialog(String title,String fontName){
this.title=title;
this.fontName=fontName;
}
}
从表面上看,该类只定义了一个成员变量fontName,实际上还从父类继承了title,可以当成自己的变量一样使用。
下面用一个主函数进行测试:
public class Entrance{
public static void main(String[] args) {
FontDialog fd=new FontDialog("字体","宋体");
fd.show();
}
}
显然,FontDialog从父类那里继承了show方法,也能当成自己方法一样来使用。
注:如果一个成员要被子类继承之后使用,这个成员不能是private,因为私有的成员不能在类的外部使用,当然也不能被子类使用。一般情况下,成员变量定义为protected类型,成员函数定义为public类型。