JavaSE 进阶 (2) 多态

多态概述(polymorphism)

多态: 指的是同一个方法调用,由于对象不同可能会有不同的行为。

现实生活中,同一个方法,具体实现会完全不同。比如:同样是调用人的 休息 方法,张三是睡觉,李四是旅游,数学教授是做数学题;同样是调用人 吃饭 的方法,中国人用筷子吃饭,英国人用刀叉吃饭,印度人用手吃饭。


多态的要点:

  1. 多态是方法的多态,不是属性的多态(多态与属性无关)
  2. 多态的存在要有3个必要条件:有继承/实现关系,有方法重写,父类引用指向子类对象
  3. 父类引用指向子类对象后,用该父类引用调用子类重写的方法,此时多态就出现了

之所以每类人的吃饭方法都不一样,是因为,在人这个类的下边,中国人子类,美国人子类,印度人子类都对吃这个方法进行了重写,所以每类人的吃饭方法不一样。多态是指方法的多态也是因为这样。

多态中成员访问特点

/images/java/JavaSE 进阶 (2) 多态/1.png
(图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();
	}
}

多态转型内存图解

/images/java/JavaSE 进阶 (2) 多态/2.png
(图2)
/images/java/JavaSE 进阶 (2) 多态/3.png
(图3)
/images/java/JavaSE 进阶 (2) 多态/4.png
(图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以前)

/images/java/JavaSE 进阶 (2) 多态/5.png
(图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() 两个动作,这个时候,我们可以分别使用抽象类和接口来定义这个抽象概念

/images/java/JavaSE 进阶 (2) 多态/6.png
(图6)

随着科技的发展,现在的门有了报警的功能,现在有两种方案,一种是都放在抽象类中,一种是都放在接口中

/images/java/JavaSE 进阶 (2) 多态/7.png
(图7)

如果都放在抽象类中,那么每个门都有报警的功能,这是不太现实的

如果都放在接口中,那么并不是每个东西都有开门、关门的功能,例如: 报警器

开门、关门属于门本身固有的行为,而报警属于延伸的行为

/images/java/JavaSE 进阶 (2) 多态/8.png
(图8)

抽象类是对事物的抽象,例如门

接口是对行为的抽象,例如警报

案例(木门和电动报警门综合案例)

需求:请采用面向对象的思想实现木门和电动报警门的案例,并在测试类中进行测试

/images/java/JavaSE 进阶 (2) 多态/9.png
(图9)
/images/java/JavaSE 进阶 (2) 多态/10.png
(图10)

分析

①木门

  • 成员变量:宽,高,品牌
  • 成员方法:开门,关门

②电动报警门:

  • 成员变量:宽,高,品牌
  • 成员方法:开门,关门,报警

思路

①定义报警(Alarm)接口

  • 成员方法:报警

②定义门(Door)抽象类

  • 成员变量:宽,高,品牌
  • 构造方法:无参,带参
  • 成员方法:gt/set方法,开门,关门

③定义木门类(WoodDoor),继承门类

  • 构造方法:无参,带参
  • 成员方法:开门,关门

④定义电动报警门类(ElectricAlarmDoor),继承门类,实现报警接口

  • 构造方法:无参,带参
  • 成员方法:开门,关门,报警
  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
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
 /*
	报警接口
*/
public interface Alarm {
	void alarm();
}

/*
	抽象门类
*/
public abstract class Door {
	//宽
	private double width;
	//高
	private double height;
	//品牌
	private String brand;
	//开门
	public abstract void open();
	//关门
	public abstract void close();
	
	public Door() {
	}
	
	public Door(double width,double height,String brand) {
		this.width = width;
		this.height = height;
		this.brand = brand;
	}
	
	//set/get......
}

/*
	木门
*/
public class WoodDoor extends Door {
	
	@Override
	public void open() {
		System.out.println("手动开门")
	}

	@Override
	public void close() {
		System.out.println("手动关门")
	}
	
	
	public WoodDoor() {
	}

	public WoodDoor(double width,double height,String brand) {
		super(width,height,brand);
	}
}

/*
	电动报警门
*/
public class ElectricAlarmDoor extends Door implents Alarm {

	public ElectricAlarmDoor() {
	}

	public ElectricAlarmDoor(double width,double height,String brand) {
		super(width,height,brand);
	}

	@Override
	public void alarm() {
		System.out.println("具有报警功能")
	}

	@Override
	public void open() {
		System.oUt.println("自动开门")
	}

	@Override
	public void close() {
		System.oUt.println("自动关门")
	}
}

/*
	测试类
*/
public class DoorDemo {
	public static void main(String[] args) {
		//木门
		WoodDoor wd = new WoodDoor();
		wd.open();
		wd.close();
		//wd.alarm();   报错,没有这个功能
		
		//电动报警门
		ElectricAlarmDoor ed = new ElectricAlarmDoor();
		ed.open();
		ed.close();
		ed.alarm();
		
		//多态
		Door d = new WoodDoor();
		d.open();
		d.close();

		d = new ElectricAlarmDoor();
		d.open();
		d.close();
		//d.alarm();  报错,因为门这个抽象类里没有报警的方法
		
		Alarm a = new ElectricAlarmDoor();
		//a.open();  报错,因为报警的接口中没有开门和关门的方法
		//a.close();  报错,因为报警的接口中没有开门和关门的方法

		a.alarm();
	}
}

JD8 以后接口中新增的方法

接口的组成

  • 常量:public static final
  • 抽象方法:public abstract
  • 默认方法 (Java 8)
  • 静态方法 (Java 8)
  • 私有方法 (Java 9)

默认方法

接口中默认方法的定义格式

  • 概述:JDK8 之后才出现的方法,实现类可以重写,也可以不重写
  • 格式:public default 返回值类型 方法名 (参数列表) {方法体}
  • 范例:public default void show1( ) {方法体}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
public interface Inter {
	public default void show1() {
		System.out.println("默认方法被调用")
	}
}

public class InterImpl implements Inter {

}

public class InterDemo {
	public static void main(String[] args) {
		Inter i = new InterImpl();
		i.show1();
	}
}

接口中默认方法的 注意事项Public 可以省略,default 不能省略

静态方法

接口中静态方法的定义格式

  • 概述:JDK8 之后才出现的方法,只能通过 接口名.方法名() 调用
  • 格式:public static 返回值类型 方法名 (参数列表){方法体}
  • 范例:public static void show2( ){方法体}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
public interface Inter {
	public static void show2() {
		System.out.println("静态方法被调用")
	}
}

public class InterImpl implements Inter {

}

public class InterDemo {
	public static void main(String[] args) {
		Inter i = new InterImpl();
		//i.show2();  报错,不能通过对象.方法名调用
		//InterImpl.show2();   报错,不能通过实现类.方法名调用
		Inter.show2();
	}
}

接口中静态方法的 注意事项

  • 静态方法只能通过接口调用,不能通过实现类名或者对象名调用
  • Public 可以省略,static 不能省略

私有方法

私有方法 (就是为了给接口中的默认方法和静态方法使用的,外界是不能访问的)

概述:JDK9 之后才出现的方法

接口中私有方法的定义格式

  • 格式1:private 返回值类型 方法名 (参数列表){ 方法体}
  • 范例1:private void show3( ){ }
  • 格式2:private static 返回值类型 方法名 (参数列表){ 方法体}
  • 范例2:private static void show4( ){ }
 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
public interface Inter {
	//默认方法
	public default void show1() {
		System.out.println("默认方法被调用");
		show3();
		show4();
	}
	
	//静态方法
	public static void show2() {
		System.out.println("静态方法被调用")
		//show3(); 报错,静态的方法只能访问静态的方法
		show4();
	}
	
	//私有方法
	private void show3() {
		System.out.println("私有方法")
	}
	
	//静态私有方法
	private static void show4() {
		System.out.println("静态私有方法")
	}
}

public class InterImpl implements Inter {

}

public class InterDemo {
	public static void main(String[] args) {
		Inter i = new InterImpl();
		//i.show3();  报错,因为私有方法在外界是不能访问的
		//i.show4();  报错,因为私有方法在外界是不能访问的
	}
}

接口中私有方法的 注意事项:

  • 默认方法可以调用 私有 的静态方法和非静态方法
  • 静态方法只能调用私有的静态方法

函数式接口概念

函数式接口(Functional Interface)就是一个 有且仅有一个抽象方法,但是可以有多个非抽象方法 的接口。注解 @FunctionalInterface 可以检测接口是否是一个函数式接口。(在合作开发中,避免同事修改此接口造成错误)

 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
/*
函数式接口
1.interface接口,其次有且只能有一个待实现的抽象方法,允许有default默认方法的存在
2.@FunctionalInterface 编译期检测是否符合函数式接口的规范,类似于@Override
3.建议加上注解@FunctionalInterface, 以避免同事接口改造升级,而带来的简化出现问题
*/

@FunctionalInterface
interface MyFunctionalInterfacel {
	void test();
	//void a();
	
	default void b(){};
}
-----------------------------------------
class Test {
	public void method(MyFunctionalInterfacel my) {
		my.test();
	}
}
-----------------------------------------
public class FunctionalDemo1 {
	public static void main(String[] args) {
		Test test = new Test();
		test.method(() -> System.out.println("Hello!"));  //匿名内部类,重写MyFunctionalInterfacel中的抽象方法
	}
}

函数定义及使用

定义一个函数式接口:

1
2
3
4
5
//问候
@FunctionalInterface
public interface GreetingService {
	void sayMessage();
}

定义此函数式接口的实现类:

1
2
3
4
5
6
7
public class GreetingServiceImpl implements GreetingService {
	
	@Override
	public void sayMessage() {
		System.out.println("say hello!");
	}
}

函数式接口一般作为方法的参数和返回值类型:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
public class Demo {
	//定义一个方法,参数使用函数式接口GreetingService
	public static void show(GreetingService greetingService) {
		greetingService.sayMessage();
	}
	
	public static void main(String[] args) {
		//调用show方法,方法的参数是一个接口,所以可以传递接口的实现类
		show(new GreetingServiceImpl());
		
		//调用show方法,方法的参数是一个接口,所以可以传递接口的匿名内都类
		show(new GreetingService(){
			@Override
			public void sayMessage(){
				System.out.println("使用匿名内类重写接口中的抽象方法");
			}
		});
	}
}

Lambda+functionInterface

Lambda + functionInterface: 具有延迟加载的效果

 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
/*
通过lambda + Functional Interface的好处?
1.简化代码,使代码更加简洁
2.延迟加载效果
需求:日志记录(时间+异常类型+异常信息行数) 日志级别达到3才需要记录输出
*/

@FunctionalInterface
interface MyFunctionalInterface2{
	//记录日志的操作
	void test();
}
----------------------------------------
public class FunctionalDemo2{

	/**
	* 日志记录器
	* @param leveL  日志级别
	* @param msg    日志信息
	*/
	public static void testLog(int level, string msg){
		if(level == 3){
			System.out.println(msg);
		}
	}
	
	/**
	* 带有函数式接口FunctionalInterface的,日志记录器
	* @param leveL  日志级别
	* @param my
	*/
	public static void testLogAndFunctionalInterface(int level, MyFunctionalInterface2 my){
		if(level == 3){
			my.test();
		}
	}
	
	public static void main(string[] args){
		//日志记录(时间+异常类型+异常信息行数)
		LocalDateTime now = LocalDateTime.now();
		String type = "NullPointerException";
		String line = "Bank.java 112";

		//无论是否符合日志等级,都将会先拼接好日志信息
		testLog(1, now + "," + type + "," + line);

		//等级一旦不符合,不会进行日志信息的拼接操作
		testLogAndFunctionalInterface(2, () -> System.out.println(now + "," + type + "," + line));
	}

}

0%