- 在设计LOL的时候,进攻类英雄有两种,一种是进行物理系攻击,一种是进行魔法系攻击
- 这时候,就可以使用接口来实现这个效果。
- 接口就像是一种约定,我们约定某些英雄是物理系英雄,那么他们就一定能够进行物理攻击。
- 物理攻击接口
- 创建一个接口 File->New->Interface
- AD ,声明一个方法 physicAttack 物理攻击,但是没有方法体,是一个“空”方法
package charactor; public interface AD { //物理伤害 public void physicAttack(); }
- 设计一类英雄,能够使用物理攻击
- 设计一类英雄,能够使用物理攻击,这类英雄在LOL中被叫做AD
- 类:ADHero
- 继承了Hero 类,所以继承了name,hp,armor等属性
- 实现某个接口,就相当于承诺了某种约定
- 所以,实现了AD这个接口,就必须提供AD接口中声明的方法physicAttack()
- 实现在语法上使用关键字 implements
package charactor; public class ADHero extends Hero implements AD{ @Override public void physicAttack() { System.out.println("进行物理攻击"); } }
- 魔法攻击接口
- 创建一个接口 File->New->Interface
- AP ,声明一个方法 magicAttack 魔法攻击,但是没有方法体,是一个“空”方法
package charactor; public interface AP { public void magicAttack(); }
- 设计一类英雄,只能使用魔法攻击
- 设计一类英雄,只能使用魔法攻击,这类英雄在LOL中被叫做AP
- 类:APHero
- 继承了Hero 类,所以继承了name,hp,armor等属性
- 同时,实现了AP这个接口,就必须提供AP接口中声明的方法magicAttack()
- 实现在语法上使用关键字 implements
package charactor; public class APHero extends Hero implements AP{ @Override public void magicAttack() { System.out.println("进行魔法攻击"); } }
- 设计一类英雄,既能进行物理攻击,又能进行魔法攻击
package charactor; //同时能进行物理和魔法伤害的英雄 public class ADAPHero extends Hero implements AD,AP{ @Override public void magicAttack() { System.out.println("进行魔法攻击"); } @Override public void physicAttack() { System.out.println("进行物理攻击"); } }
- 设计一个治疗者接口:Healer
- 该接口声明有方法: heal()
- 设计一个Support类,代表辅助英雄,继承Hero类,同时实现了Healer这个接口
- 接口:
public interface Healer { public void heal(); //加血 }
- 实现接口:
public class Support extends Hero implements Healer{ @Override public void heal() { System.out.println(name+" 加了一口血"); } }
- 引用类型与对象类型的概念
- 首先,明确引用类型与对象类型的概念
- 在这个例子里,有一个对象 new ADHero(), 同时也有一个引用ad
- 对象是有类型的, 是ADHero
- 引用也是有类型的,是ADHero
- 通常情况下,引用类型和对象类型是一样的
- 接下来要讨论的类型转换的问题,指的是引用类型和对象类型不一致的情况下的转换问题
package charactor; public class Hero { public String name; protected float hp; public static void main(String[] args) { ADHero ad = new ADHero(); } }
- 子类转父类(向上转型)
- 所谓的转型,是指当引用类型和对象类型不一致的时候,才需要进行类型转换
- 类型转换有时候会成功,有时候会失败(参考基本类型的类型转换)
- 到底能否转换成功? 教大家一个很简单的判别办法
- 把右边的当做左边来用,看说得通不
Hero h = new Hero(); ADHero ad = new ADHero(); h = ad;
- 右边ad引用所指向的对象的类型是 物理攻击英雄
- 左边h引用的类型是 普通英雄
- 把物理攻击英雄 当做 普通英雄,说不说得通? 说得通,就可以转
- 所有的子类转换为父类,都是说得通的。比如你身边的例子
- 苹果手机 继承了 手机,把苹果手机当做普通手机使用
- 怡宝纯净水 继承了 饮品, 把怡宝纯净水 当做饮品来使用
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; } }
- 父类转子类(向下转型)
- 父类转子类,有的时候行,有的时候不行,所以必须进行强制转换。
- 强制转换的意思就是 转换有风险,风险自担。
- 什么时候行呢?
Hero h =new Hero(); ADHero ad = new ADHero(); h = ad; ad = (ADHero) h;
- 第3行,是子类转父类,一定可以的
- 第4行,就是父类转子类,所以要进行强转。
- h这个引用,所指向的对象是ADHero, 所以第4行,就会把ADHero转换为ADHero,就能转换成功。
- 什么时候转换不行呢?
Hero h =new Hero(); ADHero ad = new ADHero(); Support s =new Support(); h = s; ad = (ADHero)h;
- 第4行,是子类转父类,是可以转换成功的
- 第5行,是把h引用所指向的对象 Support,转换为ad引用的类型ADHero。 从语义上讲,把物理攻击英雄,当成辅助英雄来用,说不通,所以会强制转换失败,并且抛出异常
- 没有继承关系的两个类,互相转换
- 没有继承关系的两个类,互相转换,一定会失败
- 虽然ADHero和APHero都继承了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(); // 没有继承关系的类型进行互相转换一定会失败,所以会出现编译错误 ad = (ADHero) ap; } }
- 实现类转换成接口(向上转型)
- 引用ad指向的对象是ADHero类型,这个类型实现了AD接口
- 10行: 把一个ADHero类型转换为AD接口
- 从语义上来讲,把一个ADHero当做AD来使用,而AD接口只有一个physicAttack方法,这就意味着转换后就有可能要调用physicAttack方法,而ADHero一定是有physicAttack方法的,所以转换是能成功的。
package charactor; public class Hero { public String name; protected float hp; public static void main(String[] args) { ADHero ad = new ADHero(); AD adi = ad; } }
- 接口转换成实现类(向下转型)
- 10行: ad引用指向ADHero, 而adi引用是接口类型:AD,实现类转换为接口,是向上转型,所以无需强制转换,并且一定能成功
- 12行: adi实际上是指向一个ADHero的,所以能够转换成功
- 14行: adi引用所指向的对象是一个ADHero,要转换为ADAPHero就会失败。
- 假设能够转换成功,那么就可以使用magicAttack方法,而adi引用所指向的对象ADHero是没有magicAttack方法的
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(); } }
- instanceof
- instanceof 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
- 父类Item
- 父类Item有一个方法,叫做effect
package property; public class Item { String name; int price; public void buy(){ System.out.println("购买"); } public void effect() { System.out.println("物品使用后,可以有效果"); } }
- 子类LifePotion
- 子类LifePotion继承Item,同时也提供了方法effect
package property; public class LifePotion extends Item{ public void effect(){ System.out.println("血瓶使用后,可以回血"); } }
- 调用重写的方法
- 调用重写的方法
- 调用就会执行重写的方法,而不是从父类的方法
- 所以LifePotion的effect会打印:
- "血瓶使用后,可以回血"
package property; public class Item { String name; int price; public void effect(){ System.out.println("物品使用后,可以有效果"); } public static void main(String[] args) { Item i = new Item(); i.effect(); LifePotion lp =new LifePotion(); lp.effect(); } }
- 如果没有重写这样的机制怎么样?
- 如果没有重写这样的机制,也就是说LifePotion这个类,一旦继承了Item,所有方法都不能修改了。
- 但是LifePotion又希望提供一点不同的功能,为了达到这个目的,只能放弃继承Item,重新编写所有的属性和方法,然后在编写effect的时候,做一点小改动.
- 这样就增加了开发时间和维护成本
- 操作符的多态
- + 可以作为算数运算,也可以作为字符串连接
- 类的多态
- 父类引用指向子类对象
- 操作符的多态
- 同一个操作符在不同情境下,具备不同的作用
- 如果+号两侧都是整型,那么+代表 数字相加
- 如果+号两侧,任意一个是字符串,那么+代表字符串连接
package charactor; public class Hero { public String name; protected float hp; public static void main(String[] args) { int i = 5; int j = 6; int k = i+j; //如果+号两侧都是整型,那么+代表 数字相加 System.out.println(k); int a = 5; String b = "5"; String c = a+b; //如果+号两侧,任意一个是字符串,那么+代表字符串连接 System.out.println(c); } }
- 观察类的多态现象
- 观察类的多态现象:
- 1. i1和i2都是Item类型
- 2. 都调用effect方法
- 3. 输出不同的结果
- 多态: 都是同一个类型,调用同一个方法,却能呈现不同的状态
- LifePotion.java文件:
package property; public class LifePotion extends Item { public void effect(){ System.out.println("血瓶使用后,可以回血"); } }
- MagicPotion.java文件
package property; public class MagicPotion extends Item{ public void effect(){ System.out.println("蓝瓶使用后,可以回魔法"); } }
- Item.java文件
package property; public class Item { String name; 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.print("i1 是Item类型,执行effect打印:"); i1.effect(); System.out.print("i2也是Item类型,执行effect打印:"); i2.effect(); } }
- 类的多态条件
- 要实现类的多态,需要如下条件
- 1. 父类(接口)引用指向子类对象
- 2. 调用的方法有重写
- 那么多态有什么作用呢? 通过比较不使用多态与使用多态来进一步了解
- 类的多态-不使用多态
- 如果不使用多态,
- 假设英雄要使用血瓶和魔瓶,就需要为Hero设计两个方法
- useLifePotion
- useMagicPotion
- 除了血瓶和魔瓶还有很多种物品,那么就需要设计很多很多个方法,比如
- usePurityPotion 净化药水
- useGuard 守卫
- useInvisiblePotion 使用隐形药水
- 等等
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); } }
- 类的多态-使用多态
- 如果物品的种类特别多,那么就需要设计很多的方法
- 比如useArmor,useWeapon等等
- 这个时候采用多态来解决这个问题
- 设计一个方法叫做useItem,其参数类型是Item
- 如果是使用血瓶,调用该方法
- 如果是使用魔瓶,还是调用该方法
- 无论英雄要使用什么样的物品,只需要一个方法即可
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); } }
- 案例演示;
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个对象
- Mortal .java文件
package charactor; public interface Mortal { public void die(); }
- AD.java文件
package charactor; public interface AD { public void physicAttack(); }
- AP .java文件
package charactor; public interface AP { public void magicAttack(); }
- ADHero .java文件
package charactor; public class ADHero extends Hero implements AD ,Mortal{ @Override public void physicAttack() { } @Override public void die() { System.out.println(name+ " 这个物理英雄挂了"); } }
- APHero .java文件
package charactor; public class APHero extends Hero implements AP,Mortal { public void magicAttack() { } @Override public void die() { System.out.println(name+ " 这个魔法英雄挂了"); } }
- ADAPHero .java文件
package charactor; public class ADAPHero extends Hero implements AD,AP,Mortal{ @Override public void magicAttack() { // TODO Auto-generated method stub } @Override public void physicAttack() { // TODO Auto-generated method stub } @Override public void die() { System.out.println(name+ " 这个混合英雄挂了"); } }
- Hero.java
package charactor; public class Hero { public String name; protected float hp; public void kill(Mortal m){ System.out.println(name + " 放了一个大招" ); m.die(); } public static void main(String[] args) { Hero h =new Hero(); h.name = "盖伦"; ADHero ad = new ADHero(); ad.name = "艾希"; APHero ap = new APHero(); ap.name = "安妮"; ADAPHero adap = new ADAPHero(); adap.name = "库奇"; h.kill(ad); h.kill(ap); h.kill(adap); } }