JavaSE面向对象基础_接口

接口

需求:需要飞到北京出差

飞机、鸟和超人不能归于一个类属,但是具备有相同的特性:会飞的。所以引入了一个新概念叫做接口,可以用于规范实现接口的类中必须实现接口中抽象方法。接口可以理解为一种契约

使用关键字interface定义接口

public interface 会飞的 {
    
    
	public void 起飞();
	public void 巡航飞行();
	public void 降落();
}

接口不能直接使用,必须有对应的实现类

public class 飞机类 implements 会飞的 {
    
     //共性是通过实现接口来表示的
private String name; //名称,这是类属的属性,这里可以定义字节类的成员,和接口无关
//如果当前类不是抽象类,则必须实现接口中的所有抽象方法
@Override
public void 起飞() {
    
    
System.out.println("使劲的跑,一抬头就飞起来了");
}
@Override
public void 巡航飞行() {
    
    
System.out.println("使劲的烧油...");
}
@Override
public void 降落() {
    
    
System.out.println("我对准...");
}
}

通过接口定义变量,使用具体类的实例,进行调用

会飞的 obj = new 飞机类();
obj.起飞();
obj.巡航飞行();
obj.降落();

引入接口的目的在于隔离实现

public void 出差(飞机 obj){
    
    }
这种写法当前类和飞机是耦合的,如果需要坐超人出差,则必须修改源代码
public void 出差(会飞的 obj){
    
    }
当前类只是和接口耦合,任何实现了接口的对象都可以作为参数进行传入

使用接口而不是使用具体的类,则可以实现在实现接口的多个具体实现类之间进行更换,例如定义出超人类

什么是接口

在Java中不直接支持多继承,因为会出现调用的不确定性,所以Java将多继承机制进行改良,在Java中变成了多实现。一个类可以实现多个接口,一个接口可以继承多个接口

interface IA{
    
    }
interface IB{
    
    }
interface IC extends IA,IB{
    
    } 正确的,其中IC中的方法等于IA+IB
class A implements IA,IB{
    
    }
IA a=new A();
IB b=new A();
  • 接口是一种特殊的抽象类,接口只能被abstract或者public修饰
  • 没有构造器方法
public interface IA{
    
    
	public IA(){
    
    } //语法报错
}
  • 没有属性,只能定义常量
public class Test {
    
    
	public static void main(String[] args) {
    
    
		System.out.println(IA.name); //常量可以直接使用
		System.out.println(B.name); //实现类也可以直接访问常量
	}
}
interface IA {
    
    
	String name = "yanjun";// 接口中只能定义常量,不能定义属性,默认限定词
	public static final,自定义限定词不能冲突
}
class B implements IA{
    
    }
  • 可以包含抽象方法,也可以没有抽象方法
interface IA1 {
    
    
}// 这个接口中没有包含任何抽象方法
class A1Impl implements IA1 {
    
    
}
    • 接口中的方法只能被public、default、abstract、static修饰
      • 一般情况下接口中只定义抽象方法
      • 定义的方法默认为公开的抽象方法

抽象方法必须在实现类中提供实现

class A2Impl implements IA2{
    
     //在实现类中必须实现接口中的所有抽象方法,否则当前
类只能是抽象类
public int ee() {
    
    
return 0;
}
public void pp() {
    
    
System.out.println("A2Impl....");
}
public void cc() {
    
    
System.out.println("A2Impl.....");
}
//dd方法由于在接口中有默认实现,所以可以不用重新定义,也允许重新定义
}
  • 可以使用default关键字给抽象方法提供默认实现,有默认实现的方法在实现类中可以重新定义,也可以不重新定义

报错原因:Java中不支持类的多重继承,一个类只能有一个双亲类

接口允许多重继承

一个类在继承另一个类的同时,还可以实现多个接口

接口允许多重继承
interface IA1{
    
    
public void p1();
}
interface IA2{
    
    
public void p2();
}
interface IC extends IA1,IA2{
    
    
public void p3();
}//可以继承多个父接口,此时IC中实际有3个抽象方法
类是单根继承,不允许继承多个父类

允许一个类实现多个接口,但是每个接口的抽象方法都必须提供实现,否则是抽象类。提供的实现也可以是继承

public class A1{
    
    
public void p1(){
    
    }
}
public class A2 extends A1 implements IA1,IA2{
    
    
public void p2(){
    
    }
}
public abstract class A3 extends A1 implements IA1,IA2{
    
    }//因为p2方法没有实现

接口的出现避免了单继承的局限性,这样定义C接口则拥有A+B的所有定义,可以使用A和B接口以及父类D声明变量类型,直接new T。但是约束时,用谁声明变量编译器系统识别就是谁这种类型,也就意味只能调用识别类型中的方法,不能调用其他方法

interface A{
    
    
public void p1();
}
interface B{
    
    
public void p2();
}
interface C extends A,B{
    
    }
class D{
    
    
public void abc(){
    
    }
}
class T extends D implements C{
    
    
public void p1(){
    
    }
public void p2(){
    
    }
}
D d=new T();这里只能调用abc方法,如果使用其他方法则需要进行类型转换
A a=new T();这里只能调用A接口中声明的方法p1
B b=new T();这里只能调用B接口中声明的方法p2

声明接口的语法

访问修饰符 interface 接口名{
    
     } 一般建议接口名称使用形容词
  • 如果定义public接口,则规则和定义public类一致,要求接口名称和文件名称一致
  • 外部的接口只能使用public、默认这两个范围限定词;如果定义内部接口则可以使用4大范围限定词
  • 接口实际上提供了同一的操作界面(方法),如果是JDK1.8-版本则一个方法也不实现,等待某类或某几个类去实现它的方法【接口中的所有方法必须是抽象的】。如果使用的是JDK1.8+允许使用default在接口中定义默认实现,这个实现允许在实现类中重新定义覆盖
public class Test1 {
    
    
public static void main(String[] args) {
    
    
IA2 a2=new A2Impl();
a2.pp();
}
}
interface IA2 {
    
    
default void pp(){
    
    
System.out.println("this is pp....");
this.ff();
}
void ff();
}
class A2Impl implements IA2{
    
    
public void ff() {
    
    
System.out.println("this is ff...");
}
}

default默认方法实现使用的限制

public class Test1 {
    
    
public static void main(String[] args) {
    
    
IA2 a2 = new A2Impl();
a2.pp();
}
}
interface IA2 {
    
    
default void pp() {
    
    
System.out.println("this is No2 pp....");
}
}
interface IA3 {
    
    
default void pp(){
    
    
System.out.println("this is No3 pp");
}
}
class A2Impl implements IA2, IA3 {
    
    //如果IA2中有一个default实现的方法和IA3中
的方法一致,则必须在实现类中重新定义
public void pp() {
    
    
IA2.super.pp();//调用IA2接口中的pp方法的默认实现
IA3.super.pp();
}
}
  • 接口中没有属性,只能定义常量,它提供一些常量,实现它的类可以共享这些常量
  • 接口可以给出访问控制符,用public修饰的是公共接口,到处可见;如果定义接口没有范围限定词,则只能在同包中访问

接口中只能定义常量和抽象方法

  • 接口中只能定义公共的常量,接口中的属性默认是public static final类型的,必须是public staticfinal类型的
interface IA{
    
    
String name="yanjun";//这里实际上是常量定义,不是属性,它的限定词是public
static final String name="yanjun",必须在声明的同时进行赋值
} 在接口中不能定义静态块 static{
    
    }
接口中不能定义构造器
  • 接口中只能定义公共的抽象方法, 只有在JDK1.8+中可以使用default关键字定义方法实现。接口中的方法默认是public abstract类型的,而且必须是public abstract类型的。只有在JDK1.8+中可以使用default关键字定义方法默认实现,同时允许在实现类中覆盖重新定义
  • 接口不能被实例化,只能通过实现类所实现,但是可以用于声明变量的类型。
    接口 变量名=new 实现接口类();
  • 接口没有构造函数
  • 接口中的抽象方法必须在非抽象子类中提供实现,这个实现可以是继承来的
interface IA2 {
    
    
public void pp();
}
class A2 {
    
    
public void pp() {
    
    
}
}
class B2 extends A2 implements IA2 {
    
     //这里的pp方法的实现是从父类中继承来的
}

一个类实现接口的语法

class 类名称 implements 接口名称 {
    
     }
  • 接口抽象方法的默认修饰符为public,在实现接口时必须用public关键字在方法头上说明
interface IA2 {
    
    
void pp();
}
class B2 implements IA2 {
    
    
void pp() {
    
     //语法报错,因为IA2接口中的方法为public abstract
}
}
  • 一个接口可以被多个子类实现。一个子类还可以实现多个接口

类实现接口

  • 一个类在实现某接口的抽象方法时,必须使用完全相同的方法头public
  • 如果一个类实现多接口,用逗号隔开
  • 如果一个类实现了声明相同方法的两个接口,则被多个接口共用
public class Test1 {
    
    
public static void main(String[] args) {
    
    
IA2 a2=new B2(); a2.pp();
IA3 a3=new B2(); a3.pp();
}
}
interface IA2 {
    
    
void pp();
}
interface IA3{
    
    
void pp();
}
class B2 implements IA2,IA3 {
    
    
public void pp() {
    
    
System.out.println("this is B2.pp()");
}
}
  • 类在实现接口时可以定义它自己的附加成员,这也是最常见的形式
public class Test1 {
    
    
public static void main(String[] args) {
    
    
IA2 a2=new B2();
a2.pp();//这里不能直接调用B2实现类中的其它方法,如果需要调用则应该进行强制
类型转换
((B2)a2).dd();
}
}
interface IA2 {
    
    
void pp();
}
class B2 implements IA2 {
    
    
public void pp() {
    
    
System.out.println("this is B2.pp()");
}
private int age=100;
public void pp(int k){
    
    
System.out.println("this is pp(int)");
}
public void dd(){
    
    
System.out.println("this is dd()");
}
}
  • 如果一个类不完全实现接口的所有方法,必须把类定义成abstract类型的类,任何继承该类的子类必须实现该接口

接口中常量

可以为多个接口的实现类共享常量

public class Test1 {
    
    
public static void main(String[] args) {
    
    
System.out.println(IA2.PI);//可以直接使用
A3 a=new A3();
a.setR(4);
System.out.println(a.getArea());
}
}
interface IA2 {
    
    
double PI=3.1415;
}
class A3 implements IA2{
    
     //在实现类中也可以直接使用
private double r;
public double getArea(){
    
    
return PI*r*r;
}
public void setR(double r) {
    
    
this.r = r;
}
}
  • 只需在接口中定义并赋值,此后不能再修改
  • 在类中说明是接口的实现类,就可以使用该接口的常量了

一个对象往往有多重身份

  • Java中一个子类只能继承一个父类 (不能表示)
  • java中一个类可以实现多个接口。(可以表示)
class 类名称 implements 接口1,接口2,{
    
     }
  • 通过继承和运行时多态的双重机制,可以定义一个被很多不同却相关的对象类型的运用的一致的接口
  • 维持抽象接口,甚至不需要重新编译,就可以调用新类的实例
public void 出差(会飞的 obj){
    
     obj.起飞(); ...}
调用的时候可以传入不能类型的对象
出差(new 飞机()); --- 出差(new());

接口的特殊特征

  • 一个类只能有一个父类!
  • 一个类可以实现多个接口!
  • 一个接口可以继承多个接口
interface 接口名 extends 接口名称1 ,父接口名称2 , ... {
    
     }

接口的作用

  • 统一访问
接口 obj=new 实现1(); 可以还有实现2,实现3等
obj只能调用接口中定义的方法
  • 解耦 通过接口可以隔离具体实现
    解耦就是 在使用者 和 实现者 之间没有关系。 无论实现者如何改变实现,对于使用者使用不会变化

接口和抽象类的异同点

  • 相同点:都是不断向上抽取而来的
  • 不同点:
    • 抽象类需要被继承,而且只能单继承
    • 接口需要被实现,而且可以多实现
  • 抽象类中可以定义抽象方法和非抽象方法,子类继承后可以直接使用非抽象方法
  • 接口中可以定义抽象方法和default方法,抽象方法必须由子类去提供实现;JDK1.8+中允许接口中的方法有默认实现,实现类中可以直接使用默认实现,允许覆盖定义
  • 抽象类的继承是is a关系,在定义该体系的基本共性内容
  • 接口的实现是like a关系,在定义体系额外功能
  • 接口中只能定义常量,而且必须被初始化,抽象类中可以定义属性,允许在声明时直接初始化,也可以不初始化,同时允许定义常量

接口中的公共方法应该全部是抽象的,JDK1.8+版本中可以通过default关键字定义方法的默认实现,允许定义静态方法,JDK1.9开始可以定义私有方法;抽象类中可以有抽象方法也可以有普通方法

public class Test1 {
    
    
public static void main(String[] args) {
    
    
IA2.pp();
A2Impl.pp();
IA2 a2 = new A2Impl();
a2.pp();// 语法报错
}
}
interface IA2 {
    
    
public default void cc(){
    
    }
public static void pp() {
    
    
System.out.println("IA2 static....");
}
}
class A2Impl implements IA2 {
    
    
public static void pp() {
    
    
System.out.println("A2Impl static..");
}
}

在接口种定义调用规范,抽象类实现接口,提供公共功能部分,最终具体实现类实现特殊的方法

public class Test2 {
    
    
public static void main(String[] args) {
    
    
Fa ff = new Son();
ff.pp();
}
}
class Fa {
    
    
public static void pp() {
    
    
System.out.println("Fa static....");
}
}
class Son extends Fa {
    
    
public static void pp() {
    
    
System.out.println("Son static....");
}
}

public void pp();在接口中是抽象方法,但是如果public void pp(){}语法错误,在JDK1.8+版本中可以使用public default void pp(){}就合法了

如何使用接口

一般使用接口隔离具体实现,可以将类之间的相互依赖变为类对接口的依赖。例如出差类和会飞的东西是通过会飞的接口进行隔离,这样不管出差类需要修改或者会飞的东西需要修改,都不会相互影响

如果一组相关的类中有公共的方法和特殊的方法,可以使用抽象类,在抽象类中固化公共的方法【算法骨架】,而无需具体子类重复实现;但是在抽象类中无法实现的方法可以延迟到子类中再实现。例如排序器BubbleSorter,其中抽象类BubbleSorter固化了所使用的冒泡排序算法,而将无法实现的bigger比较算法延迟到BubbleSorter的子类PigSorter中实现,同时PigSorter中也不需要重新定义排序算法

最佳软件开发实践:先定义接口规范调用方法,在使用抽象类实现接口定义公共方法,最后再定义具体子类实现所有的方法

接口和抽象类的使用场景

从设计层面看,抽象类体现继承关系is a,它主要描述类的从属关系或者父子关系,抽象类和它的派生类之间是典型的IS-A关系,即子is a父。

interface可以多实现,而且不要求实现者和interface定义在概念本质上是一致的,仅仅是实现了interface定义的契约而已。它主要描述的是类型间的行为合同,接口和它的实现类之间是典型的CANDO关系,即子can do父。

为什么接口需要默认方法

在接口添加默认方法不需要修改实现类,接口新增的默认方法在实现类中直接可用。

另外还要注意默认方法冲突问题。

猜你喜欢

转载自blog.csdn.net/qq_39756007/article/details/127234376