0%

接口与继承

接口

接口就像是一种约定,我们约定某些英雄是物理系英雄,那么他们就一定能够进行物理攻击。

创建接口

举例:

1
2
3
4
5
6
package charactor;

public interface AD {
//物理伤害
public void physicAttack();
}

实现接口

实现某个接口,就相当于承诺了某种约定

所以,实现AD这个接口,就必须提供AD接口中声明的方法physicAttack() 实现在语法上使用关键字 implements

默认方法

定义

接口也可以提供具体方法了,而不像以前,只能提供抽象方法

举例

1
2
3
4
5
6
7
8
9
package charactor;

public interface Mortal {
public void die();

default public void revive() {
System.out.println("本英雄复活了");
}
}

为什么会有默认方法

假设没有默认方法这种机制,那么如果要为Mortal增加一个新的方法revive,那么所有实现了Mortal接口的类,都需要做改动。

但是引入了默认方法后,原来的类,不需要做任何改动,并且还能得到这个默认方法

通过这种手段,就能够很好的扩展新的类,并且做到不影响原来的类

对象转型

引用类型与对象类型

在这个例子里,有一个对象 new ADHero(), 同时也有一个引用ad 对象是有类型的, 是ADHero 引用也是有类型的,是ADHero 通常情况下,引用类型和对象类型是一样的

1
2
3
4
5
6
7
8
9
10
11
12
package charactor;

public class Hero {
public String name;
protected float hp;

public static void main(String[] args) {

ADHero ad = new ADHero();

}
}

向上转型

子类转父类

一个很简单的判别办法 把右边的当做左边来用,看说得通不

所有的子类转换为父类,都是说得通的

举例

1
2
3
4
5
6
7
8
9
10
11
package charactor;

public class Hero {
public String name;
protected float hp;
public static void main(String[] args) {
Hero h = new Hero();//h引用的类型是Hero
ADHero ad = new ADHero();//ad引用的类型是ADHero
h = ad; //把ADHero当做Hero使用,一定可以
}
}

类转换接口

举例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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当做AD来使用,而AD接口只有一个physicAttack方法,这就意味着转换后就有可能要调用physicAttack方法,而ADHero一定是有physicAttack方法的,所以转换是能成功的。

向下转型

父类转子类

父类转子类,有的时候行,有的时候不行,所以必须进行强制转换。 强制转换的意思就是 转换有风险,风险自担。

接口转实现类

举例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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, 而adi引用是接口类型:AD,实现类转换为接口,是向上转型,所以无需强制转换,并且一定能成功

ADHero adHero = (ADHero) adi; //adi实际上是指向一个ADHero的,所以能够转换成功

ADAPHero adapHero = (ADAPHero) adi; // adi引用所指向的对象是一个ADHero,要转换为ADAPHero就会失败。
adapHero.magicAttack();
}

}

接口要先向下转型成为一个类,这个接口实际上已经只想一个类(应该不全,有空看看文章)

所以继承并实现这个接口的类才有可能让接口向下转型,与这个接口没有继承关系的类一定不能

instanceof

判断一个引用所指向的对象

举例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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);
}
}

多态

操作符的多态

如果+号两侧都是整型,那么+代表 数字相加 如果+号两侧,任意一个是字符串,那么+代表字符串连接

举例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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
2
3
4
5
6
Item i1= new LifePotion();	// i1实际上指向LifePotion这种对象
Item i2 = new MagicPotion(); //i2实际上指向MagicPotion这种对象
System.out.print("i1 是Item类型,执行effect打印:"); //i1调用LifePotion对象中方法
i1.effect();
System.out.print("i2也是Item类型,执行effect打印:"); //i2调用MagicPotion对象中effect方法
i2.effect();

类的多态条件

  1. 父类(接口)引用指向子类对象
  2. 调用的方法有重写

使用多态与不使用多态区别

不使用多态

关键代码

1
2
3
4
5
6
public void useLifePotion(LifePotion lp){
lp.effect();
}
public void useMagicPotion(MagicPotion mp){
mp.effect();
}

完整代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
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);

}

}

使用多态

关键代码

1
2
3
public void useItem(Item i){
i.effect();
}

完整代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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);

}

}

exercise

  1. 设计一个接口

接口叫做Mortal,其中有一个方法叫做die

  1. 实现接口

分别让ADHero,APHero,ADAPHero这三个类,实现Mortal接口,不同的类实现die方法的时候,都打印出不一样的字符串

  1. 为Hero类,添加一个方法,在这个方法中调用 m的die方法。

    1
    public void kill(Mortal m)
  2. 在主方法中 首先实例化出一个Hero对象:盖伦 然后实例化出3个对象,分别是ADHero,APHero,ADAPHero的实例 然后让盖伦 kill 这3个对象

answer

关键代码

1
2
3
4
5
6
7
8
9
10
public void kill(Mortal m) {
m.die();
}
ADHero shibing1 = new ADHero();
APHero shibing2 = new APHero();
ADAPHero shibing3 = new ADAPHero();
garen.kill(shibing1);
garen.kill(shibing2);
garen.kill(shibing3);

上面在尝试的时候发现只能在声明shibing的时候必须用对应的类或者声明成接口类型Mortal,如果声明成Hero类型会报错。个人理解原因是Hero与Mortal之间没有继承关系

完整代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
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 void kill(Mortal m) {
m.die();
}
public static void main(String[] args) {

Hero garen = new Hero();
garen.name = "盖伦";

Mortal shibing1 = new ADHero();
Mortal shibing2 = new APHero();
Mortal shibing3 = new ADAPHero();
garen.kill(shibing1);
garen.kill(shibing2);
garen.kill(shibing3);
}

}

隐藏

与重写类似,方法的重写是子类覆盖父类的对象方法

隐藏,就是子类覆盖父类的类方法

子类中的静态方法覆盖率父类中的静态方法

举例:

父类

1
2
3
4
5
6
7
8
9
10
11
12
13
package charactor;

public class Hero {
public String name;
protected float hp;

//类方法,静态方法
//通过类就可以直接调用
public static void battleWin(){
System.out.println("hero battle win");
}

}

子类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package charactor;

public class ADHero extends Hero implements AD{

@Override
public void physicAttack() {
System.out.println("进行物理攻击");
}

//隐藏父类的battleWin方法
public static void battleWin(){
System.out.println("ad hero battle win");
}

public static void main(String[] args) {
Hero.battleWin();
ADHero.battleWin();
}

}

exercise:

Hero h =new ADHero();

h.battleWin(); //battleWin是一个类方法 h是父类类型的引用 但是指向一个子类对象 h.battleWin(); 会调用父类的方法?还是子类的方法?

answer:

静态方法与类直接关联,而不是与实例对象关联。所以声明为Hero的类,就调用Hero的类方法

super关键字

父类显式提供无参构造方法

关键代码

1
2
3
public Hero(){
System.out.println("Hero的构造方法 ");
}

完整代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package charactor;

import property.Item;

public class Hero {

String name; //姓名

float hp; //血量

float armor; //护甲

int moveSpeed; //移动速度

public void useItem(Item i){
System.out.println("hero use item");
i.effect();
}

public Hero(){
System.out.println("Hero的构造方法 ");
}

public static void main(String[] args) {
new Hero();
}

}

实例化子类,父类的构造方法一定会被调用

实例化一个ADHero(), 其构造方法会被调用 其父类的构造方法也会被调用 并且是父类构造方法先调用 子类构造方法会默认调用父类的 无参的构造方法

关键代码

1
2
3
4
5
6
7
public ADHero(){

System.out.println("AD Hero的构造方法");
}

new ADHero();

完整代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package charactor;

public class ADHero extends Hero implements AD{

@Override
public void physicAttack() {
System.out.println("进行物理攻击");
}

public ADHero(){

System.out.println("AD Hero的构造方法");
}

public static void main(String[] args) {

new ADHero();

}

}

输出

1
2
Hero的构造方法 
AD Hero的构造方法

父类显式提供两个构造方法

分别是无参的构造方法和带一个参数的构造方法

关键代码

1
2
3
4
5
6
7
8
public Hero(){
System.out.println("Hero的无参的构造方法 ");
}

public Hero(String name){
System.out.println("Hero的有一个参数的构造方法 ");
this.name = name;
}

完整代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package charactor;

import property.Item;

public class Hero {

String name; //姓名

float hp; //血量

float armor; //护甲

int moveSpeed; //移动速度

public void useItem(Item i){
System.out.println("hero use item");
i.effect();
}

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();
}

}

子类显式调用父类带参构造方法

使用关键字super 显式调用父类带参的构造方法

关键代码

1
2
3
4
public ADHero(String name){
super(name);
System.out.println("AD Hero的构造方法");
}

完整代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package charactor;

public class ADHero extends Hero implements AD{

@Override
public void physicAttack() {
System.out.println("进行物理攻击");
}

public ADHero(String name){
super(name);
System.out.println("AD Hero的构造方法");
}

public static void main(String[] args) {
new ADHero("德莱文");
}

}

调用父类属性

Objext类

Object类是所有类的父类

toString()

toString()的意思是返回当前对象的字符串表达

举例说明

直接调用原式版本的toString

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package charactor;

public class Hero {
public String name;
protected float hp;

// public String toString(){
// return name;
// }

public static void main(String[] args) {

Hero h = new Hero();
h.name = "盖伦";
System.out.println(h.toString());
//直接打印对象就是打印该对象的toString()返回值
System.out.println(h);
}
}

重写toString

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package charactor;

public class Hero {
public String name;
protected float hp;

@Override
public String toString(){
return name;
}

public static void main(String[] args) {

Hero h = new Hero();
h.name = "盖伦";
System.out.println(h.toString());
//直接打印对象就是打印该对象的toString()返回值
System.out.println(h);
}
}

finalize()

当一个对象没有任何引用指向的时候,它就满足垃圾回收的条件

当它被垃圾回收的时候,它的finalize() 方法就会被调用。

finalize() 不是开发人员主动调用的方法,而是由虚拟机JVM调用的。

举例

equals()

equals() 用于判断两个对象的内容是否相同

原始

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package charactor;

public class Hero {
public String name;
protected float hp;

// public boolean equals(Object o){
// if(o instanceof Hero){
// Hero h = (Hero) o;
// return this.hp == h.hp;
// }
// return false;
// }

public static void main(String[] args) {
Hero h1= new Hero();
h1.hp = 300;
Hero h2= new Hero();
h2.hp = 400;
Hero h3= new Hero();
h3.hp = 300;

System.out.println(h1.equals(h2));
System.out.println(h1.equals(h3));
}
}

输出

1
2
false
false

改写:

假设,当两个英雄的hp相同的时候,我们就认为这两个英雄相同

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package charactor;

public class Hero {
public String name;
protected float hp;

public boolean equals(Object o){
if(o instanceof Hero){
Hero h = (Hero) o;
return this.hp == h.hp;
}
return false;
}

public static void main(String[] args) {
Hero h1= new Hero();
h1.hp = 300;
Hero h2= new Hero();
h2.hp = 400;
Hero h3= new Hero();
h3.hp = 300;

System.out.println(h1.equals(h2));
System.out.println(h1.equals(h3));
}
}

==

这不是Object的方法,但是用于判断两个对象是否相同 更准确的讲,用于判断两个引用,是否指向了同一个对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package charactor;

public class Hero {
public String name;
protected float hp;

public boolean equals(Object o){
if(o instanceof Hero){
Hero h = (Hero) o;
return this.hp == h.hp;
}
return false;
}

public static void main(String[] args) {
Hero h1= new Hero();
h1.hp = 300;
Hero h2= new Hero();
h2.hp = 400;
Hero h3= new Hero();
h3.hp = 300;

System.out.println(h1==h2);
System.out.println(h1==h3);

}
}

输出

1
2
false
false

final

final修饰类

当Hero被修饰成final的时候,表示Hero不能够被继承 其子类会出现编译错误

final修饰方法

父类中被final修饰的方法不能在子类中重写

final修饰基本类型变量

final修饰基本类型变量,表示该变量只有一次赋值机会

final修饰引用

final修饰引用 h引用被修饰成final,表示该引用只有1次指向对象的机会

抽象类

定义

在类中声明一个方法,这个方法没有实现体,是一个“空”方法

这样的方法就叫抽象方法,使用修饰符“abstract"

当一个类有抽象方法的时候,该类必须被声明为抽象类

抽象类可以没有抽象方法,一旦一个类被声明为抽象类,就不能够被直接实例化

与子类的关系

为Hero增加一个抽象方法 attack,并且把Hero声明为abstract的。继承Hero类后,这些子类就必须提供不一样的attack方法实现。

抽象类和接口的区别

区别1:

  • 子类只能继承一个抽象类,不能继承多个

  • 子类可以实现多个接口

区别2:

  • 抽象类可以定义 public,protected,package,private、静态和非静态属性、final和非final属性
  • 接口中声明的属性,即便没有显式的声明,也只能是public、静态和final

内部类

非静态内部类

定义:

非静态内部类可以直接在一个类里面定义

语法

new 外部类().new 内部类()

使用

作为Hero的非静态内部类,是可以直接访问外部类的private实例属性name

举例

关键代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 非静态内部类,只有一个外部类对象存在的时候,才有意义
// 战斗成绩只有在一个英雄对象存在的时候才有意义
class BattleScore {
int kill;
int die;
int assit;

public void legendary() {
if (kill >= 8)
System.out.println(name + "超神!");
else
System.out.println(name + "尚未超神!");
}
}

// 实例化内部类
// BattleScore对象只有在一个英雄对象存在的时候才有意义
// 所以其实例化必须建立在一个外部类对象的基础之上
BattleScore score = garen.new BattleScore();

完整代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
package charactor;

public class Hero {
private String name; // 姓名

float hp; // 血量

float armor; // 护甲

int moveSpeed; // 移动速度

// 非静态内部类,只有一个外部类对象存在的时候,才有意义
// 战斗成绩只有在一个英雄对象存在的时候才有意义
class BattleScore {
int kill;
int die;
int assit;

public void legendary() {
if (kill >= 8)
System.out.println(name + "超神!");
else
System.out.println(name + "尚未超神!");
}
}

public static void main(String[] args) {
Hero garen = new Hero();
garen.name = "盖伦";
// 实例化内部类
// BattleScore对象只有在一个英雄对象存在的时候才有意义
// 所以其实例化必须建立在一个外部类对象的基础之上
BattleScore score = garen.new BattleScore();
score.kill = 9;
score.legendary();
}

}

静态内部类

定义

与非静态内部类不同,静态内部类水晶类的实例化 不需要一个外部类的实例为基础,可以直接实例化

语法

new 外部类.静态内部类();

使用

因为没有一个外部类的实例,所以在静态内部类里面不可以访问外部类的实例属性和方法 除了可以访问外部类的私有静态成员外,静态内部类和普通类没什么大的区别

举例

关键代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
static class EnemyCrystal{
int hp=5000;

//如果水晶的血量为0,则宣布胜利
public void checkIfVictory(){
if(hp==0){
Hero.battleWin();

//静态内部类不能直接访问外部类的对象属性
System.out.println(name + " win this game");
}
}
}

//实例化静态内部类
Hero.EnemyCrystal crystal = new Hero.EnemyCrystal();

完整代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package charactor;

public class Hero {
public String name;
protected float hp;

private static void battleWin(){
System.out.println("battle win");
}

//敌方的水晶
static class EnemyCrystal{
int hp=5000;

//如果水晶的血量为0,则宣布胜利
public void checkIfVictory(){
if(hp==0){
Hero.battleWin();

//静态内部类不能直接访问外部类的对象属性
System.out.println(name + " win this game");
}
}
}

public static void main(String[] args) {
//实例化静态内部类
Hero.EnemyCrystal crystal = new Hero.EnemyCrystal();
crystal.checkIfVictory();
}

}

匿名类

定义

  1. 匿名类指的是在声明一个类的同时实例化它,使代码更加简洁精练 通常情况下,要使用一个接口或者抽象类,都必须创建一个子类
  2. 有的时候,为了快速使用,直接实例化一个抽象类,并“当场”实现其抽象方法。 既然实现了抽象方法,那么就是一个新的类,只是这个类,没有命名。 这样的类,叫做匿名类
  3. 在匿名类中使用外部的局部变量,外部的局部变量必须修饰为final

举例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package charactor;

public abstract class Hero {
String name; //姓名

float hp; //血量

float armor; //护甲

int moveSpeed; //移动速度

public abstract void attack();

public static void main(String[] args) {

ADHero adh=new ADHero();
//通过打印adh,可以看到adh这个对象属于ADHero类
adh.attack();
System.out.println(adh);

Hero h = new Hero(){
//当场实现attack方法
public void attack() {
System.out.println("新的进攻手段");
}
};
h.attack();
//通过打印h,可以看到h这个对象属于Hero$1这么一个系统自动分配的类名

System.out.println(h);
}

}

本地类

定义

本地类可以理解为有名字的匿名类,可以直接声明在代码块里面,可以是主方法,for循环里等等地方

举例

关键代码

1
2
3
4
5
6
//与匿名类的区别在于,本地类有了自定义的类名
class SomeHero extends Hero{
public void attack() {
System.out.println( name+ " 新的进攻手段");
}
}

完整代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package charactor;

public abstract class Hero {
String name; //姓名

float hp; //血量

float armor; //护甲

int moveSpeed; //移动速度

public abstract void attack();

public static void main(String[] args) {

//与匿名类的区别在于,本地类有了自定义的类名
class SomeHero extends Hero{
public void attack() {
System.out.println( name+ " 新的进攻手段");
}
}

SomeHero h =new SomeHero();
h.name ="地卜师";
h.attack();
}

}

UML图

类图

接口图

继承关系