抽象与多态

抽象与多态

1 抽象abstract

1.1 抽象类

使用抽象关键字abstract声明的类是抽象类,抽象类不能直接实例化创建对象。

案例:

package demo01;
/*
 *Person类的作用是为子类提供代码复用
 */
public class Person{
    String name;
    int age;
    
    public void whoru(){
        System.out.println("我是"+name);
    }
}
package demo01;
public class Student extends Person{
    public Student(String name,int age){
        this.name=name;
        this.age=age;
    }
    public void study(){
        System.out.println("学习");
    }
}
package demo01;
public class Teacher extends Person{
     public Teacher(String name,int age){
        this.name=name;
        this.age=age;
    }
    public void teach(){
        System.out.println("讲课");
    }
}
package demo01;
public class Worker extends Person{
     public Worker(String name,int age){
        this.name=name;
        this.age=age;
    }
    public void work(){
        System.out.println("工作");
    }
}

测试案例:

package demo01;
public class Demo01{
    public static void main(String[] args){
        Student s=new Student("Tom",12);
        Teacher t=new Teacher("Andy",28);
        Worker w=new Worker("Jerry",28);
        
        s.whoru();
        s.study();
        
        t.whoru();
        t.teach();
        
        w.whoru();
        w.work();
        
        //问题:如果能够直接创建Person对象,其方法运算结果不理想
        Person p=new Person();
        p.whoru();
        
        //运行结果:
        //我是Tom 学习
        //我是Andy 讲课
        //我是Jerry 工作
        //我是null    
    }
}

面向对象设计时候根据子类泛化得到的半成品父类,应该定义为抽象类。

如何使用抽象类:

  1. 在类名前面添加abstract关键字以后就是抽象类了
  2. 抽象类可以作为父类被子类继承,可以定义变量
  3. 抽象类不能直接创建对象

案例:

package demo02;
/*
 *Person类的作用是为子类提供代码复用
 *设计为抽象类,只能被继承,不能创建对象
 */
public abstract class Person{
    String name;
    int age;
    
    public void whoru(){
        Systrm.out.println("我是"+name);
    }
}
package demo02;
public class Demo01{
    public static void main(String[] args){
        Student s=new Student("Tom",12);
        Teacher t=new Teacher("Andy",28);
        Worker w=new Worker("Jerry",28);
        
        s.whoru();
        s.study();
        
        t.whoru();
        t.teach();
        
        w.whoru();
        w.work();
        
        //问题:如果能够直接创建Person对象,其方法运算结果不理想
        //Java编译器检查,不允许创建抽象类型的对象
        //Person p=new Person();
        //p.whoru();
    }
}
1,2 抽象方法

使用abstract关键字声明,不包含方法体的方法称为抽象方法。

抽象使用规则:每个子类都有,但是每个子类实现都不同的方法泛化为抽象方法!

语法:

  1. 使用abstract声明方法,不能有方法体

  2. 包含抽象方法的类必须声明为抽象类,因为包含抽象方法的类一定是不完整的半成品类

  3. 子类继承抽象类时必须重写(实现)抽象方法,否则出现编译错误

    a.可以将抽象方法看作父类对子类的行为约定,必须被子类重写实现

案例:

package demo03;
public class Teacher extends Person{
     public Teacher(String name,int age){
        this.name=name;
        this.age=age;
    }
    public void teach(){
        System.out.println("讲课");
    }   
        //日程计划
    public void schedule(){
        System.out.println("吃饭、讲课");
    }
}
package demo03;
public class Student extends Person{
     public Student(String name,int age){
        this.name=name;
        this.age=age;
    }
    public void study(){
        System.out.println("学习");
    }    
        //日程计划
    public void schedule(){
        System.out.println("吃饭、听课");
    }
}
package demo03;
public class Worker extends Person{
     public Worker(String name,int age){
        this.name=name;
        this.age=age;
    }
    public void work(){
        System.out.println("学习");
    }    
        //日程计划
    public void schedule(){
        System.out.println("吃饭、开车");
    }
}
package demo02;
/*
 *Person类的作用是为子类提供代码复用
 *设计为抽象类,只能被继承,不能创建对象
 */
public abstract class Person{
    String name;
    int age;
    
    public void whoru(){
        Systrm.out.println("我是"+name);
    }
        //日程计划
    public abstract void schedule();
}

测试案例:

public class demo02{
public static void main(String[] args){
    Student s=new Student("Tom",12);
    Teacher t=new Teacher("Andy",28);
    Worker w=new Worker("Jerry",28);
    
    s.schedule();
    t.schedule();
    w.schedule();
    
    Person p=new Teacher("Wang",22);//person引用也可以
	p.schedule();
   }
}
1.3 使用抽象类重构飞机大战

案例:

package demo04;
import java,awt.Graphics;
import javax.swing.ImageIcon;
/*
 * 父类中定义从子类抽取的属性和方法
 * 这种抽取方式称为“泛化”
 */
public abstract class FlyingObject {
	 protected double x;
	 protected double y;
	 protected double width;
	 protected double height;
     protected double step;
     protected ImageIcon image;
    
    //public FlyingObject(double x, double y, double width, double height) {
		//this.x = x;
		//this.y = y;
		//this.width = width;
		//this.height = height;
   // }
  
    public abstract void move();
        
    public void paint(Graphics g){
        image.paintIcon(null,g,(int)x, (int)y);
    }
	@Override
	public String toString() {
        String className=getClass().getName();
		return className+" [x=" + x + ", y=" + y + ", width=" + width + ", height=" + height + "]";
	}
}
package demo04;
public abstract class Plane extends FlyingObject{
    public void move();
    y+=step;
}
package demo04;
public class Airplane extends Plane{
    
	public Airplane(double x, double y, double width, double height, double step) {
		//利用super()调用父类有参数构造器,复用了父类中构造器算法
		//super(x,y,width,height);
        this.x=x;
        this.y=y;
		this.step = step;
        image=Images.airplane[0];
        width=image.getIconWidth();
        height=image.getIconHeight();
	}
}
package demo04;
public class Bigplane extends Plane{
    
	public Bigplane(double x, double y, double width, double height, double step) {
		//利用super()调用父类有参数构造器,复用了父类中构造器算法
		//super(x,y,width,height);
        this.x=x;
        this.y=y;
		this.step = step;
        this.image=Images.bigplane[0];
        width=image.getIconWidth();
        height=image.getIconHeight();
	}
}
package demo04;
public class Bee extends Plane{
    public Bee(double x, double y, double width, double height, double step){
        //super(x,y,width,height);
        this.x=x;
        this.y=y;
        this.step=step;
        this.image=Images.bee[0];
        width=image.getIconWidth();
        height=image.getIconHeight();
    }
    /**
     *重写父类型move方法修改为斜向飞行
     */
    public void move(){
        //调用父类型方法,复用父类型定义的算法
        super.move();//向下飞行     x++;
    }
}
package demo04;
import java.awt.Graphics;
import javax.swing.ImageIcon;
public class Sky extends FlyingObject{

 	protected double yo;
	public Sky(double x, double y, double width, double height, double step) {
		//super(x,y,width,height);
		step =0.8;
		image=Images.sky;
		x=0;
		y=0;
		yo=-image.getIconHeight();//照片的高度
		height=image.getIconHeight(); //天空的高度(照片)
		width=image.getIconWidth();//天空的宽度(照片)
		//System.out.println("y="+y+" y0="+yo+" width="+width+" height="+height);
	//}
	public void move() {
		y+=step;
		yo+=step;//(两个坐标都在移动)

		if(y>=height) {
			//System.out.println("第一个照片返回:y="+y);
			y=-height;
            //System.out.println("y="+y+" y0="+yo+" width="+width+" height="+height);
		}
		if(yo>=height) {
			//System.out.println("第二个照片返回:y0="+yo);
			yo=-height;
			//System.out.println("y="+y+" y0="+yo+" width="+width+" height="+height);
		}
	}
	public void paint(Graphics g) {//改写父类中的方法
		image.paintIcon(null, g, (int)x, (int)y);
		image.paintIcon(null, g, (int)x, (int)yo);//对天空做修改,对两张照片进行处理
	}
}
package demo04;
public class Bullet extends FlyingObject{
    
	public Bullet(double x, double y, double width, double height, double step) {
		//利用super()调用父类有参数构造器,复用了父类中构造器算法
		//super(x,y,width,height);
        this.x=x;
        this.y=y;
		this.step = step;
        image=Images.bullet;
        width=image.getIconWidth();
        height=image.getIconHeight();
	}
    /**
     *重写继承与超类的move方法,作用就是修改了超类move行为
     *超类是向下移动,修改为向上移动
     */
    public void move(){
        y-=step;
    }
}
package demo04;
public class Hero extends FlyingObject{
    
	public Hero(double x, double y, double height, double width) {
		//super(x,y,width,height);
        this.x=x;
        this.y=y;
        this.image=Images.hero[0];
        width=image.getIconWidth();
        height=image.getIconHeight();
	}
	/*
	 * 重写move方法,空方法,目的是不动
	 * 修改超类中规定的向下飞,改成不动
	 */
	public void move() {
	}
	/*
	 *将鼠标机移动到鼠标位置X,Y
	 *@param x鼠标位置x
	 *@param y鼠标位置y
	 */
	public void move(int x, int y) {
		this.x=x;
		this.y=y;
	}
}
package demo04;
import java.awt.Graphics;
import java.util.Timer;
import java.util.TimerTask;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class World extends JPanel{
	/*
	 * 添加飞机大战世界中的物体
	 */
	private Airplane[] airplanes;
    private Bigplane[] bigplanes;
    private Bullet[]bullets;
    private Sky sky;
    private Hero hero;
    
	/*
	 * 利用构造器初始化世界中每个物体
	 */
	public World(){
		airplanes=new Airplane[2];//创建两个元素的数组
		airplanes[0]=new Airplane(10,10,1.5);//宽高去掉了50,40
		airplanes[1]=new Airplane(10,100,1.5);
		//x,y   宽      高        速度
		bigplanes=new Bigplane[2];
		bigplanes[0]=new Bigplane(100,20,2);//宽高去掉了100,200
		bigplanes[1]=new Bigplane(100,220,2);
	
		bullets =new Bullet[6];
		bullets[0]=new Bullet(200,400,4);//宽高去掉了10,10
		bullets[1]=new Bullet(200,350,4);
        bullets[2]=new Bullet(200,300,4);
        bullets[3]=new Bullet(200,250,4);
        bullets[4]=new Bullet(200,200,4);
        bullets[5]=new Bullet(200,150,4);

		sky=new Sky();
		hero=new Hero(200,500);
	}

	public void paint(Graphics g) {
        sky.paint(g);
		hero.paint(g);

		for(int i=0; i<bullets.length; i++) {
			//i=0  1  2  3  4  5
			bullets[i].paint(g);
		}
		//调用每个飞机的多态方法,实现多态的绘制
		for(int i=0; i<planes.length; i++) {
			airplanes[i].paint(g);
		}
        for(int i=0; i<planes.length; i++) {
			bigplanes[i].paint(g);
		}
	}

	public static void main(String[] args) {
		JFrame frame=new JFrame();
		World world=new World();
		frame.add(world);
		frame.setSize(400,700);
		frame.setLocationRelativeTo(null);
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.setVisible(true);
        //调用action方法启动定时器
        world.action();
	}
    /*
	 * 添加内部类,实现定时计划任务
	 * 为何使用内部类实现定时任务
	 * 1,隐藏定时任务到world类中
	 * 2,可以访问外部类中的数据,飞机,子弹等
	 */
   private class PaintTask extends TimerTask{
        public void run(){
            //执行飞机移动方法;是多态的移动方法,每个飞机都不同
            for(int i=0; i<airplanes.length; i++){
                airplanes[i].move();
            }
            for(int i=0; i<bigplanes.length; i++){
                bigplanes[i].move();
            }
            for(int i=0; i<bullets.length; i++){
                bullets[i].move();
            }
            sky.move();
            repaint();	//调用重写绘制方法,这个方法会自动执行paint
        }
    }
    public void action(){//启动方法
        Timer timer=new Timer();
        PaintTask task=new PaintTask();//定时器任务
        timer.schedule(task,1000,1000/100);//规定计划每间隔多少时间执行
    }
}

2 多态

2.1 什么是多态

多态是面向对象三大基本特征之一。

  1. 个体多态:父类型定义的变量引用的子类型个体是多种多样的,个体多态是用向上造型实现的
  2. 行为多态:父类型变量引用子类型实例后,执行方法时候可以得到多种结果,行为多态是利用方法重写实现的

使用多态编程可以使不同类型个体进行统一管理,可以实现不同行为的功能进行统一处理。

2.2 飞行物与多态

开发步骤:

  1. 重构World类,使用一个FlyingObject数组存储各种飞机
  2. 重构定时器,执行每个飞行物体的move
  3. 重构绘制代,执行每个飞行物的paint方法

代码:

package demo05;
import java.awt.Graphics;
import java.util.Timer;
import java.util.TimerTask;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class World extends JPanel{
	/*
	 * 添加飞机大战世界中的物体
	 */
	private FlyingObject[]Planes;//所有的可以被打掉的敌机
    
    private Bullet[]bullets;
    private Sky sky;
    private Hero hero;
    
	/*
	 * 利用构造器初始化世界中每个物体
	 */
	public World(){
        //多态数组,数组中可以存储多个类型对象
        //好处:一个数组统一管理各种对象
		planes=new FlyingObject[6];//创建数组对象
        planes[0]=new Airplane(10,10,1.5);
        planes[1]=new Airplane(10,100,1.5);
        planes[2]=new Bigplane(100,20,2);
        planes[3]=new Bigplane(100,220,2);
        planes[4]=new Bee(200,200,1.5);
        planes[5]=new Bee(200,280,1.5);
            
		bullets =new Bullet[6];
		bullets[0]=new Bullet(200,400,4);//宽高去掉了10,10
		bullets[1]=new Bullet(200,350,4);
        bullets[2]=new Bullet(200,300,4);
        bullets[3]=new Bullet(200,250,4);
        bullets[4]=new Bullet(200,200,4);
        bullets[5]=new Bullet(200,150,4);

		sky=new Sky();
		hero=new Hero(200,500);
	}

	public void paint(Graphics g) {
        sky.paint(g);
		hero.paint(g);

		for(int i=0; i<bullets.length; i++) {
			//i=0  1  2  3  4  5
			bullets[i].paint(g);
		}
		//调用每个飞机的多态方法,实现多态的绘制
		for(int i=0; i<planes.length; i++) {
			planes[i].paint(g);
		}
	}

	public static void main(String[] args) {
		JFrame frame=new JFrame();
		World world=new World();
		frame.add(world);
		frame.setSize(400,700);
		frame.setLocationRelativeTo(null);
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.setVisible(true);
        //调用action方法启动定时器
        world.action();
	}
    /*
	 * 添加内部类,实现定时计划任务
	 * 为何使用内部类实现定时任务
	 * 1.隐藏定时任务到world类中
	 * 2.可以访问外部类中的数据,飞机,子弹等
	 */
   private class PaintTask extends TimerTask{
        public void run(){//执行飞机移动方法;是多态的移动方法,每个飞机都不同
            for(int i=0; i<planes.length; i++){
               planes[i].move();
            }
          
            for(int i=0; i<bullets.length; i++){
                bullets[i].move();
            }
            sky.move();
            repaint();	//调用重写绘制方法,这个方法会自动执行paint
        }
    }
    public void action(){//启动方法
        Timer timer=new Timer();
        PaintTask task=new PaintTask();//定时器任务
        timer.schedule(task,1000,1000/100);//规定计划每间隔多少时间执行
    }
}
3 播放动画
3.1 动画原理

动画原理是将连续动画帧按照一定时间间隔播放就能实现动画。

3.2 实现播放动画

实现步骤:

  1. 在FlyingObject定义图片数组存储图片,一个存储动画,一个存储爆炸动画
  2. 在构造器中初始图片数组
  3. 在FlyingObject中增加index变量,作为计数索引
  4. 添加nextimage方法,实现切换动画帧功能
  5. 编写案例进行单元测试

案例:父类FlyingObject中声明动画帧图片数组和图片序号index,以及nextimage方法计算下一张图片:

package demo06;
import java,awt.Graphics;
import javax.swing.ImageIcon;
/*
 * 父类中定义从子类抽取的属性和方法
 * 这种抽取方式称为“泛化”
 */
public abstract class FlyingObject {
	 protected double x;
	 protected double y;
	 protected double width;
	 protected double height;
     protected double step;
     protected ImageIcon image;
    
    //当前对象动画帧,如果没有:如子弹、天空,则此属性保持null
    protected ImageIcon[] images;
    
    //爆炸效果动画帧,如果没有保持null
    protected ImageIcon[] bom;
    
    //动画帧播放计数器,%数组长度得到播放动画帧的位置
    protected int index=0;
  
    public abstract void move();
    
    //动画帧播放方法
    public void nextImage(){
        //没有动画帧时候,不播放动画帧图片
        if(images==null){
            return;
        }
        //System.out.println(index +","+(index % images.length));//打桩
		image=images[index++/30 % images.length];//除以30等于调慢30倍
    }
        
    public void paint(Graphics g){
        nextImage();//换动画帧,然后再绘制
        image.paintIcon(null,g,(int)x, (int)y);
    }
	@Override
	public String toString() {
        String className=getClass().getName();
		return className+" [x=" + x + ", y=" + y + ", width=" + width + ", height=" + height + "]";
	}
}

子弹和天空没有动画帧,无需初始化动画帧数组。

package demo06;
public class Airplane extends Plane{
    
	public Airplane(double x, double y, double width, double height, double step) {
		//利用super()调用父类有参数构造器,复用了父类中构造器算法
		//super(x,y,width,height);
        this.x=x;
        this.y=y;
		this.step = step;
        image=Images.airplane[0];
        width=image.getIconWidth();
        height=image.getIconHeight();
        bom=Images.bom;
	}
}
package demo06;
public class Bee extends Plane{
    public Bee(double x, double y, double width, double height, double step){
        //super(x,y,width,height);
        this.x=x;
        this.y=y;
        this.step=step;
        this.image=Images.bee[0];
        width=image.getIconWidth();
        height=image.getIconHeight();
        bom=Images.bom;
    }
    /**
     *重写父类型move方法修改为斜向飞行
     */
    public void move(){
        //调用父类型方法,复用父类型定义的算法
        super.move();//向下飞行     x++;
    }
}
package demo06;
public class Hero extends FlyingObject{
    
	public Hero(double x, double y, double height, double width) {
		//super(x,y,width,height);
        this.x=x;
        this.y=y;
        this.image=Images.hero[0];
        width=image.getIconWidth();
        height=image.getIconHeight();
        images=Images.hero;
        bom=Images.bom;
	}
	/*
	 * 重写move方法,空方法,目的是不动
	 * 修改超类中规定的向下飞,改成不动
	 */
	public void move() {
	}
	/*
	 *将鼠标机移动到鼠标位置X,Y
	 *@param x鼠标位置x
	 *@param y鼠标位置y
	 */
	public void move(int x, int y) {
		this.x=x;
		this.y=y;
	}
}
package demo06;
public class Bigplane extends Plane{
    
	public Bigplane(double x, double y, double width, double height, double step) {
		//利用super()调用父类有参数构造器,复用了父类中构造器算法
		//super(x,y,width,height);
        this.x=x;
        this.y=y;
		this.step = step;
        this.image=Images.bigplane[0];
        width=image.getIconWidth();
        height=image.getIconHeight();
        bom=Images.bom;
	}
}

编织写测试案例测试:

package demo06;

public class NextImageTest {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		/*
		 * 测试动画帧播放算法
		 */
		Airplane plane=new Airplane(10,10,2);
		plane.nextImage();
		System.out.println(plane.image);
		plane.nextImage();
		System.out.println(plane.image);
		plane.nextImage();
		System.out.println(plane.image);
		plane.nextImage();
		System.out.println(plane.image);
		plane.nextImage();
		System.out.println(plane.image);
		plane.nextImage();
		
		Bigplane p2=new Bigplane(10,10,5);
		p2.nextImage();
		System.out.println(p2.image);
		p2.nextImage();
		System.out.println(p2.image);
		p2.nextImage();
		System.out.println(p2.image);
		p2.nextImage();
		System.out.println(p2.image);
		p2.nextImage();
		System.out.println(p2.image);
		p2.nextImage();
		System.out.println(p2.image);
		
		Bullet b=new Bullet(10,10);
		b.nextImage();
		System.out.println(b.image);
		b.nextImage();
		System.out.println(b.image);
		b.nextImage();
		System.out.println(b.image);
		b.nextImage();
		System.out.println(b.image);
		b.nextImage();
		System.out.println(b.image);
		b.nextImage();
	}
}

4 敌机入场

4.1 敌机入场位置计算

屏幕上方随机入场

案例:小飞机

package demo06;
import java.util.Random;
public class Airplane extends Plane{
    public Airplane(){
        Random random=new Random();
        image=Images.airplane[0];
        width=image.getIconWidth();
        height=image.getIconHeight();
        x=random.nextInt(400-(int)width);//0~400-w
        y=-height;
        step=random.nextDouble()*5+1;
        //初始化小飞机动画帧
        images=Images.airplane;
        bom=Images.bom;
    }
    
	public Airplane(double x, double y, double width, double height, double step) {
		//利用super()调用父类有参数构造器,复用了父类中构造器算法
		//super(x,y,width,height);
        this.x=x;
        this.y=y;
		this.step = step;
        image=Images.airplane[0];
        width=image.getIconWidth();
        height=image.getIconHeight();
        //初始化小飞机动画帧
        images=Images.airplane;
        bom=Images.bom;
	}
}

小飞机测试案例:

package demo06;
public class AirplaneTest {
	public static void main(String[] args) {
		/*
		 * 测试小飞机从屏幕上方出场
		 */
		Airplane plane=new Airplane();
		System.out.println(plane);
		System.out.println(plane.image);
		System.out.println(plane.images);
		System.out.println(plane.bom);
		System.out.println(plane.step);
	}
}

大飞机:

package demo06;
import java.util.Random;
public class Bigplane extends Plane{
	
	public Bigplane() {
		Random random=new Random();
		image=Images.bigplane[0];
		width=image.getIconWidth();
		height=image.getIconHeight();
		x=random.nextInt(400-(int)width);
		y=-height;
		step=random.nextDouble()*3+0.7;
		images=Images.bigplane;
		bom=Images.bom;
	}

	public Bigplane(double x, double y, double step) {
		//super(x,y,width,height);
		this.x=x;
		this.y=y;//初始化
		this.step = step;
		this.image=Images.bigplane[0];
		width=image.getIconWidth();
		height=image.getIconHeight();
		images=Images.bigplane;
		bom=Images.bom;
	}

大飞机测试案例:

package demo06;
public class BigplaneTest {
	public static void main(String[] args) {
		/*
		 * 大飞机出场位置测试
		 */
		Bigplane plane=new Bigplane();
		System.out.println(plane);
		System.out.println(plane.step);
		System.out.println(plane.image);
		System.out.println(plane.images);
		System.out.println(plane.bom);
	}
}

蜜蜂:

package demo06;
import java.util.Random;
public class Bee extends Plane{
	
	public Bee() {
		Random random=new Random();
		image=Images.bee[0];
		width=image.getIconWidth();
		height=image.getIconHeight();
		x=random.nextInt(400-(int)width);
		y=-height;
		step=random.nextDouble()*3+0.7;
		images=Images.bee;
		bom=Images.bom;
	}

	public Bee(double x, double y, double step) {
		//super(x,y,width,height);
		this.x=x;
		this.y=y;//初始化
		this.step = step;
		this.image=Images.bee[0];
		width=image.getIconWidth();
		height=image.getIconHeight();
		images=Images.bee;
		bom=Images.bom;
	}
     /**
     *重写父类型move方法修改为斜向飞行
     */
    public void move(){
        //调用父类型方法,复用父类型定义的算法
        super.move();//向下飞行     x++;
    }
}

蜜蜂测试案例:

package demo06;
public class BeeTest {
	public static void main(String[] args) {
		/*
		 * 蜜蜂出场位置测试
		 */
		Bee plane=new Bee();
		System.out.println(plane);
		System.out.println(plane.step);
		System.out.println(plane.image);
		System.out.println(plane.images);
		System.out.println(plane.bom);
	}
}
4.2 重构构造器

可将以上构造器算法抽取到他们的父类Plane中,子类利用super复用这个构造器算法,减少冗余提高代码复用。

实现步骤:

  1. 在FlyingObject中创建两个构造器

    • 无参数构造器:被子类默认调用,避免子类继承错误
    • 有参数构造器:根据位置初始化飞行物的数据,被全部子类调用
  2. 在Plane增加三个构造器

    • 默认构造器:被子类默认调用,避免子类继承错误
    • 有参数构造器1:调用父类根据位置初始化数据
    • 有参数构造器2:根据图片计算出从屏幕上方出场的位置
  3. 测试

案例:父类型定义构造器

package demo07;
import java,awt.Graphics;
import javax.swing.ImageIcon;
/*
 * 父类中定义从子类抽取的属性和方法
 * 这种抽取方式称为“泛化”
 */
public abstract class FlyingObject {
	 protected double x;
	 protected double y;
	 protected double width;
	 protected double height;
     protected double step;
     protected ImageIcon image;
    
    //当前对象动画帧,如果没有:如子弹、天空,则此属性保持null
    protected ImageIcon[] images;
    
    //爆炸效果动画帧,如果没有保持null
    protected ImageIcon[] bom;
    
    //动画帧播放计数器,%数组长度得到播放动画帧的位置
    protected int index=0;
    
    //无参数构造器,减少子类的编译错误
    public FlyingObject(){
    }
    //根据位置初始化x,y,image,images,bom
    public FlyingObject(double x, double y, 
                        ImageIcon image, ImageIcon[] Images, ImageIcon[] bom) {
		                      //当前图片        全部动画帧       爆炸图片                             
		this.x=x;
		this.y=y;
		this.image=image;
		this.images=Images;
		this.bom=bom;
		width=image.getIconWidth();
		height=image.getIconHeight();
	}
  
    public abstract void move();
    
    //动画帧播放方法
    public void nextImage(){
        //没有动画帧时候,不播放动画帧图片
        if(images==null){
            return;
        }
        //System.out.println(index +","+(index % images.length));//打桩
		image=images[index++/30 % images.length];//除以30等于调慢30倍
    }
        
    public void paint(Graphics g){
        nextImage();//换动画帧,然后再绘制
        image.paintIcon(null,g,(int)x, (int)y);
    }
	@Override
	public String toString() {
        String className=getClass().getName();
		return className+" [x=" + x + ", y=" + y + ", width=" + width + ", height=" + height + "]";
	}
}
package demo07;
import java.util.Random;
import javax.swing.ImageIcon;
public abstract class Plane extends FlyingObject{
	public Plane() {
	}
	/*
	 * 根据位置初始化对象数据
	 */
	public Plane(double x, double y, ImageIcon image, ImageIcon[] Images, ImageIcon[] bom) {
		super(x, y, image, Images, bom);
	}
	/*
	 * 利用算法实现飞机从屏幕上方出场
	 */
	public Plane(ImageIcon image, ImageIcon[] Images, ImageIcon[] bom) {
		Random random=new Random();
		this.image=image;
		width=image.getIconWidth();
		height=image.getIconHeight();
		x=random.nextInt(400-(int)width);
		y=-height;
		this.images=Images;
		this.bom=bom;
	}

	public void move() {
		y +=step;
	}
}

子类调用父类构造器

package demo07;
import java.awt.Graphics;
public class Sky extends FlyingObject{

 	protected double yo;
	public Sky(){
        super(0,0,Images.sky,null,null);
        step=0.8;
        yo=-height;
    }
	public void move() {
		y+=step;
		yo+=step;//(两个坐标都在移动)

		if(y>=height) {
			//System.out.println("第一个照片返回:y="+y);
			y=-height;
            //System.out.println("y="+y+" y0="+yo+" width="+width+" height="+height);
		}
		if(yo>=height) {
			//System.out.println("第二个照片返回:y0="+yo);
			yo=-height;
			//System.out.println("y="+y+" y0="+yo+" width="+width+" height="+height);
		}
	}
	public void paint(Graphics g) {//改写父类中的方法
		image.paintIcon(null, g, (int)x, (int)y);
		image.paintIcon(null, g, (int)x, (int)yo);//对天空做修改,对两张照片进行处理
	}
}
package demo07;
public class Bullet extends FlyingObject{
    
	public Bullet(double x, double y) {
		super(x,y,Images.bullet,null,null);
        this.step=4;
	}
    /**
     *重写继承与超类的move方法,作用就是修改了超类move行为
     *超类是向下移动,修改为向上移动
     */
    public void move(){
        y-=step;
    }
}
package demo07;
public class Hero extends FlyingObject{
    
	public Hero(double x, double y){
        super(x, y, Images.hero[0], Images.hero, Images.bom);
    }
	/*
	 * 重写move方法,空方法,目的是不动
	 * 修改超类中规定的向下飞,改成不动
	 */
	public void move() {
	}
	/*
	 *将鼠标机移动到鼠标位置X,Y
	 *@param x鼠标位置x
	 *@param y鼠标位置y
	 */
	public void move(int x, int y) {
		this.x=x;
		this.y=y;
	}
}
package demo07;
public class Airplane extends Plane{
    //飞机从屏幕上方随机位置出场
    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;
	}
}
package demo07;
public class Bigplane extends Plane{
    //飞机从屏幕上方随机位置出场
    public Bigplane(){
        super(Images.bigplane[0], Images.bigplane, Images.bom);
        step=Math.random()*3+0.5;
    }
    //从自定位置出场
	public Bigplane(double x, double y, double step) {
		//利用super()调用父类有参数构造器,复用了父类中构造器算法
		super(x, y, Images.bigplane[0], Images.bigplane, Images.bom);
        this.step=step;
	}
}
package demo07;
public class Bee extends Plane{
	
	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();//向下飞行     x++;
    }
}

单元测试案例:

package demo07;
public class Demo01{
	/*
	 * 测试构造器
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Sky sky=new Sky();
		System.out.println(sky);
		System.out.println(sky.image);
		
		Bullet bullet=new Bullet(10,10);
		System.out.println(bullet);
		System.out.println(bullet.image);
		
		Hero hero=new Hero(200,200);
		System.out.println(hero);
		System.out.println(hero.image);
		System.out.println(hero.images);
		System.out.println(hero.bom);
		
		Airplane airplane=new Airplane();
		System.out.println(airplane);
		System.out.println(airplane.image);
		System.out.println(airplane.images);
		System.out.println(airplane.bom);
		System.out.println(airplane.step);
		
		Bigplane bigplane=new Bigplane();
		System.out.println(bigplane);
		System.out.println(bigplane.image);
		System.out.println(bigplane.images);
		System.out.println(bigplane.bom);
		System.out.println(bigplane.step);
		
		Bee bee=new Bee();
		System.out.println(bee);
		System.out.println(bee.image);
		System.out.println(bee.images);
		System.out.println(bee.bom);
		System.out.println(bee.step);
	}
}

整合测试:

package demo07;
import java.awt.Graphics;
import java.util.Timer;
import java.util.TimerTask;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class World extends JPanel{
	/*
	 * 添加飞机大战世界中的物体
	 */
    //private Airplane[]airplanes;
    //private Bigplane[]bigplanes;
	private FlyingObject[]Planes;//所有的可以被打掉的敌机
    
    private Bullet[]bullets;
    private Sky sky;
    private Hero hero;
    
	/*
	 * 利用构造器初始化世界中每个物体
	 */
	public World(){
        //多态数组,数组中可以存储多个类型对象
        //好处:一个数组统一管理各种对象
		planes=new FlyingObject[6];//创建数组对象
        planes[0]=new Airplane(10,10,1.5);
        planes[1]=new Airplane(10,100,1.5);
        planes[2]=new Bigplane(100,20,2);
        planes[3]=new Bigplane(100,220,2);
        planes[4]=new Bee(200,200,1.5);
        planes[5]=new Bee(200,280,1.5);
        planes[6]=new Bee();
        planes[7]=new Airplane();
        planes[8]=new Airplane();
        planes[9]=new Bigplane();
            
		bullets =new Bullet[6];
		bullets[0]=new Bullet(200,400,4);//宽高去掉了10,10
		bullets[1]=new Bullet(200,350,4);
        bullets[2]=new Bullet(200,300,4);
        bullets[3]=new Bullet(200,250,4);
        bullets[4]=new Bullet(200,200,4);
        bullets[5]=new Bullet(200,150,4);

		sky=new Sky();
		hero=new Hero(200,500);
	}

	public void paint(Graphics g) {
        sky.paint(g);
		hero.paint(g);

		for(int i=0; i<bullets.length; i++) {
			//i=0  1  2  3  4  5
			bullets[i].paint(g);
		}
		//调用每个飞机的多态方法,实现多态的绘制
		for(int i=0; i<planes.length; i++) {
			planes[i].paint(g);
		}
	}

	public static void main(String[] args) {
		JFrame frame=new JFrame();
		World world=new World();
		frame.add(world);
		frame.setSize(400,700);
		frame.setLocationRelativeTo(null);
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.setVisible(true);
        //调用action方法启动定时器
        world.action();
	}
    /*
	 * 添加内部类,实现定时计划任务
	 * 为何使用内部类实现定时任务
	 * 1.隐藏定时任务到world类中
	 * 2.可以访问外部类中的数据,飞机,子弹等
	 */
   private class PaintTask extends TimerTask{
        public void run(){
            //执行飞机移动方法;是多态的移动方法,每个飞机都不同
            for(int i=0; i<planes.length; i++){
               planes[i].move();
            }
          
            for(int i=0; i<bullets.length; i++){
                bullets[i].move();
            }
            sky.move();
            repaint();	//调用重写绘制方法,这个方法会自动执行paint
        }
    }
    public void action(){//启动方法
        Timer timer=new Timer();
        PaintTask task=new PaintTask();//定时器任务
        timer.schedule(task,1000,1000/100);//规定计划每间隔多少时间执行
    }
}
4.3 定时出场

实现方案:

  1. 在定时器任务中创建飞机对象
  2. 将飞机数组扩容
  3. 将飞机对象追加到数组中

Word类中:

private int index=0;//计数器
//index=0 1 2 3 4 5 6 7 8 9 ...
//      *       *       *
//index % 4==0
 private class PaintTask extends TimerTask{
        public void run(){
            index++;
            if(index % 16==0){
                Airplane plane=new Airplane();
                //数组扩容
                planes=Arrays.conpyOf(planes, planes.length+1);
                //将新飞机添加到新数组最后位置
                planes[planes.length-1]=plane;
            }
            //执行飞机移动方法,是多态的移动方法,每个飞机都不同
            for(int i=0; i<planes.length;i++){
               planes[i].move();
            }
          
            for(int i=0; i<bullets.length;i++){
                bullets[i].move();
            }
            sky.move();
            repaint();	//调用重写绘制方法,这个方法会自动执行paint
        }
    }
4.4 随机出场

实现步骤:

  1. 生成随机数0~9
  2. 数字是0~范围创建小飞机
  3. 数字是7~8范围创建大飞机
  4. 数字是9创建蜜蜂
  5. 由于算法复杂,可以抽取封装为方法createPlane()

代码:World类

private int index=0;//计数器
//index=0 1 2 3 4 5 6 7 8 9 ...
//      *       *       *
//index % 4==0
//创建一个飞机,进入场地
public void createPlane(){
    if(index % 16==0){
        Random random=new Random();
        int n=random.nextInt(10);//0~9
        Plane palne;
        if(n<=6){
            plane=new Airplane();
        }else if(n<=8){
            plane=new Bigplane();
        }else{
            plane=new Bee();
        }
         
        //数组扩容
        planes=Arrays.conpyOf(planes, planes.length+1);
        //将新飞机添加到新数组最后位置
        planes[planes.length-1]=plane;
        }
    }
	/*
	 * 添加内部类,实现定时计划任务
	 * 为何使用内部类实现定时任务
	 * 1.隐藏定时任务到world类中
     * 2.可以访问外部类中的数据,飞机,子弹等
	 */
 private class PaintTask extends TimerTask{
        public void run(){
            index++;
            createPlane();
            //执行飞机移动方法,是多态的移动方法,每个飞机都不同
            for(int i=0; i<planes.length; i++){
               planes[i].move();
            }
          
            for(int i=0; i<bullets.length; i++){
                bullets[i].move();
            }
            sky.move();
            repaint();	//调用重写绘制方法,这个方法会自动执行paint
        }
    }
4.7 switch...case语句

多路分支语句。可以根据整数,字符串,枚举类型进行分支。

具体语法:

  • Java 5开始switch case支持枚举类型
  • Java 7开始switch case支持String类型
  • break用于结束switch语句
  • byte、short、char可以自动转换为int,可以作为分支条件
  • case后只能是常量,不能是表达式

案例:

  • 输入成绩是百分制
  • 评定等级,90~100 A
  • 80~89 B
  • 60~79 C
  • 59以下 D
package demo09;
import java.util.Scanner;
public class SwichDemo {
	public static void main(String[] args) {
		/*
		 * switch case演示
		 */
		Scanner console=new Scanner(System.in);
		System.out.println("输入分数");
		int score=console.nextInt();
		//100-0整除得到;10 9 8 7 6 5 4 3 2 1 0
		switch(score/10) {
		case 10:
		case 9:
			System.out.println("A");
			break;
		case 8:
			System.out.println("B");
			break;
		case 7:
		case 6:
			System.out.println("c");
			break;
		default:
			System.out.println("D");
		}
	}
}
4.8 利用switch...case优化创建飞机算法

案例:

package demo07;
import java.awt.Graphics;
import java.util.Timer;
import java.util.TimerTask;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class World extends JPanel{
	/*
	 * 添加飞机大战世界中的物体
	 */
    //private Airplane[]airplanes;
    //private Bigplane[]bigplanes;
	private FlyingObject[]Planes;//所有的可以被打掉的敌机
    
    private Bullet[]bullets;
    private Sky sky;
    private Hero hero;
    
	/*
	 * 利用构造器初始化世界中每个物体
	 */
	public World(){
        //多态数组,数组中可以存储多个类型对象
        //好处:一个数组统一管理各种对象
		planes=new FlyingObject[6];//创建数组对象
        planes[0]=new Airplane(10,10,1.5);
        planes[1]=new Airplane(10,100,1.5);
        planes[2]=new Bigplane(100,20,2);
        planes[3]=new Bigplane(100,220,2);
        planes[4]=new Bee(200,200,1.5);
        planes[5]=new Bee(200,280,1.5);
        planes[6]=new Bee();
        planes[7]=new Airplane();
        planes[8]=new Airplane();
        planes[9]=new Bigplane();
            
		bullets =new Bullet[6];
		bullets[0]=new Bullet(200,400,4);//宽高去掉了10,10
		bullets[1]=new Bullet(200,350,4);
        bullets[2]=new Bullet(200,300,4);
        bullets[3]=new Bullet(200,250,4);
        bullets[4]=new Bullet(200,200,4);
        bullets[5]=new Bullet(200,150,4);

		sky=new Sky();
		hero=new Hero(200,500);
	}

	public void paint(Graphics g) {
        sky.paint(g);
		hero.paint(g);

		for(int i=0; i<bullets.length; i++) {
			//i=0  1  2  3  4  5
			bullets[i].paint(g);
		}
		//调用每个飞机的多态方法,实现多态的绘制
		for(int i=0; i<planes.length; i++) {
			planes[i].paint(g);
		}
	}

	public static void main(String[] args) {
		JFrame frame=new JFrame();
		World world=new World();
		frame.add(world);
		frame.setSize(400,700);
		frame.setLocationRelativeTo(null);
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.setVisible(true);
        //调用action方法启动定时器
        world.action();
	}
    
    private int index=0;//计数器
    //index=0 1 2 3 4 5 6 7 8 9 ...
//      *       *       *
//index % 4==0
//创建一个飞机,进入场地
public void createPlane(){
    if(index % 16==0){
        Random random=new Random();
        int n=random.nextInt(10);//0~9
        Plane palne;
        switch(n){
            case 8:
            case 7:
                plane=new Bigplane();
                break;
            case 9:
                plane=new Bee();
                break;
            default:
                plane=new Airplane();
        }
        //数组扩容
        planes=Arrays.conpyOf(planes, planes.length+1);
        //将新飞机添加到新数组最后位置
        planes[planes.length-1]=plane;
        }
    }
    
    /*
	 * 添加内部类,实现定时计划任务
	 * 为何使用内部类实现定时任务
	 * 1.隐藏定时任务到world类中
	 * 2.可以访问外部类中的数据,飞机,子弹等
	 */
   private class PaintTask extends TimerTask{
        public void run(){
            index++;
            //执行飞机移动方法;是多态的移动方法,每个飞机都不同
            for(int i=0; i<planes.length; i++){
               planes[i].move();
                createPlane();
            }
          
            for(int i=0; i<bullets.length; i++){
                bullets[i].move();
            }
            sky.move();
            repaint();	//调用重写绘制方法,这个方法会自动执行paint
        }
    }
    public void action(){//启动方法
        Timer timer=new Timer();
        PaintTask task=new PaintTask();//定时器任务
        timer.schedule(task,1000,1000/100);//规定计划每间隔多少时间执行
    }
}
posted @ 2021-03-01 00:28  指尖上的未来  阅读(49)  评论(0)    收藏  举报