接口
接口
JAVA编程思想有一句话说:接口被用来建立类与类之间的协议。
如果你想在JAVA实现多重继承,那你必须使用接口。(Java语言特性:单继承多实现)
在接口和抽象类的选择上,必须遵守这样一个原则:行为模型应该总是通过接口而不是抽象类定义。
1.1 接口概述
接口就是一种公共的规范标准,只要符合规范标准,大家都可以通用
JAVA中的接口更多的体现在 “对行为的抽象”
1.2 接口特点
* 接口用关键字interface修饰
* 类实现接口用implements标识
* 接口不能实例化 (通过实现类对象实例化,这叫接口多态)
多态的形式:具体类多态,抽象类多态,接口多态。
多态的前提:有继承或者实现关系;有方法重写;有父(类/接口)引用指向(子/实现)类对象
* 接口的实现类
要么重写接口中的所有抽象方法
要么是抽象类
1.3 接口的成员特点
* 成员变量 只能是常量 默认修饰符 public static final
* 构造方法
接口没有构造方法,因为接口主要是对行为进行抽象的,没有具体存在
一个类如果没有父类,默认继承自Object类
* 成员方法
接口中普通抽象方法是由public abstract修饰的。
(java8提供default方法和static方法)
(java9提供private方法)
java 接口静态方法要点:
- Java 接口静态方法是接口的一部分,不能用于实现类对象。
- Java 接口静态方法适合于提供实用方法,例如空检查、集合排序等。
- Java 接口静态方法通过不允许实现类重写它们来帮助我们提供安全性。
- 我们不能为 Object 类方法定义接口静态方法,我们将得到编译器错误为“这个静态方法不能从 Object 中隐藏实例方法”。这是因为在 java 中不允许这样做,因为 Object 是所有类的基类,我们不能有一个类级静态方法和另一个具有相同签名的实例方法。
- 我们可以使用 java 接口静态方法来移除诸如集合之类的实用工具类,并将其所有静态方法移动到相应的接口,这样就很容易找到和使用。
关于 java 接口默认方法的要点:
- Java 接口默认方法将帮助我们扩展接口,而不必担心破坏实现类。
- Java 接口默认方法弥补了接口和抽象类之间的差异。
- Java 8 接口默认方法将帮助我们避免使用工具类,例如所有 Collections 类方法都可以在接口本身中提供。
- Java 接口默认方法将帮助我们删除基本实现类,我们可以提供默认实现,实现类可以选择重写哪个。
- 在接口中引入默认方法的一个主要原因是为了增强 Java 8 中的 Collections API 以支持 lambda 表达式。
- 如果层次结构中的任何类具有具有相同签名的方法,则默认方法将变得不相关。默认方法不能重写 java.lang.Object 中的方法。推理非常简单,因为 Object 是所有 java 类的基类。因此,即使我们在接口中将 Object 类方法定义为默认方法,它也将是无用的,因为 Object 类方法将始终被使用。这就是为什么要避免混淆,我们不能有覆盖 Object 类方法的默认方法。
- Java 接口默认方法也称为 Defender 方法或虚拟扩展方法。
为什么要有私有方法:我理解是 因为多个默认方法或者静态方法中包含同一段代码实现,程序必然考虑将实现代码抽取成一个共性方法,所以JAVA9增加了私有方法。
为什么要有默认方法:当接口需要增加功能,如果直接增加接口方法,还需要重新修改实现类。如果说采用增加接口继承原接口,继承关系又会变得复杂。
为什么要有静态方法:接口中的静态方法背后的思想是提供一种简单的机制,允许通过将相关的方法内聚在接口中,而不必创建新的对象。抽象类也可以做同样的事情。主要的区别在于抽象类可以有构造函数、成员变量和方法。
推荐把和只和接口相关的静态utility方法放在接口中(提高内聚性),而不需要额外创建一些utility类专门去放置这些方法。
1.4 类和接口的关系
* 类和类的关系
继承关系,只能单继承,但是可以多层继承
* 类和接口的关系
实现关系,可以单实现,也可以多实现,还可以在继承一个类的同时实现多个接口。
* 接口和接口的关系
继承关系,可以单继承,也可以多继承。
1.5 抽象类和接口的区别
* 成员区别
抽象类 常量,变量;有构造方法;有抽象方法,也有非抽象方法。
接口 常量,抽象方法。
* 关系区别
类与类 继承,单继承
类与接口 实现,可以单实现,也可以多实现。
接口与接口 继承,单继承,多继承。
* 设计理念区别
抽象类 对类抽象,包括属性、行为
接口 对行为抽象,主要是行为。
上面只是从语法层次和编程角度来区分它们之间的关系,这些都是低层次的,要真正使用好抽象类和接口,我们就必须要从较高层次来区分了。只有从设计理念的角度才能看出它们的本质所在。
一般来说他们存在如下三个不同点:
1、 抽象层次不同。抽象类是对类抽象,而接口是对行为的抽象。抽象类是对整个类整体进行抽象,包括属性、行为,但是接口却是对类局部(行为)进行抽象。
2、 跨域不同。抽象类所跨域的是具有相似特点的类,而接口却可以跨域不同的类。我们知道抽象类是从子类中发现公共部分,然后泛化成抽象类,子类继承该父类即可,但是接口不同。
实现它的子类可以不存在任何关系,共同之处。例如猫、狗可以抽象成一个动物类抽象类,具备叫的方法。鸟、飞机可以实现飞Fly接口,具备飞的行为,这里我们总不能将鸟、飞机共用一个父类吧!
所以说抽象类所体现的是一种继承关系,要想使得继承关系合理,父类和派生类之间必须存在"is-a" 关系,即父类和派生类在概念本质上应该是相同的。
对于接口则不然,并不要求接口的实现者和接口定义在概念本质上是一致的, 仅仅是实现了接口定义的契约而已。
3、 设计层次不同。对于抽象类而言,它是自下而上来设计的,我们要先知道子类才能抽象出父类,
而接口则不同,它根本就不需要知道子类的存在,只需要定义一个规则即可,至于什么子类、什么时候怎么实现它一概不知。
比如我们只有一个猫类在这里,如果你这是就抽象成一个动物类,是不是设计有点儿过度?我们起码要有两个动物类,猫、狗在这里,我们在抽象他们的共同点形成动物抽象类吧!
所以说抽象类往往都是通过重构而来的!但是接口就不同,比如说飞,我们根本就不知道会有什么东西来实现这个飞接口,怎么实现也不得而知,我们要做的就是事前定义好飞的行为接口。
所以说抽象类是自底向上抽象而来的,接口是自顶向下设计出来的。
1.6 JAVA编程思想:
“确定接口是理想选择,因而应该总是选择接口而不是具体的类。”这其实是一种诱饵。当然,对于创建类,几乎在任何时刻,都可以替代为创建一个接口和一个工厂。
许多人都掉进了这种诱惑的陷阱,只要有可能就去创建接口和工厂。这种逻辑看起来是因为需要使用不同的具体实现,因此总是应该添加这种抽象性。这实际上已经变成了一种草率的设计优化。
任何抽象性都应该是应真正的需求而产生的。当必须时,你应该重构接口而不是到处添加额外级别的间接性,并由此带来的额外的复杂性。这种额外的复杂性非常显著,
如果你让某人去处理这种复杂性,只是因为你意识到由于以防万一而添加了新接口,而没有其他更有说服力的原因,那么好吧,如果我碰上了这种事,那么就会质疑此人所做的所有设计了。
恰当的原则应该是优先选择类而不是接口。从类开始,如果接口的必须性变得非常明确,那么就进行重构。接口是一种重要的工具,但是他们容易被滥用。
Nice to see you all!