首页 > 其他 > 详细

5.6 接口

时间:2020-03-05 21:52:48      阅读:52      评论:0      收藏:0      [点我收藏+]

抽象类是从多个类中抽象出来的模板,如果将这种抽象更彻底,则可以提炼出一种更加特殊的"抽象类"——接口(interface)。Java 9对接口进行改造,允许在接口中定义默认方法和类方法,接口方法和类方法都可以提供方法实现。Java 9为接口增加了一种私有方法,私有方法可提供方法实现。

一、接口的概念

接口定义了一种规范,接口定义了某一批类所需要遵循的规范,接口不关心这些类内部状态数据,也不关心这些类里方法的是实现细节,它只规定了这批类里必须提供某些方法,提供这些方法的类就可以满足实际需要。

二、Java 9定义接口

定义接口使用interface关键定义接口,接口定义的基本语法:

[修饰符] interface 接口名 extends 父接口1,父接口2...
{
    //常量定义(接口中的成员变量默认使用static final)
    //多个抽象方法定义
    //零到多个内部类、接口、枚举
    //零到多个私有方法、默认方法或类方法定义...
}

说明:
1、接口修饰符可以是public或省略,如果省略public修饰符访问控制符,则默认包访问权限。
2、修饰符:public。接口是彻底抽象,所以不能用final;接口已经够抽象了,因此不需要abstract修饰。
3、接口名等同于类名。一般推荐使用形容词。田间able就是形容词。
4、一个接口可以有多个直接父类,但接口只能继承接口,不能继承类。
5、Java 8以上版本才允许在接口中定义默认方法、类方法。
6、因为接口里不能包含构造器,所以也就不能包含初始化块(初始化块会还原到构造器中)。接口里可以包含成员变量(只能是静态常量)、方法(抽象实例方法、类方法、默认方法、私有方法)、内部类(包括接口、枚举)。

2.1 定义接口中成员变量

??对于接口的静态常量,他们与接口相关,系统为自动为这些成员变量增加static和final修饰符。也就是说,在接口中定义成员变量时,不管是否使用public static final 修饰符,接口中的成员变量总是使用这三个修饰符来修饰。因为接口中没有构造器和初始化块,因此接口里定义的成员变量只能在定义时由程序员显示指定默认值。**
接口中定义的成员变量如下两行代码完全结果一样:

int MAX_SIZE=20;
public static final MAX_SIZE=20;

2.2 接口无构造器和初始化块

我们已经知道抽象类获得了抽象方法,但是失去了创建子类的能力。而接口是更加抽象的类,也就不能创建实例,因此接口中无构造器。接口中的成员变量,在定义时就必须指定默认值,所以初始化块存在也就没有任何意义。

2.3定义接口中方法

接口中定义的方法只能是抽象方法、类方法、默认方法、私有方法(类方法或实例方法),因此如果不是定义默认方法、类方或私有方法,系统将自动为普通方法增加abstract修饰符;定义接口里的普通方法时不管是否使用public abstract修饰符,接口里的普通方法总是使用public abstract修饰。接口里的普通方法不能有方法体;但类方法、默认方法、私有方法都必须有方法体必须有方法实现。

1、抽象方法

接口是更加抽象的类,因此接口中一定存在着抽象方法。在接口中定义的方法如果没有指定修饰符abstract,且不含方法体,则该方法是抽象方法系统会默认在前面加上public abstract修饰符。下面两行代码的效果是一样的:

public interface A 
{
    void test();//这是一个抽象方法
    //public abstract void main();//与上面的语句等价
}

2、类方法

接口中存在着类方法。类方法需要使用static修饰符,如没有在定义方法时加上public,系统会默认为static方法加上public修饰符。

public interface StaticMed 
{
    static void test()
    {
        System.out.println("这是一个类方法");
    }
}

E:\Java\第六章 面向对象(下)\6.6 接口>javap -c StaticMed
Compiled from "StaticMed.java"
public interface StaticMed {
  public static void test();
    Code:
       0: getstatic     #1                  // Field java/lang/System.out:Ljava/io/PrintStream;
       3: ldc           #2                  // String 这是一个类方法
       5: invokevirtual #3                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
       8: return
}

3、默认方法

接口中定义默认方法,必须使用default修饰,因为为了告诉编译这不是一个抽象方法。默认方法总是使用public修饰。可以理解为类中的实例方法。

interface DefaultMed
{
    default void test()
    {
        System.out.println("这是一个默认方法");
    }
}

4、私有方法

Java 9 增加了私有方法。Java 8允许在接口中定义默认方法和类方法,当两个默认方法(或类方法)中包含一段相同的实现逻辑时,程序将考虑将这段实现逻辑抽取成工具方法,而且该工具方法不希望被接口的实现类调用,应该隐藏起来,故必须使用private修饰。
例如下面程序中的默认方法fly()和run()都包含了预备阶段的实现逻辑,这与程序要求减少代码重复不符。

interface PrivateMed
{
    default void fly()
    {
        System.out.println("预备阶段阶段:3,2,1!go");
        System.out.println("起飞...");    
    }
    default void run()
    {
        System.out.println("预备阶段阶段:3,2,1!go");
        System.out.println("飞迸前进...");      
    }
}

我们现在将这段重复逻辑抽取为工具方法,只提供给接口内部的默认方法(类方法不能调用—— 错误: 无法从静态上下文中引用非静态 方法 ready())使用。

public interface PrivateMed
{
    private void ready()
    {
        System.out.println("预备阶段阶段:3,2,1!go");
    }
    default void fly()
    {
        ready();
        System.out.println("起飞...");    
    }
    default void run()
    {
        ready();
        System.out.println("飞迸前进...");      
    }
}

2.4 定义接口中的内部类、内部接口、内部枚举

接口里的内部类、内部接口、内部枚举都会默认使用public static两个修饰符,不管是否指定这两个修饰符,系统将会自动使用public static对它修饰。
总结图:
技术分享图片
定义接口举例:

package lee;
public interface Output
{
    //定义接口中的成员变量只能是常量
    int MAX_CACHE_LINE=50;//等价于public static final int MAX_SIZE=50;

    //普通方法
    //接口里定义普通方法只能是public抽象方法
    void out();
    //等价于public abstract void out();
    void getData(String msg);

    //默认方法  需要使用default修饰
    default void print(String...msgs)
    {
        for(var msg:msgs)
        {
            System.out.println(msg);
        }
    }
    default void test()
    {
        System.out.println("默认的test()方法");
    }

    //类方法   需要使用static修饰
    static String staticTest()
    {
        return "接口中的类方法";
    }

    //定义私有方法   需要private修饰
    private void foo()
    {
        System.out.println("foo私有方法");
    }

    //私有静态方法
    private static void bar()
    {
        System.out.println("bar私有静态方法");
    }
}

该Output接口里包含一个成员变量MAX_CACHE_LINE。还定义了两个普通方法:表示获取数据的getData()方法和表示输出的out()方法。这就定了了Output接口的规范:某个类只能获取数据,并可以将数据输出,它就是一个输出设备,至于设备的实现细节,暂不关心。
接口里的成员默认使用public static final修饰,因此即使在不同包下,也可以通过接口来访问接口里的成员变量。

package yeeku;
public class  OutputFieldTest
{
    public static void main(String[] args) 
    {
        System.out.println(lee.Output.MAX_CACHE_LINE);
        //下面语句将出错,因为是final修饰
        //lee.Output.MAX_CACHE_LINE=20;
        //使用接口调用方法
        System.out.println(lee.Output.staticTest());
    }
}

三、接口的继承

接口完全支持多继承,即一个接口可以有多个直接父接口。和类继承相似,子接口扩展某个父接口,将会获得接口里定义的所有抽象方法、常量。
一个接口继承多个父接口时,多个父接口在extends关键字之后,多个父接口之间以英文逗号(,)隔开,下面定义了三个接口,第三个接口继承了前面两个接口

interface InterfaceA
{
    int PROP_A=5;//默认使用public static final修饰
    void testA();//默认使用public abstract修饰
} 

interface InterfaceB
{
    int PROP_B=6;//默认使用public static final修饰
    void testB();//默认使用public abstract修饰
}

interface InterfaceC extends InterfaceA,InterfaceB
{
    int PROP_C=7;
    void testC();
} 

public class InterfaceExtendsTest
{
    public static void main(String[] args)
    {
        System.out.println(InterfaceC.PROP_A);//输出5
        System.out.println(InterfaceC.PROP_B);//输出6
        System.out.println(InterfaceC.PROP_C);//输出7
    }
}

上面程序中,接口InterfaceC接口继承了InterfaceA和InterfaceB,所以InterfaceC中获得了它们的常量。

四、使用接口

接口不能创建实例,当接口可用于声明引用类型变量。当使用接口来声明引用类型的变量时,这个引用类型变量必须引用到其实现类的对象。
接口的主要用途:
1、定义变量,也可以进行强制类型转换。
2、调用接口中定义的常量
2、被其他类实现
一个类可以实现一个或多个接口,继承使用extends关键字,实现则使用implements关键字。因为一个类可以实现多接口,这也是Java为单继承灵活性不足所做的补充。类实现的语法格式:

[修饰符] class 类名 extends 父类 implements 接口1,接口2...
{
    //类体部分
}

  实现接口和继承父类相似,一样可以获得接口里定义的常量、方法(包括抽象方法、默认方法)。
  一个类实现某个接口时,这个类必须实现接口中所定义的全部抽象方法;否则,该类将保留从父类那里继承的抽象方法,该类也就必须定义为抽象类。

import lee.Output;
//定义一个Product接口
interface Product
{
    int getProduceTime();
}

//让printer类实现Output和Product接口
public class Printer implements Output,Product
{
    private String[] printData=new String[MAX_CACHE_LINE];
    //用于记录当前需打印的作业数
    private int dataNum=0;
    @ Override
    public void out()
    {
        //只要还有作业,就继续打印
        while(dataNum>0)
        {
            System.out.println("打印机打印:"+printData[0]);
            //把作业队列整体前移一位,并将身下的作业数减1
            System.arraycopy(printData,1,printData,0,--dataNum);
        }
    }
    @Override 
    public void getData(String msg)
    {
        if (dataNum>MAX_CACHE_LINE)
        {
            System.out.println("输出队列已满,添加失败");
        }
        else
        {
            //把打印的数据添加到队列里,已保存的数据数量+1
            printData[dataNum++]=msg;
        }
    }
    @Override
    public int getProduceTime()
    {
        return 45;
    }
    public static void main(String[] args)//重写方法访问权限需要更大
    {
        //创建一个printer对象,当成Output使用
        Output o=new Printer();//向上转换
        o.getData("轻量级Java EE");
        o.getData("疯狂Java讲义");
        o.out();

        //调用Output接口中的默认方法
        o.print("hello","world","firend","!");
        o.test();

        //创建一个Printer对象,当成product使用
        Product p=new Printer();
        System.out.println(p.getProduceTime());

    }

}

从上面的程序可以看出,Printer类实现了Output接口和Product接口,因此Printer对象即可以直接赋给Output变量,也可以直接赋给Product变量。这就是Java提供的模拟多继承。
注:实现接口方法时,需要重写父接口中所有的父类接口中的所有抽象方法,必须使用public修饰符,因为默认接口中的抽象方法使用public abstract修饰。重写方法满足两同两小一大:方法名相同,形参类列表相同,返回值类型相同或更小,抛出的异常类型相同或更小,一大访问权限相同或更大。

五、接口和抽象类

接口和抽象类很像,具有如下特征:
1、接口和抽象了都不可以被实例化,它们位于继承树的顶端,用于被其他实现和继承
2、抽象类和接口都可以包含抽象方法,继承抽象类的普通子类和实现接口都必须实现这些抽象方法。
二者的设计目的不同:
1、接口作为系统与外界交互的窗口,接口体现的是一种规范。对于接口实现着而言,接口规定了实现者必须向外提供哪些服务(以方法的形式实现);对于接口的调用者而言,接口规定了调用者可以调用哪些服务,以及如何调用这些服务。当在一个程序中使用接口时,接口是多个模块之间耦合标准;当在多个程序中使用接口时,接口是多个程序之间通信标准。
2、抽象类作为系统中多个子类的共同父类,它所体现的是一种模板式设计。抽象类作为多个子类的抽象父类,可以当作系统实现的中间产品,这个产品已经实现了系统部分功能,但这个产品依然不能当成最终产品,必须有进一步完善。
接口和抽象类的用法上区别:
★接口里只能包含抽象方法、静态方法、默认方法、私有方法,不能为普通方法提供方法实现;抽象类可以包含普通方法。
★接口里只能定义静态常量,不能定义普通成员;抽象类里既可以包含实例成员变量,也可以包含静态常量。
★接口里不包含构造器;抽象类可以包含构造器,但抽象类里的构造器并不是用于创建对象,而是让子类调用这些构造器来完成属于抽象类的初始化操作。
★接口里不能包含初始化块;但抽象类完全可以。
★一个类只能有一个直接父类,包括抽象类;但一个类可以实现对多个接口,通过接口弥补Java单继承的不足。

5.6 接口

原文:https://www.cnblogs.com/weststar/p/12416940.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!