目录
3、方法的重载 (overload)-----------------------可以大大的简化方法的访问
5、this:指代当前对象,哪个对象调用方法它指的就是那个对像
3、访问控制修饰符:-----------------访问控制的权限,保护数据的安全
4、final:最终的、不可改变的-------------单独应用几率低
3、匿名内部类:------------------便于访问数据
*方法:函数、过程**
1、方法:
1)封装一段特定的业务逻辑功能
2)方法尽可能独立,一个方法只干一件事
3)反复多次使用
4)减少代码重复,有利于代码的维护
2、 方法的定义:五要素
修饰词 返回值类型 方法名(参数列表){
方法体
}
3、方法的调用:
public class MethodDemo {
public static void main(String[] args) {
say();
sayHi("WMZ");
}
//有参无返回值
public static void sayHi(String name){
System.out.println("Hello,My name is "+name+",I am 38 years old");
}
//无参无返回值
public static void say(){
System.out.println("Hello,My name is SSM,I am 38 years old");
}
}
面向对象
OO:面向对象
OOA:面向对象分析
OOD:面向对象设计
OOP:面向对象编程
面向对象:----难(比较抽象)
----很多的东西,不这么写也行,但这样写一定是最好的高质量代码:
复用性好、扩展性好、维护性好、可移植性好、健壮性好、可读性好、效率好...
1、什么是类?什么是对象?
1)现实世界是由很多的对象组成的
2)对象:真实存在的东西/个体
类:类型/类别,代表一类个体
3)类是对象的模版,对象是类的具体的实例
4)类可以包含:
4.1)对象所共用的属性/特征---------变量
4.2)对象所共用的行为/动作---------方法
5)一个类可以创建多个对象
2、如何创建类?如何创建类?如何访问成员?
数据类型 引用类型变量 指向 对象
Student zs = new Student();
Student zs = new Student();
Student ls = new Student();
Student ww = new Student();
类:是一种数据类型(引用类型)
class Airplane{ //小敌机类
int width;
int height;
int x;
int y;
void step(){ //移动
}
void bangBullet(){ //和子弹撞
}
void bangHero(){ //和英雄机撞
}
}
air1Width,air1Height,air1X,air1Y
air2Width,air2Height,air2X,air2Y
air3Width,air3Height,air3X,air3Y
...
air100Width,air100Height,air100X,air100Y
class BigAirplane{
}
class Bee{
}
...
射击游戏
射击游戏需求:
1、所参与的角色:英雄机、子弹、小敌机、大敌机、小蜜蜂、天空
2、角色间的关系:
1)英雄机发射子弹(单倍火力、双倍火力)
2)子弹可以射击敌人(小敌机、大敌机、小蜜蜂),若击中了;
2.1)子弹直接消失、敌人先爆破再消失
2.2)若击中的是小敌机,则玩家得1分
若击中的是大敌机,则玩家得3分
若击中的是小蜜蜂,则英雄机得奖励(1条命或40火力值)
3)敌人(小敌机、大敌机、小蜜蜂)和英雄机相撞,若撞上了:
3.1)敌人先爆破再消失
3.2)英雄机减1条命,同时,清空火力值
------当英雄机命数为0时,游戏结束
4)英雄机、子弹、小敌机、大敌机、小蜜蜂都在天空上飞
Hero: width,height,x,y,step(), life,fire
Airplane: width,height,x,y,step(), speed
BigAiplane: width,height,x,y,step(), speed
Bee: width,height,x,y,step(), xSpeed,ySpeed,awardType
Bullet: width,height,x,y,step(), speed
Sky: width,height,x,y,step(), speed,y1
class Hero{//英雄机类 宽、高、x坐标、y坐标、命、火力值
void shoot(){//英雄机发射子弹
}
void step(){//切换图片
}
void move(){//随着鼠标移动
}
}
class Airplane{//小敌机类 宽、高、x坐标、y坐标、speed移动速度
void step(){ y向下动
}
}
class BigAirplane(){//大敌机类 同 Airplane 类
}
class Bullet{//子弹类
void step(){ y向上移动
}
}
class Bee{//蜜蜂类 宽、高、x坐标、y坐标、xSpeed移动速度、ySpeed移动速度、奖励类型
void step(){ x、y坐标均移动
}
}
class Sky{//天空类 宽、高、x坐标、y坐标、speed移动速度、第二张天空图的坐标
}
方法签名:方法名+参数列表
3、方法的重载 (overload)-----------------------可以大大的简化方法的访问
-
发生在同一个类中,方法名相同,参数列表不同
-
编译器在编译的时候会根据方法的签名自动绑定调用的方法
-
public class Aoo { void show(){ System.out.println("正在进行展示...."); } void show(String name){ System.out.println(name+"正在进行展示...."); } void show(int age){ System.out.println(age+"岁,已经进行展示....."); } void show(String name,int age){ System.out.println(name+"在"+age+"岁的时候已经开始表演了....."); } void show(int age,String name){ System.out.println(name+"在"+age+"岁的时候已经开始表演了....."); } //int show(){return 1; } //编译错误,重载与返回值无关 //void show(String adress){} //编译错误,重载与参数名称无关 }
4、构造方法:构造函数、构造器、构建
-
给成员变量赋初值
-
与类同名,没有返回值类型(连void都没有)
-
在创建(new)对象时被自动调用
-
若自己不写构造方法,则编译器默认提供一个无参构造方法
若自己写了构造方法,则不再默认提供
构造方法可以重载
class Student{
String name;
int age;
String address;
Studnet(){
}
//给成员变量赋初值
Student(String name1,int age1,String address1){
name = name1;
age = age1;
address = address1;
}
}
/*1、创建一个学生对象
2、给成员变量赋默认值
3、调用 Student 构造方法
*/
Student zs = new Student();
Student zs = new Student("zhangsan",25,"LF");
Studnet ls = new Student("lisi",26,"JMS");
5、this:指代当前对象,哪个对象调用方法它指的就是那个对像
只能用在方法中,方法中访问成员变量之前默认有个 this.
this的用法:
1)this.成员变量名---------------访问成员变量(当成员变量与局部变量同名时,访问成员变量时this不能省略)
2)this.方法名()----------------调用方法(一般不用)
3)this()-----------------------调用构造方法(一般不用)
规定:
1)成员变量和局部变量是可以同名的 --------使用时默认采取的是就近原则
2)当成员变量与局部变量同名时: ------若想访问成员变量,此时this不能省略
Student zs = new Student("周星星",19,"北京海淀");//调用方法
zs.study();
zs.sayHi();
class Student {
String name; //成员变量(作用于整个类)
int age;
String address;
Student(String name1,int age1,String address1){ //局部变量(作用于当前方法中)
this.name = name1; //zs.name = "天龙人"
this.age = age1; //zs.age = 19
this.address = address1; //zs.address = "北京海淀"
}
void study(){
System.out.println(this.name+"正在划水...");
}
void sayHi(){
System.out.println("大家好,我叫"+this.name+",今年"+this.age+"岁了,家住"+this.address);
}
}
zs.study();----------------------study()中的this指的是zs
ls.study();----------------------study()中的this指的是ls
6、null 和 NullPointerException
内存管理:
由JVM来管理
1)堆:new 出来的对象(包括成员变量)
2)栈:局部变量(包括方法的参数)
3)方法区:
null:表示空,没有指向任何对象
- 若引用的值为null,则该引用不能再进行点操作
- 若操作则发生NullPointerException空指针异常
2021-6-9(引用类型数组,继承,super)
1、引用类型数组
基本数据类型图示:
** 引用数据类型图示:**
int[] arr = new int[3]; //基本数据类型
Student[] stus = new Student[3]; //引用类型数组
Airplane[] as = new Airplane[10]; //引用类型数组
//声明int型数组arr,包含10个元素,每个元素都是int型,默认值为 0
int[] arr = new int[10];
arr[0] = 100;
System,out,println(arr[0]);
//声明Student型数组stus,包含3个元素,每个元素都是Student型,默认值为 null
(1)Student[] stus = new Students[3]; //创建Student数组对象
stus[0] = new Stuent("张三",25,"北京海淀"); //创建Student对象
stus[1] = new Student("李四",20,"打不咧爹");
stus[2] = new Student("李四真",36,"四岔");
System.out.println(stus[0].name) //输出第一个学生的名字
stus[1].age = 27; //修改第2个学生的年龄为27
stus[2].sayHi() //第3个学生跟大家问好
for(int i=0;i<stus.length;i++){ //遍历所有学生
System.out.println(stus[i].name); //输出每个学生的名字
stus[i].sayHi(); //每个学生跟大家问好
}
//声明Airplane型数组as,包含20个元素,每个元素都是Airplane型,默认值为 null
Airplane[] as = new Airplane[20];
Bee[] bs = new Bee[50];
(2)另一个定义方法:
Student[] stus = new Student[]{
new Stuent("张三",25,"北京海淀");
new Student("李四",20,"打不咧爹");
new Student("李四真",36,"四岔");
}
引用数组类型的实例使用:
//学生类
public class Student {
//成员变量
String name;
int age;
String address;
Student(){
this("天龙额额",19,"北京朝阳");//this调用构造方法
}
//构造方法
Student(String name,int age,String address){
this.name = name;
this.age = age;
this.address = address;
}
//方法
void study(){
System.out.println(name+" 正在努力学生...");
}
void sayHi(){
System.out.println("大家好,我叫"+name+",今年"+age+"岁了,家住"+address);
}
/*重写toString方法*/
public String toString(){
return "姓名:"+name+" 年龄:"+age+" 家庭地址:"+address+"\t";
}
}
/*引用类型数组的演示 */
public class RefArrayDemo {
public static void main(String[] args) {
Student[] stus = new Student[3];
stus[0] = new Student("张三",25,"北京海淀");
stus[1] = new Student("李四",20,"浙江杭州");
stus[2] = new Student("李四真",36,"四岔");
System.out.println("第一个学生的姓名为:"+stus[0].name);
System.out.println("第一个学生的年龄为:"+stus[0].age);
System.out.println("第一个学生的地址为:"+stus[0].address);
stus[1].name = "周星星"; //修改第二个学生的名字
for(int i=0;i<stus.length;i++){ //遍历所有学生
System.out.print("学生的姓名为:"+stus[i].name+" ");
System.out.print("学生的年龄为:"+stus[i].age+" ");
System.out.print("学生的地址为:"+stus[i].address+"\n");
stus[i].study();
stus[i].sayHi();
System.out.println();
}
System.out.println(Arrays.toString(stus)); //在Student中重写toString()方法,直接输出数组
}
}
public class RefArrayDemo2 {
public static void main(String[] args) {
Student[] stus = new Student[]{
new Student("张三",25,"北京海淀"),
new Student("李四",20,"浙江杭州"),
new Student("李四真",36,"四岔")
};
for(int i=0;i<stus.length;i++){
System.out.println("学生的姓名为: "+stus[i].name+" 年龄为: "+stus[i].age+" 地址为中: "+stus[i].address);
stus[i].study();
stus[i].sayHi();
System.out.println();
}
}
}
2、继承
- 代码不用自己写,自己也能用
1)作用:代码复用
2)通过extends 继承
3)超类/父类: 派生类所共有的属性和行为 派生类/子类: 派生类所特有的属性和行为
4)派生类可以访问: 派生类+超类的,但超类的只能访问超类的
5)一个超类可以有多个超类,一个派生类只能有一个超类---------单一继承
6)继承具有传递性
7)java规定:构造派生类之前必须先构造超类
7.1) 在派生类的构造方法中,若没有调用构造方法 ---------则编译器默认super()调用超类的无参构造方法 7.2)在派生类的构造方法中,若自己调用了超类的构造方法 ---------则不再默认提供
注意:super()调用超类的构造方法,必须位于派生类构造方法的第一行
//继承class Person{ //----------------------超类/父类
String name;
int age;
String address;
void eat(){}
void sleep(){}
}
class Student extends Person{ //---------------派生类/子类
String stuId;
void study(){}
}
class Teacher extends Person{ //---------------派生类/子类
double salary;
void teach(){}
}
class Docter extends Person{ //---------------派生类/子类
String level;
void cut(){}
}
Student zs = new Student();
zs.stuId/study();
zs.name/age/address/eat()/sleep();
Teacher ls = new Teacher();
ls.salary/teach();ls.name/age/address/eat()/sleep();
//-----继承具有传递性:
class Aoo{ //------------------Aoo只能访问变量 a
int a;
}
class Boo extends Aoo{ //--------------Boo可以访问变量 a,b
int b;
}
class Coo extends Boo{ //--------------Coo可以访问变量 a,b,c
int c;
}继承-----------代码虽然我没有写,但是也属于我,只是没有写在一起而已
3、super:指代当前对象的超类对象
1)super.成员变量名-------------------------访问超类的变量
2)super.方法名()---------------------------调用超类的方法
3)super()---------------------------------调用超类的构造方法
4、数组的赋值:
FlyingObject[] ememies;FlyingObject[] ememies = null; //两种写法一样,null,会报空指针异常
FlyingObject[] ememies = {};FlyingObject[] ememies = new FlyingObject[0]; //两种写法一样,0个元素
2021-6-10
1、向上造型:
- 超类型的引用指向派生类的对象
超类 派生类Animal o3 = new Tiger()Person p1 = new Student()
- 能点出来什么,看引用的类型---------------这是规定
超类 派生类Animal o3 = new Tiger()o3.只能点出Animal类中的
//一个动物对象是动物类型的
Animal o1 = new Animal();
//一个老虎对象是老虎类型的
Tiger o2 = new Tiger();
//一个老虎对象是动物类型的
Animal o3 = new Tiger();//一个动物对象是老虎类型-------语义不同
//Tiger o4 = new Animal(); //编译错误
class Animal{ //动物
}
class Tiger extends Animal{ //老虎
}
for(int i=0;i<bullets.length;i++){ //遍历所有子弹
Bullet b = bullets[i]; //获取每一个子弹
System.out.println(b.x+","+b.y); //获取子弹的x和y坐标
}
2、方法的重写(override):重新写、覆盖
1)发生在父子类中,方法名相同,参数列表相同
2)重写方法被调用时,看对象的类型---------------------------这是规定
3)遵循"两同两小一大"原则:------------------了解,一般都是一模一样
1)两同:
1.1)方法名相同
1.2)参数列表相同
2)两小:
2.1)派生类方法的返回值类型小于或等于超类方法的
2.2)派生类方法的抛出异常小于或等于超类方法的
3)一大:
3.1)派生类方法的访问权限大于或等于超类方法的
2021-6-11
1、重写与重载的区别:
-
重写:发生在父子类中,方法名相同,参数列表相同
-
重载:发生在同一个类中,方法名相同,参数列表不同
class Aoo{
void show(){ }
}
class Boo extends Aoo{
void show(String name){ //此处发生了show方法的重载
}
}
Boo o = new Boo();
o.show();
o.show("zhangsan");
2、package 和 import:
package:
1)作用:避免类名的冲突
2)同一包中的类不能同名,
3)类的全称:包名.类名
4)建议:所有字母都小写
import:
1)同包中的类可以直接访问, 不同包的类不能直接访问,若想访问:
1.1)先import 声明在访问类
1.2)类的全称
3、访问控制修饰符:-----------------访问控制的权限,保护数据的安全
常规操作:数据(成员变量)私有化(private),行为(方法)公开化(public)
-
public:公开的,任何类都能访问
-
private:私有的,只有本类才可以访问
-
protected:受保护的,本类、派生类、同包类都可以访问使用
-
默认的:什么也不写,本类、同包类访问使用用 -------------------------java不建议默认权限
-
1)类的访问权限只能是public或默认的
-
2)类中成员的访问权限如上四种都可以
-
Card{ //银行卡
private String cardId; //卡号
private String cardPwd; //密码
private double balance; //账户余额
public boolean payMoney(double money){ //支付金额
if(balance >= money){
balance -= money;
return true;
}else{
return false;
}
}
public boolean checkPwd(String pwd){ //检测密码是否输入正确
if(pwd == cardPwd){
return true;
}else{
return false;
}
}
}
4、final:最终的、不可改变的-------------单独应用几率低
-
修饰变量:变量不能被改变
-
修饰方法:方法不能被重写
-
修饰类:类不能被继承
5、static:静态的
成员变量分为两种:
1)实例变量:没有 static 修饰,属于对象,存储在堆中,有几个对象就有几份,通过对象点来访问
2)静态变量:由 static 进行修饰,属于类,存储在方法区中,只有一份,通过类名点来访问
1)静态变量:
1.1)由 static 修饰
1.2)属于类的,存储在方法区中,只有一份
1.3)常常通过类名点来访问
1.4)何时用:所有对象所共享的数据(图片、音频、视频等)
2)静态方法:
2.1)由 static 修饰
2.2)属于类的,存储在方法区中,只有一份
2.3)常常通过类名点来访问
2.4)静态方法中没有隐式 this 传递,没有 this 就意味着没有对象,所以静态方法中不能直接访问实例成员
2.5)何时用:方法的操作与对象无关
3)静态块:
3.1)由 static 修饰
3.2)属于类,在类被加载期间会自动执行,一个类只被加载一次,所以静态块也只被执行一次 3.3)何时用:加载/初始化静态资源(图片、音频、视频等)
堆:new出来的对象栈:局部变量(包括方法的参数)方法区:class字节码文件(包括静态变量、所有方法)
2、静态方法:
package ooday06;
//演示静态方法
public class StaticDemo {
public static void main(String[] args){
}
}
class Boo{
int a; //实例变量
static int b; //静态变量
public void show(){ //有个隐式的this传递
b = 25;
System.out.println(a); //this.a
System.out.println(b); //Boo.b
}
public static void test(){
//没有隐式的this传递
/*静态方法中没有隐式的this传递
没有this就意味着没有对象
而实例变量a必须通过对象点来访问
*/
System.out.println(new Boo().a); //
System.out.println(b);
//Boo.b
}
}
class Coo{
int a; //不适合做成静态的,因为和对象有关
public void show(){ //需要访问对象的数据a,所以不适合做成静态方法
a++;
}
public void test(int num1,int num2){
//不需要访问对象数据,意味着与对象无关,更适合做成静态方法
int num = num1 + num2;
System.out.println(num);
}
}
3、静态块:
package ooday06;
public class StaticDemo {
public static void main(String[] args) {
Doo o4 = new Doo();
Doo o5 = new Doo();
}
}
class Doo{
static {
System.out.println("静态块");
}
public Doo(){
System.out.println("构造方法");
}
}结果:(静态方法执行一次,构造方法每次都执行) 静态块 构造方法 构造方法
2021-6-15
1、static final 常量:
1)必须声明同时初始化
2)常常通过类名点来访问,不能被改变(class.)
3)建议:所以字母都大写,多个单词用 _ 分隔
4)编译器在编译时会将常量直接替换为具体的值,效率高
5)何时用:数据永远不变,并且经常使用
package ooday06;
public class StaticFinalDemo {
public static void main(String[] args) {
System.out.println(Eoo.PI); //常常通过类名点来访问
/*
* 1)加载 Foo.class 到方法区中
* 2)静态变量 num 一并存储到方法区中
* 3)到方法区中获取 num 的值并输出
* */
System.out.println(Foo.num);
//编译器在编译时会将常量直接替换为具体的值,相当于System.out.println(5);---效率高
System.out.println(Foo.COUNT);
}
}
class Eoo{
public static final double PI = 3.14159;
//public static final double NUM; //编译错误,常量必须声明同时初始化
}
class Foo{
public static int num = 5; //静态变量
public static final int COUNT = 5; //常量
}
2、抽象方法:
-
设计规则:
-
将派生类所共有的属性和行为,抽到超类中------------抽共性
-
若派生类的行为都一样,设计为普通方法
若派生类的行为不一样,设计为抽象方法
1)由 abstract 修饰
2)只有方法的定义,没有具体的实现(连{}都没有)
3、抽象类:
1、由 abstract 修饰
2、包含抽象方法的类必须是抽象类
3、抽象类不能被实例化(不能 new 对象)
4、抽象类是需要被继承的,派生类
4.1)重写所有抽象方法------常用
4.2)也声明为抽象类--------不常用
5、抽象类的意义:
5.1)封装共有的属性和行为----------代码复用
5.2)为所有派生类提供统一的类型-----向上造型
5.3)包含抽象方法,为所有派生类统一的入口(能点出来)
派生类的具体实现不同,但入口是一致的
达到强制必须重写的目的
FlyingObject 0; //正确
FlyingObjectp[] 0; //正确
new FlyingObject[3]; //正确,创建FlyingObject数组对象--------只要是数据类型,就能创建数组对象
new FlyingObject(); //编译错误
1、问:抽象方法并没有达到让派生类复用的效果,为何还要存在?
答:不是的,抽象方法存在的意义在于,当向上造型通过超类的引用能点出step()来
2、问:既然抽象方法的意义仅仅在于造型后能点出来,那设计为普通方法也能点,为什么非要设计为抽象方法?
答:设计为普通方法,则派生类可以重写也可以不重写,而设计为抽象方法,可以强制派生类必须重写
---------抽象方法可以达到强制必须重写的目的
abstract class FlyingObject{
int width,height,x,y;
public abstract void step();
}
class Airplane extends FlyingObject{
@Override
public void step(){
System.out.println("神奇的事物");
}
}
class Bee extends FlyingObject{
@Override
public void step()
{
System.out.printn("神奇的海豚");
}
}
2021-6-16
1、画对象
1)想画对象需要获取对象的图片,每个对象都得获取图片,意味着获取图片行为为共有的行为,所以设计在FlyingObject中, 每个对象获取图片的行为都是不一样的,所以设计为抽象方法 ---------在FlyingObject中设计getImage()获取对象图片
2)准备对象的图片需要判断对象的状态,因为在不同的状态下获取的图片是不一样的,每个对象都有状态,意味着状态为共 有的属性,所以设计在FlyingObject中状态一般设计为常量,同时在设计变量 state 来表示当前状态 ---------在FlyingObject中设计LIVE,DEAD,REMOVE状态常量,state变量来表示当前状态,状态有了,还得去判断,每个对象都能判断状态,意味着判断状态行为为共有行为,所以设计在FlyingObject中,每个对象判断状态行为都是一样的,所以设计为普通方法------在FlyingObject中设计isLive(),isDead(),isRemove()判断状态 每个对象都得判断状态,意味着判断状态行为为共有状态,所以设计在FlyingObject中
3)重写getImage()获取图片:
3.1)天空sky,直接返回sky图片即可
3.2)英雄机hero,返回heros[0]和heros[1]来回切换
3.3)子弹Bullet:
3.3.1)若活着的,返回bullet图片即可
3.3.2)若死亡的,直接删除(不返回图片)
3.3.3)若删除的,不反悔图片
3.4)小敌机Airplane:
3.4.1)若活着的,返回airs[0]图片即可
3.4.2)若死亡的,依次返回airs[1]和airs[4]的爆破图,4后删除不返回图片)
3.5)大敌机BigAirplane:
3.5.1)若活着的,返回bairs[0]图片即可
3.5.2)若死亡的,依次返回bairs[1]和bairs[4]的爆破图,4后删除(不返回图片)
3.6)小蜜蜂Bee:
3.6.1)若活着的,返回bees[0]图片即可
3.6.2)若死亡的,依次返回bees[1]和bees[4]的爆破图,4后删除(不返回图片)
往窗口上画东西,需要三个数据: 1、对象的图片 2、对象的x坐标 3、对象的y坐标
2、成员内部类:
-
类中套类,外面的称为外部类,里面的称之为内部类
-
内部类通常只服务于外部类,对外不具备可见性
-
内部类对象通常在外部类中创建
-
内部类中可以直接访问外部类的成员(包括私有的)
----------内部类中有个隐式的引用指向了创建它的外部类对象-----外部类名.this
public class InnerClassDemo {
public static void main(String[] args) {
Mother m = new Mother();
m.create();
}
}
class Mother{ //外部类
private String name;
void create(){
Son s = new Son();
s.show();
}
class Son{ //内部类
void show(){
name = "三毛";
System.out.println(name);
System.out.println(Mother.this.name);
}
}
}
3、匿名内部类:------------------便于访问数据
-
若想创建一个类(派生类)的对象,并且对象只被创建一次,此时该类不必命名,称为匿名内部类
-
匿名内部类中不能修改外面变量的值,因为在此处系统默认该变量为 final 的
/* 匿名内部类演示*/
public class NsInnerClassDemo {
public static void main(String[] args) {
int num = 5;
/**
* 1)创建了Aoo的一个派生类,但是没有名字
* 2)为该派生类创建了一个对象,名为 o1
* 3)大括号中的为派生类的类体,
* */
Boo o1 = new Boo() {
@Override
void show() {
System.out.println("this is show...");
//num = 55; //匿名内部类中不能修改外面变量的值,因为在此处该变量会默认为final
}
};
o1.show();
}
}
abstract class Boo{
abstract void show();
}
**内部类命名:
外部类名 $ 内部类名 .class
Mother $ Son .class
**匿名内部类命名:
外部类名 $ 内部类名 .class
NsInnerClassDemo $ 1 .class
Shoot射击游戏第八天
敌人入场:-------------------------自动发生
敌人对象是由窗口产生的,所以将创建敌人方法设计在窗口World类中
在World中设计 nextOne() 方法生成敌人对象
敌人入场为定时发生的,所以在run中调用 enterAction() 实现敌人入场
在enterAction()中:
每400毫秒,获取敌人对象obj,enemies扩容,将obj装到最后一个元素上
----------------------在run中调用enterAction()后,需调用repaint()来重画
子弹入场:-------------------------自动发生
子弹对象是由英雄机发射出来的,所以将创建子弹的方法设计在英雄Hero类中
子弹是由英雄机发射出来的,所以在Hero类中设计 shoot() 方法生成子弹对象
子弹入场为定时发生的,所以在run中调用shootAction() 实现子弹入场
在shootAction() 中:
每300毫秒,获取子弹数组bs,bullets扩容,数组的追加
飞行物移动:-----------------------自动发生
飞行物移动为所有对象共有行为,所以将飞行物移方法设计在超类FlyingObject类中
飞行物移动为所以派生类共有的行为,所以在FlyingObject中设计抽象 step() 移动,派生类重写
飞行物移动为定时发生的,所以在 run 中调 stepAction() 实现飞行物移动
在stepAction() 中:
天空动,遍历敌人动,遍历子弹动
英雄机移动:-----------------------自动发生
英雄机移动为英雄机特有的行为,所以将英雄机移动方法设计在英雄机Hero类中
英雄机移动为英雄机的行为所以在Hero类中设计 moveTo() 实现英雄机的移动
英雄机移动为事件触发的,所以在监听器中中重写mouseMoved()鼠标移动事件
删除越界的敌人和子弹
在 FlyingObject中设计isOutOfBounds检测敌人是否越界
在Bullet中重写isOutOfBounds检测子弹是否越界
删除越界的敌人和子弹为定时发生的,所以在run中调用outOfBoundsAction()删除越界敌人和子弹
在outOfBoundsAction()中:
遍历敌人/子弹数组,判断若越界了,将越界敌人/子弹替换为最后一个元素,在将数组缩容
子弹与敌人想碰撞,若击中了
子弹消失,敌人先爆破在消失
若击中的是小敌机,则玩家得1分
若击中的是大敌机,则玩家得3分
若击中的是小蜜蜂,则英雄机得奖励(1条命或40火力值)
若击中的是大黄蜂,则英雄机得奖励
设计EnemyScore得分接口:Airplane和BigAirplane实现得分接口
设计EnemyAward奖励接口:
子弹与敌人的碰撞
在FlyingObject中设计isHit()碰撞检测,goDead()飞行物去死
在Hero设计addLife()增命,addFire()增火力
子弹与敌人的碰撞为定时发生的,所以在run中调用bulletBangAction() 实现子弹与敌人的碰撞
class Bullet{ //子弹撞敌人 public boolean isHit(FlyingObject other){ //this:子弹 other:敌人 } } class FlyingObject{ //敌人撞子弹 public boolean isHit(Bullet other){ this:敌人 other:子弹 } //敌人撞英雄机 public boolean isHit(Hero other){ this:敌人 other:英雄 } } class FlyingObject{ public boolean isHit(? other){ //?为FlyingObject---超类 类型 } } enemies[i].isHit(bullet[i]) //敌人撞子弹 this为敌人 other为子弹 enemies[i].isHit(Hero) //敌人撞英雄机 this为英雄 other为子弹 接口、超类 ? 处设计成什么类型: 既能接受子弹对象 也能接受英雄机对象 使用超类:FlyingObject 类型
检测是否碰撞:
英雄机与敌人的碰撞
借用FlyingObject的isHit() 检测碰撞,goDead() 飞行物死亡
在Hero中设计subtractLife()减命,clearFire()清空火力值
英雄机与敌人相撞是定时发生的,所以在run中调用heroBangAction() 实现英雄机与敌人碰撞
在run中设计 heroBangAction() 方法实现英雄和敌人的碰撞
检测游戏结束
借用Hero的getLife()获取命数
检测游戏结束为定时发生的,所以在run中调用checkGameOverAction() 实现检测游戏结束
在checkGameOverAction() 中:
判断若英雄机的命数<=0,表示游戏结束
画状态
在World类中设计START、RUNNING、PAUSE、GAME_OVER状态量、state变量
在Images类中设计start,pause,gameover状态图片,static块中赋值
在World的paint()中,设计在不同的状态下画不同的图片
在run中那一堆 action设计为仅在运行状态下执行
将英雄机随着鼠标移动设计为仅在运行状态下执行
重写mouseClicked()鼠标点击事件:
启动变运行,游戏结束时清理现场再变启动
重写mouseExited()鼠标移出事件:
运行状态变为暂停状态
重写mouseEntered()鼠标移入事件:
暂停状态变为运行状态
-
接口的好处?
-
达到所以实现该接口的都能执行,提供复用性
-
-
行为的多态、对象的多态?
-
getScore() 和 getAwardType() 体现了行为的多态,不同对象有不同的实现
-
被撞敌机为多态的,它作为FlyingObject时只能操作FlyingObject中的,作为接口类型时,只能操作接口中的
-
-
向上造型的好处?
-
将所以敌人封装到FlyingObject数组中,来提高复用性
-
-
为什么要强制类型转换?
-
若想访问的东西在超类中有,则不需要强转;若想访问的东西在超类中没有,则必须强转
-
-
为什么要 instranceof 判断?
-
为了避免ClassCastException 类型转换异常
-
定时器:
timer.schedule(7,10,10);
//第一个参数:TimeTask类型
//第一个10:从程序启动开始到第一次触发的时间间隔(long类型)
//第二个10:从第一次触发到第二次触发的时间间隔(long类型)
从第二次触发到第三次触发的时间间隔
做功能的套路:
先写行为(方法):
若为某个派生类所特有的行为,就将方法设计在特定的类中
若为所有派生类所共有的行为,就将方法设计在超类中
窗口去调用
若为定时发生的,就在定时器中调用
若为事件触发的,就在监听器中调用
如何调错:
-
编译错误:
-
异常:
-
程序的运行结果与你预期的结果不同:
调错方法
-
锁定错误方法:将方法注释掉,挨个放开,放开哪个方法出错,说明错误位置就是那个方法
-
打桩:System.out.println(数据)
-
2020-6-18
1、接口:
-
是一种数据类型(引用类型)
-
由 interface 定义
-
只能包含常量和抽象方法
-
默认成员权限为------public
-
接口不能被实例化
-
接口是需要被实现/继承的,实现/派生类类:
-
必须重写接口中的所有抽象方法
-
-
一个类可以实现多个接口,用逗号分隔,若又继承又实现时,应先继承后实现
-
接口可以继承接口
-
何时用:
//接口的演示
public class InterfaceDemo {
public static void main(String[] args) {
}
}
interface Inter{
public static final int NUM = 5;
public abstract void show();
int COUNT = 5; //默认public static final
void test(); //默认public abstract
//int NUMBER; //编译错误,常量必须声明同时初始化
}
class Aoo implements Inter{
@Override
public void show() { //重写接口中的抽象方法,必须加public
}
@Override
public void test() {
}
}
接口继承接口:
interface Inter5{
void show();
}
interface Inter6 extends Inter5{
void test();
}
class Boo implements Inter6{
@Override
public void show() {
}
@Override
public void test() {
}
}
设计规则:
-
将派生类所共有的属性和行为,抽到超类中
-
若派生类的行为都一样,设计为普通方法
-
若派生类的行为都不一样,设计为抽象方法
-
将部分派生类所共有的属性和行为,抽到接口中
接口是对继承的单根性的扩展---------------------实现多继承
2、多态:------------------多种形态
(1)意义:
-
行为的多态:cut()、step()、getImage()...
-
-
同一类型引用在指向不同的对象时,有不同的实现------所有抽象方法都是多态的
-
-
对象的多态:我、水 ....
-
-
同一对象在被造型为不同的类型时,有不同的功能--------所有对象都是多态的
-
(2)向上造型:
-
超类的引用指向派生类的对象
-
能造型成为的类型有:超类+所实现的接口
-
能点出来什么,看引用的类型
(3)强制类型转换,成功的条件只有如下条件:
-
引用所指向的对象,就是该类型
-
引用所指向的对象,实现了该接口或继承了该类
(4)强转时若不符合如上条件,则发生ClassCastException类型转换异常
-
建议:在强转之前先通过 instanceof 来判断引用指向的对象是否是该类型
public class MultiTypeDemo{
public static void main(String[] args) {
Aoo o = new Boo(); //向上造型
Boo o1 = (Boo)o; //强转,引用o所指向的对象,就是Boo类型
Inter o2 = (Inter)o; //引用o所指向的对象,实现了Inter接口
//Coo o3 = (Coo)o; //运行时会发生ClassCastException类型转换异常
//做判断
if (o instanceof Coo){
Coo o4 = (Coo)o; //强转失败
}else{
System.out.println("o不是Coo类型");
}
System.out.println(o instanceof Boo); //true
System.out.println(o instanceof Inter); //true
System.out.println(o instanceof Coo); //false
}
}
多态的演示
public class Test {
public static void main(String[] args) {
show(new Cat()); // 以 Cat 对象调用 show 方法
show(new Dog()); // 以 Dog 对象调用 show 方法
Animal a = new Cat(); // 向上转型
a.eat(); // 调用的是 Cat 的 eat
Cat c = (Cat)a; // 向下转型
c.work(); // 调用的是 Cat 的 work
}
public static void show(Animal a) {
a.eat();
// 类型判断
if (a instanceof Cat) { // 猫做的事情
Cat c = (Cat)a;
c.work();
} else if (a instanceof Dog) { // 狗做的事情
Dog c = (Dog)a;
c.work();
}
}
}
abstract class Animal {
abstract void eat();
}
class Cat extends Animal {
public void eat() {
System.out.println("吃鱼");
}
public void work() {
System.out.println("抓老鼠");
}
}
class Dog extends Animal {
public void eat() {
System.out.println("吃骨头");
}
public void work() {
System.out.println("看家");
}
}
2021-6-21
get/set的用法:
内存管理:由JVM来管理
堆:
-
存储 new 出来的对象(包括实例变量)
-
垃圾:没有任何引用所指向的对象
垃圾回收器 (GC) 不定时到内存中清扫垃圾,回收过程是透明的(看不到的),
并非一看到垃圾就立即回收,通过调用System.gc() 建议JVM尽快调度GC来回收
-
实例变量的生命周期:
当创建对象时存储在堆中,对象被回收时一并被回收
-
内存泄漏:不在使用的对象没有被及时的回收,严重的泄漏会导致系统的崩溃
建议:不再使用的对象应及时将引用设置为 null
栈:
-
存储的是 正在调用的方法中的所有局部变量(包括方法的参数)
-
调用方法时:会在栈中为该方法分配一块对应的栈帧,栈帧中存储局部变量(包括参数),
方法调用结束时,栈帧自动被清除,局部变量一并被消除
-
局部变量的生命周期:调用方法时存储在栈中,方法调用结束时与栈帧一并被清除
方法区:
-
存储的是 .class 字节码文件(包括静态变量、所有方法)
-
方法只有一份,通过this 来区分具体的方法对象
实例变量和局部变量:
-
实例变量:
-
类中,方法外
-
创建对象时存储在堆中,对象被回收时一并被回收
-
有默认值
-
-
局部变量:
-
方法中
-
调用方法时存储在栈中,方法调用结束时与栈帧一并被清除
-
没有默认值
-
面向对象总结
面向对象三大特征:
封装:
类:封装的是 对象的属性和行为
方法:封装的是 具体的业务逻辑实现
访问控制修饰符:封装的是 具体的访问权限
继承:
作用:实现 代码复用
超类:所有派生类所共有的属性和行为
接口:部分派生类所共有的属性和行为
派生类:派生类所特有的属性和行为
单一继承、多接口实现,传递性
多态:
行为多态:所有抽象方法都是多态的(通过方法的重写来实现)
对象多态:所有对象都是多态的(通过向上造型来实现)
向上造型、强制类型转化、instanceof 判断