1.interface接口
-
一方面,有时必须从几个类中派生出一个子类,继承它们所有的属性和方法。但是,Java不支持多重继承。有了接口,就可以得到多重继承的效果。
-
另一方面,有时必须从几个类中抽取出一些共同的行为特征,而它们之间又没有is-a的关系,仅仅是具有相同的行为特征而已。例如:鼠标、键盘、打印机、扫描仪、摄像头、充电器、MP3机、手机、数码相机、移动硬盘等都支持USB连接。
-
接口就是规范,定义的是一组规则,体现了现实世界中“如果你是/要…则必须能…”的思想。继承是一个"是不是"的关系,而接口实现则是 "能不能"的关系。
-
接口的本质是契约,标准,规范,就像我们的法律一样。制定好后大家都要遵守。
-
接口(interface)是抽象方法和常量值定义的集合。
-
接口的特点:
1.用interface来定义。
2.接口中的所有成员变量都默认是由public static final修饰的。
3.接口中的所有抽象方法都默认是由public abstract修饰的。
4.接口中没有构造器。
5.接口采用多继承机制。 -
接口定义举例
public interface Runner {
int ID = 1;
void start();
public void run();
void stop();
}
等同于
public interface Runner {
public static final int ID = 1;
public abstract void start();
public abstract void run();
public abstract void stop();
}
- 接口使用interface来定义
- Java中,接口和类是并列的两个结构
- 如何定义接口:定义接口中的成员
3.1JDK7及以前:只能定义全局常量和抽象方法
全局常量:public static final的.但是书写时,可以省略不写
抽象方法:public abstract的,但是书写时,可以省略不写
3.2JDK8:除了定义全局常量和抽象方法之外,还可以定义静态方法、默认方法(下面有详细讲解)
- 接口中不能定义构造器的!意味着接口不可以实例化
- Java开发中,接口通过让类去实现(implements)的方式来使用.
如果实现类覆盖了接口中的所抽象方法,则此实现类就可以实例化
如果实现类没覆盖接口中所的抽象方法,则此实现类仍为一个抽象类 - Java类可以实现多个接口 —>弥补了Java单继承性的局限性
格式:class AA extends BB implements CC,DD,EE - 接口与接口之间可以继承,而且可以多继承
- 接口的具体使用,体现多态性
- 接口,实际上可以看做是一种规范
- 定义Java类的语法格式:先写extends,后写implements
class SubClass extends SuperClass implements InterfaceA{ } - 一个类可以实现多个接口,接口也可以继承其它接口。
- 实现接口的类中必须提供接口中所有方法的具体实现内容,方可实例化。否则,仍为抽象类。
- 接口的主要用途就是被实现类实现。(面向接口编程)
- 与继承关系类似,接口与实现类之间存在多态性
- 接口和类是并列关系,或者可以理解为一种特殊的类。从本质上讲,接口是一种特殊的抽象类,这种抽象类中只包含常量和方法的定义(JDK7.0及之前),而没有变量和方法的实现。
1.1案例:
1.1.1定义一个接口用来实现两个对象的比较
package com.my.exer2;
/**
*
* @ClassName: CompareObject
* @Description: 定义一个接口用来实现两个对象的比较
* @author Redamancy
* @date 2022-07-03 11:46:32
*/
public interface CompareObject {
/**
*
* @MethodName: compareTo
* @Description: TODO
* @author Redamancy
* @param o
* @return int //若返回值是 0 , 代表相等; 若为正数,代表当前对象大;负数代表当前对象小
* @date 2022-07-03 11:45:40
*/
public int compareTo(Object o);
}
1.1.2定义一个Circle类,声明radius属性,提供getter和setter方法
package com.my.exer2;
/**
*
* @ClassName: Circle
* @Description: 定义一个Circle类,声明radius属性,提供getter和setter方法
* @author Redamancy
* @date 2022-07-03 11:46:38
*/
public class Circle {
private double radius;
public Circle() {
super();
}
public Circle(double radius) {
super();
this.radius = radius;
}
public double getRadius() {
return radius;
}
public void setRadius(double radius) {
this.radius = radius;
}
}
1.1.3定义一个ComparableCircle类,继承Circle类并且实现CompareObject接口。
package com.my.exer2;
/**
*
* @ClassName: ComparableCircle
* @Description: 定义一个ComparableCircle类,继承Circle类并且实现CompareObject接口。
* 在ComparableCircle类中给出接口中方法compareTo的实现体,用来比较两个圆的半径大小。
* @author Redamancy
* @date 2022-07-03 11:48:46
*/
public class ComparableCircle extends Circle implements CompareObject{
public ComparableCircle(double radius) {
super(radius);
}
@Override
public int compareTo(Object o) {
if(this == o)
return 0;
if(o instanceof ComparableCircle) {
ComparableCircle c = (ComparableCircle)o;
if(this.getRadius() > c.getRadius()) {
return 1;
}else if(this.getRadius() < c.getRadius()){
return -1;
}else {
return 0;
}
}else {
// return 0;
throw new RuntimeException("传入的数据类型不匹配!");
}
}
}
1.1.4测试类InterfaceTest,创建两个ComparableCircle对象,调用compareTo方法比较两个类的半径大小
package com.my.exer2;
/**
*
* @ClassName: InterfaceTest
* @Description: 测试类InterfaceTest,创建两个ComparableCircle对象,调用compareTo方法比较两个类的半径大小
* @author Redamancy
* @date 2022-07-03 12:10:58
*/
public class InterfaceTest {
public static void main(String[] args) {
ComparableCircle c1 = new ComparableCircle(3.14);
ComparableCircle c2 = new ComparableCircle(3.5);
int compareValue = c1.compareTo(c2);
if(compareValue > 0) {
System.out.println("c1对象大");
}else if(compareValue < 0) {
System.out.println("c2对象大");
}else {
System.out.println("一样大");
}
}
}
2.代理模式(Proxy)
代理模式是Java开发中使用较多的一种设计模式。代理设计就是为其
他对象提供一种代理以控制对这个对象的访问
2.1案例:
package com.my.exer3;
/**
*
* @ClassName: StaticProxyTest
* @Description: 代理模式的练习题
* @author Redamancy
* @date 2022-07-03 01:57:44
*/
public class StaticProxyTest {
public static void main(String[] args) {
Programmer s = new Proxy(new RealProgrammer());
s.confer();
s.signContract();
s.bookTicket();
s.PrintCode();
s.collectMoney();
}
}
interface Programmer {
void confer();// 面谈
void signContract();// 签合同
void bookTicket();// 订票
void PrintCode();// 打代码
void collectMoney();// 收钱
}
class RealProgrammer implements Programmer {
public void confer() {
}
public void signContract() {
}
public void bookTicket() {
}
public void PrintCode() {
System.out.println("打代码中~~~");
}
public void collectMoney() {
}
}
class Proxy implements Programmer {
private Programmer real;
public Proxy(Programmer real) {
this.real = real;
}
public void confer() {
System.out.println("老板面谈");
}
public void signContract() {
System.out.println("老板签合同");
}
public void bookTicket() {
System.out.println("老板订票");
}
public void PrintCode() {
real.PrintCode();
}
public void collectMoney() {
System.out.println("老板收钱");
}
}
- 应用场景:
1.安全代理:屏蔽对真实角色的直接访问。
2.远程代理:通过代理类处理远程方法调用(RMI)
3.延迟加载:先加载轻量级的代理对象,真正需要再加载真实对象 - 比如你要开发一个大文档查看软件,大文档中有大的图片,有可能一个图片有100MB,在打开文件时,不可能将所有的图片都显示出来,这样就可以使用代理模式,当需要查看图片时,用proxy来进行大图片的打开。
- 分类
1.静态代理(静态定义代理类)
2.动态代理(动态生成代理类)
JDK自带的动态代理,需要反射等知识
3.工厂模式
工厂模式:实现了创建者与调用者的分离,即将创建对象的具体过程屏蔽隔离起来,达到提高灵活性的目的。
其实设计模式和面向对象设计原则都是为了使得开发项目更加容易扩展和维
护,解决方式就是一个“分工”。
社会的发展也是这样,分工越来越细。
原始社会的人:人什么都要会,自己找食物,自己打猎,自己织衣服,自己治病等
现在的人:可以只会一样,其他都不会,只会大数据也能活,不会装修,不会开发硬件,不会研发手机…
3.1面向对象的设计原则
(总共六个):这里说几个和工厂模式相关的
3.1.1OCP(开闭原则,Open-Closed Principle)
- 一个软件的实体应当对扩展开放,对修改关闭。
- 当我们写完的代码,不能因为需求变化就修改。我们可以通过新增代码的方式来解决变化的需求。如果每次需求变动都去修改原有的代码,那原有的代码就存在被修改错误的风险,当然这其中存在有意和无意的修改,都会导致原有正常运行的功能失效的风险,这样很有可能会展开可怕的蝴蝶效应,使维护工作剧增。
- 说到底,开闭原则除了表面上的可扩展性强以外,在企业中更看重的是维护成本。
所以,开闭原则是设计模式的第一大原则,它的潜台词是:控制需求变动风险,缩小维护成本。
3.1.2DIP(依赖倒转原则,Dependence Inversion Principle)
- 要针对接口编程,不要针对实现编程。
- 如果 A 中关联 B,那么尽量使得 B 实现某个接口,然后 A 与接口发生关系,不与 B 实现类发生关联关系。
- 依赖倒置的潜台词是:面向抽象编程,解耦调用和被调用者。
3.1.3LOD(迪米特法则,Law Of Demeter)
- 只与你直接的朋友通信,而避免和陌生人通信。
- 要求尽量的封装,尽量的独立,尽量的使用低级别的访问修饰符。这是封装特性的典型体现。
- 一个类如果暴露太多私用的方法和字段,会让调用者很茫然。并且会给类造成不必要的判断代码。所以,我们使用尽量低的访问修饰符,让外界不知道我们的内部。这也是面向对象的基本思路。这是迪米特原则的一个特性,无法了解类更多的私有信息。
- 另外,迪米特原则要求类之间的直接联系尽量的少,两个类的访问,通过第三个中介类来实现。
- 迪米特原则的潜台词是:
不和陌生人说话,有事去中介
。
3.2解决的问题
实现了创建者与调用者的分离,即将创建对象的具体过程屏蔽隔离起来,达到提高灵活性的目的。
3.3具体模式
简单工厂模式:用来生产同一等级结构中的任意产品。(对于增加新的产品,需要修改已有代码)
工厂方法模式:用来生产同一等级结构中的固定产品。(支持增加任意产品)
抽象工厂模式:用来生产不同产品族的全部产品。(对于增加新的产品,无能为力;支持增加产品族)
3.3.1核心本质:
实例化对象,用工厂方法代替 new 操作。
将选择实现类、创建对象统一管理和控制。从而将调用者跟我们的实现类解耦。
3.3.2无工厂模式
package com.my.exer3;
/**
*
* @ClassName: Car
* @Description: 无工厂模式
* @author Redamancy
* @date 2022-07-03 02:27:10
*/
interface NoFactoryCar{
void run();
}
class NoFactoryAudi implements NoFactoryCar{
public void run() {
System.out.println("奥迪A7");
}
}
class NoFactoryBYD implements NoFactoryCar{
public void run() {
System.out.println("比亚迪");
}
}
public class NoFactory{
public static void main(String[] args) {
NoFactoryCar audi = new NoFactoryAudi();
NoFactoryCar byd = new NoFactoryBYD();
audi.run();
byd.run();
}
}
3.3.3简单工厂模式
简单工厂模式,从命名上就可以看出这个模式一定很简单。它存在的目的很简单:定义一个用于创建对象的工厂类。
package com.my.exer3;
/**
*
* @ClassName: SimpleFactoryCar
* @Description: 简单工厂模式
* @author Redamancy
* @date 2022-07-03 02:38:31
*/
interface SimpleFactoryCar{
void run();
}
class SimpleFactoryAudi implements SimpleFactoryCar{
public void run() {
System.out.println("奥迪A7");
}
}
class SimpleFactoryBYD implements SimpleFactoryCar{
public void run() {
System.out.println("比亚迪");
}
}
class CarFactory {
//方式一
public static SimpleFactoryCar getCar(String type) {
if ("奥迪".equals(type)) {
return new SimpleFactoryAudi();
} else if ("比亚迪".equals(type)) {
return new SimpleFactoryBYD();
} else {
return null;
}
}
//方式二
// public static Car getAudi() {
// return new Audi();
// }
//
// public static Car getByd() {
// return new BYD();
// }
}
public class SimpleFactory {
public static void main(String[] args) {
SimpleFactoryCar a = CarFactory.getCar("奥迪");
a.run();
SimpleFactoryCar b = CarFactory.getCar("比亚迪");
b.run();
}
}
调用者只要知道他要什么,从哪里拿,如何创建,不需要知道。分工,多出了一个专门生产 Car 的实现类对象的工厂类。把调用者与创建者分离
。
3.3.3.1小结:
简单工厂模式也叫静态工厂模式,就是工厂类一般是使用静态方法,通过接收的参数的不同来返回不同的实例对象。
3.3.3.2缺点:
对于增加新产品,不修改代码的话,是无法扩展的。违反了开闭原则(对扩展开放;对修改封闭)
3.3.4工厂方法模式
为了避免简单工厂模式的缺点,不完全满足 OCP(对扩展开放,对修改关闭)。工厂方法模式和简单工厂模式最大的不同在于,简单工厂模式只有一个(对于一个项目或者一个独立的模块而言)工厂类,而工厂方法模式有一组实现了相同接口的工厂类。这样在简单工厂模式里集中在工厂方法上的压力可以由工厂方法模式里不同的工厂子类来分担。
package com.my.exer3;
/**
*
* @ClassName: FactoryMethodCar
* @Description: 工厂方法模式
* @author Redamancy
* @date 2022-07-03 02:43:48
*/
interface FactoryMethodCar{
void run();
}
class FactoryMethodAudi implements FactoryMethodCar{
public void run() {
System.out.println("奥迪A7");
}
}
class FactoryMethodBYD implements FactoryMethodCar{
public void run() {
System.out.println("比亚迪");
}
}
//工厂接口
interface Factory{
FactoryMethodCar getCar();
}
//两个工厂类
class AudiFactory implements Factory{
public FactoryMethodAudi getCar(){
return new FactoryMethodAudi();
}
}
class BydFactory implements Factory{
public FactoryMethodBYD getCar(){
return new FactoryMethodBYD();
}
}
public class FactoryMethod {
public static void main(String[] args) {
FactoryMethodCar a = new AudiFactory().getCar();
FactoryMethodCar b = new BydFactory().getCar();
a.run();
b.run();
}
}
3.3.4.1总结:
简单工厂模式与工厂方法模式真正的避免了代码的改动了?没有。在简单工厂模式中,新产品的加入要修改工厂角色中的判断语句;而在工厂方法模式中,要么将判断逻辑留在抽象工厂角色中,要么在客户程序中将具体工厂角色写死(就像上面的例子一样)。而且产品对象创建条件的改变必然会引起工厂角色的修改。面对这种情况,Java 的反射机制与配置文件的巧妙结合突破了限制——这在Spring 中完美的体现了出来
。
3.3.5抽象工厂模式
抽象工厂模式和工厂方法模式的区别就在于需要创建对象的复杂程度上。而且抽象工厂模式是三个里面最为抽象、最具一般性的。
抽象工厂模式的用意为:给客户端提供一个接口,可以创建多个产品族中的产品对象。
而且使用抽象工厂模式还要满足一下条件:
- 系统中有多个产品族,而系统一次只可能消费其中一族产品。
- 同属于同一个产品族的产品以其使用。
4.接口和抽象类之间的对比
No. | 区别点 | 抽象类 | 接口 |
---|---|---|---|
1 | 定义 | 包含抽象方法的类 | 主要是抽象方法和全局常量的集合 |
2 | 组成 | 构造方法、抽象方法、普通方法、常量、变量 | 常量、抽象方法、(jdk8.0:默认方法、静态方法) |
3 | 使用 | 子类继承抽象类(extends) | 子类实现接口(implements) |
4 | 关系 | 抽象类可以实现多个接口 | 接口不能继承抽象类,但允许继承多个接口 |
5 | 常见设计模式 | 模板方法 | 简单工厂、工厂方法、代理模式 |
6 | 对象 | 通过对象的多态性产生实例化对象 | 通过对象的多态性产生实例化对象 |
7 | 局限 | 抽象类有单继承的局限 | 接口没有此局限 |
8 | 实际 | 作为一个模板 | 是作为一个标准或是表示一种能力 |
9 | 选择 | 如果抽象类和接口都可以使用的话,优先使用接口,因为避免单继承的局限 | 如果抽象类和接口都可以使用的话,优先使用接口,因为避免单继承的局限 |
在开发中,常看到一个类不是去继承一个已经实现好的类,而是要么继承抽象类,要么实现接口
父类非抽象的方法在子类重新定义方法的时候叫重写
父类抽象的方法和接口抽象的方法在子类重新定义方法的时候叫实现
5.Java8中关于接口的新规范
下次更新
6.面试题:抽象类与接口有哪些异同?
- 相同点:不能实例化;都可以包含抽象方法的。
- 不同点:
1)把抽象类和接口(java7,java8,java9)的定义、内部结构解释说明
2)类:单继承性 接口:多继承
类与接口:多实现