接口、造型与计分

接口、造型与计分

1 接口interface

物品归类:

  1. 相同类别商品有相似特征,属性类似

  2. 方便管理统一储存调度,相同存储条件

  3. 存在跨类别分类标签:地方特产,国际美食

    a.分类标签体现了一个物品属于多种类型的现象,这种现象称为“多继承”。

  4. Java的继承可以实现树形分类,但是无法处理跨类别标签

1.1 什么是接口

在Java编程语言中是一个抽象类型,接口通常以interface来声明

  1. 统一类的公共行为和属性可以抽取到抽象类
  2. 不同种类的公共行为抽取到接口中

抽象类和接口的设计原则:

  1. 所有子类都有的方法抽象设计到父类中
  2. 部分子类中公共方法,抽象设计到接口中
1.2接口的语法

接口的语法约定如下:

  1. 接口是Java的引用类型,可以定义引用类型变量

  2. 使用interface定义接口

  3. 接口中只能定义常量与抽象方法

    • 可以省略常量的修饰词public static final
    • 可以省略抽象方法修饰词public abstract
  4. 接口不能实例化创建对象,可以作为父类型被子类型实现(继承)

  5. 子类使用implements实现接口,具体语法:

    • 接口只能作为父类型被子类型实现implements
    • 一个类可以实现多个接口,用逗号分隔
    • 子类实现接口时必须实现(重写)接口的全部抽象方法

案例:

Award 接口

package demo01;
public interface Award{
    //代表双倍火力奖励
    int DOUBLE_FIRE=1;
    //生命
    int LIFE=2;
    
    //获得当前对象携带的奖励
    int getAward();
}

接口继承接口

public interface MyAward extends Award{
    int getType();
}

Enemy接口:

package demo01;
public interface Enemy{
    //获取当前敌机对象的得分
    int getScore();
}

实现接口:

public class Stone implements Award, Enemy{
    public int getScore(){
        return 10;
    }
    
    public int getAwary(){
        return DOUBLE_FIRE;
    }
}
1.3接口与多继承

生活存在的多继承现象:

  • 银币:既是银子,也是钱
  • 纸币:既是纸张,也是钱
  • 比特币:既是区块链,也是钱

Java不支持“类的多继承”。但是Java支持一个类继承一个父类实现多个接口:Java利用接口实现了多继承

案例:

package demo01;
/*
 * Bird类继承一个父类FlyingObject,同时实现类两个接口
 * 先继承父类,再实现接口,顺序不能变
 */
public class Bird extends FlyingObject implements Award, Enemy{
    @Override
    public int getScore(){
        return 5;
    }
    
    @Override
    public int getAward(){
        return DOUBLE_FIRE;//FIRE
    }
    
    @Override
    public void move(){
        y +=5;//step
    }
}

测试:

package demo01;
public class BirdDemo {
	public static void main(String[] args) {
		/*
		 * Bird继承父类,实现接口以后,父类和接口都可以作为Bird的父类型
		 */
		Bird bird=new Bird();
		//bird多个类型
		FlyingObject obj=bird;//bird属于飞行物类型
		Award award=bird;//bird属于奖品类型
		Enemy enemy=bird;//bird属于敌人类型
		
		obj.move();
		System.out.println(award.getAward());
		System.out.println(enemy.getScore());//5
	}
}
1.4 约定敌人和奖励

利用接口约定奖励和敌人,使对象的类型更加合理

案例:

小飞机:

package demo01;
public class Airplane extends Plane implements Enemy{
    //飞机从屏幕上方随机位置出场
    public Airplane(){
        super(Images.ariplane[0], Images.airplane, Images.bom);
        step=Math.random()*4+1.5;
    }
    //从自定位置出场
	public Airplane(double x, double y, double step) {
		//利用super()调用父类有参数构造器,复用了父类中构造器算法
		super(x, y, Images.airplane[0], Images.airplane, Images.bom);
        this.step=step;
	}
    @Override
    public int getScore(){
        return 10;
    }

大飞机:

package demo01;
public class Bigplane extends Plane implements Enemy{
	
	public Bigplane() {
		super(Images.bigplane[0],Images.bigplane,Images.bom);
		step=Math.random()*3+0.5;
		life=5;//生命值为5
	}

	public Bigplane(double x, double y, double step) {
		super(x,y,Images.bigplane[0],Images.bigplane,Images.bom);
		this.step = step;
		life=5;//生命值为5
	}

	@Override
	public int getScore() {
		return 100;
	}
}

蜜蜂:

package demo01;
public class Bee extends Plane implements Award{

	public Bee() {
		super(Images.bee[0],Images.bee,Images.bom);
		step=Math.random()*5+2;
	} 
    
	public Bee(double x, double y, double step) {
		super(x,y,Images.bee[0],Images.bee,Images.bom);
		this.step=step;
	}
	/*
	 * 重写父类型move方法修改为斜向飞行
	 */
	public void move() {
		//调用父类型方法,复用父类型定义的算法
		super.move();//向下飞行y++
		x++;
	}
    
	@Override
	public int getAward() {
		return Math.random()>0.5 ? DOUBLE_FIRE:LIFE;
	}
}

2 向上造型,向下转型

2.1 向上造型

Java中可以将子类类型对象赋值给父类型变量,这种现象称为向上造型。

案例:

package demo11;
public class Demo01 {
	public static void main(String[] args) {
		/*
		 * 测试向上造型
		 * 将小类型变量赋值给大类型变量
		 */
		Airplane plane=new Airplane();
		//向上造型;小飞机赋值给飞行物
		FlyingObject obj=plane;
		//接口类型可以作为向上造型的目标类型
		Enemy enemy=plane;
		System.out.println(enemy.getScore());//10
		//向上造型的优点;多态
		Bigplane bigplane=new Bigplane();
		enemy = bigplane;
		System.out.println(enemy.getScore());//100
		//上述代码的优势;使用一个enemy变量
		//可以管理任何实现Enemy接口的对象
		//既可以管理小飞机,也可以管理大飞机
		//如果有其他类型的对象实现类Enemy接口
		//也可以被enemy变量管理
		//按照Enemy类型写的程序将可以适用任何子类型实例
	}
}
2.2 向下转型

将父类型引用的对象赋值给子类型变量就需要向下转型,需要执行具体子类型方法就必须使用向下转型处理以后才能执行。(向下转型有风险,使用需谨慎)

package demo11;

public class Demo02 {
	public static void main(String[] args) {
		/*
		 * 测试向下转型
		 * 转型有风险,使用需谨慎
		 */
		FlyingObject obj=new Airplane();//向上造型。小飞机赋值给FlyingObject
		//向下转型;大类型FlyingObject赋值给Airplane
		Airplane b1=(Airplane)obj;
		//转型风险
		Bigplane b2Bigplane=(Bigplane)obj;//实验结果为错误	
	}
}
2.3 instanceof

instanceof是Java的运算符,用于检测对象的是否是指定的类型,类型检测结果:

  1. 是指定类型对象返回true
  2. 是指定类型的子类型对象返回ture
  3. 不是指定类型的对象返回false
package demo01;
public class InstanceofDemo {
	public static void main(String[] args) {
		/*
		 * 测试instanceof运算符
		 * 变量instanceof 类型;检测变量引用的对象是否为指定“类型”
		 */
		FlyingObject obj=new Airplane();
		boolean b=obj instanceof Airplane;//true
		System.out.println(b);
		b=obj instanceof Bigplane;//false
		System.out.println(b);//结果是不成功的
		
		b=obj instanceof Enemy;//true
		System.out.println(b);
		b=obj instanceof Award;//false
		System.out.println(b);
		
		//instanceof经常和向下转型搭配使用,避免异常
		if(obj instanceof Enemy) {
			Enemy enemy=(Enemy)obj;
			System.out.println(enemy.getScore());
		}
	}
}

3 计分功能

将每个死亡的飞机得分就行统计记录到变量中然后显示在屏幕上:

  1. 检测每个被打击到飞机如果死亡了就进行分数统计

    a.如果飞机是敌机也就是实现了Enemy接口,就统计分数

    b.如果飞机是奖品也就得实现了Awary接口,就处理奖品

  2. 将数据存储到World类中变量中

  3. 将变量的值显示到屏幕上

3.1 利用向下转型统计分数

在飞机被击打以后进行分数统计:使用“向下转型”将变量从FlyingObject转换为接口类型Enemy,转型后就可以调用接口上定义的getScore方法了,将处理分数的算法独立封装成方法更加方便:

在World 类中声明计分方法和分数变量

Private int sore:
//计分方法
public void scores(FlyingObject plane) {//对飞行物进行统计
    if(plane.isDead()) {//检测一下飞机是不是死飞机		
        if(plane instanceof Enemy) {//检测一下plane是不是敌机
            Enemy enemy=(Enemy)plane;//如果是就进行转换
            score +=enemy.getScore();//对分数进行统计
            System.out.println(score);//控制台输出一下分数
        }
    }
}

在hitDetection中调用计分方法

public void hitDetection() {
		//拿到每个子弹
		for(int i=0; i<bullets.length; i++) {
			if(!bullets[i].isLiving()) {
				continue;
			}
			for(int j=0; j<planes.length; j++) {
				if(!planes[j].isLiving()) {
					continue;
				}
				if(planes[j].duang(bullets[i])) {
					//System.out.println(planes[j]+"撞到:"+bullets[i]);
					bullets[i].goDead();//子弹去死
					planes[j].hit();//飞机被打一下
                    //调用计分方法
					scores(planes[j]);
				}
			}
		}
	}
3.2 显示分数

更新World类本身就在绘图面板,更新paint方法就可以绘制分数:

public void paint(Graphics g){
    sky.paint(g);
    hero.paint(g);
    
    for(int i=0; i<bullets.length; i++){
        bullets[i].paint[g];

     //调用每个飞机的多态方法,实现多态的绘制
        for(int i=0; i<planes.length; i++) {
            planes[i].paint(g);
        }
        g.setColor(Color.white);
        g.drawString("SCORE:"+score,20,40);
    }
}

4 处理奖励

  1. 双枪:英雄能够支持双枪状态,支持定时双枪功能,需要重构Hero类支持双枪功能
  2. 生命:记录当前的生命数量,功能简单,在World类中加个变量记录即可
4.1 双枪功能
/*
 * 开火方法
 */
public Bullet fire() {
    double x=this.x+width/2-5;//当前英雄机的位置加上英雄一半减5
    double y=this.y-20;//y减去高度,假设为20,利用飞机的xy算出子弹的xy
    Bullet bullet=new Bullet(x,y);
    return bullet;
}
private int doubleFire=0;
public void doubleFire() {
    doubleFire=20;//表示双枪次数为20
}
public Bullet[] openFire(){
    if(doubleFire>0) {//双枪状态大于0,处于双枪状态
        doubleFire--;//表示次数减1
        double x=this.x+width/2-5;//当前英雄机的位置加上英雄一半减5
        double y=this.y-20;//y减去高度,假设为20,利用飞机的xy算出子弹的xy
        Bullet b1=new Bullet(x+15,y);//创建子弹+15
        Bullet b2=new Bullet(x-15,y);//创建子弹-15
        return new Bullet[] {b1,b2};//创建数组封装返回,不能省略new Bullet
    }else {
        Bullet bullet=fire();//调用fire方法
        return new Bullet[] {bullet};//回到单枪
    }
}

测试:

package demo11;
public class OpenFireDemo {
	public static void main(String[] args) {
		/*
		 * 测试Hero自动支持单枪和双枪
		 */
		Hero hero=new Hero(200,500);
		//单枪测试
		Bullet[]bullets=hero.openFire();
		System.out.println(bullets.length);
		System.out.println(bullets[0]);
		//测试双枪
		hero.doubleFire();
		for(int i=0; i<20; i++) {
			bullets=hero.openFire();
			System.out.println(bullets.length);
			System.out.println(bullets[0]);
			System.out.println(bullets[1]);
		}
		//双枪打完变单枪
		bullets=hero.openFire();
		System.out.println(bullets.length);
		System.out.println(bullets[0]);
	}
}
4.2 使用openFire方法
private void fireAction(){
    if(index % 15==0){
        //在定时任务中执行英雄开火方法
        Bullet[] bubu=hero.openFire();
        //子弹数组扩容
        int len=bullets.length;
        Bullet[] arr=Arrays.copyOf(bullets,len+bubu.length);
        //将子弹添加到新数组最后位置
        System.arraycopy(bubu, 0, arr, len, bubu.length);
        //替换原数组
        bullets=arr;
    }
}
4.3英雄生命值

在World类中记录生命数量:

public class World extends JPanel{
    private int lift;
}
4.4 奖励算法

与处理分数类似,检测被打的飞机是否是奖品类型:

Private int sore:
//计分方法
public void scores(FlyingObject plane) {//对飞行物进行统计
    if(plane.isDead()) {//检测一下飞机是不是死飞机		
        if(plane instanceof Enemy) {//检测一下plane是不是敌机
            Enemy enemy=(Enemy)plane;//如果是就进行转换
            score +=enemy.getScore();//对分数进行统计
            System.out.println(score);//控制台输出一下分数
        }
/*
 * 处理奖励规则
 */
        if(plane instanceof Award) {//如果被打掉的飞机是奖品类型
            Award award=(Award)plane;//转换为奖品类型
            int type=award.getAward();//拿到奖品
            if(type==Award.DOUBLE_FIRE) {//如果奖品为双枪
                hero.doubleFire();//把英雄设为双枪
            }else if(type==Award.LIFE){//如果奖品为生命
                life++;//生命加1
            }
        }
    }
}

在paint 方法中显示英雄的生命数量:

public void paint(Graphics g){
    sky.paint(g);
    hero.paint(g);
    
    for(int i=0; i<bullets.length; i++){
        bullets[i].paint[g];

     //调用每个飞机的多态方法,实现多态的绘制
        for(int i=0; i<planes.length; i++) {
            planes[i].paint(g);
        }
        g.setColor(Color.white);//画成白色
        g.drawString("SCORE:"+score,20,40);//分数在框内20,40位置显示
        g.drawString("LIFE:"+life,20,60);//血量在框内20,60位置显示
    }
}
4.5 蜜蜂反弹功能

重构蜜蜂的飞行方法:

package demo11;
public class Bee extends Plane implements Award{

	public Bee() {
		super(Images.bee[0],Images.bee,Images.bom);
		step=Math.random()*5+2;
	} 
    
	public Bee(double x, double y, double step) {
		super(x,y,Images.bee[0],Images.bee,Images.bom);
		this.step=step;
	}
    private int direction;//给蜜蜂一个方向
	/*
	 * 重写父类型move方法修改为斜向飞行
	 */
	public void move() {
		//调用父类型方法,复用父类型定义的算法
		super.move();//向下飞行y++
		x+=direction;
        if(x<0){//如果蜜蜂碰到了左边缘
            direction=1;//往反方向飞,向右飞
        }else if(x>400-width){//x+蜜蜂的宽度大于屏幕的宽度
            direction=-1;//从右向左飞
        }
	}
	@Override
	public int getAward() {
		return Math.random()>0.5 ? DOUBLE_FIRE:LIFE;
	}
}
posted @ 2021-03-05 22:29  指尖上的未来  阅读(106)  评论(0)    收藏  举报