项目---飞机大战
day01
面向对象:OO
面向对象的分析:OOA
面向对象的设计:OOD
面向对象的分析与设计:OOAD---拿高薪
面向对象的编程:OOP----你们以后所参与的
正课:
1.什么是类?什么是对象?
1)现实生活中存在很多很多的对象(东西),基于对象抽出了类
2)对象:软件中真实存在的单个的个体/东西
类:类型,类别,代表一类个体---自己所设计出来的一种数据类型(引用类型)
3)类是对象的模板,对象是类的具体的实例
4)类中包含:
4.1)对象所共有的属性/特征-----成员变量(数据)
4.2)对象所共有的行为/动作------成员方法
5)一个类可以创建多个对象
2.如何创建类?如何创建对象?如何访问成员?
类 对象
月饼模子 月饼
小敌机类---代表所有小敌机
小蜜蜂类---代表所有小蜜蜂
射击游戏需求:
1.所参与的角色:天空、英雄机、大小敌机、子弹、小蜜蜂
2.角色间的关系:
2.1)英雄机发射子弹,分单倍火力和双倍火力。
2.2)子弹射击敌人(小敌机、大敌机、小蜜蜂),若射击到了
2.2.1)子弹直接消失,敌人先爆破再消失
2.2.2)子弹打掉小敌机----玩家得1分
子弹打掉大敌机----玩家得3分
子弹打掉小蜜蜂---英雄机得奖励(1条命或40火力值)
2.3)敌人(小敌机、大敌机、小蜜蜂)可以和英雄机撞,若撞上了:
2.3.1)敌人先爆破再消失
2.3.2)英雄机减1条命,同时清空火力值-----当英雄机命数为0时,游戏结束
2.4)英雄机、大小敌机、子弹、小蜜蜂都在天空上飞
射击游戏第一天:
1.创建了6个对象类,同时创建了一个World窗口类并测试
day02
正课:
1.方法的签名:方法名称+参数列表
2.方法的重载(overload):
1)发生在一个类中,方法名称相同,参数列表不同,方法体不同
2)编译器在编译时,会根据方法的签名自动绑定调用的方法
注意:1.重载与返回值的类型无关!
2.重载与参数名称无关!
3.构造方法:
1)给成员变量赋初值
2)与类同名,没有返回值类型,连void都不允许有
3)创建(new)对象时被自动调用
4)若自己没有定义构造方法,则编译器会默认提供一个无参构造方法;若自己定义了构造方法,则不再默认提供无参构造方法。
5) 构造方法也是可以支持重载的
4.this:指代当前对象,哪个对象调用方法它就代表哪个对象
this的用法:
1)this.成员变量名 --- 访问成员变量(成员变量与局部变量同名时,this不能省略)
2)this.方法名---------调用方法(一般都省略不写)
3)this()---------------调用构造方法
注意: 1.只能用在方法中,方法中访问成员变量之前默认有个this.
2.成员变量和局部变量是可以同名的--用的时候采取的是就近原则
3.当成员变量与局部变量同名时,此时若想访问成员变量,则this不能省略
5.内存管理----------null:由JVM来管理的
1)堆:存储new出来的对象(包括成员变量【类中方法外的变量】)
2)栈:局部变量【方法内部的变量】(包括方法的参数)
3)方法区:class字节码文件(包括方法)
数组也是一个对象----因为数组也是new出来的---凡是new出来的都是对象,统统存放在堆内存中
射击游戏第二天:
1.给6个对象类添加构造方法,并测试
day03
正课:
1.null:表示空,没有指向任何对象
若引用的值为null,则该引用不能再进行任何操作了,否则就发生NullPointerException空指针异常
2.引用类型数组:
3.继承:
1)作用:代码复用
2)通过extends关键字来实现继承
3)超类/父类:派生类所共有的属性和行为
派生类/子类:派生类所特有的属性和行为
4)派生类继承超类后,派生类具有:派生类的+超类的
5)一个超类可以有多个派生类,但是一个派生类有且只能有一个超类
6)继承具有传递性
7)Java规定:构造派生类之前必须先构造超类;
在派生类的构造方法中,若没有调用超类的构造方法,则默认super()调用超类的无参构造
在派生类的构造中,若自己调用了超类的构造,则不再默认提供
4.super:指代当前对象的超类对象/父类对象
super的用法:
4.1)super.成员变量名-----------访问超类的成员变量
4.2)super.方法名----------------访问超类的方法
4.3)super.()------------------访问超类的构造方法
射击游戏第三天:
1.设计小敌机数组、大敌机数组、小蜜蜂数组、子弹数组,并测试
2.设计FlyingObject超类/父类,6个对象类作为其子类/派生类继承该类
3.在FlyingObject设计2个构造方法,6个对象类分别调用
day04
正课:
1.引用类型变量之间划等号:
1)指向同一个对象
2)通过一个引用对数据的修改会影响另一个引用对数据的访问
例子:我出差,我留一把备用钥匙给我弟弟妹妹,结果他们整来一堆狐朋狗友来我家一顿霍霍;我回家后看到的是我出差前收拾好的景象还是给我霍霍完的?
基本类型变量之间划等号:
1)赋值
2)对一个变量的修改不会影响另一个变量
例子:身份证复印件,我拿着身份证原件,别人拿着我的复印件,无论怎么改,怎么坏我,对我有影响吗?
2.向上造型:
1)超类型的引用指向了派生类的对象
2)能点出什么,看引用的类型【编译看左边】
射击游戏第四天:
1.将小敌机数组、大敌机数组和小蜜蜂数组,组合成为FlyingObject数组,并测试
day05
正课:
1.方法的重写(override):重新写、覆盖
1)发生在父子类中,方法名相同,参数列表相同,但方法体不同
2)重写方法被调用时,看对象的类型(new的是谁就是谁在调用)。【运行看右边】
3)重写需遵循“两同两小一大”原则:---了解,一般情况都是一模一样的
3.1)两同:
3.1.1)方法名相同
3.1.2)参数列表相同
3.2)两小:
3.2.1)派生类方法的返回值类型小于或等于超类方法的
3.2.2)派生类方法抛出的异常小于或等于超类方法的
3.3)一大:
3.3.1)派生类方法的访问权限大于或等于超类方法的
2.重写与重载的区别---------常见面试题
2.1)重写(override):
2.1.1)发生在父子类中/超类和派生类中/具有继承关系的两个类中,方法名相同,参数列表相同,方法体不同/方法内部逻辑不同。
2.1.2)遵循“运行期绑定”,看对象的类型来绑定方法(看具体的指向类型,即=右边,运行看右边)
2.2)重载(overload):
2.2.1)发生在一个类中,方法名相同,参数列表不同,方法体不同。【仅仅只有方法名相同,其他一律不相同】
2.2.2)遵循“编译期间绑定”,看参数/引用的类型来绑定方法【对象能不能通过.来调用方法,即在编译期间,要想语法不报错,先看的是=左边的引用类型中是否存在该方法;若存在,则编译通过。在运行时,再去根据你所传入的参数去具体执行方法,可能是父类中的方法/也可能是子类中重写的方法。】
3.package:
1)作用:避免类的命名冲突
2)包名可以有层次结构类的全称:包名.类名
3)同包中的类不能同名,不同包中的类可以同名
4)建议:包名所有字母都小写
import:
1)同包中的类可以直接访问,不同包中的类不能直接访问;若想访问只有如下两种方式:
1.1)先import声明类再访问类---建议
1.2)类的全称--------------------不建议
4.访问控制修饰符:---数据(变量)私有化(private),行为(方法)公开化(public)
1)public:公共的,任何类
2)private:私有的,本类
3)protected:受保护的,本类、派生类和同包类
4)默认的:什么都不写,本类,同包类
说明:
1)类的访问修饰权限只能是public和默认的
2)类中成员的访问权限上述皆可
5.final:最终的,不可改变的,单独应用机率低
1)修饰变量:变量不能被改变
2)修饰方法:方法不能被重写
3)修饰类:类不能被继承
射击游戏第五天:
1.在6个对象类中重写step()移动方法
2.给类中成员添加访问权限控制修饰符
3.画窗口:----在World类中
1)导包:javax.swing.JFrame+JPanel
2)设计World类--继承JPanel类
3)复制粘贴代码
Day06
正课:
1.static:静态的
1)静态变量:
1.1)由static修饰
1.2)属于类的,存储在方法区中,只有一份
1.3)常常通过类名点来访问
1.4)何时用:所有对象所共享的数据(图片、音频、视频等)
2)静态方法:
2.1)由static修饰
2.2)属于类的,存储在方法区中,只有一份
2.3)常常通过类名点来访问
2.4)静态方法中没有隐式的this传递,所以静态方法中不能直接访问实例成员
2.5)何时用:方法的操作与对象无关
3)静态块/静态代码块:
3.1)由static修饰
3.2)在类被加载期间自动执行,因为类只被加载依次,所以静态块只执行一次
3.3)何时用:初始化/加载静态资源(图片、音频、视频等)
2.static final:静态常量,应用率高
1)必须声明同时初始化
2)通过类名点来访问,不能被改变
3)建议:常量名所有字母都大写,多个单词之间用_分隔
4)编译器在编译时会将常量直接替换成为具体的值,效率高
5)何时用:数据永远不变,并且经常使用
3.抽象方法:
1)由abstract修饰
2)只有方法的定义,没有具体的实现(连{}都没有)
4.抽象类:
1)由abstract修饰
2)包含抽象方法的类必须是抽象类
3)抽象类不能被实例化(不能new对象)
4)抽象类是需要被继承的,派生类:---换言之,一个类是抽象类就不可能没有子类,虽然语法层面可以,但是实际开发中这么做是毫无意义的!
4.1)重写抽象类中的所有抽象方法---变不完整为完整
4.2)也声明为抽象类---仅仅语法层面,实际开发中不可能这么干!
常见面试题:
1.派生类反正需要重写,那么抽象方法可否在超类中删除?若不能,为什么?存在的意义?
1.答:当使用多态对象【向上造型】时,通过超类/父类的引用能够点出来【访问】--但是最终具体调用的还是派生类重写之后的方法----【编译看左边,运行看右边】
2.不设计成抽象方法,就设计为普通方法,为什么不可以?设计抽象方法的意义?
2.答:java中,方法可以被重写,也可以不被重写。若把step()设计为普通方法,则派生类中可以重写也可以不重写;这样就达不到统一管理的效果了,设计为抽象方法,在语法层面就强制了我们所有的派生类必须重写该方法,这是一种开发规范目的为了加限制、作统一管理。
5)抽象类的意义:
5.1)封装派生类所共有的属性和行为-----代码复用
5.2)为所有派生类提供统一的类型-------向上造型
5.3)可以包含抽象方法,为所有派生类提供统一的入口
派生类的具体实现不同,但入口是一致的
但是要注意一下这种情况:以该项目为例:
FlyingObject f;//正确,因为FlyingObject是一个类,类就是一个数据类型,只要是数据类型就能够声明变量。
FlyingObject[] fs = new FlyingObject[3];//正确,FlyingObject是一个数据类型,只要是数据类型就能声明该数据类型的数组对象。---创建FlyingObject数组对象
new FlyingObject();//错误,抽象类不能被实例化----这是在创建FlyingObject对象!!!区分!
射击游戏第六天:
1.设计Images图片工具类来准备图片
2.设计窗口的宽和高为常量,适当地方做修改
3.设计超类中的step()为抽象方法,从而引发超类也要跟着改为抽象类
Day07
正课:
1.成员内部类:了解即可,应用率极低
1)类中套类,外面的称为外部类Outer,里面的称为内部类Inner
2)内部类通常只服务于外部类,对外不具备可见性
3)内部类对象通常是在外部类中创建的
4)内部类中可以直接访问外部类的成员(包括私有的)---想象你家的房间
在内部类中有个隐式的引用指向了创建它的外部类对象。
射击游戏第七天:
1.画对象:
1)想画对象得需要先获取对象的图片,每个对象都能获取图片,意味获取图片为对象的共有行为,所以将获取图片的方法设计在超类中,每个对象获取图片的行为都是不一样的,所以设计为抽象方法。---具体步骤:
1.1)在FlyingObject中,设计抽象方法getImage()用于获取对象的图片
2)获取图片时,需要去考虑对象的状态,因为在不同状态下获取图片的方式是不同的;每个对象都有状态,意味着状态为对象所共有的属性,所以设计在超类中,状态一般都是固定的,所以都设计为常量,同时设计一个state变量来表示当前的状态。----具体实现步骤:
2.1)在FlyingObject中,设计三个常量LIFE、DEAD、REMOVE,设计state变量表示当前状态。
2.2)状态有了,还需要判断状态,每个对象都得进行判断,意味着判断状态为共有行为,所以也要设计在超类中,每个对象判断状态的方式都是一样的,所以设计为普通方法即可。---具体的实现步骤:
--在FlyingObject中设计isLife()、isDead()和isRemove()来判断对象的状态。
不考虑爆破的话,两种状态:
1.活着的:
2.死了的:
考虑爆破的话,三种状态:
1.活着的:
2.死了的:(没有死彻底,还要爆破)
3.删除的:
3)重写getImage()获取图片
3.1)天空Sky,直接返回sky图片即可 state==LIFE
3.2)子弹Bullet:
3.2.1)若活着的,直接返回bullet图片即可
3.2.2)若死了的,直接删除(不返回图片)
3.3)英雄机Hero:
3.3.1)若活着的,直接返回heros[0]和heros[1]的来回切换
3.4)小敌机Airplane:
3.4.1)若活着的,直接返回airs[0]和airs[1]的来回切换
3.4.2)若死了的,返回airs[2]到airs[5]的轮换,5后删除(不返回图片)
3.5)大敌机BigAirplane:
3.5.1)若活着的,直接返回bairs[0]和bairs[1]的来回切换
3.5.2)若死了的,返回bairs[2]到bairs[5]的轮换,5后删除(不返回图片)
3.6)小蜜蜂Bee:
3.6.1)若活着的,直接返回bees[0]和bees[1]的来回切换
3.6.2)若死了的,返回bees[2]到bees[5]的轮换,5后删除(不返回图片)
4)图片有了就可以开画了,需要往窗口上画,所以在窗口World类中重写paint()方法实现画对象。
步骤: 1.抽象方法getImage()获取图片
2.准备状态常量、判断状态
3.重写getImage()方法
4.在World类中重写paint()
day08
正课:
理论知识:
1.匿名内部类:
1)若想创建一个类的对象,并且对象只被创建一次;此时该类不必命名,称为匿名内部类---优点:代码结构更加简洁
2)问:内部类有独立的.class字节码文件吗?----常见面试题
答:有
项目功能实现:
1.敌人入场:---------------定时发生的
敌人对象是由窗口产生的,所以将创建敌人行为设计在窗口World类中
2.子弹入场:---------------定时发生的
子弹对象是由英雄机发射出来的,所以将创建子弹的行为设计在英雄机Hero类中
3.飞行物移动:-------------定时发生的
飞行物移动是所有派生类所共有的行为,所以将飞行物移动的行为设计在超类FlyingObject类中
定时器:Timer
timer.schedule(TimerTask,long,long);---计划表方法
第一个10:程序启动到第一次触发的时间间隔
第二个10:第一次触发到第二次触发的时间间隔(上下两次的间隔)
举例:现在晚上7点整---定闹钟(每天早上7点的闹钟)
timer.schedule(A,12小时的毫秒数【从现在晚7点到明天早上7点要12小时】,24小时的毫秒数【以后每次上下两次的时间就是一天24h】);
TimerTask它是一个抽象类,是不能new对象的,其内部包含抽象方法run,在run中执行需要定时的任务。--类似线程中的run方法。
线程:
业务功能的实现套路:
1.先写行为:
1.1)若为派生类所特有的行为,就将方法设计在特定的类中
1.2)若为派生类所共有的行为,就将方法设计在超类中
2.窗口调用:
2.1)定时触发的,在定时器中调用
2.2)事件触发的,在监听器中调用
paint属于多线程并发,当我们生成敌人对象后,发现看不到。这是因为我们刚开始调用paint时,还没有生成任何敌人对象,而paint方法我们只调用了一次。
解决:再次调用;
paint的调用方式:
1.Frame.setVisible(true);
2.直接调用repaint()
射击游戏第八天:
1.敌人入场:
1)敌人对象是由窗口产生的,所以在World窗口类中设计nextOne()方法,来生成敌人对象
2)敌人入场为定时发生的,所以nextOne()方法要由定时器调用。在run中调用enterAction()实现敌人入场。
设计一个enterAction方法,其中:
2.1)先定义一个计数变量
2.2)数组扩容
2.3)将生成的对象存入到数组中
2.子弹入场:
1)子弹是由英雄机发射出来的,所以在Hero中设计shoot()生成子弹对象
2)子弹入场为定时发生,所以在run()中调用shootAction()实现子弹入场
3)在shootAction中:
3.1)每15*10ms,获取子弹数组对象bs,bullets扩容,将bs追加到bullets的末尾
3.飞行物移动:
1)飞行物移动为派生类所共有的行为,所以在超类FlyingObject中设计抽象step()移动,派生类中重写
2)飞行物移动为定时发生的,所以在run中调用stepAction()实现飞行物移动。
在stepAction中:
天空动、遍历敌人敌人动,遍历子弹子弹动
Day09
正课:
1.接口:
射击游戏第九天:
1.英雄机随着鼠标移动:
1)英雄机随着鼠标移动为英雄机特有的行为,所以在Hero类中设计moveTo()来实现英雄机随着鼠标移动
2)英雄机随着鼠标移动---事件触发的(swing相关)--了解;所以在侦听器中重写mouseMoved()鼠标移动事件
在mouseMoved()中:
获取鼠标的x和y坐标,调用Hero类对象的moveTo()方法,实现随鼠标移动
2.1)swing中的事件:
2.1.1)事件:发生了一件事
事件 处理
鼠标点击 启动状态改为运行状态
鼠标移动 英雄机随着鼠标移动
鼠标移出 运行状态改为暂停状态
鼠标移入 暂停状态改为运行状态
2.1.2)事件处理:事件发生后做的操作
2.1.3)侦听器:不需要掌握 MouseAdapter
2.1.3.1)有一个侦听器对象
2.1.3.2)把侦听器装到面板上去
程序的状态:
1)启动状态(车打着火了)
2)运行状态(车正常行驶)
3)暂停状态(红灯停车等待)
4)游戏结束(停车熄火拔钥匙)
2.解决运行时间长卡的问题,内存溢出-----删除越界的敌人和子弹
1)在FlyingObject类中设计outOfBounds()检测敌人是否越界,在Bullet中重写outOfBounds()检测子弹是否越界
2)删除越界的敌人和子弹为定时发生的,所以在run中调用outOfBoundsAction()删除越界的敌人和子弹
在outOfBoundsAction()中:
声明不越界敌人/子弹数组,遍历enemies/bullets数组,判断若不越界,则将对象装到不越界数组中,最后将不越界数组复制到原数组中
3.设计接口:
1)接口是一种引用数据类型
2)由interface定义
3)只能包含常量和抽象方法---jdk8之后,可以有普通方法,但是必须前面有default修饰
4)接口不能被实例化
5)接口是需要被实现的,实现类:必须重写所有抽象方法
6)一个类可以实现多个接口【干爹可以有多个】,用逗号分隔;若既要继承又要实现时,应该先继承后实现。【先管亲爹再管干爹!】
7)接口可以继承接口
接口的好处:
扩展:
软件的设计规则:
1.将派生类所共有的属性和行为,抽取到超类中----抽共性
2.所有派生类的行为都一样,设计为普通方法
所有派生类的行为都不一样,设计为抽象方法
3.将部分派生类共有的行为,抽到接口中
接口是对继承单根型的扩展
接口实现了代码的松耦合,增加了开发的灵活性。----实现多继承
举例子:子弹射击敌人
子弹射击敌人,若打中了:
1.打掉小敌机,玩家得1分
2.打掉大敌机,玩家得3分
3.打掉小蜜蜂,英雄机得奖励(1条命或40火力值)
得分行为是小敌机和大敌机所共有的
得奖励行为是小蜜蜂和大黄蜂所共有的
day10
射击游戏第十天:
1.敌人与子弹的碰撞:
1)在FlyingObject中设计hit()检测敌人与子弹的碰撞、goDead()飞行物去死
在Hero中设计addLife()增命、addFire()增加火力值
2)敌人与子弹的碰撞是定时发生的,所以在run()中调用bulletBangAction()实现敌人与子弹的碰撞
在bulletBangAction()中:
2.1)遍历子弹和敌人,判断类型award还是enemy
2.2)判断是否活着并且是否撞击上了
2.2.1)若撞上了:
2.2.1.1)敌人去死,子弹去死
2.2.1.2)若被撞的是小敌机-------玩家得1分
若被撞的是大敌机------玩家得3分
若被撞的是小蜜蜂------英雄机得1条命/40火力值
若被撞的是大黄蜂-----英雄机得2条命/80火力值
碰撞是谁的行为----敌人的还是子弹的?---皆可
2.画分,命和关卡:
1)在Hero中添加分、命属性的getter,获取英雄机的命数和当前得分
2)在World类的paint()中,画上即可
正课:
1.多态:
1)意义:
1.1)同一类型的引用,在指向不同的对象时,有不同的实现---行为的多态:cut()、step()、getImage()...
1.2)同一个对象,被造型为不同的类型时,有不同的功能-----对象的多态:你我他水
2)向上造型:
2.1)超类型的引用指向派生类的对象
2.2)能造型成为的数据类型有:超类+所实现的接口
2.3)能点出来什么,看引用的类型【编译看左边】
3)强制类型转换,成功的条件只有两种:
3.1)引用所指向的对象,就是该类型
3.2)引用所指向的对象,实现了该接口或继承了该类
4)强转时若不符合如上两个条件,则发生ClassCastException类型转换异常
建议:强转之前先通过instanceof来判断引用的对象是否是该类型
Day11
正课:
1.内存管理:
由JVM来管理的
1)堆:
1.1)存储所有new出来的对象(包括实例变量)
1.2)垃圾:没有任何引用所指向的对象
垃圾回收器(GC):不定时到堆内存中清扫垃圾,回收过程是透明的,隐式的(看不到);不一定一发现垃圾就立即回收,通过调用System.gc()可以建议虚拟机尽快来调度GC来回收。--想象你去酒店,打扫房间的阿姨(GC),System.gc()相当你忍无可忍给前台打电话,让阿姨尽快来打扫房间。
1.3)内存泄露:不再使用的内存没有被及时地回收,严重的泄露会导致系统的崩溃
建议:不再使用的对象及时将引用设置为null
1.4)实例变量的声明周期:
创建(new)对象时存在堆内存中,对象被回收时一并被回收
2)栈:
2.1)存储正在调用的方法中的局部变量(包括方法的参数)
2.2)调用对象时会在栈中为该对象分配一块对应的栈帧,栈帧中存储方法中的局部变量(包括方法参数);当方法调用结束时,栈帧被清除,局部变量(包括方法参数)一并被清除
3)方法区:
面向对象总结:
射击游戏第十一天(最后一天):
1.英雄机与敌人的碰撞:
1)借用FlyingObject中的hit()碰撞检测、参数类型决定了和谁撞、goDead()去死
在Hero中设计substractLife()减命、clearFire()清空火力值
2)敌人与英雄机的碰撞为定时发生的,所以在run()中调用heroBangAction()实现英雄机与敌人撞
在heroBangAction()中:
遍历敌人得敌人,判断都活着并且撞上了:
敌人去死,英雄机减命并清空火力值
2.检测游戏结束:
1)借助于Hero的getLife()获取当前英雄机的命数
2)检测游戏是否结束也为定时发生的,所以在run()中调用checkGameOverAction()检测游戏结束
在checkGameOverAction()中:
当英雄机的命数<=0时,表示游戏结束,则
3.添加游戏暂停、运行状态---补充鼠标事件
4.画状态:
1)在World中设计START、RUNNING、PAUSE、GAME_OVER四种状态,同时设计sate变量表示当前状态,默认为START启动状态。在Imagaes中设计start、pause、gameover三个状态图,在static块中赋值;在World类的paint()中设计在不同的状态下画不同的图片。
2)设计run()中那一堆action,仅仅在运行状态时运行;设计英雄机随鼠标移动,仅仅在运行状态时执行。
3)重写mouseCliked()鼠标点击:启动状态时变运行状态,游戏结束状态时,先清理战场后变启动状态。
重写mouseExited()鼠标移出:运行变暂停
重写mouseEntered()鼠标移入:暂停变运行
若撞上了:
1.敌人去死
2.英雄机减1条命,同时火力值清零