多态概述(polymorphism)
多态: 指的是同一个方法调用,由于对象不同可能会有不同的行为。
现实生活中,同一个方法,具体实现会完全不同。比如:同样是调用人的 休息
方法,张三是睡觉,李四是旅游,数学教授是做数学题;同样是调用人 吃饭
的方法,中国人用筷子吃饭,英国人用刀叉吃饭,印度人用手吃饭。
多态的要点:
多态是方法的多态,不是属性的多态(多态与属性无关)
多态的存在要有3个必要条件:有继承/实现关系,有方法重写,父类引用指向子类对象
父类引用指向子类对象后,用该父类引用调用子类重写的方法,此时多态就出现了
之所以每类人的吃饭方法都不一样,是因为,在人这个类的下边,中国人子类,美国人子类,印度人子类都对吃这个方法进行了重写,所以每类人的吃饭方法不一样。多态是指方法的多态也是因为这样。
多态中成员访问特点
(图1
)
多态中,成员变量的访问特点
System.out.println(a.weight);
编译的时候看的是左边,看的是父类中有没有,如果父类中有,不会报错,如果父类中没有,就会报错
成员变量,编译看左边,运行也看左边
成员方法的访问特点
成员方法编译的时候看的也是左边,看的是父类中有没有,如果父类中有,不会报错,如果父类中没有,就会报错
成员方法,编译看左边,运行看右边
为什么成员变量和成员方法的访问不一样呢?
因为成员方法有重写,而成员变量没有
多态的好处和弊端
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
39
40
41
42
43
44
45
46
public class Animal {
public void eat () {
System . out . printun ( "动物吃东西" );
}
}
public class Cat extends Animal {
@Override
public void eat (){
System . out . println ( "猫吃鱼" );
}
}
public class Dog extends Animal {
@Override
public void eat () {
System . out . println ( "狗吃骨头" );
}
}
//动物操作类
public class AnimalOperator {
public void useAnimal ( Cat c ) {
c . eat ();
}
public void useAnimal ( Dog d ) {
d . eat ();
}
}
public class AnimalDemo {
public static void main ( string [] args ) {
//创建动物操作类的对象,调用方法
AnimalOperator ao = new AnimalOperator ();
Cat c = new Cat ();
ao . useAnimal ( c ); //猫吃鱼
Dog d = new Dog ();
ao . useAnimal ( d ); //狗吃骨头
}
}
用多态进行简化
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
39
40
41
42
43
44
45
46
47
48
public class Animal {
public void eat () {
System . out . printun ( "动物吃东西" );
}
}
public class Cat extends Animal {
@Override
public void eat () {
System . out . println ( "猫吃鱼" );
}
}
public class Dog extends Animal {
@Override
public void eat () {
System . out . println ( "狗吃骨头" );
}
public void LookDoor () {
System . oUt . println ( "狗看门" );
}
}
//动物操作类
public class AnimalOperator {
public void useAnimal ( Animal a ) { //Animal a = new Cat(); Animal a = new Dog();
a . eat ();
//a.LookDoor(); //编译看左边, Animal中没有LookDoor()方法, 报错
}
}
public class AnimalDemo {
public static void main ( string [] args ) {
//创建动物操作类的对象,调用方法
AnimalOperator ao = new AnimalOperator ();
Cat c = new Cat ();
ao . useAnimal ( c ); //猫吃鱼
Dog d = new Dog ();
ao . useAnimal ( d ); //狗吃骨头
}
}
多态的好处:提高了程序的扩展性,具体体现: 定义方法的时候,使用父类型作为参数,将来在使用的时候,使用具体的子类型参与操作
多态的弊端:不能使用子类的特有功能 (比如:某个方法只有子类有,父类没有,如果要使用,编译看左边,父类中没有,编译不通过,无法使用)
多态中的转型
为什么要学习多态的转型?
多态的弊端是不能使用子类的特有功能,而通过转型,我们就可以使用子类的特有功能
向上转型
向下转型
父类引用指向子类对象,我们称这个过程为向上转型,属于自动类型转换
向上转型后的父类引用变量只能调用它编译类型的方法,不能调用它运行时类型的方法。这时,我们就需要进行类型的强制转换,我们称之为向下转型
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
39
public class Animal {
public void eat () {
System . out . println ( "动物吃东西" );
}
}
public class Cat extends Animal {
@Override
public void eat () {
System . out . println ( "猫吃鱼" );
}
public void playGame () {
System . out . println ( "猫捉迷藏" );
}
}
public class AnimalDemo {
public static void main ( string [] args ) {
//向上转型
Animal a = new Cat ();
a . eat ();
//a.playGame(); 报错
//我现在想使用playGame()怎么办
//①创建Cat对象
Cat c = new Cat ();
c . eat ();
c . playGame ();
//②向下转型
Cat c = ( Cat ) a ; //父类引用转为子类对象
c . eat ();
c . playGame ();
}
}
多态转型内存图解
(图2
)
(图3
)
(图4
)
为了防止在向下转型时,出现类型转换异常,在向下转型中使用 instanceof
,加入 instanceof
进行判断
instanceof
是二元运算符,左边是对象,右边是类;当左边对象是右面的类或右面子类所创建的对象时,返回 true
;否则,返回 false
。
案例(猫和狗多态版)
需求:请采用多态的思想实现猫和狗的案例,并在测试类中进行测试
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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
public class Animal {
private String name ;
private int age ;
public Animal () {
}
public Animal ( String name , int age ) {
this . name name ;
this . age age ;
}
public void eat () {
System . oUt . println ( "动物吃东西" ) ;
}
//set/get......
}
public class Cat extends Animal {
public cat (){
}
public Cat ( String name , int age ) {
super ( name , age );
}
@Override
public void eat () {
System . out . println ( "猫吃鱼" );
}
}
public class Dog extends Animal {
public Dog (){
}
public Dog ( String name , int age ) {
super ( name , age );
}
@Override
public void eat (){
System . out . println ( "狗吃骨头" );
}
}
public class AnimalDemo {
public static void main ( String [] args ) {
//按照多态的方式创建对象并进行测试
Animal a = new Cat ();
a . setName ( "加菲" );
a . setAge ( 5 );
System . out . println ( a . getName () + "," + a . getAge ());
a . eat ();
a = new Cat ( "加菲" , 5 );
System . out . println ( a . getName () + "," + a . getAge ());
a . eat ();
}
}
抽象类概述
在 java
中,一个没有方法体的方法应该定义为抽象方法,而类中如果有抽象方法,该类必须定义为抽象类
1
2
3
public abstract class Animal {
public abstract void eat ();
}
抽象类特点
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
//抽象类
public abstract class Animal {
//抽象方法
public abstract void eat ();
public void sleep () {
System . out . println ( "睡觉" );
}
}
public class Cat extends Animal {
@Override
public void eat () {
System . out . println ( "猫吃鱼" );
}
}
public class AnimalDemo {
public static void main ( String [] args ) {
Animal a = new Cat (); //抽象类多态
a . eat ();
a . sleep (); //sleep()通过继承得到的
}
}
(1) 抽象类和抽象方法必须使用 abstract 关键字修饰
public abstract class 类名{ }
public abstract void eat( );
(2) 抽象类中不一定有抽象方法,有抽象方法的类一定是抽象类
(3) 抽象类不能实例化,即不能用 new
来实例化抽象类(即不能直接用抽象类创建对象)
抽象类如何实例化呢?参照多态的方式,通过子类对象实例化,这叫抽象类多态
(4) 抽象类的子类
要么重写抽象类中的所有抽象方法
要么本身也是抽象类 (如果不将继承的抽象方法重写,那么自己本身也是抽象类)
抽象类通过多态使用
抽象类只能用来被继承
抽象类成员特点
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
//抽象类
public abstract class Animal {
private int age = 20 ;
private final String name = "动物" ;
public Animal () {
}
public Animal ( int age ) {
this . age = age ;
}
public void show () {
age = 40 ;
System . out . println ( age );
//name = "老虎";报错 name是常量不能被修改
System . out . println ( name );
}
public abstract void eat ();
}
public class Cat extends Animal {
@Override
public void eat () {
System . out . println ( "猫吃鱼" );
}
}
public class AnimalDemo {
public static void main ( String [] args ) {
Animal a = new Cat (); //抽象类多态
a . eat ();
a . show (); //通过继承得到的 40 动物
}
}
(1) 成员变量
(2) 构造方法
有构造方法,但是不能实例化
那么,构造方法的作用是什么呢?用于子类访问父类数据的初始化
(3) 成员方法
可以有抽象方法:限定子类必须完成某些动作
也可以有非抽象方法:提高代码复用性
抽象类可以包含属性,方法,构造方法。但是构造方法不能用来 new
实例,只能用来被子类调用,用于子类访问父类数据的初始化
案例(猫和狗抽像类版)
需求:请采用抽象类的思想实现猫和狗的案例,并在测试类中进行测试
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
39
40
41
42
43
44
45
46
public abstract class Animal {
private String name ;
private int age ;
public Animal () {
}
public Animal ( String name , int age ) {
this . name = name ;
this . age = age ;
}
public abstract void eat ();
//set/get......
}
public class Cat extends Animal {
public Cat () {
}
public Cat ( String name , int age ) {
super ( name , age );
}
@Override
public void eat () {
System . out . println ( "猫吃鱼" );
}
}
public class AnimalDemo {
public static void main ( String [] args ) {
//按照多态的方式创建对象并进行测试
Animal a = new Cat ();
a . setName ( "加菲" );
a . setAge ( 5 );
System . out . println ( a . getName () + "," + a . getAge ());
a . eat ();
a = new Cat ( "加菲" , 5 );
System . out . println ( a . getName () + "," + a . getAge ());
a . eat ();
}
}
接口概述
接口就是一种 公共的规范标准 ,只要符合规范标准,大家都可以通用
Java
中的接口更多的体现在 对行为的抽象
接口是类层次的一种结构
public class 类名
Public interface 接口名
父类 → 本质上是具有共同的内容
接口 → 本质上是提供功能(老鼠夹子和猫都具有抓老鼠的功能)
接口就是比 “抽象类” 还 “抽象” 的 “抽象类”,可以更加规范的对子类进行约束。全面地专业地实现了:规范和具体实现的分离 。
从接口的实现者角度看,接口定义了可以向外部提供的服务。
从接口的调用者角度看,接口定义了实现者能提供那些服务。
区别
普通类:具体实现
抽象类:具体实现,规范(抽象方法)
接口:规范!
声明格式 (JDK8以前)
(图5
)
访问修饰符:只能是 public
或 默认
接口名:和类名采用相同命名机制
Extends:接口可以多继承
常量:接口中的属性只能是常量,总是 public static final
修饰。不写也是
方法:接口中的方法只能是:public abstract
。省略的话,也是 public abstract
接口中没有构造方法,因为接口主要是扩展功能的,而没有具体存在,一个类如果没有父类,默认继承自 Object
类,实现类中无参构造方法的 super()
,访问的是 Object
类中的无参构造方法,Public class A implemrnts B{}
相当于 Public class A extends Object implements B{}
技巧
(1) 子类通过 implements
来实现接口中的规范。
(2) 接口不能创建实例,但是可用于声明引用变量类型。
(3) 一个类实现了接口,必须实现接口中所有的方法,并且这些方法只能是 public
的。
(4) JDK1.8
(不含8)之前,接口中只能包含静态常量、抽象方法,不能有普通属性、构造方法、普通方法。
(5) JDK1.8
(含8)后,接口中包含普通的静态方法、默认方法(default)。
接口特点
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public interface Inter {
public abstract void show ();
}
public class InterImpl implments Inter {
@Override
public void show () {
System . out . println ( "show..." );
}
}
public class InterDemo {
public static void main ( String [] args ) {
Inter i = new InterImpl (); //接口的多态
i . show ();
}
}
1
2
public abstract class InterImpl implments Inter {
}
抽象类 InterImpl
可以实现接口,只不过到时候需要提供一个 InterImpl
的子类才能使用。
(1) 接口用关键字 interface
修饰
public interface 接口名
(2) 类实现接口用 implements
表示
public class 类名 implements 接口名
(3) 接口不能实例化
接口如何实例化呢?参照多态的方式,通过实现类对象实例化,这叫接口多态
多态的形式:具体类多态,抽象类多态,接口多态
多态的前提:有继承或者实现关系;有方法重写;有父(类/接口)引用指向(子/实现)类对象
(4) 接口的实现类
要么重写接口中的所有抽象方法
要么是抽象类
接口成员特点
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public interface Inter {
public int num = 10 ;
public final int num2 = 20 ;
public abstract void show ();
}
public class InterDemo {
public static void main ( String [] args ) {
Inter i = new InterImpl ();
System . out . println ( i . num ); //10
System . out . println ( i . num2 ); //20
//i.num = 40; 报错,默认前边加了一个final,被final修饰,不能修改
//i.num2 = 30; 报错,被final修饰,不能修改
}
}
1
2
3
4
5
6
7
8
9
10
11
//public class InterImpl implements Inter{
public class InterImpl extends Object implements Inter {
public InterImpl () {
super ();
}
@Override
public abstract void show () {
System . out . println ( "show..." );
}
}
如果没有父类,则默认继承 Object
,那么类中的构造方法里的 super()
调用的则是 Object
类的构造方法。
(1) 成员变量
只能是常量
默认修饰符:public static final
(2) 构造方法
没有,因为接口主要是扩展功能的,而没有具体存在
一个类如果没有父类,默认继承自 Object
类
(3) 成员方法
只能是抽象方法
默认修饰符:public abstract
类和接口关系
继承关系,只能单继承,但是可以多层继承
实现关系,可以单实现,也可以多实现,还可以在继承一个类的同时实现多个接口
继承关系可以单继承,也可以多继承 (接口之间可以多继承)
1
2
3
4
5
6
7
// 类和接口的关系
public class InterImpl extends Object implements Inter1 , Inter2 , Inter3 {
}
// 接口和接口的关系
public interface Inter3 extends Inter1 , Inter2 {
}
抽象类和接口的区别
(1) 成员区别
抽象类 变量,常量;有构造方法;有抽象方法,也有非抽象方法
接口 常量;抽象方法
(2) 关系区别
类与类 继承,单继承
类与接口 实现,可以单实现,也可以多实现
接口与接口 继承,单继承,多继承
(3) 设计理念区别
抽象类 对类抽象,包括属性、行为
接口 对行为抽象,主要是行为
对设计理念区别进行详细说明
门和警报的例子
门:都有 open()
和 close()
两个动作,这个时候,我们可以分别使用抽象类和接口来定义这个抽象概念
(图6
)
随着科技的发展,现在的门有了报警的功能,现在有两种方案,一种是都放在抽象类中,一种是都放在接口中
(图7
)
如果都放在抽象类中,那么每个门都有报警的功能,这是不太现实的
如果都放在接口中,那么并不是每个东西都有开门、关门的功能,例如: 报警器
开门、关门属于门本身固有的行为,而报警属于延伸的行为
(图8
)
抽象类是对事物的抽象,例如门
接口是对行为的抽象,例如警报
案例(木门和电动报警门综合案例)
需求:请采用面向对象的思想实现木门和电动报警门的案例,并在测试类中进行测试
(图9
)
(图10
)