第八天:
1、接口:
1)是一种数据类型(引用类型)
2)由interface定义 -------------类是class ,接口与类是并列关系
3)只能包含 常量 和 抽象方法 -------------不能写变量和普通方法
4)接口不能被实例化(不能new对象) -----------不完整(因为接口里面都是抽象方法)
抽象类也不能被实例化(不能new对象)
5)接口是需要被实现的,实现类: implements 实现
实现类必须重写接口中的所有抽象方法(重写时必须加public关键字)
6)一个类可以实现多个接口,用逗号分隔
若又继承又实现时,应先继承后实现(先亲爹后干爹)
继承: 超类是亲爹,只能有一个
一个超类可以有多个派生类(子类),
一个派生类只能有一个超类(父类)
接口: 接口是干爹,可以有多个
7)接口可以继承接口(可以继承多个接口)
接口里的东西就是让别人用的,所以默认访问权限一定是public,不能是 private、protected
同类型之间是 继承,不同类型之间是 实现;
类和类----------------继承
接口和接口------------继承
类和接口---------------实现
举例:
2)的举例
声明了一个接口: interface Inter{
}
声明了一个类: class Aoo{
}
3)的举例
//演示接口的语法
interface Inter{
public static final int NUM = 5; //声明一个常量赋值为5
public abstract void show(); //抽象方法
int COUNT = 5; //常量,前面不写,也默认有 public static final
void test(); //前面不写,也默认有 public abstract
int number; //编译错误,常量必须声明同时初始化(建议:常量名所有字母都大写)
//这是在接口里,它默认就是常量,前面不写 也默认有public static final
//若在类中,它才是变量
void say(){} //编译错误,抽象方法不能有方法体
//这是在接口中,Java默认它就是抽象方法
}
4)的举例
//接口的演示
public class InterfaceDemo {
public static void main(String[] args) {
Inter1 o1; //正确,接口是数据类型,可以声明引用
//Inter5 o1 = new Inter5(); //编译错误,接口不能被实例化
}
}
5)的举例
//演示接口的实现
interface Inter1{
void show(); //这两方法是抽象方法;接口里面的默认访问权限是public
void test();
}
class Boo implements Inter1{
public void show(){} //重写接口中的抽象方法时,访问权限必须是public
public void test(){}
//超类接口 已经是public 公开了(因为接口里默认访问权限是public),
//派生类什么都不写 就是小于超类方法的,要大于或等于,
//所以实现类重写接口中的抽象方法时必须写 public
//如果 是抽象类的子类 重写抽象方法,就不是必须写成public了,只要大于或等于就行
}
6)的举例
//演示接口的多实现
① interface Inter2{
void show();
}
interface Inter3{
void test();
}
class Doo implements Inter2,Inter3{
public void show(){}
public void test(){}
}
② interface Inter2{
void show();
}
interface Inter3{
void test();
}
abstract class Coo{
abstract void say(); //这的abstract不能省,只有接口里才默认,普通类不能省
}
class Doo extends Coo implements Inter2,Inter3{
public void show(){}
public void test(){}
void say(){} //这里可不写public,Doo、Coo两个都是类,都是默认的(大于或等于)
}
7)的举例
//演示接口的继承
interface Inter4{
void show();
}
interface Inter5 extends Inter4{ //现在Inter5有两个抽象方法(继承了Inter4一个)
void test();
}
class Eoo implements Inter5{
public void test(){}
public void show(){}
}
注:
① //一个接口可以继承多个接口
interface Inter1{
void show();
}
interface Inter2{
void test();
}
interface Inter3 extends Inter1,Inter2{
void say(); //一个接口可以继承多个接口
}
②
interface Inter1{
void show();
}
interface Inter2 extends Inter1{
void test();
}
class Aoo implements Inter2{ //Inter超类(接口是干爹) Aoo派生类(子类)
public void show(){}
public void test(){}
}
//Inter2 o1 = new Inter2(); //编译错误,接口不能被实例化
Inter2 o2 = new Aoo(); //正确,向上造型(接口是干爹,也是父类)
Inter1 o3 = new Aoo(); //正确,向上造型,继承有传递性
//Inter1爷爷辈,Inter2爸爸辈,Aoo孙子辈(Inter1也属于Aoo的父类)
// 向上造型:超类型的引用指向派生类的对象
设计规则:
1)将所有派生类所共有的属性和行为,抽到超类中-------------抽共性
2)若派生类的行为都一样,设计为 普通方法
若派生类的行为都不一样,设计为 抽象方法
3)将部分派生类 所共有的属性和行为,抽到接口中
接口是对继承单根性的扩展----------------------实现多继承
举例:
子弹与敌人的碰撞,若撞上了:
1)子弹直接消失,敌人先爆破再消失
2)若被击中的是 小敌机,则玩家得1分
若被击中的是 大敌机,则玩家得3分
(得分 这个行为是小敌机 大敌机部分派生类共有的,所以 抽到接口中)
int getScore(); 如果 放到超类中,那子类都可以继承了,所以不行
class EnemyScore{ //若 单独放到一个类中,底下子类 一个类只能有一个超类(父类)
int getScore();
}
所以 抽到接口中(Java可以又继承类,又实现接口)
class FlyingObject{ //超类
width,height,x,y
void step(){}
BufferedImage getImage(){}
}
interface EnemyScore{ //得分接口
int getScore();
}
class Airplane extends FlyingObject implements EnemyScore{
public int getScore(){ return 1; } //重写抽象方法
}
class BigAirplane extends FlyingObject implements EnemyScore{
public int getScore(){ return 3; }
}
class Bullet extends FlyingObject{
}
class Hero extends FlyingObject{
}
class Sky extends FlyingObject{
}
若被击中的是小蜜蜂,则英雄机得奖励(1条命或40火力值)
若被击中的是大黄蜂,则英雄机得奖励(2条命或100火力值)、玩家得8分
class FlyingObject{ //超类
width,height,x,y
void step(){}
BufferedImage getImage(){}
}
interface EnemyScore{ //得分接口
int getScore();
}
interface EnemyAward{ //奖励接口
int getAwardType();
}
class Bee extends FlyingObject implements EnemyAward{
public int getAwardType(){ ... }
}
class BigYellowBee extends FlyingObject implements EnemyAward,EnemyScore{
public int getAwardType(){ ... }
public int getScore(){ return 8; }
}
如何调错:---------------慢慢来,慢慢积累经验
1、先锁定错误位置:
----将功能一个一个注释掉,再一个一个的放开,看哪个方法放开之后出的错误
2、打桩:
----在你觉得有问题的位置,输出数据的值查看 System.out.println(数据);
第九天:
1、多态:
1)多态的意义: (因为 对象的行为不一样所以才抽象,表现出多态的效果了)
1.1)同一类型的引用在指向不同的对象时,有不同的实现------所有抽象方法都是多态的
----行为的多态:cut()、step()、getImage()…
1.2)同一个对象被造型为不同的类型时,有不同的功能------所有对象都是多态的(明上午细讲)
----对象的多态:我、水…
2)向上造型(相当于 自动类型转换):
2.1)超类型的引用指向派生类的对象
2.2)能造型成为的数据类型有: 超类 或 它所实现的接口(干爹)
2.3)能点出来什么,看引用的类型
3)强制类型转换,成功的条件只有如下两种:
3.1)引用所指向的对象,就是该类型
3.2)引用所指向的对象,继承了该类或实现了该接口
强转时若不符合如上两个条件,则发生 ClassCastException 类型转换异常
建议:在强转之前应先通过instanceof来判断引用指向的对象是否是该类型
举例:
1.1)的举例:
abstract class 人{
abstract void cut();
}
class 理发师 extends 人{
void cut(){ 剪发 }
}
class 外科医生 extends 人{
void cut(){ 开刀 }
}
class 演员 extends 人{
void cut(){ 停止表演 }
}
人 p1 = new 理发师(); //向上造型
人 p2 = new 外科医生();
人 p3 = new 演员();
p1.cut(); //剪发
p2.cut(); //开刀
p3.cut(); //停止表演
1.2)的举例:
interface 讲师{
void 授课();
}
interface 孩子他妈{
void 揍他();
}
interface 老公的老婆{
void 咬他();
void 收工资();
}
class 我 implements 讲师,孩子他妈,老公的老婆{
public void 授课(){} //重写 抽象方法
public void 揍他(){}
public void 咬他(){}
public void 收工资(){}
}
我 me = new 我();
讲师 o1 = me; //向上造型,接口也是超类(干爹也是爹)
孩子他妈 o2 = me;
老公的老婆 o3 = me;
o1.授课();
o2.揍他();
o3.咬他();
o3.收工资();
//向上造型:超类型的引用指向派生类的对象
2)的举例:
超类(大) 派生类(小)
FlyingObject o = new Airplane(); //向上造型了
基本类型那叫 自动类型转换(把一个小类型赋值给一个大类型 从后往前),
引用类型这叫 向上造型
FlyingObject类型能点出来的东西少
Airplane类型能点出来的东西多 所以 造型后 能点出来的东西少了
3)的举例:
如果还想 点出来的东西多,再强制类型转换回去
把小敌机造型成超类型了,能点出来的东西就少了,如果还想 点小敌机里的东西,那就把 大 再强制为 小
基本类型强转 一定能成功,不会报错,只会丢精度、溢出等;
引用类型强转 是有可能报错的;
package oo.day09;
//多态的演示
public class MultiTypeDemo {
public static void main(String[] args) {
Aoo o = new Boo(); //向上造型
① Boo o1 = o; //报错,发生了语法错误,o是Aoo类型,o1是Boo类型,Aoo大,
//把大类型赋给小类型,所以报错;
//这里编译期语法都错误了,还没到运行期 创建对象 JVM分配内存
Boo o1 = (Boo)o; //引用o所指向的对象就是Boo类型(上面向上造型那句),符合条件一
② Inter o2 = o; //编译错误,o和Inter没有关系,
//o是Aoo类型,一个干爹(接口),一个亲爹(父类)
Inter o2 = (Inter)o; //引用o所指向的对象实现了Inter接口
//o所指向的对象是Boo类型,Boo实现了Inter接口,符合条件二
//条件一 二都不符合,Boo Coo是两个独立的子类
③ //Coo o3 = (Coo)o; //运行时会发生ClassCastException类型转换异常
if(o instanceof Boo){ //true
if(o instanceof Inter){ //true
if(o instanceof Coo){ //false(满足上面两条件是true,否则是false)
Coo o3 = (Coo)o;
}else{
System.out.println("o不是Coo类型");
}
//以后做强转前,一定要先写这个if判断语句,再强转
}
}
interface Inter{ }
class Aoo{ }
class Boo extends Aoo implements Inter{ }
class Coo extends Aoo{ }
- 接口的好处?
----判断时用接口,可以代表所有实现该接口的类-------复用性好 - 行为的多态:
----getScore()、getAwardType()都是多态的(不同对象有不同的实现)
对象的多态:
----被撞对象f就是多态的(同一对象造型为不同类型时有不同的功能) - 向上造型:
----将所有敌人装到FlyingObject数组中------代码复用 - 强制类型转换的原因?
----isLive()、isHit()、goDead()在FlyingObject中,那就不必强转
----我们想调用的getScore()、getAwardType()在超类中没有,那就得强转 - 为何要instanceof判断?
----避免类型转换异常
面试题:
1)重写与重载的区别?
重写(override):发生在父子类中,方法名相同,参数列表相同
重载(overload):发生在同一类中,方法名相同,参数列表不同
2)switch分支语句可以作用在哪些数据类型上?
byte,short,int,char,枚举,String(JDK1.7才开始支持)
3)简述java的8种基本数据类型?
—共分四大类: 整型,浮点型,布尔型,字符型
3.1)byte:整型(字节型),1个字节
3.2)short:整型(短整型),2个字节
3.3)int:整型,4个字节
3.4)long:整型(长整型),8个字节
3.5)float:浮点型(单精度),4个字节
3.6)double:浮点型(双精度),8个字节
3.7)boolean:布尔型,1个字节,存储true或false
3.8)char:字符型,2个字节,存储单个字符
4)简述public、private、protected以及默认权限的访问范围?
----都是访问控制修饰符,目的是为了保护数据的安全
4.1)public:公开的,任何类
4.2)private:私有的,本类
4.3)protected:受保护的,本类、派生类、同包类
4.4)默认的:什么也不写,本类、同包类
5)问:抽象类与接口的区别?
答:
①抽象类中可以包含变量、常量、普通方法和抽象方法,而接口中只能包含常量和抽象方法
②一个类只能继承一个抽象类,但是一个类可以实现多个接口
③实现类在实现接口后,必须重写接口中的所有抽象方法
派生类继承抽象类后,可以也声明为抽象类,而并非必须重写所有抽象方法
或者:
相同点:
抽象类和接口都是数据类型,都属于引用数据类型,有很多相同点,
比如说都可以包含抽象方法,都不能被实例化,都需要被继承/实现才有意义
不同点:
1)抽象类中可以包含很多东西(常量、变量、普通方法、抽象方法、构造方法)
而接口中只能包含常量和抽象方法
2)一个类只能继承一个抽象类,但可以实现多个接口,用逗号分隔
继承抽象类的派生类,可以重写抽象方法也可以不重写,不重写将派生类也做成抽象类
接口的实现类必须重写所有抽象方法
3)抽象类中成员的默认访问权限就是默认的,即同包中访问
接口中成员的默认访问权限是public
//-------------------复用性差、扩展性差、维护性差-------垃圾代码
if(f instanceof Airplane){ //--------------只能适用于Airplane类
Airplane a = (Airplane)f;
score += a.getScore();
}
if(f instanceof BigAirplane){ //-----------只能适用于BigAirplane类
BigAirplane ba = (BigAirplane)f;
score += ba.getScore();
}
if(f instanceof Bee){ //-------------------只能适用于Bee类
Bee bee = (Bee)f;
int type = bee.getAwardType();
switch(type){
case 0:
hero.addFire();
break;
case 1:
hero.addLife();
break;
}
}
if(f instanceof BigYellowBee){ //-----------只能适用于BigYellowBee类
BigYellowBee byb = (BigYellowBee)f;
score += byb.getScore();
int type = byb.getAwardType();
switch(type){
case 0:
hero.addFire();
break;
case 1:
hero.addLife();
break;
}
}
}}}
//--------------------复用性好、扩展性好、维护性好-----高质量代码
if(f instanceof EnemyScore){ //-------能适用于所有实现EnemyScore接口的类
EnemyScore es = (EnemyScore)f;
score += es.getScore();
}
if(f instanceof EnemyAward){ //-------能适用于所有实现EnemyAward接口的类
EnemyAward ea = (EnemyAward)f;
int type = ea.getAwardType();
switch(type){
case 0:
hero.addFire();
break;
case 1:
hero.addLife();
break;
}
}
------------------------------------------------------
//假设被撞对象为小敌机----------调用的是小敌机的getScore()----1分
//假设被撞对象为大敌机----------调用的是大敌机的getScore()----3分
//假设被撞对象为大黄蜂----------调用的是大黄蜂的getScore()----8分
if(f instanceof EnemyScore){
EnemyScore es = (EnemyScore)f;
score += es.getScore();
}
//假设被撞对象为小蜜蜂----------调用的是小蜜蜂的getAwardType()
//假设被撞对象为大黄蜂----------调用的是大黄蜂的getAwardType()
if(f instanceof EnemyAward){
EnemyAward ea = (EnemyAward)f;
int type = ea.getAwardType();
switch(type){
case 0:
hero.addFire();
break;
case 1:
hero.addLife();
break;
}
}
第十天:
1、内存管理:由JVM来管理
1)堆:
1.1)存储new出来的对象(包括实例变量)
1.2)垃圾:没有任何引用所指向的对象
垃圾回收器(GC)不定时到内存中清扫垃圾,
回收过程是透明的(看不到的),不一定一发现就立刻回收,
通过调用System.gc()可以建议JVM尽快调度GC来回收垃圾
1.3)实例变量的生命周期:
创建对象时存储在堆中,对象被回收时一并被回收
1.4)内存泄漏:不再使用的内存还没有被及时的回收,严重的泄漏会导致系统的崩溃
建议:不再使用的对象应及时将引用设置为null
注: -------------面向对象第一天this那
只要是在方法中的都叫局部变量,main方法也是,局部变量(包括方法的参数)放在栈中,所以a是局部变量,a是引用类型变量(简称 引用)
栈中 a是引用类型,里面装的是地址 (就是这个对象的地址)
注:是引用类型a里装的是地址,不是栈 中装的是地址
基于a的地址 指向了Airplane对象,a=null,给a赋值为null,null表示为空,就啥也没有了,a就没有地址了,即 a没有指向任何对象,所以就认为 Airplane对象和实例变量 是垃圾
Airplane a1 = a;
a1是Airplane类型,a也是Airplane类型,是可以直接赋值的;因为没new,所以堆中没有东西,栈中会有个a1(a1是引用 是局部变量);
是把a里的地址存到a1中了,所以a1=0x1111(本来是a=0x1111),所以a a1它两地址的值相同,所以 它两指向了同一个对象
a=null,给a赋值为null,null表示为空,就啥也没有了,a就没有地址了,即 a没有指向任何对象
所以此时 Airplane对象和实例变量 不是垃圾
2)栈:
2.1)存储正在调用的方法中的局部变量(包括方法的参数)
2.2)调用方法时,会在栈中为该方法分配一块对应的栈帧,
栈帧中存储方法中的 局部变量和方法的参数,
方法调用结束时,栈帧被自动清除,局部变量一并被清除
2.3)局部变量的生命周期:
调用方法时存储在栈中,方法调用结束时与栈帧一并被清除
注:GC回收的是堆中的对象,栈帧是被自动清除
main的栈帧被清除了,小o也就没有了,堆中的垃圾也就被GC回收了
3)方法区:
3.1)存储 .class 字节码文件(包括静态变量、所有方法)
3.2)方法只有一份,通过 this来区分具体的调用对象
(一个类只会生成一个字节码文件)
Airplane a1 = new Airplane();
① new Airplane对象时,先分配 方法区,因为第一步 先去加载Airplane这个类,一加载就把字节码文件放到方法区中
(只要用到了Airplane这个类,就在方法区中分配 Airplane.class{包括静态变量、所有方法} )
② new Airplane对象了,所以在堆中 分配new出来的对象(即 Airplane对象),包括实例变量(宽 高 speed等)
③调用main方法时,会在栈中为该方法分配一块对应的栈帧,栈帧中分配一个a1,a1装的是地址 指向 堆中的该对象
a1.y=100;
这句话一写,先找a1,就找到了地址,基于这个地址,找到堆中的Airplane这个对象,找到对象后就找到了y,把y从0改为100
Airplane a2 = new Airplane();
① 又一个 new Airplane对象,方法区中不改变(因为一个类只被加载一次),
② 堆中要变(因为又new对象了),所以在堆中 又分配new出来的对象(即 Airplane对象),包括实例变量(宽 高 speed等),
③ 调用main方法时,会在栈中为该方法分配一块对应的栈帧,栈帧中分配一个a2,a2装的是地址 指向 堆中的该对象
a2.y=200;
这句话一写,先找a2,就找到了地址,基于这个地址,找到堆中的Airplane这个对象,找到对象后就找到了y,把y从0改为200
a1.step(); //a1.y=102; (因为step里 y+=speed,相当于 y=y+speed,speed为2 )
a2.step(); //a2.y=202;
a1、a2 两个都调step方法了,step在内存中只有一份,a1、a2调的都是方法区中的那一份step()
那在step()中怎么区分是a1调的step() 还是a2调的step(),然后改对应的值 (a1的y改为102,a2的y改为202)?
用 this关键字区分是a1调的 还是a2调的,this关键字只能用在方法中
a1点的时候,step()中的this指的是a1,a1的 y+=speed
a2点的时候,step()中的this指的是a2,a2的 y+=speed
面向对象三大特征:
1.封装:
1)类:封装的是对象的属性和行为
2)方法:封装的是具体的业务功能实现
3)访问控制修饰符:封装的是具体的访问权限
2.继承:
1)作用:代码复用
2)超类:所有派生类所共有的属性和行为
接口:部分派生类所共有的属性和行为
派生类:派生类所特有的属性和行为
3)单一继承、多接口实现,具有传递性
3.多态:
1)行为多态:所有抽象方法都是多态的,通过重写来表现多态
对象多态:所有对象都是多态的,通过向上造型来表现多态
2)向上造型、强制类型转换、instanceof
实例变量:
1.类中,方法外
2.创建对象时存储在堆中,对象被回收时一并被回收
3.有默认值
局部变量: 面向对象第一天this那
1.方法中
2.调用方法时存储在栈中,方法调用结束时与栈帧一并被清除
3.没有默认值
注:
a b c都没有赋值,但只有c会报错,变量在用之前必须声明并初始化,
程序要想走到 红色大括号{ 里面,要先调用show方法,想调show方法 要先new对象,
有了对象 才能调show
一旦new对象,在堆中分配实例变量a,同时 给a赋默认值0(即 new的时候,a已经有值了,为0)
o.show(5); 有参必须传参,即 给b赋值为5
往下走,走到c的位置,c没值,所以 c 编译错误
所以 重点看 方法里的变量,不给值是不能用的
Object:所有类的鼻祖
所有类都直接或间接的继承了Object--------API第二天/第三天详细讲解
状态有四种:
1.启动状态------------------车点着火了,但是还没有走
2.运行状态------------------车往前走着了
3.暂停状态------------------碰到红灯,暂时停止了
4.游戏结束状态---------------车熄火了
补充get/set
package oo.day10;
//补充get/set
public class GetSetDemo {
public static void main(String[] args) {
Student zs = new Student(); //创建了一个学生对象
//给下面Student类的成员变量(属性)name和age 赋值,赋值调set,取值调get
zs.setName("zhangsan"); //赋值
System.out.println(zs.getName()); //取值
zs.setAge(25);
System.out.println(zs.getAge());
}
}
class Student{
private String name; //数据私有,行为公开(私有的 只能本类访问,别的类不能访问,set/get)
private int age;
public String getName(){ //get取值
return name;
}
public void setName(String name){ //set赋值
this.name = name;
}
public int getAge(){
return age;
}
public void setAge(int age){
this.age = age;
}
}
数据私有,行为公开(私有的 只能本类访问,别的类不能访问,如果想访问,会提供一套组件 get获取 / set设置)
class Student{
private String name; //数据私有,行为公开(私有的 只能本类访问,别的类不能访问,set/get)
private int age;
举例:
name已经私有了,外面其他类想用,做成 公开 getName(),在getName() 中 返回name(get获取,想获取一个东西,肯定要有返回值)
public String getName(){ //get取值
return name; //获取名字 就返回名字
}
set设置,想赋值,就要知道赋值为几,所以需要参数(String name)
赋值不需要返回值 不需要知道结果,把值赋上就行了,所以需要参数
public void setName(String name){ //set赋值
this.name = name; //赋值
}
可以认为 set在赋值,get在取值
get是有返回值的,set是有参数的
对于一个私有的数据(如:name),一般会公开 set 和 get 这一套,让别人用
怎么用: