接口、造型与计分
接口、造型与计分
1 接口interface
物品归类:
-
相同类别商品有相似特征,属性类似
-
方便管理统一储存调度,相同存储条件
-
存在跨类别分类标签:地方特产,国际美食
a.分类标签体现了一个物品属于多种类型的现象,这种现象称为“多继承”。
-
Java的继承可以实现树形分类,但是无法处理跨类别标签
1.1 什么是接口
在Java编程语言中是一个抽象类型,接口通常以interface来声明
- 统一类的公共行为和属性可以抽取到抽象类
- 不同种类的公共行为抽取到接口中
抽象类和接口的设计原则:
- 所有子类都有的方法抽象设计到父类中
- 部分子类中公共方法,抽象设计到接口中
1.2接口的语法
接口的语法约定如下:
-
接口是Java的引用类型,可以定义引用类型变量
-
使用interface定义接口
-
接口中只能定义常量与抽象方法
- 可以省略常量的修饰词public static final
- 可以省略抽象方法修饰词public abstract
-
接口不能实例化创建对象,可以作为父类型被子类型实现(继承)
-
子类使用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的运算符,用于检测对象的是否是指定的类型,类型检测结果:
- 是指定类型对象返回true
- 是指定类型的子类型对象返回ture
- 不是指定类型的对象返回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 计分功能
将每个死亡的飞机得分就行统计记录到变量中然后显示在屏幕上:
-
检测每个被打击到飞机如果死亡了就进行分数统计
a.如果飞机是敌机也就是实现了Enemy接口,就统计分数
b.如果飞机是奖品也就得实现了Awary接口,就处理奖品
-
将数据存储到World类中变量中
-
将变量的值显示到屏幕上
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 处理奖励
- 双枪:英雄能够支持双枪状态,支持定时双枪功能,需要重构Hero类支持双枪功能
- 生命:记录当前的生命数量,功能简单,在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;
}
}