Java学习第八项——接口与继承
一、接口
在设计LOL英雄时,有两类进攻性英雄,一类ADC 一类APC
可以使用 接口 实现这一功能
接口就像是一种约定,
约定好的ADC 就打物理输出,不会打别的。
IDea如何创建接口?
点开后,选择,并输入名字即可
1、物理攻击接口(魔法攻击接口)
两种接口创建编码基本一致,此处合在一起记录。
在接口AD中需要声明出,我们会在类中使用的方法。只是声明一个方法,并没有方法体,也就是“空方法”。
package charactor;public interface AD {public void physicAttack();//声明一个方法,但没有方法体,是一个“空方法”
}
package charactor;public interface AP {public void magicAttack();
}
2、创建一个物理(魔法和同时有物理魔法)的英雄类
重新创建一个英雄类,需要继承Hero类,(使用extends)
也就拥有了Hero中的属性。
然后需要使用相应的方法(已经在接口中声明。若为声明,需要重新声明),使用implements
上述第二步 被称为 实现(在语法上使用implements)
实现一个接口,就相当于承认了这个接口的约束。
同样,实现了这个接口,就必须提供该接口声明的方法(是不是意味着,必须提供方法体。)
package charactor;public class APHeroCarry extends Hero implements AP{@Overridepublic void magicAttack() {System.out.println("进行魔法伤害!");}
}
package charactor;public class ADAPHeroCarry extends Hero implements AD,AP{//当接口不止一个时,用逗号隔开。@Overridepublic void magicAttack() {System.out.println("可以进行魔法攻击!");}@Overridepublic void physicAttack() {System.out.println("也可以进行物理攻击!");}
}
二、对象转型
1、明确引用类型和对象类型的关系
在这个例子中,
有一个对象 new ADHero()
也有一个引用 ad
两者都有类型,都是ADHero
通常情况下,两者时一样的。
现在讲的对象转型,指的是两者之间不一致的情况。
package charactor;public class Hero {public String name; protected float hp;public static void main(String[] args) {ADHero ad = new ADHero();}
}
2、子类转父类(向上转型)
转型指的是 引用类型和对象类型不一致时使用的方法
类型转换有时候会成功,但有时候也会失败的。
判断方法:
把右边当左边看,如果可以就说明转型成功了。
Hero h = new Hero();
ADHero ad = new ADHero();
h = ad;
这个例子就是:转换类型
右边是物理攻击英雄
左边是普通英雄
把物理攻击英雄当作普通英雄,行得通,所以可以转换。
其实,所有的子类转父类都是行的通的。毕竟子类继承的父类。
苹果手机 继承了 手机,把苹果手机当做普通手机使用
怡宝纯净水 继承了 饮品, 把怡宝纯净水 当做饮品来使用
苍老师 继承了动物, 把苍老师 。。。
package charactor;public class Hero {public String name; protected float hp;public static void main(String[] args) {Hero h = new Hero();ADHero ad = new ADHero();//类型转换指的是把一个引用所指向的对象的类型,转换为另一个引用的类型//把ad引用所指向的对象的类型是ADHero//h引用的类型是Hero//把ADHero当做Hero使用,一定可以 h = ad;}
}
3、父类转子类
父类转子类,有时候成功,有时候失败。
所以必须强制转换。
什么时候行?
1. Hero h =new Hero();
2. ADHero ad = new ADHero();
3. h = ad;
4. ad = (ADHero) h;
第三行是子类转父类,可行。h现在指向ADHero;
第四行是父类转子类,要强制执行,此时(h现在指向ADHero),这一步就是ADHero转换为ADHero不变,也可以成功。
什么时候不行?
1. Hero h =new Hero();
2. ADHero ad = new ADHero();
3. Support s =new Support();
4. h = s;
5. ad = (ADHero)h;
这里引用了三个对象。
第四行,子类转父类,可行。此时h指向Support这个对象。
第五行,想把指向Support的h转换为ADHero。从语义上讲,把物理攻击英雄变为辅助英雄是不可以的,并且系统会抛出异常。(同为子类,不行)
package charactor;import charactor1.Support;public class Hero {public String name;protected float hp;public static void main(String[] args) {Hero h =new Hero();ADHero ad = new ADHero();Support s =new Support();h = ad;ad = (ADHero) h;h = s;ad = (ADHero)h;}}
14行: 把ad当做Hero使用,一定可以
转换之后,h引用指向一个ad对象
15行: h引用有可能指向一个ad对象,也有可能指向一个support对象
所以把h引用转换成AD类型的时候,就有可能成功,有可能失败
因此要进行强制转换,换句话说转换后果自负
到底能不能转换成功,要看引用h到底指向的是哪种对象
在这个例子里,h指向的是一个ad对象,所以转换成ADHero类型,是可以的
16行:把一个support对象当做Hero使用,一定可以
转换之后,h引用指向一个support对象
17行:这个时候,h指向的是一个support对象,所以转换成ADHero类型,会失败。
失败的表现形式是抛出异常 ClassCastException 类型转换异常
总结:(右边放左边,看成立不)
(1)子转父,一定行;
(2)父转子,不一定,需强转。(强转的结果:强转等号两边类型一样或有构成子转父,可以转;等号两边变为同级,不可以转)
5、实现类转换为接口(向上转型)
package charactor;public class Hero {public String name;protected float hp;public static void main(String[] args) {ADHero ad = new ADHero();AD adi = ad;}}
引用ad指向的对象是ADHero类型的。这个类型已经实现了AD接口。
AD adi = ad;
这一句讲ADHero类型的引用ad,转换为接口类型AD。
而AD接口只有一个方法physicAttack,那么ad也可能会调用该方法,由于已知ad已实现了AD接口,所以转换时成功的。
6、接口转换为实现类(向下转型)
package charactor;public class Hero {public String name;protected float hp;public static void main(String[] args) {ADHero ad = new ADHero();AD adi = ad;ADHero adHero = (ADHero) adi;ADAPHero adapHero = (ADAPHero) adi;adapHero.magicAttack();}}
AD adi = ad;
可以实现,就是类转换为接口。
ADHero adHero = (ADHero) adi;
此时adi已经指向了一个ADHero对象,所以也可以转换为ADHero
ADAPHero adapHero = (ADAPHero) adi;
adi指向ADHero,但是不可以转换为ADAPHero。
因为,如果要转换为ADAPHero,就可能会调用magicAttack方法,但是adi引用的对象ADHero并没有该方法。
总结:(从右边看,是右边要转换为什么)
(1)类转接口(向上转型),如该类已经实现过接口,即当转换为接口时,可以调用接口的方法,则成功。
(2)接口转类(向下转型),须先看等号左边“转化成的类”已经实现了哪些接口。再看“要转化的接口”是否有这些方法,如果有,则成功,反之,则失败。
7、instanceof
X instancof Hero 来判断一个引用所指的对象,是否是Hero类型或者是Hero的子类
package charactor;public class Hero {public String name;protected float hp;public static void main(String[] args) {ADHero ad = new ADHero();APHero ap = new APHero();Hero h1= ad;Hero h2= ap;//判断引用h1指向的对象,是否是ADHero类型System.out.println(h1 instanceof ADHero);//判断引用h2指向的对象,是否是APHero类型System.out.println(h2 instanceof APHero);//判断引用h1指向的对象,是否是Hero的子类型System.out.println(h1 instanceof Hero);}
}
三、重写
子类可以继承父类的对象方法,继承之后对方法进行重新编码--重写。
又称 覆盖override
1、父类
package property;public class Item {public String name;public int price;//创建了物品类int i = 1;public void method1( int i){System.out.println(i); i = 5;}public void buy(){System.out.println("购买");}public void effect(){System.out.println("物品使用后,可以有效果");}
}
2、子类
package property;public class LifePotion extends Item{public void effect(){System.out.println("血瓶使用后,可以回血!");}
}
3、main
package property;public class Item {public String name;public int price;//创建了物品类int i = 1;public void method1( int i){System.out.println(i); i = 5;}public void buy(){System.out.println("购买");}public void effect(){System.out.println("物品使用后,可以有效果");}class Armor extends Item{int ac; //护甲等级}public static void main(String[] args) {Item i = new Item();i.effect();LifePotion ip = new LifePotion();ip.effect();}}
可以看见,当我们在子类里重写了父类方法后,方法体改变了。
四、多态
操作符的多态,
+可以作为 运算符,也可以作为字符串连接
类的多态
父类引用指向子类对象
1、操作符的多态
+ 两边为整型,运算符而已
+ 两边为 任意一个为字符串,就是把字符串拼接。
int i = 5;int j = 6;int s = i + j;System.out.println(s);//字符串运算int a = 7;String b = "5";String d = "5";String c = d + b;System.out.println(c);String e = a + b;System.out.println(e);
后两个为字符串拼接。
2、类的多态
父类
package property;public class Item {public String name;public int price;//创建了物品类public void buy(){System.out.println("购买");}public void effect(){System.out.println("物品使用后,可以有效果");}public static void main(String[] args) {Item i1 = new LifePotion();Item i2 = new MagicPotion();System.out.println("i1 是 Item类型,执行effect");i1.effect();System.out.println("i2 是 Item类型,执行effect");i2.effect();}}
两个子类:
package property;public class LifePotion extends Item{public void effect(){System.out.println("血瓶使用后,可以回血!");}
}
package property;public class MagicPotion extends Item{public void effect(){System.out.println("蓝瓶使用后,可以恢复魔法!");}
}
输出结果:
观察可知:
(1)i1和i2类型都是为Item;
(2)但是指向了两个不同的子类对象,都执行了effect(重写过);
(3)输出的是重写过的 结果。
3、类的多态条件
(1)父类(接口)引用指向子类对象
(2)子类对继承的方法有重写
4、使用类的多态和不使用有什么区别?
(1)不使用类的多态
假如不使用多态,英雄要使用蓝瓶和血瓶。就需要设定两个方法。
useLifePotion
useMagicPotion
去调用不同物品子类里的effect方法。
但是游戏里还有很多的物品,这样对每一个物品都需要有一个方法,就会使英雄类的main函数显得很复杂。
package charactor;import property.LifePotion;
import property.MagicPotion;public class Hero {public String name; protected float hp;public void useLifePotion(LifePotion lp){lp.effect();}public void useMagicPotion(MagicPotion mp){mp.effect();}public static void main(String[] args) {Hero garen = new Hero();garen.name = "盖伦";LifePotion lp =new LifePotion();MagicPotion mp =new MagicPotion();garen.useLifePotion(lp);garen.useMagicPotion(mp);}}
虽然结果一样,但是这里多更新了两个对象。对于更多的物品就会有更多的方法需要声明。
(2)使用类的多态
假如使用类的多态,就不用给每个物品创建一个新的方法
只需要一个方法就可以让每个子类对象的重写方法被调用。
package charactor;import property.Item;
import property.LifePotion;
import property.MagicPotion;public class Hero {public String name;protected float hp;public void useItem(Item i){i.effect();}public static void main(String[] args) {Hero garen = new Hero();garen.name = "凯瑟琳";LifePotion lp =new LifePotion();MagicPotion mp =new MagicPotion();garen.useItem(lp);garen.useItem(mp); }}
这里就只使用了一个方法useItem(),根据参数的不同,就可以调用不同子类的方法。
由于在声明时,参数类型是 Item,
根据类的多态,就可以使用该类Item下的子类。
5、练习
immortal是不朽的,不死的意思
mortal就是终有一死的,凡人的意思
1. 设计一个接口
接口叫做Mortal,其中有一个方法叫做die
2. 实现接口
分别让ADHero,APHero,ADAPHero这三个类,实现Mortal接口,不同的类实现die方法的时候,都打印出不一样的字符串
3. 为Hero类,添加一个方法,在这个方法中调用 m的die方法。
public void kill(Mortal m)
4. 在主方法中
首先实例化出一个Hero对象:盖伦
然后实例化出3个对象,分别是ADHero,APHero,ADAPHero的实例
然后让盖伦 kill 这3个对象
(1)首先是接口Mortal
package charactor;public interface Mortal {public void die();
}
(2)三个子类
package charactor;public class ADAPHeroCarry extends Hero implements AD,AP,Mortal{//当接口不止一个时,用逗号隔开。@Overridepublic void magicAttack() {System.out.println("可以进行魔法攻击!");}@Overridepublic void physicAttack() {System.out.println("也可以进行物理攻击!");}public void die(){System.out.println("魔剑士英雄死亡!");}
}
package charactor;public class ADHeroCarry extends Hero implements AD,Mortal{public void physicAttack(){System.out.println("进行物理攻击!");}public void die(){System.out.println("物理英雄死亡!");}}
package charactor;public class APHeroCarry extends Hero implements AP,Mortal{@Overridepublic void magicAttack() {System.out.println("进行魔法伤害!");}public void die(){System.out.println("魔法英雄死亡!");}}
(3)主函数及输出
package charactor;import property.*;
public class Hero {static String copyRight = "Riot"; //声明该属性时初始化static {copyRight = "YSKM"; //静态初始化块}protected int id;{id = 10001; //初始化块}public String name;float hp;float armor;int moveSpeed;//类方法public void useItem(Item i){i.effect();}public void kill(Mortal m){m.die();}public static void main(String[] args) {Hero h1 = new Hero();h1.name = "凯瑟琳";ADAPHeroCarry a1 = new ADAPHeroCarry();ADHeroCarry a2 = new ADHeroCarry();APHeroCarry a3 = new APHeroCarry();h1.kill(a1);h1.kill(a2);h1.kill(a3);}}
五、隐藏
与重写类似,
重写是 子类覆盖父类的方法;
隐藏是子类覆盖父类的类方法。
(方法的调用需要有创建的有对象,而类方法不需要具体对象就可以使用
当需要调用某个属性时,必须有对象;但只是无属性方法,就可以用类方法)
1、父类(包含一个类方法)
package charactor;import property.*;
public class Hero {static String copyRight = "Riot"; //声明该属性时初始化static {copyRight = "YSKM"; //静态初始化块}protected int id;{id = 10001; //初始化块}public String name;float hp;float armor;int moveSpeed;//类方法()public static void battleWin(){System.out.println("战斗胜利!");}}}
2、子类隐藏父类的类方法
package charactor;public class ADHeroCarry extends Hero implements AD,Mortal{public void physicAttack(){System.out.println("进行物理攻击!");}public void die(){System.out.println("物理英雄死亡!");}public static void battleWin(){System.out.println("物理英雄战斗胜利!");}//子类的类方法 覆盖掉父类的类方法public static void main(String[] args) {Hero.battleWin();ADHeroCarry.battleWin();}
}
不同类调用该方法,就会有不同结果。(类方法已经被隐藏)
3、练习
Hero h =new ADHero();
h.battleWin(); //battleWin是一个类方法
h是父类类型的引用
但是指向一个子类对象
h.battleWin(); 会调用父类的方法?还是子类的方法?
可见输出的结果还是 Hero的结果,表明,虽然静态方法(类方法可以被实例化对象调用,但是和h指向哪个对象无关,只和h的【引用类型】有关系,这里仍是父类引用)
六、super关键字
1、准备一个显示的无参构造方法;
package charactor;import property.*;
public class Hero {static String copyRight = "Riot"; //声明该属性时初始化static {copyRight = "YSKM"; //静态初始化块}protected int id;{id = 10001; //初始化块}public String name;float hp;float armor;int moveSpeed;//父类的类方法public Hero(){//父类的构造方法System.out.println("Hero的构造方法 ");}public static void main(String[] args) {new Hero();new ADHeroCarry();}}
此时实例化父类对象或者子类对象,都会打印这句话。
2、实例化子类,父类的构造方法一定会被调用
实例化一个子类对象,其构造方法(子类)会调用。
同时也会调用其父类的显示构造方法(隐式也会,只是没有显示出来)
且,父类构造方法会先于子类调用。
package charactor;import property.*;
public class Hero {static String copyRight = "Riot"; //声明该属性时初始化static {copyRight = "YSKM"; //静态初始化块}protected int id;{id = 10001; //初始化块}public String name;float hp;float armor;int moveSpeed;//父类的类方法public Hero(){//父类的构造方法System.out.println("Hero的构造方法 ");}public static void main(String[] args) {//new Hero();new ADHeroCarry();}}
public ADHeroCarry(){System.out.println("AD Hero的构造方法");}
3、父类显示的设置两个构造函数
package charactor;import property.*;
public class Hero {static String copyRight = "Riot"; //声明该属性时初始化static {copyRight = "YSKM"; //静态初始化块}protected int id;{id = 10001; //初始化块}public String name;float hp;float armor;int moveSpeed;//父类的类方法public Hero(){//父类的构造方法System.out.println("Hero的无参构造方法 ");}public Hero(String name){//父类的构造方法System.out.println("Hero的含一个参数的构造方法 ");this.name = name;}public static void main(String[] args) {new Hero();new Hero("亚索");//new ADHeroCarry();}}
4、子类显示调用父类的构造方法。
如果只是在子类里构造一个有一参数的构造方法,在调用该方法时,会先调用父类的显示无参构造方法。
package charactor;public class ADHeroCarry extends Hero implements AD,Mortal{public void physicAttack(){System.out.println("进行物理攻击!");}public void die(){System.out.println("物理英雄死亡!");}public ADHeroCarry(){System.out.println("AD Hero的构造方法");}public ADHeroCarry(String name){System.out.println("AD Hero的一参数构造方法");}public static void main(String[] args) {Hero h = new ADHeroCarry("凯瑟莉");}
}
如果使用super关键字来调用的话。
public ADHeroCarry(String name){super(name);System.out.println("AD Hero的一参数构造方法");}
就会调用,父类的含参构造方法。
5、调用父类属性
通过super调用父类的mobeSpeed属性
ADHeroCarry也提供了moveSpeed属性
package charactor;public class ADHeroCarry extends Hero implements AD,Mortal{int moveSpeed = 350;public void physicAttack(){System.out.println("进行物理攻击!");}public void die(){System.out.println("物理英雄死亡!");}public ADHeroCarry(){System.out.println("AD Hero的构造方法");}public int getMoveSpeed1(){return this.moveSpeed;}public int getMoveSpeed2(){return super.moveSpeed;}public ADHeroCarry(String name){super(name);//调用父类System.out.println("AD Hero的一参数构造方法");}public static void main(String[] args) {ADHeroCarry h = new ADHeroCarry();System.out.println(h.getMoveSpeed1());System.out.println(h.getMoveSpeed2());//Hero h = new ADHeroCarry("凯瑟莉");}
}
根据不同 的方法可以调用(this)本子类的速度,也可以得到(super)父类的速度。
6、调用父类的方法
不使用super,
且不在子类里重写方法。调用时会调用父类的方法。
package charactor;import property.Item;
import property.LifePotion;public class ADHeroCarry extends Hero implements AD,Mortal{int moveSpeed = 350;public void physicAttack(){System.out.println("进行物理攻击!");}public void die(){System.out.println("物理英雄死亡!");}public ADHeroCarry(){System.out.println("AD Hero的无参数构造方法");}public int getMoveSpeed1(){return this.moveSpeed;}public int getMoveSpeed2(){return super.moveSpeed;}public ADHeroCarry(String name){super(name);//调用父类System.out.println("AD Hero的一参数构造方法");}public static void main(String[] args) {ADHeroCarry h = new ADHeroCarry();LifePotion lp = new LifePotion();h.useItem(lp);}
}
重写并 加上super后
public void useItem(Item i){super.useItem(i);System.out.println("ADHero use Item!");}
七、Object类
Object类时所有子类的父类
1、Object类时所有子类的父类
声明一个类的同时,默认继承了Object这个类
public class Hero extends Object
2、toString()
Object类提供了一个toString()方法,也就是所有类都可以调用该方法。
该方法的作用就是返回当前对象的字符串表达
sout返回的作用就时toString()
public static void main(String[] args) {new Hero();Hero h =new Hero("亚索");System.out.println(h);System.out.println(h.toString());//new ADHeroCarry();}
可以观察到两个打印方式的结果是一样的。
3、finalize
当一个对象没有任何引用指向时,就满足垃圾回收的条件。
当他被垃圾回收时,就会调用它的finalize()方法。
该方法不是开发人员主动调用的,而是JVM虚拟机自带的。
public void finalize(){System.out.println("该英雄正在被回收!");}public void useItem(Item i){System.out.println("hero use Item!");}public static void main(String[] args) {Hero h;//只有引用 没有对象for (int i = 0; i < 9999; i++){h = new Hero();//不断创建新的对象, 并让h指向该对象;// 那么前一个对象就会失去它的引用,当这样的对象堆积多了。// 就会触发垃圾回收}new Hero();//new ADHeroCarry();}
每次调用一次finalize()方法就会打印该语句一次。
4、equals()
equalis()的作用是,判断两个对象的内容是否一致。
假设,我们认为两个英雄血量hp一致时,该两个英雄就是一样的。
public boolean equals(Object o){//返回类型是boolean,因为时判断是否相等。if (o instanceof Hero){//interface的作用是判断,o是否是Hero类型或者是不是其子类。Hero h = (Hero) o;//强制转换,如果是同类型,直接转;如果是子类转父类,也可以转。此处不存在失败的问题。return this.hp == h.hp;//这里返回的是(true或者false)}else return false;}public static void main(String[] args) {Hero h1 = new Hero();h1.hp = 200;h1.name = "的";Hero h2 = new Hero();h2.hp = 300;Hero h3 = new Hero();h3.hp = 400;System.out.println(h1.equals(h3));System.out.println(h2.equals(h3));System.out.println(h1.equals(h2));}
可以看见,血量不一样时,就会显示false
但是修改数据后,就会判断true。
根据不同的equals()方法,来通过不同条件,判断。
5、==
这不是Object的方法,但是可以用来判断两个对象是否相同,
准确的来说,是判断两个引用是否指向同一个对象。
System.out.println(h1 == h3);System.out.println(h1.equals(h2));System.out.println(h1 == h2);
可以看见 h1 h2 的对象的hp是一样的,但是并不代表着两个引用指向同一个对象。
6、hashCode()
hashCode()返回一个哈希值。
7、线程同步相关方法
Object还提供线程同步相关方法
八、final
final修饰类、方法、基本类型变量,引用的时候分别有不同的意思。
1、final修饰类
当Hero被修饰为final时,表示其不能被继承。
其子类会报错误。
public final class Hero extends Object{
public class ADHeroCarry extends Hero implements AD,Mortal{
2、final修饰方法
当父类的方法被修饰为fianl时,子类无法对该方法进行重写。
public final void useItem(Item i){System.out.println("hero use Item!");}
子类的
public void useItem(Item i){super.useItem(i);System.out.println("ADHero use Item!");}
编译时会报错
3、final修饰基本类型变量
表示该变量只有一次赋值机会
final int a ;a = 1 ;a=2;System.out.println(a);
4、final修饰引用
h引用被修饰为final,表示为该引用只有一次指向机会。当其完成指向后,不能再赋予新对象。
但是可以修改该对象的属性,因为其属性并没有被final修饰。
final Hero h;h =new Hero();h =new Hero();h.hp = 5;
5、常量
常量指的是可以公开,直接访问,不会变化的值
比如 itemTotalNumber 物品栏的数量是6个
public class Hero extends Object {public static final int itemTotalNumber = 6;//物品栏的数量String name; //姓名float hp; //血量float armor; //护甲int moveSpeed; //移动速度
九、抽象类
当一个类声明了一个没有方法体的方法时,称这个“空”方法为抽象方法,使用abstract修饰
当一个类有抽象方法时,该类也要被声明为抽象类。
1、抽象类
为Hero增加一个attack的抽象方法,用abstract修饰,并且用abstract修饰Hero类
Hero的各个类都会继承其属性和方法,但对于该方法应该是有不同的输出方法的。 (而且是必须具备该方法)
父类部分代码:
public abstract class Hero extends Object{static String copyRight = "Riot"; //声明该属性时初始化static {copyRight = "YSKM"; //静态初始化块}public String name;float hp;float armor;int moveSpeed = 375;public Hero(){}public abstract void attack();
子类必须具备的代码:
public void physicAttack(){System.out.println("进行物理攻击!");}public void attack(){physicAttack();}
public void magicAttack() {System.out.println("进行魔法伤害!");}public void attack(){magicAttack();}
public void attack() {System.out.println("既可以进行物理攻击,也可以进行魔法攻击");}
但是此时会有异常显示,因为Hero是抽象abstract的,所以不能创建
2、抽象类没有抽象方法
当一个抽象类,没有声明抽象方法时。(可以)
此时的抽象类不能被实例化
3、抽象类与接口的区别
区别1 :
子类只能继承一个抽象类,
但是可以实现多个接口。
区别2:
抽象类可以定义:
public、private、protected、package
静态和非静态属性
final和非final属性
但接口声明的属性只能是:
public
静态的
final
即便没有显式的声明,也会默认显示
注:接口和抽象类都可以有实体方法。接口中的有实体的方法,叫做默认方法
接口里需要先声明“空”方法,其实也都是抽象方法。
package charactor;public interface AP {public static final int resistPhyical = 100;int restistMagic = 0;public void magicAttack();
}
其中 int resistPhysical即使没有显式的声明为public static final,
也会默认为该类型;
总结:
(1)抽象类可以被定义,(类似普通类),抽象方法并不是必须的。(若有,则必须再子类中进行重写)
(2)接口声明的空方法,也是抽象方法。接口被实现时,不一定需要重写该方法。
十、内部类
分为四种:
非静态内部类
静态内部类
匿名类
本地类
1、非静态内部类
非静态内部类 BattleScore“战斗成绩”
非静态内部类可以直接在一个类里面定义
比如:
战斗成绩只有在一个战斗英雄存在时才有意义
所有,非静态内部类,只有在外部类实例化后才有意义。
语法:new 外部类().内部类()
由于BattleScore是Hero 的非静态内部类,所以是可以调用Hero的私有属性name
public class Hero extends Object{private String name1;class BattleScore{int kill;int die;int assist;public void legendary(){if (kill > 8)System.out.println(name1 + "超神!");elseSystem.out.println(name1 + "原来是小瘪三");}}public static void main(String[] args) {Hero h = new Hero();h.name1 = "张辽";//首先实例化外部类Hero//接下来实例化内部类//由于BattleScore的实例化必须建立在一个已有对象上,才有意义//战斗成绩只有在英雄存在时才有用BattleScore b = h.new BattleScore();b.kill = 10;b.legendary();}}
可以看见在实例化非静态内部类时,
BattleScore b = h.new BattleScore();
必须是英雄(外部类对象存在才有意义)
2、静态内部类
在一个类里声明一个静态内部类;
比如敌方水晶,当敌方水晶被摧毁时,己方所有英雄都会获得胜利,并不是具体谁取得胜利。
因此,静态内部类的实现,并不需要外部类的实例化。可以直接实例化;
语法:new 外部类.静态内部类()
因为没有外部类的实例,所以静态内部类的实例化,不能访问外部类的属性和方法。
除了可以访问外部类的私有静态成员外,静态内部类和普通类没有区别。
私有静态成员
public class Hero extends Object{static String copyRight = "Riot"; //声明该属性时初始化static {copyRight = "YSKM"; //静态初始化块}public String name;float hp;float armor;int moveSpeed = 375;private static void battleWin(){//私有静态方法System.out.println("battle win");}static class EnemyCrystal{//静态内部类int hp = 50;public void checkIfVictory(){if(hp ==0){Hero.battleWin();//System.out.println(name + "win the game!");//静态内部类,不能访问外部属性,因为没有实例化}else {System.out.println("The Enemy Crystal is still alive!");}}}public static void main(String[] args) {//静态内部对象,不需要提前实例化外部对象Hero.EnemyCrystal c = new Hero.EnemyCrystal();c.checkIfVictory();}}
静态内部对象,不需要提前实例化外部对象,直接创建了静态内部对象。
Hero.EnemyCrystal c = new Hero.EnemyCrystal();
实际上就是 外部类.静态内部类 引用名 = new 外部类.静态内部类
3、匿名类
匿名类指的是,声明一个类的同时九实例化它,简化代码。
通常情况下,要实现一个接口或者使用一个抽象类,都必须要创建一个子类。
但有时候,为了方便使用,我们直接实例化抽象类,并在其中 实现其抽象方法
因为,实现的抽象方法的必是类,所以此处的也是一个类,只是没有命名,
即,匿名类。
public abstract class Hero {//加上abstract就变为了抽象类String name; //姓名float hp; //血量float armor; //护甲int moveSpeed; // 移动速度int killNumber; //击杀数int budaoNumber; // 补刀数int money; //金钱int deadNumber; // 死亡次数int assistantNumber; //助攻次数float attackSpeed; // 攻击速度// 这里就是创建了一个英雄类,设定了英雄的属性。public abstract void attack();//父类声明主函数public static void main(String[] args) {ADHero adHero = new ADHero();adHero.attack();System.out.println( "看看adHero属于哪个类\n"+ adHero);Hero h = new Hero() {@Overridepublic void attack() {System.out.println("新的进攻手段!");}}; //此处需要一个分号,是因为这里属于实例化的一部分h.attack();//这样是直接实例化对象,并且实现了抽象方法,说明这也是一个类。System.out.println( "看看h属于哪个类\n"+ h);}
}
可以观察到,adHero属于ADHero类,但是h的类是由JVM随机生成的一个类。
PS:为什么这个实例化,和抽象方法的重写,在main函数里?
(1)实例化本身就应该在main函数里。
(2) 实际上,抽象类本身是不可以实例化的,只有通过转化为匿名类,才可以。并且是看做一个新的类,并对抽象方法重写。
(3)就算将这部分代码放在main函数外,也不会报错。但是由于此函数没有命名,所以不可以在main中实例化,也就无法调用attack()方法。而原方法可行。
4、本地类
本地类就是 有名字的匿名类。
内部类、匿名类、本地类区别:
(1)内部类只能声明在成员位置,即与属性和方法同一等级;
(2)匿名类和本地类,直接声明在代码块中,可以是主方法,也可以是for循环等等;
package charactor;public abstract class Hero {//加上abstract就变为了抽象类String name; //姓名float hp; //血量float armor; //护甲int moveSpeed; // 移动速度int killNumber; //击杀数int budaoNumber; // 补刀数int money; //金钱int deadNumber; // 死亡次数int assistantNumber; //助攻次数float attackSpeed; // 攻击速度// 这里就是创建了一个英雄类,设定了英雄的属性。public abstract void attack();//父类声明主函数public static void main(String[] args) {//与匿名类的区别在于,本地类有了自定义的类名;class SomeHero extends Hero{public void attack(){System.out.println(name + "(本地类)刹那月华!");}}SomeHero someHero = new SomeHero();someHero.name = "张队长";someHero.attack();System.out.println(someHero);}
}
本地类的对象,既有系统分配的类,也有自定义的类。
(我以为继承了父类,所以会有两个,但其他子类,也只有一个,见ADHero)
5、在匿名类中使用外部的局部变量
在匿名类里使用外部局部变量,必须修饰为final,
注:在jdk8中,已经不需要强制修饰成final了,如果没有写final,不会报错,因为编译器偷偷的帮你加上了看不见的final
package charactor;public abstract class Hero {//加上abstract就变为了抽象类String name; //姓名float hp; //血量float armor; //护甲int moveSpeed; // 移动速度int killNumber; //击杀数int budaoNumber; // 补刀数int money; //金钱int deadNumber; // 死亡次数int assistantNumber; //助攻次数float attackSpeed; // 攻击速度// 这里就是创建了一个英雄类,设定了英雄的属性。public abstract void attack();//父类声明主函数public static void main(String[] args) {final int damage =5;Hero h = new Hero() {@Overridepublic void attack() {System.out.println("新的进攻手段,造成"+damage+"点伤害");}}; //此处需要一个分号,是因为这里属于实例化的一部分h.attack();//这样是直接实例化对象,并且实现了抽象方法,说明这也是一个类。}
}
为什么要声明为final?
因为,在使用外部局部变量时,匿名类里也会声明一个同名变量,并且用构造方法初始化这个值。
若不用final,就意味着,可以对该变量进行多次赋值。
当调用attack()方法时,实际上是调用的这个匿名类内部的值,而非外部变量。
当在attack中,修改damage的值,就会被暗示修改了外部damage 的值,但实际上,这两个变量有不同存储地址,并不是同一个变量,也就无法同步修改。
为避免这种误差,就使用fianl来修饰外部damage,让其看起来像是不能被修改一样。
6、练习
创建一个Item的匿名类
package property;public abstract class Item {public String name;public int price;//创建了物品类public void buy(){System.out.println("购买");}public void effect(){System.out.println("物品使用后,可以有效果");}public abstract void disposable();public static void main(String[] args) {Item i1 = new LifePotion();//创建子类的方法Item i2 = new MagicPotion();//System.out.println("i1 是 Item类型,执行effect");//i1.effect();//System.out.println("i2 是 Item类型,执行effect");// i2.effect();Item item =new Item() {@Overridepublic void disposable() {System.out.println("全新的物品!");}};item.disposable();System.out.println(item);}}
系统分配的类名。
十一、默认方法
1、什么是默认方法
默认方法指的是 接口可以提供具体的方法了,而不是只声明一个空方法。
Mortal这个接口,提供了一个有实体的方法revive,并且声明为default;
package charactor;public interface Mortal {public void die();default public void revive(){System.out.println("此英雄复活了!");}
}
2、为什么有默认方法
假设没有默认方法的机制,那么若想给接口Mortal新添加一个功能,所有已实现该接口的类,都要对该方法进行重写,工作量繁杂。
但如果能直接在接口里实现默认方法(有实体的方法),那么对于所有实现该接口的类,都不用再重新修改此方法了,并可以直接使用。
3、练习
为AD接口增加一个默认方法 attack()
为AP接口也增加一个默认方法 attack()
问: ADAPHero同时实现了AD,AP接口,那么 ADAPHero 对象调用attack()的时候,是调用哪个接口的attack()?
答:
系统会报错。此时应该在ADAPHero中重写该方法,直接会调用他自身的attack()。
或者使用ADHero.super.attack()
APHero.super.attack()
来分别调用。
十二、综合练习
1、UML图-----类之间的关系
UML-Unified Module Language
统一建模语言,可以很方便的用于描述类的属性,方法,以及类和类之间的关系
2、解释UML-类图
3、解释UML-接口图
4、解释UML-继承关系
带箭头的实线,表示 Spider,Cat, Fish都继承于Animal这个父类
5、 解释UML-实现关系
表示 Fish实现了 Pet这个接口虚线
6、练习-Animal类
1. 创建Animal类,它是所有动物的抽象父类。
2. 声明一个受保护的整数类型属性legs,它记录动物的腿的数目。
3. 定义一个受保护的构造器,用来初始化legs属性。
4. 声明抽象方法eat。
5. 声明具体方法walk来打印动物是如何行走的(包括腿的数目)。
package Animals;public abstract class Animal {//抽象类protected int legs; //记录动物的推的数目public abstract void eat();//抽象方法public void walk(){System.out.println("动物都会运动,且该动物有%d条腿"+this.legs);}protected void Animal(int legs){this.legs = legs;//初始化腿数}
}
7、练习-Spider类
1. Spider继承Animal类。
2. 定义默认构造器,它调用父类构造器来指明所有蜘蛛都是8条腿。
3. 实现eat方法
package Animals;public class Spider extends Animal{public Spider(int legs){super(legs);// 调用了父类的构造方法}public void eat() {System.out.println("蜘蛛会吃鱼!");}
}
8、练习-Pet接口
根据UML类创建pet(宠物)接口
1. 提供getName() 返回该宠物的名字
2. 提供setName(String name) 为该宠物命名
3. 提供 play()方法
package Animals;public interface Pet {public String getName();public String setName(String name);public void play();
}
9、练习-Cat类
1. 该类必须包含String属性来存宠物的名字。
2. 定义一个构造器,它使用String参数指定猫的名字;该构造器必须调用超类构造器来指明所有的猫都是四条腿。
3. 另定义一个无参的构造器。该构造器调用前一个构造器(用this关键字)并传递一个空字符串作为参数
4. 实现Pet接口方法。
5. 实现eat方法。
package Animals;import charactor.Hero;public class Cat extends Animal implements Pet {String name;//1public Cat(String name){super(4);2 这里用的是父类的构造器}public Cat(){this(" ");//3 这里调用了前一个构造器}@Overridepublic String getName() {return name;}@Overridepublic void setName(String name) {this.name = name;}//4public void eat(){System.out.println("猫也吃鱼!");}//5public void play(){}
}
10、练习-Fish类
1. 创建Fish类,它继承Animal类
2. 重写Animal的walk方法以表明鱼不能走且没有腿。
3. 实现Pet接口
4. 无参构造方法
5. 提供一个private 的name属性
package Animals;public class Fish extends Animal implements Pet{private String name;//5public void walk(){System.out.println("鱼没有脚!不能走路");}///1public String getName() {return name;}@Overridepublic void setName(String name) {this.name = name;}public void play(){}//3public Fish(){System.out.println("一条鱼诞生了!");}//4public void eat(){System.out.println("鱼吃虾米·");}
}
Java学习第八项——接口与继承相关推荐
- 学习笔记二:接口与继承(内部类)
学习笔记参考来源 java学习路线-推荐链接:java-接口与继承-内部类 学习笔记难免出错 望多指正!!! 什么是内部类呢? 什么是内部类?定义很简单 内部类就是定义在另一个类中的类 内部类的分类 ...
- JAVA学习笔记 15 - 函数式接口、Lambda表达式和方法引用
本文是Java基础课程的第十五课.主要介绍在JDK8中,Java引入的部分新特性,包括函数式接口.Lambda表达式和方法引用.这些新特性使得Java能够在按照面向对象思想进行开发的基础上,融合函数式 ...
- Java声明定义抽象类_接口_继承_实现
文章目录 声明定义抽象类 声明定义接口 派生类.抽象类.接口的继承要点 声明定义抽象类 public abstract class CRMSystem {public abstract Client ...
- JAVA笔记:shape类(接口,继承,lambda表达式...)
目录 写一段程序,实现shape面积,并可以对面积大小排序 定义父类shape类,并定义方法Area() 定义第一个子类Circle类 定义第二个子类Rect类 定义接口Comparator,并写入c ...
- Java学习日志(八): 可变参数,debug断点调试,静态导入,集合嵌套
JavaEE学习日志持续更新----> 必看!JavaEE学习路线(文章总汇) Java学习日志(八) 可变参数 debug断点调试 静态导入 集合嵌套 可变参数 JDK1.5之后的新特性 作用 ...
- java类、抽象类、接口的继承规则
一个接口可以继承多个接口. interface C extends A, B {}是可以的.一个类可以实现多个接口: class D implements A,B,C{}但是一个类只能继承一个类,不能 ...
- 每日学习-Java基础(十)接口和继承10(内部类)
一.内部类 内部类 // 内部类有四种类型 // 1-非静态内部类 // 2-静态内部类 // 3-匿名类 // 4-本地类 1.非静态内部类 package ia10_innerClass;publ ...
- JAVA学习第八周总结
CSS定位属性 <!DOCTYPE html> <html><head><meta charset="utf-8" /><ti ...
- 【Java多线程】实现Runnable接口方式 / 继承Thread类方式;使用synchronized锁实现线程安全;线程安全的懒汉式单例模式;死锁问题示例
Thread 的生命周期 一.实现Runnable接口方式 1.在 run 方法中使用 synchronized 块 /*** 例子:创建三个窗口卖票,总票数为100张.使用实现Runnable接口的 ...
最新文章
- 移动端制作公共样式reset
- MONO Design创建电信3D机房
- python自带的idle优点_python新手入门使用自带的IDLE、用pycharm还是visual studio ?
- 数据科学 python_为什么需要以数据科学家的身份学习Python的7大理由
- r 函数返回多个值_第四讲 R描述性统计分析
- 解决Eclipse建立Maven项目后无法建立src/main/java资源文件夹的办法
- ubuntu20.10上搭建hadoop3.2.2伪分布式
- sql server 性能_SQL Server硬件性能调整
- JS是按值传递还是按引用传递?
- 判断字符串中是否存在的几种方案:string.indexof、string.contains、list.contains、list.any几种方式效率对比...
- 大数据如何改变企业的业务
- test luasql's postgresql driver performance (not better than pgbench)
- RocketMQ消息消费之长轮询
- CenterPoint的环境配置error大全【已全部解决】
- 微信小程序可滑动周日历组件
- java mysql 公交车换乘查询算法_公交车路线查询系统后台数据库设计--换乘算法改进与优化...
- Silverlight实用窍门系列:1.Silverlight读取外部XML加载配置---(使用WebClient读取XAP包同目录下的XML文件))【附带实例源码】...
- Html中如何自定义Video显示的长宽比
- 东方梅酒:梅见的新国饮故事
- 相机调试-tuning常见缩写汇总
热门文章
- 文件服务器 损坏 发现不可读内容,文档故障咨询:某xlsx文档,编辑并保存后再打开,提示“...发现不可读取的内容 - Microsoft Community...
- charles mock数据
- PLC编程:S7-200 SMART PID向导控制竟是这么回事
- 力扣解法汇总592-分数加减运算
- 数据结构(顺序表):学生管理系统的设计与实现(C语言)
- 食物营养知识 食物相克中毒
- STM32+SD卡的原理图绘制以及用32完成对SD卡的数据读取(fat文件模式)
- UnityEditor扩展 - 编辑器中的内置属性Attribute
- 窗口看门狗 WWDG
- 【前端小实战】拼多多首页导航布局