详解Java中的接口

一、接口的基本定义

如果进行Java技术的学习和项目应用,不懂得接口的设计,那么基本上就等同于你不会Java,因为在整个程序设计之中,接口一直是一项最为重要的技术,同时随着JDK版本的更新,接口也会有一些新的特性出现。

接口属于一种特殊的Java程序结构体,它最大的特殊之处在于其主要的组成就是全局常量和抽象方法(从JDK1.8 开始,之后接口的定义有所加强),如果想在程序中定义一个接口,可以通过interface关键字来完成。

范例:定义一个接口

// 在Java里面接口和类的定义命名要求是相同的,但是如果全都一样则无法区分接口还是类
// 这样的话就在项目的开发中有一个人为规定,如果是自定义的接口前追加一个字母“I”
interface IBook {	// 定义接口
	public static final String SITE = "www.baidu.com" ; // 全局常量
	public abstract void read() ; // 抽象方法
}
public class Demo {
	public static void main(String args[]) {
		System.out.println(IBook.SITE) ;	// 可以通过接口直接进行全局常量的调用
	}
}

/**
	程序执行结果:www.baidu.com
**/

虽然这个时候可以通过接口名称直接实现接口中的全局常量的使用,但是对于接口中的抽象方法是无法使用的,因为接口本身无法直接实现实例化,所以对于接口中的抽象方法自然也就无法应用,所以在实际项目开发过程中,对于接口的使用应该采用如下的几个设计原则:

  • 接口无法直接进行对象的实例化,必须依靠子类来完成;

  • 接口的子类使用implements关键字实现接口,一个子类可以同时实现多个接口,子类的定义格式:

    class 子类 [extends 父类] [implements 父接口1,父接口2…]{}

  • 接口的子类(如果不是抽象类)则一定要覆写接口之中的全部抽象方法;

范例:使用接口

interface IBook {	// 定义接口
	public static final String SITE = "www.baidu.com" ; // 全局常量
	public abstract void read() ; // 抽象方法
}
class MathBook implements IBook {
	public void read() {	// 抽象方法覆写
		System.out.println("【MathBook子类】认真读数学图书,巩固计算机的基础知识") ;
	}
}
public class Demo { 
	public static void main(String args[]) {
		IBook book = new MathBook() ; // 通过子类进行对象实例化处理操作
		book.read() ; // 读书
	}
}

/**
	程序执行结果:【MathBook子类】认真读数学图书,巩固计算机的基础知识
**/

通过当前所给出的代码形式来看,感觉抽象类和接口还是很相似的,但是需要说明的是,抽象类里除了全局常量和抽象方法之外实际上还可以包含有大量的普通方法或者是构造方法,而接口中却不会包含有这些内容,同时接口之中的全部访问控制权限都为public,也就是说如下的两个接口虽然编写的形式不同,但最终的效果确实完全一致的。

在这里插入图片描述

接口和抽象类从定义的本质上就是有所区分的,而且它的核心组成结构相对来讲是比较容易理解的,但是在实际的项目开发过程之中,为了防止对语言不熟悉的开发者造成的困扰,所以在定义接口中方法的时候会习惯性的加上“public”。

另外还需要特别注意一件事情:一个类可以同时实现若干个接口,那么就可以通过接口来试一下子类的多继承操作,假设现在任何一本书除了书的特征之外,还需要提供有一些规格等信息。

范例:让子类同时实现多个接口

interface IBook {	// 定义接口
	String SITE = "www.baidu.com" ;
	public void read() ; // 抽象方法
}
interface ISpec { // 规格信息
	public double size() ; // 尺寸
}
class MathBook implements IBook, ISpec { // 接口的多实现
	public void read() {	// 抽象方法覆写
		System.out.println("【MathBook子类】认真读数学图书,巩固计算机的基础知识") ;
	}
	public double size() {
		return 7.9 ;
	}
}
public class Demo { 	
    public static void main(String args[]) {
		IBook book = new MathBook() ; // 通过子类进行对象实例化处理操作
		book.read() ; // 读书
		ISpec spec = new MathBook() ; // 通过子类进行对象的实例化处理
		System.out.println("图书规格:" + spec.size()) ;
	}
}

/**
	程序执行结果:
			【MathBook子类】认真读数学图书,巩固计算机的基础知识
			图书规格:7.9
**/

使用接口与类继承相比最大的优势在于:==其可以避免单继承的局限,所以在整个Java项目开发之中一定会存在有大量的接口。

二、接口相关说明

经过了之前的分析,基本上就可以清楚接口的基本操作形式了,但是如果要想深刻的理解接口的使用操作,则还需要通过更多的代码进行分析。

1、在子类实现接口的操作过程中是通过implements关键字实现了若干个接口,但是如果说此时除了接口之外还要去继承父类,则必须采用先继承后实现的模式进行定义。

范例:让子类同时继承抽象类并实现若干个父接口

 interface IBook {	// 定义接口
 	String SITE = "www.baidu.com" ;
 	public void read() ; // 抽象方法
 }
 interface ISpec { // 规格信息
 	public double size() ; // 尺寸
 }
 abstract class Print {	// 图书刊印的类
 	public abstract void batch() ; // 批次印刷
 }
 class MathBook extends Print implements IBook, ISpec { // 接口的多实现
 	public void read() {	// 抽象方法覆写
 		System.out.println("【MathBook子类】认真读数学图书,巩固计算机的基础知识") ;
 	}
 	public double size() {
 		return 7.9 ;
 	}
 	public void batch() {	// 覆写抽象类中的方法
 		System.out.println("【MathBook子类】进行数学图书的批量印刷。") ;
 	}
 }
 public class Demo {
 	public static void main(String args[]) {
 		IBook book = new MathBook() ; // 通过子类进行对象实例化处理操作
 		book.read() ; // 读书
 		ISpec spec = new MathBook() ; // 通过子类进行对象的实例化处理
 		System.out.println("图书规格:" + spec.size()) ;
 		Print print = new MathBook() ; // 通过子类进行对象的实例化处理
 		print.batch() ;
 	}
 }
 
 /**
 	程序执行结果:
 			【MathBook子类】认真读数学图书,巩固计算机的基础知识
 			图书规格:7.9
 			【MathBook子类】进行数学图书的批量印刷。
 **/

此时的结构程序是先通过extends关键字实现了类的继承关系,随后再利用了implements实现了两个父接口,所以对于当前的Mathbook类来讲,其继承的结构如图所示。

在这里插入图片描述

2、通过当前的类继承结构图中可以发现,对于Mathbook类来讲,其中实现的IBook接口、ISpec接口、Print父类彼此之间是没有任何联系的,但是他们有一个公共的后代。但是正是因为现在Mathbook类这个公共子类的存在,所以Mathbook类的实例化对象可以任意的向所有的父类或父接口转型(包括父接口之间、接口和抽象类之间的转型)

范例:观察各个类或接口直接的转型

interface IBook {	// 定义接口
	String SITE = "www.baidu.com" ;
	public void read() ; // 抽象方法
}
interface ISpec { // 规格信息
	public double size() ; // 尺寸
}
abstract class Print {	// 图书刊印的类
	public abstract void batch() ; // 批次印刷
}
class MathBook extends Print implements IBook, ISpec { // 接口的多实现
	public void read() {	// 抽象方法覆写
		System.out.println("【MathBook子类】认真读数学图书,巩固计算机的基础知识") ;
	}
	public double size() {
		return 7.9 ;
	}
	public void batch() {	// 覆写抽象类中的方法
		System.out.println("【MathBook子类】进行数学图书的批量印刷。") ;
	}
}
public class Demo {
	public static void main(String args[]) {
		IBook book = new MathBook() ;	// 向上转型
		// IBook和ISpec彼此之间没有任何的联系,但是此时的IBook的对象是通过MathBook子类实例化的
		// MathBook和ISpec接口之间有联系,所以以下的关系成立。
		ISpec spec = (ISpec) book ; 
		System.out.println(spec.size()) ;
		Print print = (Print) spec ; // 接口和抽象类也没有直接关系
		print.batch() ;
	}
}

/**
	程序执行结果:
			7.9
			【MathBook子类】进行数学图书的批量印刷。
**/

之所以现在可以实现以上的互相转型的处理,主要原因在于:MathBook子类同时继承多个父类或实现多个接口,这些父类或接口是依靠MathBook这个子类才创建的关系。

3、在子类实现接口的过程之中,一个接口是不能继承任何父类的,所以任何的接口一定都没有父类的概念,但是所有的接口对象都可以通过Object来进行接收(Object可以接受一切的引用数据类型,可以实现最终参数的统一)。

范例:通过Object接收接口对象

interface IBook {	// 定义接口
	String SITE = "www.baidu.com" ;
	public void read() ; // 抽象方法
}
class MathBook implements IBook { // 接口的多实现
	public void read() {	// 抽象方法覆写
		System.out.println("【MathBook子类】认真读数学图书,巩固计算机的基础知识") ;
	}
}
public class Demo {
	public static void main(String args[]) {
		IBook book = new MathBook() ;	// 向上转型
		Object obj = book ; // 通过Object接收接口引用
		IBook temp = (IBook) obj ; // 进行向下转型
		temp.read() ;
	}
}

/**
	程序执行结果:【MathBook子类】认真读数学图书,巩固计算机的基础知识
**/

至此为止,通过一系列前后的理解,基本上就可以得出重要的结论:Object可以实现所有Java中的数据接收,是真正参数统一的最终标准。

4、虽然一个接口不能使用extends继承一个父类,但是一个接口却可以通过extends同时继承多个父接口,这一点称为接口的多继承

范例:观察接口的多继承

interface IPrint {
	public void batch() ; // 批次印刷
}
interface ISpec {
	public double size() ; // 图书规格
}
interface IBook extends IPrint, ISpec {	// 接口多继承
	String SITE = "www.baidu.com" ;
	public void read() ; // 抽象方法
}
class MathBook implements IBook { // 接口的多实现
	public void read() {	// 抽象方法覆写
		System.out.println("【MathBook子类】认真读数学图书,巩固计算机的基础知识") ;
	}
	public double size() {
		return 7.9 ;
	}
	public void batch() {
		System.out.println("【MathBook子类】批量印刷数学书。") ;
	}
}
public class Demo {
	public static void main(String args[]) {
		IPrint print = new MathBook() ; // 子类为父接口实例化
		print.batch() ;
		ISpec spec = (ISpec) print ; // 进行不同接口的强制转换
		System.out.println(spec.size()) ;
	}
}

/**
	程序执行结果:
			【MathBook子类】批量印刷数学书。
			7.9
**/

在实际项目开发过程之中,对应接口的使用一定是慢慢积攒的开发经验,如果想要写出标准化且结构清晰的程序代码,那么就必须从接口开始。

三、适配器设计模式(Adapter)

在一个接口中,一般都会定义有大量的抽象方法,那么按照接口子类的定义来讲,如果要想正确的实现接口,那么就必须覆写接口中全部抽象方法,可是假设说在一个接口中提供的抽象方法特别多,如果全部覆写,则会有些浪费(因为可能部分的子类只需要部分的抽象方法)。

范例:观察接口本身存在的问题

interface IBook {	// 接口多继承
	public void read() ; // 抽象方法
	public void create() ; // 抽象方法
	public void message() ; // 抽象方法
}
class MathBook implements IBook { // 接口的多实现
	public void read() {	// 抽象方法覆写
		System.out.println("【MathBook子类】认真读数学图书,巩固计算机的基础知识") ;
	}
	public void create() {}
	public void message() {}
}
class LoveBook implements IBook {
	public void read() {}
	public void create() {}
	public void message() {
		System.out.println("【LoveBook子类】通过言情小说,给我心爱的女神传递爱意") ;
	}
}
class ProgramBook implements IBook {
	public void read() {}
	public void create() {
		System.out.println("【ProgramBook子类】根据编程图书上给的知识,创建属于自己的未来网络") ;
	}
	public void message() {}
}

在实际开发之中,接口最为常见的定义格式就是定义一系列的抽象方法,按照传统的实现来讲,即便某些子类不需要接口中的某些方法,那么也不得不去进行方法的覆写(因为这属于语法要求)。

在这里插入图片描述

如果真的面对程序的编写,肯定希望直接命中主题,但是现在发现所有定义的子类不得不被许多无用的方法所拖累,即便不使用,也需要去实现它。如果想要解决这个问题就需要找一个替代品,这个替代品负责假实现接口中的所有方法,同时这个替代品又不能够直接去使用,那么这种情况下最佳的选择就是抽象类

范例:引入抽象类

interface IBook {	// 接口多继承
	public void read() ; // 抽象方法
	public void create() ; // 抽象方法
	public void message() ; // 抽象方法
}
abstract class AbstractBookAdapter implements IBook {	// 定义抽象类
	public void read() {} // 假实现
	public void create() {} // 假实现
	public void message() {} // 假实现
}
class MathBook extends AbstractBookAdapter implements IBook { // 既实现接口,又继承抽象类中的方法(主要是无用的两个方法)
	public void read() {	// 抽象方法覆写
		System.out.println("【MathBook子类】认真读数学图书,巩固计算机的基础知识") ;
	}
}

/**
	下列类中如果不加接口实现其实也可以,但是加上会让结构更加清晰,更方便了我们以后的学习。
**/
class LoveBook extends AbstractBookAdapter implements IBook { // 既实现接口,又继承抽象类中的方法(主要是无用的两个方法)
	public void message() {
		System.out.println("【LoveBook子类】通过言情小说,给我心爱的女神传递爱意") ;
	}
} 
class ProgramBook extends AbstractBookAdapter implements IBook{// 既实现接口,又继承抽象类中的方法(主要是无用的两个方法)
	public void create() {
		System.out.println("【ProgramBook子类】根据编程图书上给的知识,创建属于自己的未来网络") ;
	}
}

本程序中使用抽象类实现了程序最终过渡处理操作,这样就避免了子类要大面积覆写接口中抽象方法的尴尬局面,同时这样的设计在Java开发中属于适配器设计模式,这种设计模式在整个类库以及后续的学习中都是非常常见的。

四、工厂设计模式(Factory)

如果想要去使用一个接口,那么一定要为接口定义它的子类,既然有了子类,就可以按照子类对象的向上转型为接口对象进行实例化的处理操作,于是问题也就从此展开。(现在我们是讨论的好与不好,而不是行与不行)

范例:观察接口的常规使用

interface IBook {
	public void read() ;
}
class ProgramBook implements IBook {
	public void read() {
		System.out.println("【ProgramBook】认真阅读编程图书。") ;
	}
}
public class Demo {
	public static void main(String args[]) {
		IBook book = new ProgramBook() ; // 子类为父接口实例化
		book.read() ; // 调用被覆写过的方法
	}
}

/**
	程序执行结果:【ProgramBook】认真阅读编程图书。
**/

此时的程序代码通过接口的子类为父接口进行对象实例化,随后再调用了接口中被子类所覆写的方法,那么这样的做法实际上就属于常规的使用。

在这里插入图片描述

实际上这个程序所存在的最大问题就在于主方法中那个关键字“new”(IBook book = new ProgramBook() ),因为这种代码一旦定义之后就意味着程序的主类和ProgramBook这个子类捆绑在一起,那么这种操作就存在有很强的耦合性,所以在正常的项目过程之中一般常见的做法都是中间使用一个过渡层,例如:Java程序的可移植性原理——JVM,核心思想就是不要与操作系统之间产生直接的耦合问题。

在这里插入图片描述

那么现在按照同样的道理来讲,此时对于当前程序最大的问题就是客户端的程序类与接口的子类有了直接的耦合问题,而如果想要解决这个问题,最佳的做法就是引入一个中间的过渡类——工厂类。

范例:通过工厂类解决当前程序的耦合问题、

interface IBook { //接口
	public void read() ;
}
class ProgramBook implements IBook {
	public void read() {
		System.out.println("【ProgramBook】认真阅读编程图书。") ;
	}
}
class MathBook implements IBook {
	public void read() {
		System.out.println("【MathBook】认真学习数学知识。") ;
	}
}
class Factory {
	public static IBook getInstance(String className) {
		if ("program".equalsIgnoreCase(className)) {
			return new ProgramBook() ;
		} else if ("math".equalsIgnoreCase(className)) {
			return new MathBook() ;
		}
		return null ; // 没有匹配返回null
	}
}
public class Demo {
	public static void main(String args[]) {
		IBook book = Factory.getInstance("math") ; // 通过工厂获取接口实例
		book.read() ; // 调用被覆写过的方法
	}
}

/**
	程序执行结果:【MathBook】认真学习数学知识。
**/

通过一系列的分析之后可以发现,当前程序中的主类和接口的子类没有任何的直接联系,而所有IBook接口对象全部是通过工厂类来获取的,这样就实现了解耦合的操作设计。

五、代理设计模式(Proxy)

所谓的代理设计模式主要指的是一个核心的业务功能,通过其他的辅助的业务手段来实现完整的业务操作,现在假设我需要进行一个消息的发送,那么在整个的处理过程中,消息的发送就属于一个核心的业务主题,真正的需求是将消息发送出去,但是如果想要进行消息的发送,就必须建立网络的通道,而在消息发送完成之后也需要关闭通道以释放资源。

一般来讲代理设计模式往往会有一个比较核心的主题,而这个主题都会通过接口来进行定义,也就是说在接口中定义好核心主题与代理主题的核心的业务处理方法,而后代理主题和核心业主题都有一个各自的类,负责具体的操作实现,最终实现依据代理主题来包裹核心业务主题的模式来完成的。

范例:编写代理设计模式

interface IMessage {// 核心功能
	public void send(String msg) ; // 消息发送
}
class MessageImpl implements IMessage { //真实(核心)业务主题
	public void send(String msg) {//定义真实业务负责实现我们“消息发送”的这个核心业务
		System.out.println("【核心业务 - send()】" + msg) ;	
	}
}
class MessageProxy implements IMessage {//代理业务主题
    //设置代理业务中对于真实业务处理的对象,用于调用IMessage类中的send()方法
	private IMessage messageObject ; 
    //定义一个构造函数,将参数传来的真实业务主题的实例化对象赋给本垒中定义的真实业务处理的对象
	public MessageProxy(IMessage messageObject) {
		this.messageObject = messageObject ;
	}
        
     
	public void send(String msg) {
		if (this.connect()) {	// 调用代理主题
			this.messageObject.send(msg) ;	// 执行真实主题操作
			this.close() ; // 调用代理主题
		}
	}
    
    /**
    以下是一些代理操作:包括建立网络的通道、关闭通道以释放资源
    **/
    //建立网络的通道
	public boolean connect() {
		System.out.println("【代理业务 - connect()】连接远程服务器,准备进行消息发送") ;
		return true ;
	}
    //关闭通道以释放资源
	public void close() {
		System.out.println("【代理业务 - close()】断开服务器连接,释放资源") ;
	}
}
public class Demo {
	public static void main(String args[]) {
        // 获得代理主题同时将真实主题当作代理主题构造函数的参数进行传入
		IMessage message = new MessageProxy(new MessageImpl()) ; 
        //调用代理主题中的send方法
		message.send("百度:www.baidu.com") ;
	}
}

/**
	程序执行结果:
			【代理业务 - connect()】连接远程服务器,准备进行消息发送
			【核心业务 - send()】百度:www.baidu.com
			【代理业务 - close()】断开服务器连接,释放资源
**/

在整个的程序实现过程中,MessageImpl类负责核心的功能,而如果想要完成这个消息的发送,核心业务就必须通过MessageProxy这个代理类来完成所以的辅助性功能,这样就得到了一个明确的分工。

在这里插入图片描述

但是针对于以上代理设计也存在一些问题,按照程序设计的标准性来讲,接口的子类不应该被外部的其他类所访问,会存在有类的耦合性的问题,所以此时最佳的做法应该是基于工厂类来获取代理类的对象实例。

在这里插入图片描述

范例:利用工厂设计模式修改当前程序

interface IMessage {// 核心功能
	public void send(String msg) ; // 消息发送
}
class MessageImpl implements IMessage {
	public void send(String msg) {
		System.out.println("【核心业务 - send()】" + msg) ;	
	}
}
class MessageProxy implements IMessage {
	private IMessage messageObject ;
	public MessageProxy(IMessage messageObject) {
		this.messageObject = messageObject ;
	}
	public void send(String msg) {
		if (this.connect()) {	// 调用代理主题
			this.messageObject.send(msg) ;	// 执行真实主题操作
			this.close() ; // 调用代理主题
		}
	}
	public boolean connect() {
		System.out.println("【代理业务 - connect()】连接远程服务器,准备进行消息发送") ;
		return true ;
	}
	public void close() {
		System.out.println("【代理业务 - close()】断开服务器连接,释放资源") ;
	}
}
class Factory {
	public static IMessage getInstance() {
		return new MessageProxy(new MessageImpl()) ;
	}
}
public class Demo {
	public static void main(String args[]) {
		IMessage message = Factory.getInstance() ; // 获得接口对象
		message.send("百度:www.baidu.com") ;
	}
}

/**
	程序执行结果:
			【代理业务 - connect()】连接远程服务器,准备进行消息发送
			【核心业务 - send()】百度:www.baidu.com
			【代理业务 - close()】断开服务器连接,释放资源
**/

随着个人开发经验的不断提高,会对于整个程序的设计结构有更加深刻的理解,而这些设计模式的组合应用也非常的常见。

六、定义接口标准

经过之前一系列的分析之后,应该可以发现,在实际项目开发过程中,接口应该是比类(包括抽象类)更优先定义出来的结构,是因为接口里面实际上定义的是整个开发操作的执行标准,实际上如果对于从事理工科的同学来讲,接口这一概念并不陌生,例如:USB接口、HDMI接口、Type-C接口,利用接口可以让不同的两个设备之间进行连接。

在这里插入图片描述

由于接口是连接不同操作类型之间的重要枢纽,所以现在在整个项目设计之初必须建立好相应的接口标准,随后依据此标准进行项目代码的编写。

实例:实现以上分析的结构

interface IUSB {
	public void install() ; // 安装USB软件驱动
	public void use() ; // 使用USB设备
}
class Computer { // 电脑
	private String brand ; // 电脑品牌
	public Computer() {
		this("Yootk电脑") ;
	}
	public Computer(String brand) {
		this.brand = brand ;
	}
	public void plugin(IUSB usb) {	// 允许接收USB设备
		usb.install() ; // 安装驱动
		usb.use() ; // 使用USB设备
	}
}
class Flash implements IUSB {
	public void install() {
		System.out.println("【U盘】进行U盘驱动的安装") ;
	}
	public void use() {
		System.out.println("【U盘】向U盘中拷贝一些比较重要的种子") ;
	}
}
class Phone implements IUSB {
	public void install() {
		System.out.println("【手机】电脑启动手机的连接") ;
	}
	public void use() {
		System.out.println("【手机】通过电脑进行手机资料备份") ;
	}
}
public class Demo {
	public static void main(String args[]) {
		Computer computer = new Computer("电脑") ;
		computer.plugin(new Phone()) ;
		computer.plugin(new Flash()) ;
	}
}

/**
	程序执行结果:
			【手机】电脑启动手机的连接
			【手机】通过电脑进行手机资料备份
			【U盘】进行U盘驱动的安装
			【U盘】向U盘中拷贝一些比较重要的种子
**/

这个时候的程序代码实际上都按照了USB执行标准(本次定义的标准比较简单)进行了特定的逻辑操作,如果有需要也可以设计一些更加繁琐的逻辑,这个程序的结构上是不会有任何改变。

在实际项目开发过程之中,利用这个结构可以实现更多的程序模型的定义。

例如:如果想要去餐厅吃饭,所有的餐厅一定都是禁止宠物进入的。

在这里插入图片描述

例如:在全国的高速公路上只允许设计时速超过70的机动车行驶。

在这里插入图片描述

七、接口定义加强

在整个Java设计之初对于接口只有两个核心的组成部分:全局常量、抽象方法,但是从JDK 1.8开始,接口中的结构可以进行扩充了,可以通过default定义普通方法或者通过static定义静态方法,如果想理解为什么要从JDK 1.8开始增加这样新的特点,就必须通过整个程序的设计发展结构来进行深入的理解。

在传统的Java开发过程之中(JDK 1.8以前),如果要是定义有一个接口之后,那么肯定所有的子类就都需要进行此接口的实现并且覆写接口中全部的抽象方法,那么这个时候就可以得到如下的类结构。

在这里插入图片描述

如果说现在有一个IBook接口,这个接口由于长时间的使用,已经派生出了5000个子类,突然有一个版本更新的时候发现需要在IBook接口里面提供一个新的操作方法,那么如果说此时的子类直接实现了接口,这个时候就需要在每一个子类重复实现这个提供的新方法,并且这些新方法的功能都是相同的,则按照直接实现接口的设计来讲,这种代码就会非常的糟糕了,所以早期的项目开发人员为了解决这个问题,他不会采用子类直接实现接口的形式来处理,而是在中间提供有一个抽象类。

在这里插入图片描述
但是由于很多项目的设计者可能在刚刚接触到一门语言的时候,考虑不是很全面,所以中间在实现的过程里面未必会使用抽象类作为接口和具体子类之间的过渡,那么为了解决这些项目的设计问题,所以从JDK 1.8开始,就可以在接口中定义普通方法了,这个功能就相当于代替了之前设计的抽象类的结构。

范例:在接口中定义普通方法

interface IBook {
	public void read()  ;
	public default void create() {
		System.out.println("用我们的智慧和本土化的教学模式创建属于自己的优质原创图书。") ;
	}
}
class ProgramBook implements IBook {
	public void read() {}
}
public class Demo {
	public static void main(String args[]) {
		IBook book = new ProgramBook() ; 
		book.create() ;
	}
}

/**
	程序执行结果:用我们的智慧和本土化的教学模式创建属于自己的优质原创图书。
**/

在接口中提供的default()方法,如果想使用它,那么就必须通过接口实例化对象完成操作,那么为了进一步的简化操作,所以在接口中也提供有static方法,这个方法可以由接口名称直接调用。

范例:在接口中定义static方法

interface IBook {
	public void read() ;
	public default void create() {
		System.out.println("用我们的智慧和本土化的教学模式创建属于自己的优质原创图书。") ;
	}
	public static IBook getInstance() {
		return new ProgramBook() ; // 返回接口实例
	}
}
class ProgramBook implements IBook {
	public void read() {}
}
public class Demo {
	public static void main(String args[]) {
		IBook book = IBook.getInstance() ;
		book.create() ;
	}
}

/**
	程序执行结果:用我们的智慧和本土化的教学模式创建属于自己的优质原创图书。
**/

在以后进行JavaDoc文档的浏览过程里面,经常会发现接口中除了有抽象方法外也会大量的通过default或static定义一系列功能完善的方法。

八、抽象类与接口的区别

抽象类和接口从本质上来讲都是对子类的方法覆写提出了严格的要求,同时在很多的层次上抽象类和接口又需要彼此连接共同完成最终所需要的完整的设计结构,那么考虑到实际开发和面试的需求,我们针对于这两个结构进行一个完整的比较。

在这里插入图片描述

实际上抽象类和接口在某种程度上来讲都属于普通类的更高级的抽象,理论上都可以用,但是由于抽象类存在有单继承的局限,所以在实际项目的开发过程中,如果抽象类和接口都可以使用的情况下,建议优先使用接口。

在这里插入图片描述

  • 4
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Jackson Xi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值