一、接口

在设计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学习第八项——接口与继承相关推荐

  1. 学习笔记二:接口与继承(内部类)

    学习笔记参考来源 java学习路线-推荐链接:java-接口与继承-内部类 学习笔记难免出错 望多指正!!! 什么是内部类呢? 什么是内部类?定义很简单 内部类就是定义在另一个类中的类 内部类的分类 ...

  2. JAVA学习笔记 15 - 函数式接口、Lambda表达式和方法引用

    本文是Java基础课程的第十五课.主要介绍在JDK8中,Java引入的部分新特性,包括函数式接口.Lambda表达式和方法引用.这些新特性使得Java能够在按照面向对象思想进行开发的基础上,融合函数式 ...

  3. Java声明定义抽象类_接口_继承_实现

    文章目录 声明定义抽象类 声明定义接口 派生类.抽象类.接口的继承要点 声明定义抽象类 public abstract class CRMSystem {public abstract Client ...

  4. JAVA笔记:shape类(接口,继承,lambda表达式...)

    目录 写一段程序,实现shape面积,并可以对面积大小排序 定义父类shape类,并定义方法Area() 定义第一个子类Circle类 定义第二个子类Rect类 定义接口Comparator,并写入c ...

  5. Java学习日志(八): 可变参数,debug断点调试,静态导入,集合嵌套

    JavaEE学习日志持续更新----> 必看!JavaEE学习路线(文章总汇) Java学习日志(八) 可变参数 debug断点调试 静态导入 集合嵌套 可变参数 JDK1.5之后的新特性 作用 ...

  6. java类、抽象类、接口的继承规则

    一个接口可以继承多个接口. interface C extends A, B {}是可以的.一个类可以实现多个接口: class D implements A,B,C{}但是一个类只能继承一个类,不能 ...

  7. 每日学习-Java基础(十)接口和继承10(内部类)

    一.内部类 内部类 // 内部类有四种类型 // 1-非静态内部类 // 2-静态内部类 // 3-匿名类 // 4-本地类 1.非静态内部类 package ia10_innerClass;publ ...

  8. JAVA学习第八周总结

    CSS定位属性 <!DOCTYPE html> <html><head><meta charset="utf-8" /><ti ...

  9. 【Java多线程】实现Runnable接口方式 / 继承Thread类方式;使用synchronized锁实现线程安全;线程安全的懒汉式单例模式;死锁问题示例

    Thread 的生命周期 一.实现Runnable接口方式 1.在 run 方法中使用 synchronized 块 /*** 例子:创建三个窗口卖票,总票数为100张.使用实现Runnable接口的 ...

最新文章

  1. 移动端制作公共样式reset
  2. MONO Design创建电信3D机房
  3. python自带的idle优点_python新手入门使用自带的IDLE、用pycharm还是visual studio ?
  4. 数据科学 python_为什么需要以数据科学家的身份学习Python的7大理由
  5. r 函数返回多个值_第四讲 R描述性统计分析
  6. 解决Eclipse建立Maven项目后无法建立src/main/java资源文件夹的办法
  7. ubuntu20.10上搭建hadoop3.2.2伪分布式
  8. sql server 性能_SQL Server硬件性能调整
  9. JS是按值传递还是按引用传递?
  10. 判断字符串中是否存在的几种方案:string.indexof、string.contains、list.contains、list.any几种方式效率对比...
  11. 大数据如何改变企业的业务
  12. test luasql's postgresql driver performance (not better than pgbench)
  13. RocketMQ消息消费之长轮询
  14. CenterPoint的环境配置error大全【已全部解决】
  15. 微信小程序可滑动周日历组件
  16. java mysql 公交车换乘查询算法_公交车路线查询系统后台数据库设计--换乘算法改进与优化...
  17. Silverlight实用窍门系列:1.Silverlight读取外部XML加载配置---(使用WebClient读取XAP包同目录下的XML文件))【附带实例源码】...
  18. Html中如何自定义Video显示的长宽比
  19. 东方梅酒:梅见的新国饮故事
  20. 相机调试-tuning常见缩写汇总

热门文章

  1. 文件服务器 损坏 发现不可读内容,文档故障咨询:某xlsx文档,编辑并保存后再打开,提示“...发现不可读取的内容 - Microsoft Community...
  2. charles mock数据
  3. PLC编程:S7-200 SMART PID向导控制竟是这么回事
  4. 力扣解法汇总592-分数加减运算
  5. 数据结构(顺序表):学生管理系统的设计与实现(C语言)
  6. 食物营养知识 食物相克中毒
  7. STM32+SD卡的原理图绘制以及用32完成对SD卡的数据读取(fat文件模式)
  8. UnityEditor扩展 - 编辑器中的内置属性Attribute
  9. 窗口看门狗 WWDG
  10. 【前端小实战】拼多多首页导航布局