栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

py-04-JAVAOOP

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

py-04-JAVAOOP

目录: 1、day01-产生各对象并写出移动的方法(类和对象、构造方法) 2、day02- 抽出敌人父类(数组、继承、重写) 3、day03- 各对象加载图片、使各对象移动(向上造型、访问修饰符) 4、day04- 画出敌人对象到面板上(抽象类、接口) 5、day05- 产生敌人对象、子弹对象、天空对象、画在面板上并移动(内部类、定时器) 6、day06- 英雄机随着鼠标移动、状态切换、删除越界对象、开打、画命、分、火力值(多态) 7、 day07-英雄机和敌人的碰撞 检查游戏结束,画状态 8、Java程序编译和运行的过程 9、 构造代码块、静态代码块、构造函数执行先后顺序
day01 1.内容回顾

方法

2.面向对象程序设计

OOP:Object oriented programming

2.1面向过程的结构化程序设计

案例:打印员工信息  ,修改员工信息后再打印 

面向过程编程思想:

    特点:执行过程是什么样的,代码就写成什么样,代码编写更偏向于计算机底层的执行思维

   偏向于计算机的执行思维 

面向对象程序设计:

    面向对象 - OOP:更加接近于人的思维方式 

    JAVA语言是面向对象的一门语言。

面向对象的体现:

将某一类事物的数据和行为封装成一个完整的个体,这个个体就叫做类。

打印员工信息用OOP实现

public class Emp {
String name;
int age;
char gender;
double salary;
public void printEmpInfo(){
System.out.println(name+"-"+age+"-"
+gender+"-"+salary);
}
}
public class PrintEmpObjectInfo02 {
public static void main(String[] args) {
//创建一个员工
Emp emp = new Emp();
emp.name = "amy";
emp.age = 34;
emp.gender = '女';
emp.salary = 10000;
//打印
emp.printEmpInfo();
}
}
2.2 类和对象

 什么是类:类是一类事物的模板

       类的定义:成员变量   方法

       成员变量:创建对象时会为成员变量进行默认初始化,默认初始化取决于成员变量的数据类型。

什么是对象

       经由模板创建出来的一个具体的个体

       其实类中定义的属性和行为是属于对象的

类和对象的关系:

    类是模板,对象是经由模板创建出来的具体的实例。

2.3对象的创建和使用

对象的创建:    类名 引用 = new 类名();

    举例:

       Emp emp = new Emp();

       emp.printEmpInfo();

1.  引用类型变量

2.  访问对象的成员变量,方法

练习:定义shoot项目中的对象类

     Hero:

         width,height,x,y,life,doubleFire

         step(){}

     Airplane:

         width,height,x,y,step

         step()

     BigAirPlane:

         width,height,x,y,step

         step()

     Bee:

         width,height,x,y,xstep,ystep

         step()

     Bullet:

         width,height,x,y,step

         step()

     Sky:

         width,height,x,y,step

         step()

     World:

         创建对象并赋值

         方法:

            实现各个对象的移动

public class Airplane {
int width;
int height;
int x;
int y;
int step;
//移动行为
public void step(){
System.out.println("小敌机移动了!");
}
}
public class BigAirplane {
int width;
int height;
int x;
int y;
int step;
//移动行为
public void step(){
System.out.println("大敌机移动了!");
}
}
public class Bee {
int width;
int height;
int x;
int y;
int xstep;
int ystep;
//移动行为
public void step(){
System.out.println("蜜蜂移动了!");
}
} 
public class Hero {
int width;
int height;
int x;
int y;
int life;
int doubleFire;
//移动行为
public void step(){
System.out.println("英雄级移动了!");
}
}
public class Sky {
int width;
int height;
int x;
int y;
int step;
//移动行为
public void step(){
System.out.println("天空移动了!");
}
}
public class Bullet {
int width;
int height;
int x;
int y;
int step;
//移动行为
public void step(){
System.out.println("子弹移动了!");
}
}
public class World {
Hero hero = new Hero();
Airplane airplane = new Airplane();
Airplane airplane2 = new Airplane();
BigAirplane bigAirplane = new BigAirplane();
BigAirplane bigAirplane1 = new BigAirplane();
Bee bee = new Bee();
Bee bee2 = new Bee();
Bullet bullet = new Bullet();
Bullet bullet2 = new Bullet();
Sky sky = new Sky();
public static void main(String[] args) {
World world = new World();
world.action();
}
public void action(){
//给对象赋值
hero.width = 97;
hero.height = 124;
hero.x = 140;
hero.y = 400;
hero.life = 3;
hero.doubleFire = 0;
//动起来
hero.step();
}
} 
3.方法的重载 - overload 3.1 方法签名

方法的签名包含如下两个方面:

       方法名和参数列表 一个类中,不可以有两个方法的签名完全相同,即一个类中不可以有两个方法的方法名和参

数列表都完全一样。

       如果一个类的两个方法只是方法名相同而参数列表不同,是可以的。

3.2方法的重载:

     在Java语言中,同一个类中允许多个方法的名称相同,但参数列表不同,称之为方法的重载(overload)。

方法名同,参数列表不同

参数列表不同体现在:

    1.  参数列表的个数不同

              2.  参数列表个数相同,但是类型不同

方法的重载的注意点:

      1.  方法的重载和方法的返回值类型无关

      2.  方法的重载和方法的访问控制修饰符无关

      3.  通过方法签名来确定要调用的方法

public class OverLoadDemo {
public static void main(String[] args) {
OverLoadDemo demo = new OverLoadDemo();
demo.pay("a");
}

public int pay(double money){
System.out.println("使用现金支付");
return 1;
}

private  void pay(String phone){
System.out.println("使用手机二维码支付");
}

public void pay(String cardId,String pwd){
System.out.println("刷卡支付");
}
}
4.构造方法:

    语法结构:

       修饰符 类名(){}

    作用:

       创建一个emp对象: Emp emp = new Emp();

       在创建对象时会调用,初始化成员变量

    java会为每一个类默认提供一个构造方法

public class Person01 {

public Person01() {
super();
System.out.println("调用构造方法");
}
public class Test {
public static void main(String[] args) {
new Person01();
}

}

    分类:

       无参构造方法

       有参构造方法:

public class ConstrutorDemo01 {
String name;
int age;
char gender;
//无参构造方法
public ConstrutorDemo01() {
super();
}
//有参构造方法:作用:初始化成员变量
public ConstrutorDemo01(String name, int age, char gender) {
this.name = name;
this.age = age;
this.gender = gender;
}
public void printInfo(){
System.out.println(name+"-"+age+"-"+gender);
}
}
public class ContrutorTest {
public static void main(String[] args) {
//创建ConstrutorDemo01对象,并为属性赋值
//    ConstrutorDemo01 construtorDemo01 =
//          new ConstrutorDemo01();
//    construtorDemo01.name = "tom";
//    construtorDemo01.age = 23;
//    construtorDemo01.gender = '男';
ConstrutorDemo01 demo01 =
new ConstrutorDemo01("jack", 34, '男');
//输出此对象属性的值
demo01.printInfo();

无参,有参构造方法注意点:

    1.一个类中无参构造方法最多只能有一个,可以有多个有参构造方法

    2.如果一个类中没有人为生成构造方法,那么java会默认提供无参构造方法,如果人为生成构造方法,那么java不会再默认提供无参构造方法。

this关键字:

- this关键字用在方法体中,用于指向调用该方法的当前对象;简单的说:哪个对象调用方法,this指的就是哪个对象。

- 为了方便起见,在没有歧义的情况下可以省略this

    this通常都会被省略

    不可以省略的情况:

       有参构造方法中

this关键字指向一个对象,对象是指调用当前方法的对象

this -- >对象

Emp emp = new Emp();

构造方法的重载

练习:设计对象类的构造方法

public class World {
Hero hero = new Hero();
Airplane airplane = new Airplane();
Airplane airplane2 = new Airplane();
BigAirplane bigAirplane = new BigAirplane();
BigAirplane bigAirplane1 = new BigAirplane();
Bee bee = new Bee();
Bee bee2 = new Bee();
Bullet bullet = new Bullet();
Bullet bullet2 = new Bullet();
Sky sky = new Sky();

public static void main(String[] args) {
World world = new World();
world.action();
}
public void action(){
//动起来
hero.step();
airplane.step();
airplane2.step();
bigAirplane.step();
bigAirplane1.step();
bee.step();
bee2.step();
bullet.step();
bullet2.step();
sky.step();
}
}
public class Hero {
int width;
int height;
int x;
int y;
int life;
int doubleFire;
public Hero() {
width = 97;
height = 124;
x = 140;
y = 400;
life = 3;
doubleFire = 0;
}
//移动行为
public void step(){
System.out.println("英雄级移动了!");
}
}
public class Airplane {
int width;
int height;
int x;
int y;
int step;
public Airplane() {
width = 49;
height = 36;
x = (int)(Math.random()*(400-width));
y = -height;
step = 2;
}
//移动行为
public void step(){
System.out.println("小敌机移动了!");
}
}
public class BigAirplane {
int width;
int height;
int x;
int y;
int step;
public BigAirplane() {
width = 69;
height = 99;
x = (int)(Math.random()*(400-width));
y = -height;
step = 2;
}
//移动行为
public void step(){
System.out.println("大敌机移动了!");
}
}
public class Bee {
int width;
int height;
int x;
int y;
int xstep;
int ystep;
public Bee() {
width = 60;
height = 50;
x = (int)(Math.random()*(400-width));
y = -height;
xstep = 1;
ystep = 2;
}
//移动行为
public void step(){
System.out.println("蜜蜂移动了!");
}
}
public class Bullet {
int width;
int height;
int x;
int y;
int step;
public Bullet() {
width = 8;
height = 14;
x = 100;
y = 500;
step = 2;
}
//移动行为
public void step(){
System.out.println("子弹移动了!");
}
}
public class Sky {
int width;
int height;
int x;
int y;
int y1;
int step;
public Sky() {
width = 400;
height = 700;
x = 0;
y = 0;
y1 = -700;
step = 1;
}
//移动行为
public void step(){
System.out.println("天空移动了!");
}
}

知识点梳理:

    1.  面向对象编程思想

    2.  类和对象

    3.  对象的创建和访问

    4.  类的定义:成员变量,方法

    5.  方法的重载:overload

    6.  构造方法:构造器


day02 1.内容回顾

面向对象的编程思想

类和对象

类的成员

对象的创建和使用

方法的重载 :overload

构造方法:

无参构造方法

有参构造方法:

    this:指向调用当前方法的对象

2.JAVA内存管理 2.1程序

       无论代码还是数据,都需要存储在内存中。JVM为Java程序提供并管理所需要的内存空间JVM内存分为“堆”、“栈”和“方法区”三个区域,分别用于存储不同的数据。

              1) 栈:局部变量(形参),局部变量的生命周期

        2) 堆:保存的是对象,成员变量

2.1  问题:

1.成员变量保存在哪里,生命周期是什么?

        当一个对象没有任何引用指向时,认为这个对象使用完毕,此时看成是垃圾,在不久的将来会被回收掉,回收

之后,此时对象就销毁了。

2.2垃圾回收机制:

     1)垃圾回收器(Garbage Collection,GC)是JVM自带的一个功能,用于回收没有任何引用指向的对象。Java

  程序员不用担心内存管理,因为垃圾收集器会自动进行回收管理。

     2)GC判断对象是否可以回收的依据是该对象是否有引用指向,因此,当确定该对象不再使用时,应   该及时将

  其引用设置为null。

     3)System.gc()

           GC的回收对程序员来说是透明的,并不一定一发现有无引用的对象,就立刻回收。一般情况下,当我们需要

      GC线程即刻回收无用对象时,可以调用 System.gc()方法。

          System.gc() 用于建议虚拟机马上调度GC线程回收资源,

2.3 JAVA中的内存泄露,内存溢出

   1)内存溢出:所剩内存不足以分配给请求的资源

   2)内存泄漏:分配出去的内存回收不回来

         关系:

             内存泄漏累积到一定程度,就会造成内存溢出,内存溢出异常名称:

                 java.lang.OutOfMemoryError

            3)方法区:

                    方法区用于存放类的信息,Java程序运行时,首先会通过类装载器载入类文件的字节码信息,经过解析后将其封

             装入方法区。类的各种信息(包括方法)都在方法区存储。

java程序的运行机制:

     先编译:得到.class文件

     后运行(解释执行):解释:对.class文件解析并加载到方法区,之后开始执行

对象:实例

创建对象:实例化对象

注意点:方法只有一份

当类的信息被加载到方法区时,除了类的类型信息以外,同时类内的方法定义也被加载到方法区;

类在实例化对象时,多个对象会拥有各自在堆中的空间,但所有实例对象是共用在方法区中的一份方法定义的。

class  Test{
public static void main(String[] args){
int i = 5;
int[] ary = {2,3,4};
Test test = new Test();
test.change(i,ary);
System.out.println(i);  //5
System.out.println(Arrays.toString(ary))
//ary:10  3   4
}

public void change(int i,int[] ary){
i = 10;
ary[0] = 10;
}
}
3.引用类型数组 3.1 数组是对象

数组属于引用数据类型

int[] ary = new int[3]; 

3.2引用类型数组的初始化

    String[] ary = new String[2];

    ary[0] ?  null

练习:shoot项目应用:

    构造小敌机数组,大敌机数组,蜜蜂数组,子弹数组对象  public class World {  

Hero hero = new Hero();
Airplane[] airplanes = new Airplane[3];
BigAirplane[] bigAirplanes = new BigAirplane[3];
Bee[] bees = new Bee[3];
Bullet[] bullets = new Bullet[3];
Sky sky = new Sky();

public static void main(String[] args) {
World world = new World();
world.action();
}

public void action(){
//向数组中添加敌方机对象
for(int i=0;i<3;i++){
airplanes[i] = new Airplane();
bigAirplanes[i] = new BigAirplane();
bees[i] = new Bee();
bullets[i] = new Bullet();
}

//动起来
for(int i=0;i<3;i++){
airplanes[i].step();
bigAirplanes[i].step();
bees[i].step();
bullets[i].step();
}
hero.step();
sky.step();
}


}
3.3 数组元素的类型是数组类型

     JAVA中的二维甚至多维数组

       java中并没有真正意义上的二维数组

注意点:

1.如果数组中每个数组元素的长度相同,可以采用如下的方式声明:

         int[][] arr = new int[row][col];

2.数组中的每个元素指向的数组的长度可以不同,此时不可以采用一下的方式来初始化数组。

int[][] arr = new int[row][col];

         数组的元素是数组,如何访问?

            长度    元素

            int[][] ary = {{12,2,3},{2,4,6,6}}

访问数组ary中的每个元素?

       ary中第2个元素中的第二个元素

       ary[1][1]

    将ary数组中的第二个元素的所有值遍历输出

//    for(int i=0;i

对一个长度不等的二维数组进行遍历

for(int i=0;i
4.继承

java是面向对象的语言

    面向对象的三大特征:封装   继承   多态 

    案例:Teacher类和Student类有公有的内容

extends关键字

通过extends关键字可以实现类的继承;

父类:基类:超类

子类:派生类

子类(Sub class)可以继承父类(Super class)的成员变量及成员方法,同时也可以定义自己的成员变量和成员方法;

继承了:

1、成员变量

2、成员方法

public class Person {
String name;
int age;
char gender;

}

public class Teacher extends Person {
String teacherId;

public void setValue(){
teacherId = "1001";
name= "tom";
age = 34;
gender = '男';
}

public void task(){
System.out.println("老师的任务是教书育人");
}

}

public class Student extends Person {
String studentId;

public void setValue(){
studentId = "1002";
name = "amy";
age = 24;
gender = '女';
}

public void task(){
System.out.println("学生的任务是学习");
}



}
继承的特点:

1) java中类的继承具有单继承的特点

a)   单继承:一个子类只能有一个父类

2) 继承具有传递性

      a)   class A extends  class B

b)   class B extends  class C

c)   class  C{双眼皮}

不继承的成员:

  1、构造方法

  2、私有成员:private修饰的成员

  3、static修饰的成员不继承

继承中的构造方法的调用:

创建子类对象,会调用父类的构造方法  -- super()

默认会调用父类的无参构造方法,但是可以人为调用有参构造方法。

意义:

父类中有这些字段的初始化方式,所以最好的选择就是用父类的构造方法。

public class Super {
int count;

public Super() {
super();
System.out.println("父类的无参构造方法");
}

public Super(int count) {
super();
this.count = count;
System.out.println("父类的有参构造方法");
}

public class Sub extends Super {
String name;
public Sub() {
//    super();  //调用父类的无参构造方法
super(12);
System.out.println("子类的无参构造方法");
}
public Sub(String name) {
super();
this.name = name;
}
public static void main(String[] args) {
new Sub();
}

    练习:shoot项目中,构建FlyingObject类,重构对象类

          Sky Hero Bullet BigAirplane Airplane  

               各个飞行物对象类现共有的数据和行为有:

        数据: width,height,x,y

        行为:step(){飞行物移动了!}

public class FlyingObject {
int width;
int height;
int x;
int y;

public void step(){
System.out.println("飞行物移动了!");
}
}


public class Airplane extends FlyingObject {
int step;



public Airplane() {
width = 49;
height = 36;
x = (int)(Math.random()*(400-width));
y = -height;
step = 2;
}

public class BigAirplane  extends FlyingObject {
int step;





public () {
width = 69;
height = 99;
x = (int)(Math.random()*(400-width));
y = -height;
step = 2;
}



public class Bullet extends FlyingObject {
int step;

public Bullet() {
width = 8;
height = 14;
x = 100;
y = 500;
step = 2;
}

public class Hero extends FlyingObject {
int life;
int doubleFire;

public Hero() {
width = 97;
height = 124;
x = 140;
y = 400;
life = 3;
doubleFire = 0;
}

public class Sky extends FlyingObject {
int y1;
int step;

public Sky() {
width = 400;
height = 700;
x = 0;
y = 0;
y1 = -700;
step = 1;
}
5.方法的重写:

    思考:shoot项目中父类中step方法每个子类的实现是否一样?

    重写:子类对父类中的某个方法的方法体进行重新实现。

    override

    重写的目的:

多个子类中有一些公共的行为,但是各个子类的实现方式不同,此时为了让各个子类能自己实现,采用重写 

重写规则:

1、签名(方法名、参数列表)一致

2、不能降低访问范围  子类>=父类

3、返回类型允许是父类方法返回类型的子类型

4、子类重写父类的方法,方法的返回值注意点:

       <1>如果父类的返回值类型是基本数据类型,那么子类中必须保持一致。

       <2>如果父类的返回值类型是引用类型,保持一致可以,子类的返回值可以是父类返回值类型的子类

简便记忆法:子类返回值<=父类返回值

    重写中super关键字的使用:

       在重写的方法中,可以使用super.方法(),各种方法通常用于在父类已有功能的基础上进行功能扩充。 

当子类对象的重写方法被调用时(无论是通过子类的引用调用还是通过父类的引用调用),运行的是子类的重写后的版本。

通过子类的引用指向子类对象

       Teacher teacher = new Teacher();

通过父类的引用指向子类对象

       Person teacher = new Teacher();

对象造型:

    向上造型:

     Person teacher = new Teacher();

     缺点:无法访问到子类中特有的成员。

     想要访问子类中特有的成员:Person teacher = new Teacher();

向下造型

     Teacher t =(Teacher)teacher;

     此时就可以访问子类中特有的成员了

练习:

shoot项目中重写step方法以实现各个子飞行物的移动方式。


Day03 1.     内容回顾

1.  java内存管理

     a)   栈

     b)   堆

     c)   方法区

2.  引用类型数组

     a)   数组也是引用类型

                        <1> Person[] pers = new Person[3];

3.  继承

     a)   extends关键字实现

                    <1>  父类:基类,超类

                    <2>  子类:派生类

     b)   继承的特点:

                   <1>单继承

                   <2>  传递性、

     c)   继承中不继承的成员

                    <1>构造方法

                    <2>私有成员

                    <3>static成员

     d)   继承中构造方法的调用问题

                    <1>创建子类对象,会调用子类的构造方法,默认会先调用父类的无参构造方法

     e)   继承中的重写:override

                   <1>方法的重写:

                        1.   在子类中对父类中的某个方法的方法体进行重新实现

                       2.   重写规则:3点

                       3.   重写中super关键字的使用:

                             a)   通常用于在父类已有功能的基础上进行功能扩容

                       4.   通过父类的引用指向子类对象:

                             a)   - 向上造型

                             b)   - 向下造型

2.继承(下) 2.2、重载和重写的区别

    重载:overload和重写:override是完全无关的两种现象

    重写:子类对父类中的某个方法的方法体进行重新实现

    重载:同一个类中,如果存在多个方法,方法名相同,但是参数列表不同

   重载和重写和方法的返回值类型和修饰符的关系:

       重载:只和方法签名有关,和方法的返回值类型,修饰符无关

       重写:和方法的返回值类型,修饰符有关

       重载是编译期绑定的,重写是运行期绑定的

           Person person = new Teacher();

           person.task();

练习:

1.shoot项目重构数组对象(向上造型 )

  2.画窗口

public class World {
Hero hero = new Hero();
FlyingObject[] enemies = new FlyingObject[9];
Bullet[] bullets = new Bullet[3];
Sky sky = new Sky();

public static void main(String[] args) {
World world = new World();
world.action();
}

public void action(){
//向数组中添加敌方机对象
enemies[0] = new Airplane();
enemies[1] = new Airplane();
enemies[2] = new Airplane();
enemies[3] = new BigAirplane();
enemies[4] = new BigAirplane();
enemies[5] = new BigAirplane();
enemies[6] = new Bee();
enemies[7] = new Bee();
enemies[8] = new Bee();

//动起来
for(int i=0;i 
3.访问控制修饰符 
public

private

protected

默认

    修饰符       本类     同一包下的类   不同包的子类   所有

    private       true            false                false            false

    默认           true            true                 false            false

    protected   true            true                 true             false

    public        true             true                 true             true

    访问控制修饰符可以用来修饰类的成员,也可以用来修饰类,如果修饰的是外部类,那么此时访问控制修饰符只能使用public ,默认的如果修饰的是内部类,此时可以使用所有的访问控制修饰符。

内部类:一个类定义在了另一个类的内部

4.封装:

将对象的数据和行为封装为一个整体,并尽可能地隐藏对象的内部实现细节

案例:一个GentleMan类,属性有name,age,gender,wife

要求:创建一个GentleMan对象,gender,wife不可改变只可查看,name,age可以修改可查看,如何实现?

原则:数据私有化,行为公开化

public class GentleMan {
private String name;
private int age;
private char gender;
private String wife;

public GentleMan() {
super();
}
public GentleMan(String name, int age, char gender, String wife) {
super();
this.name = name;
this.age = age;
this.gender = gender;
this.wife = wife;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public char getGender() {
return gender;
}
public String getWife() {
return wife;
}
}

public class GentleManTest {
public static void main(String[] args) {
//创建一个GentleMan对象
GentleMan gentleMan = new GentleMan("张三", 34, '男', "Linda");
//name,age可看可改的
String name = gentleMan.getName();
System.out.println(name);
int age = gentleMan.getAge();
System.out.println(age);
gentleMan.setName("张小三");
gentleMan.setAge(36);;

//gender,wife可看不可改
char gender = gentleMan.getGender();
String wife = gentleMan.getWife();


}

 练习:

shoot项目:给成员添加访问控制修饰符

public class FlyingObject {
protected int width;
protected int height;
protected int x;
protected int y;

public void step(){
System.out.println("飞行物移动了!");
}
}

public class Airplane extends FlyingObject {
private int step;

public Airplane() {
width = 49;
height = 36;
x = (int)(Math.random()*(400-width));
y = -height;
step = 2;
}

//移动行为
public void step(){
//在y方向向下匀速移动
y += step;
}
}

public class Bee extends FlyingObject {
private int xstep;
private int ystep;

public Bee() {
width = 60;
height = 50;
x = (int)(Math.random()*(400-width));
y = -height;
xstep = 1;
ystep = 2;
}
//移动行为
public void step(){
x += xstep;
y += ystep;
if(x>=400-width || x<=0){
xstep *=-1;
}

}
}

public class BigAirplane  extends FlyingObject {
private int step;

public BigAirplane() {
width = 69;
height = 99;
x = (int)(Math.random()*(400-width));
y = -height;
step = 2;
}
//移动行为
public void step(){
y += step;
}
}

public class Bullet extends FlyingObject {
private int step;

public Bullet() {
width = 8;
height = 14;
x = 100;
y = 500;
step = 2;
}

//移动行为
public void step(){
y -= step;
}
}

public class Hero extends FlyingObject {
private int life;
private int doubleFire;

public Hero() {
width = 97;
height = 124;
x = 140;
y = 400;
life = 3;
doubleFire = 0;
}

//移动行为
public void step(){
}
}

public class Sky extends FlyingObject {
private int y1;
private int step;

public Sky() {
width = 400;
height = 700;
x = 0;
y = 0;
y1 = -700;
step = 1;
}

//移动行为
public void step(){
y += step;
}
}

public class World extends JPanel {
private Hero hero = new Hero();
private FlyingObject[] enemies = new FlyingObject[9];
private Bullet[] bullets = new Bullet[3];
private Sky sky = new Sky();

public static void main(String[] args) {
//窗体的绘制
Jframe frame = new Jframe("飞机大战");

frame.setDefaultCloseOperation(Jframe.EXIT_ON_CLOSE);
//窗体的位置
frame.setSize(400, 700);
frame.setLocationRelativeTo(null);
frame.setVisible(true);

World world = new World();
world.action();
}

public void action(){
//向数组中添加敌方机对象
enemies[0] = new Airplane();
enemies[1] = new Airplane();
enemies[2] = new Airplane();
enemies[3] = new BigAirplane();
enemies[4] = new BigAirplane();
enemies[5] = new BigAirplane();
enemies[6] = new Bee();
enemies[7] = new Bee();
enemies[8] = new Bee();

//动起来
for(int i=0;i 
5.static和final关键字 
    5.1 static关键字: 

           成员变量,方法,代码块

           static修饰的成员都是属于类的

       属于对象的:

           非static的成员是属于对象

public class StaticDemo {
int age;
static int count;  //属于类的

public static void main(String[] args) {
int amount = 3;

StaticDemo demo = new StaticDemo();
//    System.out.println(demo.age);
demo.age++;
System.out.println(count);  //0
count++;   //1

StaticDemo demo2 = new StaticDemo();
//    System.out.println(demo2.age);
System.out.println(count);  //1
}
}

修饰成员变量:

      static变量是属于类的变量, 存储在方法区, 而不是在堆中,一个类的static成员变量只有“一份”,无论该类创建了多少对象,static成员变量的默认初始化是在类加载时期完成的,通过类名直接调用。

class A{
static int age;
public static void main(String[] args){
age = 34;
}
}

class B{
public static void main(String[] args){
A.age = 34;
}

public void demo(){
A.age = 23;
}
}

修饰方法:

1.  通过类名直接调用

2.  static方法中不能使用this关键字,super关键字。因为this指向调用当前方法的对象,对象和类没什么关系,对象是个体,类是模版。

3.  static方法不能调用非static方法原因

注意点:

       static成员是属于类的,保存在方法区,可以被多个对象共享的,所以通过对象访问是允许的,但是不建议使用对象访问静态成员,建议使用static方式访问    

修饰代码块:

      属于类的代码块,在类加载期间执行的代码块,只执行一次,可以用来在软件中加载静态资源。

       static:

           方法:属于类的,通过类名来调用,static不能使用this成员变量

           代码块:都是在类加载时期执行完,且执行一次

    练习:

       操作:将本地的图片资源加载成java的图片对象的代码实现:

       案例:创建一个窗口,加载一个图片,将次图片画在窗体上

public class PicDemo extends JPanel {
static BufferedImage image;
static{
//完成对一个图片的加载
try {
image =
ImageIO.read(PicDemo.class.getResource("0.png"));
System.out.println(image);
} catch (IOException e) {
e.printStackTrace();
}


}

public static void main(String[] args) {
//创建窗口
Jframe frame = new Jframe();
PicDemo demo = new PicDemo();
frame.add(demo);

frame.setDefaultCloseOperation(Jframe.EXIT_ON_CLOSE);
frame.setSize(400, 700);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}

@Override
public void paint(Graphics g) {
g.drawImage(image, 100, 200, null);
}

}  

练习:

shoot项目:为对象类添加图片属性,图片为静态资源,共多个对象共享

public class FlyingObject {
protected int width;
protected int height;
protected int x;
protected int y;

public static BufferedImage loadImage(String name){
try {
return ImageIO.read(FlyingObject.class.getResource(name));
} catch (IOException e) {
throw new RuntimeException();
}
}

public void step(){
System.out.println("飞行物移动了!");
}

}

public class Airplane extends FlyingObject {
private int step;
//加载5张图片
static BufferedImage[] images = new BufferedImage[5];
static{
for(int i=0;i 

  练习:

1.  重构shoot项目中敌方机数组对象 - 向上造型

2.  重构shoot项目,给对象类中的数据添加访问控制修饰符 - 访问控制修饰符

3.  重构shoot项目,加载各个对象类中的图片静态资源 


Day04 1.内容回顾

1.  重写和重载的区别

2.  访问控制修饰符

3.  封装

4.  static

2.final关键字

    修饰变量:变量的值一旦初始化之后不可再改变

       final  int a = 4;

    修饰方法:方法不可变

       体现在方法不可以被重写

    修饰类:类不可变

       体现在类不可以被继承

   

    static  final  成员变量 : 常量

常量命名规范:所有的字母都要大写,如果常量名由多个单词组成,那么单词用_隔开

       注意点:static final常量会在编译期被替换

练习:设置窗口宽,高,对象的生命周期

public class World extends JPanel {
public static final int WIDTH = 400;
public static final int HEIGHT = 700;

private Hero hero = new Hero();
private FlyingObject[] enemies = new FlyingObject[9];
private Bullet[] bullets = new Bullet[3];
private Sky sky = new Sky();

public static void main(String[] args) {
//窗体的绘制
Jframe frame = new Jframe("飞机大战");

frame.setDefaultCloseOperation(Jframe.EXIT_ON_CLOSE);
//窗体的位置
frame.setSize(WIDTH, HEIGHT);
frame.setLocationRelativeTo(null);
frame.setVisible(true);

World world = new World();
world.action();
}

public void action(){
//向数组中添加敌方机对象
enemies[0] = new Airplane();
enemies[1] = new Airplane();
enemies[2] = new Airplane();
enemies[3] = new BigAirplane();
enemies[4] = new BigAirplane();
enemies[5] = new BigAirplane();
enemies[6] = new Bee();
enemies[7] = new Bee();
enemies[8] = new Bee();

//动起来
for(int i=0;i 
3.抽象:abstract 
3.1 抽象方法 

    只声明方法,而没有方法体,把这种方法叫做抽象方法。

    如果一个类中有抽象方法,此时这个类就是一个抽象类。

如果一个类继承了抽象类,抽象类中有抽象方法,那么这个子类必须对父类中的抽象方法进行重写

    抽象类中可以有抽象方法,也可以有普通方法

    抽象方法的注意点:

       修饰符  abstract  报道查看类型方法名(); 

       没有方法体和方法体为空是不一样的。

3.2 抽象类 :为子类提供公共的模板

    意义:

-   为其子类提供一个公共的类型;

-   封装子类中的重复内容(成员变量和方法);

public abstract class Person {
String name;
int age;
char gender;

public abstract void task();

public void walk(){
System.out.println("具有走路这个功能!");
}
}

    二者关系

        一个类中有抽象方法,这个类就是抽象类

       如果一个类是抽象类,内部可以没有抽象方法

    注意点:

1.  子类继承抽象类(有抽象方法),如果对父类中的抽象方法没有进行全部重写,此时这个子类也成为了一个抽象类

2.  抽象类不可以实例化

3.  abstract 和final 不可以同时修饰一个类

一个类中属于对象的成员变量也叫做实例变量

属于对象的成员方法也叫做实例方法

练习:画对象

     对象。成员变量/ 方法

     null。成员变量/ 方法   - NullPointerException

思路:

1.  World 类中重写paint()

2.  将各个对象的画的操作交由各个对象去完成

a)   在每个对象类中定义paint ()

                    我。      没调用一次此方法能够画一张图片

3.  在每个对象类中定义的getImage():获取某一时刻要画的图片对象

4.  写几个对象类之后发现,每个对象类中都有的getImage()和paint(),所以应该就公共的内容提取到父类中取

5.  提取过程中发现,getImage()在不同子类中的实现是不同的,将getImage()提取到父类中成为抽象方法

6.  重构多个子类对象后发现,除了天空类中的涂料方法,其他类中的paint方法的实现是一样的,所以将paint()提取到父类中(有方法体的),在天空类中单独重写此方法

public abstract class FlyingObject {
protected int width;
protected int height;
protected int x;
protected int y;

public static final int LIFE = 0;
public static final int DEAD = 1;
public static final int REMOVE = 2;

protected int state = LIFE;

public static BufferedImage loadImage(String name){
try {
return ImageIO.read(FlyingObject.class.getResource(name));
} catch (IOException e) {
throw new RuntimeException();
}
}

public abstract void step();
public abstract BufferedImage getImage();

//画某一时刻该对象的图片
public void paint(Graphics g){
//将对象画到窗体上
g.drawImage(getImage(), x, y, null);
}

}

public class Airplane extends FlyingObject {
private int step;
//加载5张图片
static BufferedImage[] images = new BufferedImage[5];
static{
for(int i=0;i 

项目重构:将对各个对象生命状态的判断提取到父类中定义为方法

public abstract class FlyingObject {
protected int width;
protected int height;
protected int x;
protected int y;

public static final int LIFE = 0;
public static final int DEAD = 1;
public static final int REMOVE = 2;

protected int state = LIFE;

public static BufferedImage loadImage(String name){
try {
return ImageIO.read(FlyingObject.class.getResource(name));
} catch (IOException e) {
throw new RuntimeException();
}
}

public abstract void step();
public abstract BufferedImage getImage();

//画某一时刻该对象的图片
public void paint(Graphics g){
//将对象画到窗体上
g.drawImage(getImage(), x, y, null);
}

//判断是否是LIFE
public boolean isLife(){
return state == LIFE;
}
//判断对象是否是DEAD
public boolean isDead(){
return state == DEAD;
}
//判断对象是否是REMOVE
public boolean isRemove(){
return state == REMOVE;
}

}

public class Airplane extends FlyingObject {
private int step;
//加载5张图片
static BufferedImage[] images = new BufferedImage[5];
static{
for(int i=0;i 

  其他子类同理

案例:

       boolean falg = 3> 5;

       如果(标志){

}

如果(是终生())

4.接口

抽象类是从多个类中抽象出来的模板,如果将这种抽象进行的更彻底,则可以提炼出一种更加特殊的“抽象类” - 接口(interface ),接口中的所有方法都是抽象方法。

写法:

修饰符  interface 接口名{} ,

     抽象类的成员:

         成员变量,抽象方法,普通方法,构造方法

接口中的成员:2 个

    常量:默认修饰符:public static final

    抽象方法:默认修饰符:public abstract

    接口是为了提取公共的模板,用于被实现

接口的实现

       语法:实现

           类  器具接口名

一个类如果实现接口,那么必须对接口中的所有抽象方法进行重写(方法体的实现)

    注意点:

       如果一个类实现了某个接口后,只对其中的部分抽象方法进行重写,此时,这个类变成了一个抽象类

public interface PersonInter {
int I= 3;
void task();
void writeCode();
}
public abstract class Doctor implements PersonInter {
@Override
public void task() {
System.out.println("工作是医生");
}
}

术语:    

    实现类

    A类实现了Inteface B

    一个就叫做实现类

特点:

1.  可以多实现:一个实现类可以有多个接口

a)   写法:class A实现接口A,B,C

2.  接口间的继承:接口可以继承接口,而且可以多继承

一)   继承的单继承特点:

                        1、 java的中类的继承只能是单继承

                        2、  接口间的继承可以是多继承 

                           interface A extends Interface B,class C  错误

                           interface A extends interface B,interface C 正确的

                           class A extends interface B,interface C 错误   

                3.  接口不能实例化,但是接口可以用作引用类型

                4.  接口的实现和类的继承是可以混合使用的,但是一定是继承类在前,实现接口在后

                           class A extends Super implements interface C 

抽象类

接口

有哪些部件

常量,成员变量,

构造方法,静态方法,普通方法,

 成员变量,常量,

构造方法,抽象方法,普通方法,

  常量,抽象方法

可否继承

单继承

单继承

多继承(接口间)多实现

口否实例化

可以实例化

不可以实例化

不可以实例化

注意点:

1.  类实现接口成为抽象类的使用场景

  界面A:

           void f();

           void f1();

           void f2();

抽象类MiddleCls实现A {

       F(){....}

}

B类延伸MiddleCls {

       F1(){}

       F2(){}

}

C类延伸MiddleCls {

       F1(){}

       F2(){}

}

    重写后发现,B,C 中f1,f2 方法的实现都不一样,f 方法的方法体实现是一样

2.  接口是对Java的类的单继承不足做出的弥补

情景:拍摄

    抽象类FlyingObject

    界面敌人{

        int getScore();

}

    class Airplane   - getScore():返回1

    类BigAirplane- getScore():返回3

class Bee - static final int LIFE = 0;

类Bird - getAwardType()

    班级子弹

    英雄级

    班级天空

级飞机扩展FlyingObject实施敌人

         练习:拍摄项目中接口的应用

            抽取接口:敌人

public interface Enemy {
int getScore();

}

public class Airplane extends FlyingObject implements Enemy {
private int step;
//加载5张图片
static BufferedImage[] images = new BufferedImage[5];
static{
for(int i=0;i 
5、补充: 
1)Java中抽象类和接口中有构造方法吗? 

①在接口中 不可以有构造方法

在接口里写入构造方法时,编译器提示:Interfaces cannot have constructors。

A. 构造方法用于初始化成员变量,但是接口成员变量是常量,无需修改。接口是一种规范,被调用时,主要关注的是里边的方法,而方法是不需要初始化的,

B. 类可以实现多个接口,若多个接口都有自己的构造器,则不好决定构造器链的调用次序

C. 构造器是属于类自己的,不能继承。因为是纯虚的,接口不需要构造器。

②在抽象类中 可以有构造方法。

在抽象类中可以有构造方法,只是不能直接创建抽象类的实例对象,但实例化子类的时候,就会初始化父类,不管父类是不是抽象类都会调用父类的构造方法,初始化一个类,先初始化父类。

(2)补充:构造方法、抽象类、接口的定义:

①构造函数(构造器、构造函数):构造函数是一种特殊的函数。其主要功能是用来在创建对象时初始化对象, 即为对象成员变量赋初始值,总与new运算符一起使用在创建对象的语句中。

A.方法名与类名相同;

B.没有返回类型(例如return、void等);

C.不能被static、final、native、abstract和synchronized修饰,不能被子类继承。

D.父类的构造方法不能被子类调用,可以通过super语句调用父类的构造方法。

E.构造方法可以重载,以参数的个数,类型,顺序,分为空参构造方法和有参构造方法。

②抽象类:使用了关键词abstract声明的类叫作“抽象类”。如果一个类里包含了一个或多个抽象方法,类就必须指定成abstract(抽象)。“抽象方法”,属于一种不完整的方法,只含有一个声明,没有方法主体。

注:

抽象类中不一定有抽象方法,抽象方法一定存在于抽象类中。

继承抽象类的可以是普通类,但必须重写抽象类中的所有抽象方法,也可以是抽象类,无需重写抽象类中的所有抽象方法。

③接口:接口是一种规范,是一系列方法的声明,是一些方法特征的集合,一个接口只有方法的特征没有方法的实现,因此这些方法可以在不同的地方被不同的类实现,而这些实现可以具有不同的行为(功能)。

注:

可以说是一种特殊的抽象类,里面的方法全是抽象方法。

子类实现接口必须对接口中的方法全部重写。

接口和抽象类的语法区别:

1)接口不能有构造方法,抽象类可以有。

2)接口不能有方法体,抽象类可以有。

3)接口不能有静态方法,抽象类可以有。

4)在接口中凡是变量必须是public static final,而在抽象类中没有要求。 


Day05- 画出敌人对象、子弹对象、天空对象、画在面板上并移动(内部类、定时器) 1.内容回顾 1.  抽象:abstract

     a)   抽象方法:只声明方法而没有方法体

     b)   抽象类

     c)   二者的关系

     d)   抽象类的特点:

             <1>子类继承了抽象类,必须对抽象类中的所有抽象方法进行重写,

                  如果没有全部重写,那么这个子类也变成抽象类

             <2>抽象类不可以被实例化

             <3>abstrct和final不能同时用于修饰一个类

2.  接口:interface

        a)   如果提取子类的公共行为抽象的更彻底,出现了一个特殊的抽象类 - 接口

        b)   接口的成员:常量,  抽象方法

        c)   接口的定义和使用

                <1> 实现类  implements 接口

                     此时实现类必须对接口中的所有抽象方法进行全部重写,如果没有全部重写,

                     那么实现类此时成为了抽象类 --应用

        d)接口的特点:

                <1>接口的多实现

                <2>接口间的继承可以是多继承

                <3>接口的实现和类的继承是可以混合使用的,但是一定是继承在前,实现在后

                <4>接口不可以被实例化

shoot项目:

    interface  Enemy:abstract int getScore()

    interface  Award:

       static final int LIFE = 0;

       static final int DOUBLEFIRE = 1;

       int getAwardType()

public interface Award {
int LIFE = 0;
int DOUBLEFIRE = 1;

int getAwardType();

}

public class Bee extends FlyingObject implements Award {
private int xstep;
private int ystep;
private int awardType;

//加载5张图片
static BufferedImage[] images = new BufferedImage[5];
static{
for(int i=0;i=400-width || x<=0){
xstep *=-1;
}

}

//获取当前时刻要画的图片对象
int deadIndex = 1;
public BufferedImage getImage(){
if(isLife()){
return images[0];
}else if(isDead()){
BufferedImage img = images[deadIndex++];
if(deadIndex==images.length){
state = REMOVE;
}
return img;
}
return null;
}
@Override
public int getAwardType() {
return awardType;
}
}
2.抽象类和接口的比较

1.  抽象类中可以有变量,可以有常量,而接口中只能有常量

2.  抽象类中可以有抽象方法,也可以有普通方法,而接口中只能有抽象方法

3.  抽象类中有构造方法,而接口中没有构造方法

4.  抽象类如果要继承,只能是单继承,而接口的实现是多实现,接口间的继承可以是多继承

3.内部类

<1>一个类可以定义在另外一个类的内部,定义在类内部的类称之为Inner,其所在的类称之为Outer;

<2>Inner定义在Outer的内部。通常只服务于Outer,对外部不具备可见性,Inner可以直接调用Outer的成员及方法(包括私有的)。

普遍内部类:

     定义:

         class Outter{

            成员变量

-------------------------------------------------------------

            成员方法

            class Inner{  

                变量

                public void test(){...}

}

}

使用:

       InnerClass是服务于外部类的,所以通常是在外部类中来创建内部类对象,然后在此外部类的某一些方法中来调用内部类中的内容

public class InnerClassDemo01 {
int count;

public static void main(String[] args) {
InnerClassDemo01 classDemo01 = new InnerClassDemo01();
classDemo01.test();
}

public void test(){
//调用内部类中的方法
InnerCls cls = new InnerCls();
cls.demo();
}

class InnerCls{
int age = 4;
public void demo(){
System.out.println("内部类的方法");
}
}

}

内部类的访问控制修饰符如果不是private,那么此时内部类其实是可以被外界访问到的。

        如何访问:

public class InnerTest {
public static void main(String[] args) {
//访问InnerClassDemo01中的内部类中的方法
InnerClassDemo01 demo01 = new
InnerClassDemo01();
//通过外部类对象来创建内部类对象
InnerClassDemo01.InnerCls in =
demo01.new InnerCls();

//等同于
InnerClassDemo01.InnerCls in =
new InnerClassDemo01().
new InnerCls();
in.demo();
}

} 

 局部内部类(方法本地内部类) :将内部类定义在外部类中的一个方法内部

        定义:

           class Outter{

              public void test(){

                 class Inner{

                     int age=3;

                     public void demo(){...}

}

new Inner().demo();

}

}

注意点:

1、局部内部类中的方法使用方法的局部变量。如果JDK版本低于1.8,此时这个局部变量必须是final修饰才可以使用,从JDK1.8开始,是可以直接使用非final修饰的局部变量的。

2、在方法执行完后,局部变量销毁,局部内部类的对象并不能马上被GC回收,局部内部类的对象仍存在一段时间,在此调用方法中的局部变量时发现已经不存在了,

所以无法访问,用final修饰局部变量,让局部变量在栈中copy一份在栈中,使得局部内部类的对象能够继续访问copy后的局部变量。在jdk1.8的新版本中对此做了封装,

使得不用加final就能直接访问内部类的成员变量。

匿名内部类:

<1> 如果在一段程序中需要创建一个类的对象(通常这个类需要实现某个接口或者继承某个类),

       而且对象创建后,这个类的价值也就不存在了,这个类可以不必命名,称之为匿名内部类。

<2>语法:

<1> 前提:匿名的类必须实现了某个接口或继承了某个类

<2>接口/父类 引用= new  接口/父类(){

            重写接口中的抽象方法/重写父类中的某个方法

       }

实现一个接口

public class InnerClassDemo03 {

public void test(){
//创建一个Teacher对象,输出task()
Person teacher = new Person() {
@Override
public void task() {
System.out.println("teacher的任务是教书");
}
};  //红色部分整体是一个创建出来的没有名字的类
teacher.task();
}

public static void main(String[] args) {
InnerClassDemo03 demo03 =
new InnerClassDemo03();
demo03.test();
}

}

继承一个抽象类

public abstract class Super {
public abstract String getName();

}

public class NonameClassTest {

public void test(){

Super sub= new Super() {
@Override
public String getName() {
return "张三";
}
};
String name = sub.getName();
System.out.println(name);
}

public static void main(String[] args) {
NonameClassTest classTest = new
NonameClassTest();
classTest.test();
}

}

继承一个普通类

public class Super01 {

public double getPrice(){
double price = 20;
return price;
}

}

public void test01(){

Super01 sub = new Super01(){
@Override
public double getPrice() {
double price = 45;
return price;
}
};
double price = sub.getPrice();
System.out.println(price);
}

public static void main(String[] args) {
NonameClassTest classTest = new
NonameClassTest();
classTest.test01();
}

经常见的匿名内部类的用法:一个方法的参数列表中的参数是一个接口/抽象类类型

public interface Good {

String getName();
double getPrice();

}

public class Shopping {

public void buy(Good good){
//输出购买的商品的名称和价格
System.out.println(good.getName()+"-"
+good.getPrice());
}

public static void main(String[] args) {
//开始购买
Shopping shopping = new Shopping();
shopping.buy(new Good() {

@Override
public double getPrice() {
return 36.5;
}

@Override
public String getName() {
return "洗发水";
}
});
}
}

练习:敌人入场

             1.  需要定时器 - Timer.schedual(TimerTask,long,long)

             2.  任务:每隔指定的时间让敌人入场

             3.  写一个enterAction()用来完成敌人入场功能

                     a)   产生敌人对象 - 注意敌人产生的频率

                     b)   将产生的对象放入数组中

             4.  定义nextOne()用来随机产生敌人对象、

步骤1:
public void action(){
//定义一个定时器
Timer timer = new Timer();
//delay,period单位是ms  1s = 1000ms
long mills = 10;
timer.schedule(new TimerTask() {
@Override
public void run() {
//执行的任务
//敌人入场
enterAction();
repaint();
}
}, mills, mills);
}

步骤二:
//敌人入场
int count = 0;  //方法被调用的次数
public void enterAction(){
count++;
if(count%40==0){
//先产生敌人对象
FlyingObject enemy = nextOne();
//将此对象放入数组中:数组扩容
enemies = Arrays.copyOf(enemies,
enemies.length+1);
enemies[enemies.length-1] = enemy;
}
}

步骤三:
public FlyingObject nextOne(){
int ran = (int)(Math.random()*26);
if(ran<=11){
return new Airplane();
}else if(ran<=19){
return new BigAirplane();
}else{
return new Bee();
}
}

Shoot项目功能完善

1.  实现飞行物走步

思路:

       1.  在定时器的任务中添加stepAction()

       2.  定义stepAction()用来实现所有飞行物走步

注意点:

    可能出现的问题:

        敌人对象拉黑条现象,检查原因:sky对象的移动方式出现问题。

World类中:
public void action(){
//定义一个定时器
Timer timer = new Timer();
//delay,period单位是ms  1s = 1000ms
long mills = 10;
timer.schedule(new TimerTask() {
@Override
public void run() {  //要执行的任务
//敌人入场
enterAction();
//飞行物实现走步功能
stepAction();
repaint();
}
}, mills, mills);
}
//让所有飞行物实现移动
public void stepAction(){
sky.step();
//敌方机的移动
for(int i=0;i=World.HEIGHT){
y = -height;
}
if(y1>=World.HEIGHT){
y1 = -height;
}
}
public BufferedImage getImage(){
return image;
}
public void paint(Graphics g){
g.drawImage(getImage(), x, y, null);
g.drawImage(getImage(), x, y1,null);
}
}

天空移动的原理:

2.  子弹入场

思路:

1. 定时器的任务中添加一个任务,子弹产生并入场

a)   定时器中定义shootAction()

2. 定义shootAction()用于子弹入场

a)   产生子弹对象

               <1>在Hero类中定义shoot()用于让Hero射击子弹,根据火力值会产生不同数量的子弹,产生一颗和2颗子弹注意坐标问题

b)   将产生的子弹数组放入目标数组中

              <1>   对数组先扩容、

              <2>   将产生的数组的元素复制到bullets数组中

3. 在paint方法中画出所有的子弹对象

4. 在stepAction()添加上子弹的移步功能

步骤一:World类中

public void action(){
//定义一个定时器
Timer timer = new Timer();
//delay,period单位是ms  1s = 1000ms
long mills = 10;
timer.schedule(new TimerTask() {
@Override
public void run() {  //要执行的任务
//敌人入场
enterAction();
//飞行物实现走步功能
stepAction();
//子弹入场
shootAction();

repaint();
}
}, mills, mills);
}

步骤二:定义shootAction方法 - World类中

//产生子弹对象
int bulletCount = 0;
public void shootAction(){
bulletCount++;
if(bulletCount%30==0){
//产生子弹对象
Bullet[] bs = hero.shoot();
//将产生的子弹对象放入数组中:数组先扩容后保存
bullets = Arrays.copyOf(bullets,
bullets.length+bs.length);
//数组的复制
System.arraycopy(bs, 0, bullets,
bullets.length-bs.length, bs.length);
}

}

步骤三:在Hero类中定义shoot方法 - Hero类中

//用于射击产生子弹对象
public Bullet[] shoot(){
int unit = width/4;
//hero根据火力值的不同会产生不同数量的子弹
if(doubleFire>0){ //2发子弹
Bullet[] bullets = new Bullet[2];
bullets[0] = new Bullet(x+unit, y);
bullets[1] = new Bullet(x+3*unit, y);
return bullets;
}else{  //1发子弹
Bullet[] bullets = new Bullet[1];
bullets[0] = new Bullet(x+2*unit, y);
return bullets;
}
}
}

步骤四:在World类中的paint方法中完成画子弹 - World类中:

@Override
public void paint(Graphics g) {
//所有对象画出
sky.paint(g);
hero.paint(g);

for(int i=0;i 

步骤五:在World类中的stepAction中完成子弹的移动 - World类中:

//让所有飞行物实现移动
public void stepAction(){
sky.step();
//敌方机的移动
for(int i=0;i 

Day06 1.内容回顾

内部类:

1.  普通内部类作为一个类的成员

    使用:

         1.  被外部类访问

         2.  被外界访问

             a)   注意点在外界创建内部类对象的方法

2.局部内部类(方法本地内部类):

       内部类定义在了外部类中的某一个方法内部

       使用:

           所在方法的里边创建内部类对象并调用其成员

       注意点:

           在JDK1.8之前,如果局部内部类中想使用其所在方法的局部变量,此变量需+final,从JDK1.8开始不需要了

3.匿名内部类:

       前提:如果一个类实现了一个接口/继承了某个父类,而此类只在某个位置使用,使用完毕之后,这个类没有了存在的意义,此时就可以将这个类定义为匿名内部类。

       定义以及使用

          接口/父类  引用 = new 接口/父类(){

  匿名内部类的类体

      //对接口中或父类中的某个方法进行重写 

};

通过引用来调用匿名内部类中的方法

经常见的使用场景:

       接口/抽象类 作为一个方法的参数出现了,此时如果没有接口/抽象类的实现类/子类,此时需要使用匿名内部类

完善shoot项目:

       敌人入场

       敌人走步

       子弹入场

shoot项目中有定时器之后可以完善的内容:

获取鼠标坐标的方式:用到鼠标适配器

public class MouseDemo extends JPanel {
public static void main(String[] args) {
//窗体的绘制
Jframe frame = new Jframe("飞机大战");
MouseDemo world  = new MouseDemo();
frame.add(world);

frame.setDefaultCloseOperation(Jframe.EXIT_ON_CLOSE);
//窗体的位置
frame.setSize(400, 700);
frame.setLocationRelativeTo(null);
frame.setVisible(true);

world.mouseMov();
}

public void mouseMov(){
//获取鼠标的坐标打印到控制台
//MouseAdapter鼠标适配器
//鼠标监听器
MouseAdapter adapter = new MouseAdapter() {
@Override
public void mouseMoved(MouseEvent e) {
int x = e.getX();
int y = e.getY();
System.out.println(x+"-"+y);
}
};

//让当前程序监听鼠标事件
this.addMouseMotionListener(adapter);
}

}

1.  英雄机随着鼠标移动

World类中:
public void action(){
//监听鼠标移动事件
MouseAdapter adapter = new MouseAdapter() {
@Override
public void mouseMoved(MouseEvent e) {
int x = e.getX();
int y = e.getY();
//让Hero的坐标发生变化
hero.move(x, y);
}
};

this.addMouseMotionListener(adapter);

Hero类中:
//用来实现Hero坐标的变化,x,y:鼠标的坐标
public void move(int x,int y){
this.x = x - width/2;
this.y = y - height/2;
}

2.  shoot项目运行状态和暂停状态的切换 - 鼠标事件

World类中:
public static final int RUNNING = 1;
public static final int PAUSE = 2;
private int state = RUNNING;

private static BufferedImage pause;
static{
pause = FlyingObject.loadImage("pause.png");
}

public void action(){
//监听鼠标移动事件
MouseAdapter adapter = new MouseAdapter() {
@Override
public void mouseMoved(MouseEvent e) {
int x = e.getX();
int y = e.getY();
//让Hero的坐标发生变化
hero.move(x, y);
}

@Override
public void mouseExited(MouseEvent e) {
state = PAUSE;
}

@Override
public void mouseEntered(MouseEvent e) {
if(state==PAUSE){
state = RUNNING;
}
}
};

this.addMouseMotionListener(adapter);
this.addMouseListener(adapter);

public void paint(Graphics g) {
//所有对象画出
sky.paint(g);
hero.paint(g);

for(int i=0;i 

3.  删除越界的敌人和子弹

步骤一:定时器中调用删除方法
//定义一个定时器
Timer timer = new Timer();
//delay,period单位是ms  1s = 1000ms
long mills = 10;
timer.schedule(new TimerTask() {
@Override
public void run() {  //要执行的任务
if(state==RUNNING){
//敌人入场
enterAction();
//飞行物实现走步功能
stepAction();
//子弹入场
shootAction();
//删除越界的敌人和子弹
outOfBoundsAction();
}
repaint();
}
}, mills, mills);

步骤二:定义outOfBoundsAction()
//删除越界的敌人和子弹
public void outOfBoundsAction(){

FlyingObject[] enemyLife = new FlyingObject[enemies.length];
int index = 0; //enemyLife数组的下标
for(int i=0;i0){ //2发子弹
Bullet[] bullets = new Bullet[2];
bullets[0] = new Bullet(x+unit, y);
bullets[1] = new Bullet(x+3*unit, y);
return bullets;
}else{  //1发子弹
Bullet[] bullets = new Bullet[1];
bullets[0] = new Bullet(x+2*unit, y);
return bullets;
}
}

@Override
public boolean outOfBounds() {
return false;
}
}

public class Bee extends FlyingObject implements Award {
private int xstep;
private int ystep;
private int awardType;

//加载5张图片
static BufferedImage[] images = new BufferedImage[5];
static{
for(int i=0;i=400-width || x<=0){
xstep *=-1;
}

}

//获取当前时刻要画的图片对象
int deadIndex = 1;
public BufferedImage getImage(){
if(isLife()){
return images[0];
}else if(isDead()){
BufferedImage img = images[deadIndex++];
if(deadIndex==images.length){
state = REMOVE;
}
return img;
}
return null;
}
@Override
public int getAwardType() {
return awardType;
}
@Override
public boolean outOfBounds() {
return y>=World.HEIGHT;
}
}

public class BigAirplane  extends FlyingObject implements Enemy {
private int step;

//加载5张图片
static BufferedImage[] images = new BufferedImage[5];
static{
for(int i=0;i=World.HEIGHT;
}

}

public class Bullet extends FlyingObject {
private int step;
static BufferedImage image;
static{
image = loadImage("bullet.png");
}

public Bullet(int x,int y) {
width = 8;
height = 14;
this.x = x;
this.y = y;
step = 2;
}

//移动行为
public void step(){
y -= step;
}

@Override
public BufferedImage getImage() {
return image;
}

@Override
public boolean outOfBounds() {
return y<=-height;
}
}

public class Sky extends FlyingObject {
private int y1;
private int step;

static BufferedImage image;
static{
image = loadImage("background.png");
}

public Sky() {
width = World.WIDTH;
height = World.HEIGHT;
x = 0;
y = 0;
y1 = -height;
step = 1;
}

//移动行为
public void step(){
y += step;
y1 += step;
if(y>=World.HEIGHT){
y = -height;
}
if(y1>=World.HEIGHT){
y1 = -height;
}
}

public BufferedImage getImage(){
return image;
}

public void paint(Graphics g){
g.drawImage(getImage(), x, y, null);
g.drawImage(getImage(), x, y1,null);
}

@Override
public boolean outOfBounds() {
return false;
}
}
2. 多态

对象造型

一个类型的引用在指向不同的对象时会有不同的实现

父类:Person  -- abstract void task()

子类:Teacher  Student 分别重写task()

测试类中:

    Person teacher = new Teacher();

    Person student = new Student();

    teacher.task();

    student.task();

向上造型

    向下造型 :

       Father father = new Son();

       Son son = (Son)father;

       son可以调用到Son中特有的成员

       注意点:

           Father father = new Son1();

           Son2  son = (Son2)father;     //错误的; 子类对象指向父类引用强制转换成兄弟类型是错误的

           Father father = new Father();

          Son son = (Son)father;  //错误的

向下造型的类型必须和引用指向的运行期类型保持一致

属于强制转型的一种

    Airplane extends FlyingObject implements Enemy

    Bee extends FlyingObject implments Award

---------------------------------------------------------------------------------

    FlyingObject obj =   Airplane/BigAirplane

    Enemy enemy = (Enemy)obj;

    enemy.getScore()

<1> 可以通过强制转换将引用从父类型转换为子类型,前提是该引用指向的对象运行期类型确实是该子类类型。

强制转型:

       <1>可以通过强制转换将引用从父类型转换为子类型,前提是该引用指向的对象运行期类型确实是该子类类型。

       <2>也可通过强制转换将引用转换为某种接口类型,前提是该引用指向的对象确实实现了该接口。

       <3>如果在强制转换过程中出现违背上述两个前提,将会抛出ClassCastException

public interface Enemy {
int getScore();
}
public abstract class FlyingObject {
public void paint(){
}
}
public class Airplane extends FlyingObject implements Enemy {
@Override
public int getScore() {
System.out.println("小敌机得分");
return 1;
}
}
public class BigAirplane extends FlyingObject implements Enemy {
@Override
public int getScore() {
System.out.println("大敌机得分");
return 3;
}
}
public class ClassCastTest {
public static void main(String[] args) {
FlyingObject obj = new Airplane();
FlyingObject obj1 = new BigAirplane();
//obj和obj1都可以调用getScore()
Enemy enemy = (Enemy)obj;
enemy.getScore();
}
}

FlyingObject obj = Airplane/BigAirplane/Bee

//为了防止运行期出现由于强制转型引起的类型转换异常,在转换之前加上判断

instanceof关键字

    语法:

       引用  instanceof  引用类型 - boolean

   在强制转型中,为了避免出现ClassCastException,可以通过instanceof关键字判断某个引用指向的对象是否为指定类型。

public class ClassCastTest {
public static void main(String[] args) {
FlyingObject obj = new Airplane();
FlyingObject obj1 = new Bee();

//obj和obj1都可以调用getScore()
if(obj instanceof Enemy){
Enemy enemy = (Enemy)obj;
enemy.getScore();
System.out.println("得分");
}

if(obj1 instanceof Enemy){
System.out.println("不得分");
}
}

}

Shoot项目应用:

  子弹与敌人的碰撞

1.  在Bullet类中定义isHit()用于判断子弹和敌人是否撞上

//判断子弹是否和敌人撞击
public boolean isHit(FlyingObject enemy){
//对子弹和敌人的坐标比较
//求出4个边界值
int enemyX = enemy.x;
int enemyY = enemy.y;
int xmin = enemyX - width;
int xmax = enemyX + enemy.width;
int ymin = enemyY - height;
int ymax = enemyY + enemy.height;

return x>=xmin && x<=xmax
&& y>=ymin && y<=ymax;
}

2.  在World类中的定时器中调用bangAction(),用于完成子弹和敌人的撞击事件

3.  定义bangAction()

       a)   对子弹数组遍历

        b)对敌人数组遍历

          1.   子弹和敌人撞击的判断

          2.   true:goDead()-->得分/得命

               a)   注意点:如果子弹和某个敌人撞击,不需要再去判断是否和别的敌人撞击了

步骤一:定时器中调用bangAction()
Timer timer = new Timer();
//delay,period单位是ms  1s = 1000ms
long mills = 10;
timer.schedule(new TimerTask() {
@Override
public void run() {  //要执行的任务
if(state==RUNNING){
//敌人入场
enterAction();
//飞行物实现走步功能
stepAction();
//子弹入场
shootAction();
//删除越界的敌人和子弹
outOfBoundsAction();
//子弹和敌人撞击
bangAction();
}
repaint();
}
}, mills, mills);

步骤二:World类中定义bangAction()
//子弹和敌人的撞击
public void bangAction(){
//对子弹数组进行遍历,对每个子弹对象判断
for(int i=0;i 

重构越界方法,将REMOVE状态的对象也移除掉 

//删除越界的敌人和子弹
//删除状态为REMOVE对象
public void outOfBoundsAction(){

FlyingObject[] enemyLife = new
FlyingObject[enemies.length];
int index = 0; //enemyLife数组的下标
for(int i=0;i 

shoot项目完善

1.  画分和画命

Hero类中定义加火力值和加命的方法

//获取Hero的life值
public int getLife(){
return life;
}

//获取Hero的火力值
public int getDoubleFire(){
return doubleFire;
}

World类中paint()中画分,画命,画火力值

@Override
public void paint(Graphics g) {
//所有对象画出
sky.paint(g);
hero.paint(g);

for(int i=0;i 

2.英雄机和敌人的碰撞

3.检查游戏结束

4.画状态


day07 2.  英雄机和敌人的碰撞

结果:Hero  life--  同时火力值清空

    思路:

1.  在定时器中hitAction()

2.  定义hitActon():

a)   判断是否撞击

b)   true:引用后续反应

步骤一:定时器中调用hitAction()
//定义一个定时器
Timer timer = new Timer();
//delay,period单位是ms  1s = 1000ms
long mills = 10;
timer.schedule(new TimerTask() {
@Override
public void run() {  //要执行的任务
if(state==RUNNING){
//敌人入场
enterAction();
//飞行物实现走步功能
stepAction();
//子弹入场
shootAction();
//删除越界的敌人和子弹
outOfBoundsAction();
//子弹和敌人撞击
bangAction();
//Hero和敌人撞击
hitAction();
}
repaint();
}
}, mills, mills);

步骤二:
将isHit()提取到FlyingObject类中

步骤三:定义hitAction()
//Hero和敌人的撞击
public void hitAction(){
for(int i=0;i 
3.检查游戏结束,画状态 

    思路:

1.  在定时器调用checkGameOver()

2.  定义checkGameOver():

    a)   根据Hero的life值决定是否切换state的值

    b)   定义一个ENDING常量(游戏结束状态)

3.  paint()将ENDING状态下的图片画出

4.  出现的问题:

ending状态下鼠标移除窗口,状态切换为Pause,实际上如果是ending状态,鼠标移出窗口不应该切换状态、

步骤一:定时器中调用checkGameOver()
//定义一个定时器
Timer timer = new Timer();
//delay,period单位是ms  1s = 1000ms
long mills = 10;
timer.schedule(new TimerTask() {
@Override
public void run() {  //要执行的任务
if(state==RUNNING){
//敌人入场
enterAction();
//飞行物实现走步功能
stepAction();
//子弹入场
shootAction();
//删除越界的敌人和子弹
outOfBoundsAction();
//子弹和敌人撞击
bangAction();
//Hero和敌人撞击
hitAction();
//检查游戏是否结束
checkGameOver();
}
repaint();
}

}, mills, mills);

步骤二:定义checkGameOver()
//检查游戏是否结束
public void checkGameOver(){
if(hero.getLife()==0){
state = ENDING;
}
}

步骤三:paint()中画结束状态图片,并加载此图片
World类中: 
private static BufferedImage pause;
private static BufferedImage ending;
static{
pause = FlyingObject.loadImage("pause.png");
ending = FlyingObject.loadImage("gameover.png");
}


@Override
public void paint(Graphics g) {
//所有对象画出
sky.paint(g);
hero.paint(g);

for(int i=0;i 
画状态 

    思路:

1.  程序开始,默认状态为START

2.  点击鼠标左键,游戏切换状态RUNNING

    a)   mouseClicked()

    b)   点击鼠标有2种情景:

                               i.      1.start-->RUNINNG

                             ii.      2.ENDING -->START这里需要注意:重新切换为start,窗体中的对象需要重置

步骤一:画开始状态,加载开始图片
private static BufferedImage pause;
private static BufferedImage ending;
private static BufferedImage start;
static{
pause = FlyingObject.loadImage("pause.png");
ending = FlyingObject.loadImage("gameover.png");
start = FlyingObject.loadImage("start.png");
}

@Override
public void paint(Graphics g) {
//所有对象画出
sky.paint(g);
hero.paint(g);

for(int i=0;i 

shoot项目整体思路:

1.  画窗体

2.  画对象

3.  对象动起来 - 定时器

4.  Hero实现随鼠标移动

5.  撞击效果

6.  画游戏状态以及切换


8、Java程序编译和运行的过程

          Java整个编译以及运行的过程相当繁琐,本文通过一个简单的程序来简单的说明整个流程。       

          如下图,Java程序从源文件创建到程序运行要经过两大步骤:1、源文件由编译器编译成字节码(ByteCode)  2、字节码由java虚拟机解释运行。因为java程序既要编译同时也要经过JVM的解释运行,所以说Java被称为半解释语言( "semi-interpreted" language)。

图1   java程序编译运行过程

        下面通过以下这个java程序,来说明java程序从编译到最后运行的整个流程。代码如下:

Java代码  
//MainApp.java  
public class MainApp {  
public static void main(String[] args) {  
Animal animal = new Animal("Puppy");  
animal.printName();  
}  
}  
//Animal.java  
public class Animal {  
public String name;  
public Animal(String name) {  
this.name = name;  
}  
public void printName() {  
System.out.println("Animal ["+name+"]");  
}  
}  

        第一步(编译): 创建完源文件之后,程序会先被编译为.class文件。Java编译一个类时,如果这个类所依赖的类还没有被编译,编译器就会先编译这个被依赖的类,然后引用,否则直接引用,这个有点象make。如果java编译器在指定目录下找不到该类所其依赖的类的.class文件或者.java源文件的话,编译器话报“cant find symbol”的错误。

        编译后的字节码文件格式主要分为两部分:常量池和方法字节码。常量池记录的是代码出现过的所有token(类名,成员变量名等等)以及符号引用(方法引用,成员变量引用等等);方法字节码放的是类中各个方法的字节码。下面是MainApp.class通过反汇编的结果,我们可以清楚看到.class文件的结构:

图2  MainApp类常量池  

图3  MainApp类方法字节码

          第二步(运行):java类运行的过程大概可分为两个过程:1、类的加载  2、类的执行。需要说明的是:JVM主要在程序第一次主动使用类的时候,才会去加载该类。也就是说,JVM并不是在一开始就把一个程序就所有的类都加载到内存中,而是到不得不用的时候才把它加载进来,而且只加载一次。

        下面是程序运行的详细步骤:

  1. 在编译好java程序得到MainApp.class文件后,在命令行上敲java AppMain。系统就会启动一个jvm进程,jvm进程从classpath路径中找到一个名为AppMain.class的二进制文件,将MainApp的类信息加载到运行时数据区的方法区内,这个过程叫做MainApp类的加载。

  2. 然后JVM找到AppMain的主函数入口,开始执行main函数。

  3. main函数的第一条命令是Animal  animal = new Animal("Puppy");就是让JVM创建一个Animal对象,但是这时候方法区中没有Animal类的信息,所以JVM马上加载Animal类,把Animal类的类型信息放到方法区中。

  4. 加载完Animal类之后,Java虚拟机做的第一件事情就是在堆区中为一个新的Animal实例分配内存, 然后调用构造函数初始化Animal实例,这个Animal实例持有着指向方法区的Animal类的类型信息(其中包含有方法表,java动态绑定的底层实现)的引用。

  5. 当使用animal.printName()的时候,JVM根据animal引用找到Animal对象,然后根据Animal对象持有的引用定位到方法区中Animal类的类型信息的方法表,获得printName()函数的字节码的地址。

  6. 开始运行printName()函数。

 图4   java程序运行过程

特别说明:java类中所有public和protected的实例方法都采用动态绑定机制,所有私有方法、静态方法、构造器及初始化方法都是采用静态绑定机制。而使用动态绑定机制的时候会用到方法表,静态绑定时并不会用到。本文只是讲述java程序运行的大概过程,所以并没有细加区分。本文的所述的流程非常粗糙,想深入了解的读者请查阅其他资料。存在谬误的地方,请多指正。


9、构造代码块、静态代码块、构造函数执行先后顺序

构造函数和一般函数有什么区别呢?

   1、两个函数定义格式不同。

   2、构造函数是在对象创建时,就被调用,用于初始化,而且初始化动作只执行一次。

    一般函数,是对象创建后,需要调用才执行,可以被调用多次。

构造代码块和构造函数有什么区别?

   构造代码块 :给对象进行初始化,对象一建立就运行而且优于构造函数执行。

   构造代码块和构造函数的区别 :构造代码块是给所有对象进行统一初始化,而构造函数是给指定的对象进行初始化。 

   People p = new People();

   创建一个对象都在内存中做了什么事情?

   1、先将硬盘上指定位置的People.class文件加载进内存。

   2、执行main方法时,在栈内存中开辟了main方法的空间(压栈-进栈),然后在main方法的栈区分配了一个变量p。

   3、在堆内存中开辟一个实体空间,分配了一个内存首地址值。new

   4、在该实体空间中进行属性的空间分配,并进行了默认初始化。

   5、对空间中的属性进行显示初始化。

   6、进行实体的构造代码块初始化。

   7、调用该实体对应的构造函数,进行构造函数初始化。

   8、将首地址赋值给p ,p变量就引用了该实体。(指向了该对象)

[java] view plain copy
class TestDemo  
{   
static  
{  
System.out.println("主函数静态代码块");  
}  
public static void main(String[] args)   
{   
System.out.println("主函数开始");  
People p1 = new People(1);  
People p = new People();   
System.out.println("主函数结束");  
}  
}   
class People   
{  
//程序先运行静态代码块-->构造代码块-->构造函数  
//构造代码块   
{  
System.out.println("People构造代码块");   
}  
//静态代码块  
static  
{  
System.out.println("People静态代码块");  
}  
//构造函数  
People()   
{  
System.out.println("空参构造函数");  
}  
People(int num)  
{  
System.out.println("一个参数的构造函数");   
}   
}  

输出结果:



作者:Darren QQ:603026148 以上内容归Darren所有,如果有什么错误或者不足的地方请联系我,希望我们共同进步。


转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/592588.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号