JavaSE之面向对象(下)
接口
1、概述:
Java接口是一系列方法的声明,是一些方法特征的集合,一个接口只有方法的特征没有方法的实现,因此这些方法可以在不同的地方被不同的类实现,而这些实现可以具有不同的行为(功能)。
比如,一个子类需要继承多个类的全部方法,常见的继承方法无法实现,接口可以实现全部继承的目的,但并不是继承;再比如,需要从多个类中抽取共同的特征,与继承不同,而是能不能做的问题can-do。
通俗来讲,接口就是规范。定义了一组规则,定义了“能不能”“can-do”的关系。相比来讲,继承则是“是不是”“is-a”的关系。
2、接口的定义
接口是java中的引用类型,只包含方法,接口的内部封装了各种方法。一个接口只提供契约,它让实现类负责来的每一个方法
需要注意的是,java的引用数据类型:类、接口、数组、枚举、标注
在JDK8之前,接口只运行:
1.公共的静态常量:public static final可以省略
2.公共的抽象方法:public abstract可以省略
然而在JDK1.8中,却填入了以下两种:
3.公共的默认方法
4.公共的静态方法
为什么JDK1.8会允许接口定义默认方法和静态方法呢?
这是因为它违反了接口作为一个抽象标准定义的概念。
1.静态方法:因为在标准库设计中,像Collection/Colletions或者Path/Paths这样成对的接口和类,由于类中都是静态方法,与其设计对映的API,不如将静态方法直接定义到接口中使用。
2.默认方法:如果要在接口中定义新的方法,这样势必要考虑到兼容性。而默认方法的作用就是
默认方法:
JDK1.8引入默认方法新机制是考虑到一旦在接口中新增加一个方法。那么所有的实现类都要改变。默认方法解决了这一问题,可以再接口中增加新方法,而实现类不需要改变,并且还可以得到这个默认方法。
String[] array = new String[] {
"hello",
", ",
"world",
};
List<String> list = Arrays.asList(array);
list.forEach(System.out::println); // 这是 jdk 1.8 新增的接口默认方法
// jdk 1.8 的 Iterable 接口中的 forEach 默认方法
package java.lang;
import java.util.Objects;
import java.util.function.Consumer;
public interface Iterable<T> {
default void forEach(Consumer<? super T> action) {
Objects.requireNonNull(action);
for (T t : this) {
action.accept(t);
}
}
}
默认方法的继承:
1、单继承
1.不覆写默认方法,直接从父接口中获取方法的默认实现。
2.覆写默认方法,这跟类与类之间的覆写规则相类似。
3.覆写默认方法并将它重新声明为抽象方法,这样新接口的子类必须再次覆写并实现这个抽象方法。
2、多继承
多个接口同时拥有同一方法名的默认方法时,实现类同时实现这多个接口,那么怎么判断实现类到底调用的是哪个接口中的默认方法呢?
这时候我们需要在实现类内重写该方法,这样就避免了尴尬。
接口实现
实现类通过使用implements关键字实现对接口的调用。
非抽象实现类实现接口:
1. 必须重写接口内的所有抽象方法
2. 继承接口内的默认方法,可以直接调用也可以重写。(重写时不需要default,默认的概念仅存在于接口内)
3. 不能重写静态方法
抽象实现类:
1. 可以不重写接口内的所有抽象方法
2. 继承接口内的默认方法,可以直接调用也可以重写。
调用对应的方法
- 对于接口的抽象方法、默认方法,可以通过实现类对象直接调用
- 接口内的静态方法,可以通过 接口名.静态方法名 调用
接口的多实现原则
在继承体系中,一个类只能有一个父类。而对于接口的实现来说,一个类可以实现多个接口。所以,一个类可以继承一个父类并且同时实现多个接口。
注意:
1. 亲爹优先原则:当父类的成员方法与接口中的抽象方法重名时,子类选择父类中的成员方法执行。
2. 必须做出选择:当多接口中出现方法签名相同的默认方法时,我们要做出选择:可以选择保留其中的一个,通过"接口名.super.方法名"选择保留的方法;或者在实现类中对有冲突的默认方法进行重写。
接口的多继承
一个接口能继承另一个或者多个接口,接口的继承也使用 extends 关键字,子接口继承父接口的方法。
注意
1. 子接口重写默认方法时,default关键字可以保留。
2. 子类重写默认方法时,default关键字不可以保留。
接口中元素的特点
1. 接口中,无法定义成员变量,但是可以定义常量,其值不可以改变,默认使用public static final修饰。
2. 接口中,没有构造方法,不能创建对象。
3. 接口中,没有静态代码块。
接口与实现类对象的多态引用
参考多态部分
匿名内部类
在开发中,需要用到一个抽象类的子类的对象或一个接口的实现类的对象,而且只创建一个对象,那我们可以使用匿名内部类。由于匿名内部类只是用一次,因此可以极大地简化代码,而不需要新建一个子类。我们来看一下匿名内部类和非匿名内部类的实现。
非匿名内部类
abstract class Preson {
public abstract void run();
}
class Man extends Preson {
public void run() {
System.out.println("跑得快");
}
}
public class Main {
public static void main(String[] args) {
Person p = new Man();
p.run(); // 跑得快
}
}
我们再来看一下使用匿名内部类的代码
匿名内部类
abstract class Preson {
public abstract void run();
}
public class Main {
public static void main(String[] args) {
Person p = new Man() {
public void run() {
System.out.println("跑得快");
}
}
p.run(); // 跑得快
}
}
通过接口实现匿名内部类
interface Preson{
void run();
}
public class Test{
public static void main(String[] args){
Person p = new Man() {
public void run() {
System.out.println("跑得快");
}
}
p.run(); // 跑得快
}
}