zoukankan      html  css  js  c++  java
  • java 接口详解

    定义接口
    接口继承和实现继承的规则不同,一个类只有一个直接父类,但可以实现多个接口。Java 接口本身没有任何实现,只描述 public 行为,因此 Java 接口比 Java 抽象类更抽象化。Java 接口的方法只能是抽象的和公开的,Java 接口不能有构造方法,Java 接口可以有 public、Static 和 final 属性。

    接口把方法的特征和方法的实现分隔开来,这种分隔体现在接口常常代表一个角色,它包装与该角色相关的操作和属性,而实现这个接口的类便是扮演这个角色的演员。一个角色由不同的演员来演,而不同的演员之间除了扮演一个共同的角色之外,并不要求其他的共同之处。

    接口对于其声明、变量和方法都做了许多限制,这些限制作为接口的特征归纳如下:
    具有 public 访问控制符的接口,允许任何类使用;没有指定 public 的接口,其访问将局限于所属的包。
    方法的声明不需要其他修饰符,在接口中声明的方法,将隐式地声明为公有的(public)和抽象的(abstract)。
    在 Java 接口中声明的变量其实都是常量,接口中的变量声明,将隐式地声明为 public、static 和 final,即常量,所以接口中定义的变量必须初始化。
    接口没有构造方法,不能被实例化。例如:

    public interface A
    {
        publicA(){…}    //编译出错,接口不允许定义构造方法
    }
    一个接口不能够实现另一个接口,但它可以继承多个其他接口。子接口可以对父接口的方法和常量进行重写。例如:
    public interface Student!nterface extends PeopleInterface
    {
        //接口 StudentInterface 继承 PeopleInterface
        int age=25;    //常量age重写父接口中的age常量
        void getInfo();    //方法getInfo()重写父接口中的getInfo()方法
    }
    
    Java 接口的定义方式与类基本相同,不过接口定义使用的关键字是 interface,接口定义由接口声明和接口体两部分组成。语法格式如下:
    [public] interface interface_name [extends interface1_name[, interface2_name,…]]
    {
        //接口体,其中可以包含定义常量和声明方法
        [public] [static] [final] type constant_name=value;    //定义常量
        [public] [abstract] returnType method_name(parameter_list);    //声明方法
    }
    
    

    其中,public 表示接口的修饰符,当没有修饰符时,则使用默认的修饰符,此时该接口的访问权限仅局限于所属的包;interfaCe_name 表示接口的名称,可以是任何有效的标识符;extends 表示接口的继承关系;interface1_name 表示要继承的接口名称;constant_name 表示变量名称,一般是 static 和 final 型的;returnType 表示方法的返回值类型;parameter_list 表示参数列表,在接口中的方法是没有方法体的。

    提示:如果接口本身被定义为 public,则所有的方法和常量都是 public 型的。

    例如,定义一个接口 MyInterface,并在该接口中声明常量和方法,如下:

    public interface Mylnterface
    {    //接口myInterface
        String name;    //不合法,变量name必须初始化
        int age=20;    //合法,等同于 public static final int age=20;
        void getInfo();    //方法声明,等同于 public abstract void getInfo();
    }
    

    实现接口
    接口被定义后,一个或者多个类都可以实现该接口,这需要在实现接口的类的定义中包含 implements 子句,然后实现由接口定义的方法。实现接口的一般形式如下:

    <public> class <class_name> [extends superclass_name] [implements interface[, interface…]]
    {
        //主体
    }
    

    如果一个类实现多个接口,这些接口需要使用逗号分隔。如果一个类实现两个声明了同样方法的接口,那么相同的方法将被其中任一个接口使用。实现接口的方法必须声明为 public,而且实现方法的类型必须严格与接口定义中指定的类型相匹配。
    例 1
    在程序的开发中,需要完成两个数的求和运算和比较运算功能的类非常多。那么可以定义一个接口来将类似功能组织在一起。下面创建一个示例,具体介绍接口的实现方式。

    (1) 创建一个名称为 IMath 的接口,代码如下:

    public interface IMath
    {
        public int sum();    //完成两个数的相加
        public int maxNum(int a,int b);    //获取较大的数
    }
    
    (2) 定义一个 MathClass 类并实现 IMath 接口,MathClass 类实现代码如下:
    public class MathClass implements IMath
    {
        private int num1;    //第 1 个操作数
        private int num2;    //第 2 个操作数
        public MathClass(int num1,int num2)
        {
            //构造方法
            this.num1=num1;
            this.num2=num2;
        }
        //实现接口中的求和方法
        public int sum()
        {
            return num1+num2;
        }
        //实现接口中的获取较大数的方法
        public int maxNum(int a,int b)
        {
            if(a>=b)
            {
                return a;
            }
            else
            {
                return b;
            }
        }
    }
    

    在实现类中,所有的方法都使用了 public 访问修饰符声明。无论何时实现一个由接口定义的方法,它都必须实现为 public,因为接口中的所有成员都显式声明为 public。

    (3) 最后创建测试类 NumTest,实例化接口的实现类 MathClass,调用该类中的方法并输出结果。该类内容如下:

    public class NumTest
    {
        public static void main(String[] args)
        {
            //创建实现类的对象
            MathClass calc=new MathClass(100, 300);
            System.out.println("100 和 300 相加结果是:"+calc.sum());
            System.out.println("100 比较 300,哪个大:"+calc.maxNum(100, 300));
        }
    }
    
    程序运行结果如下所示。
    100 和 300 相加结果是:400
    100 比较 300,哪个大:300
    

    在该程序中,首先定义了一个 IMath 的接口,在该接口中只声明了两个未实现的方法,这两个方法需要在接口的实现类中实现。
    在实现类 MathClass 中定义了两个私有的属性,并赋予两个属性初始值,同时创建了该类的构造方法。
    因为该类实现了 MathClass 接口,因此必须实现接口中的方法。在最后的测试类中,需要创建实现类对象,然后通过实现类对象调用实现类中的方法。

    从前面对面向对象的设计原则的讲解,读者可以了解到,其实所有的设计原则和设计模式都离不开抽象,因为只有抽象才能实现上述设计原则和设计模式。

    在 Java 中,针对抽象有两种实现方式:一种是接口,一种是抽象类。很多读者对这两种实现方式比较困惑,到底是使用接口,还是使用抽象类呢?对于它们的选择甚至反映出对问题领域本质的理解,对设计意图的理解是否正确、合理?

    在面向对象的设计思想中,所有的对象都是通过类来描绘的,但是反过来,并不是所有的类都是用来描绘对象的,如果一个类中没有描绘一个具体的对象,那么这样的类就是抽象类,抽象类是对那些看上去不同,但是本质上相同的具体概念的抽象,正是因为抽象的概念在问题领域没有对应的具体概念,所以抽象类是不能够实例化的。
    基本语法区别
    在 Java 中,接口和抽象类的定义语法是不一样的。这里以动物类为例来说明,其中定义接口的示意代码如下:

    public interface Animal
    {
        //所有动物都会吃
        public void eat();
        //所有动物都会飞
        public void fly();
    }
    
    定义抽象类的示意代码如下:
    public abstract class Animal
    {
        //所有动物都会吃
        public abstract void eat();
        //所有动物都会飞
        public void fly(){};
    }
    

    可以看到,在接口内只能是功能的定义,而抽象类中则可以包括功能的定义和功能的实现。在接口中,所有的属性肯定是 public、static 和 final,所有的方法都是 abstract,所以可以默认不写上述标识符;在抽象类中,既可以包含抽象的定义,也可以包含具体的实现方法。

    在具体的实现类上,接口和抽象类的实 现类定义方式也是不一样的,其中接口实现类的示意代码如下:

    public class concreteAnimal implements Animal
    {
        //所有动物都会吃
        public void eat(){}
        //所有动物都会飞
        public void fly(){}
    }
    
    抽象类的实现类示意代码如下:
    public class concreteAnimal extends Animal
    {
        //所有动物都会吃
        public void eat(){}
        //所有动物都会飞
        public void fly(){}
    }
    
    

    可以看到,在接口的实现类中使用 implements 关键字;而在抽象类的实现类中,则使用 extends 关键字。一个接口的实现类可以实现多个接口,而一个抽象类的实现类则只能实现一个抽象类。
    设计思想区别
    从前面抽象类的具体实现类的实现方式可以看出,其实在 Java 中,抽象类和具体实现类之间是一种继承关系,也就是说如果釆用抽象类的方式,则父类和子类在概念上应该是相同的。接口却不一样,如果采用接口的方式,则父类和子类在概念上不要求相同。接口只是抽取相互之间没有关系的类的共同特征,而不用关注类之间的关系,它可以使没有层次关系的类具有相同的行为。因此,可以这样说:抽象类是对一组具有相同属性和方法的逻辑上有关系的事物的一种抽象,而接口则是对一组具有相同属性和方法的逻辑上不相关的事物的一种抽象。

    仍然以前面动物类的设计为例来说明接口和抽象类关于设计思想的区别,该动物类默认所有的动物都具有吃的功能,其中定义接口的示意代码如下:

    public interface Animal
    {
        //所有动物都会吃
        public void eat();
    }
    
    定义抽象类的示意代码如下:
    public abstract class Animal
    {
        //所有动物都会吃
        public abstract void eat();
    }
    
    

    不管是实现接口,还是继承抽象类的具体动物,都具有吃的功能,具体的动物类的示意代码如下。

    接口实现类的示意代码如下:
    public class concreteAnimal implements Animal
    {
        //所有动物都会吃
        public void eat(){}
    }
    
    抽象类的实现类示意代码如下:
    public class concreteAnimal extends Animal
    {
        //所有动物都会吃
        public void eat(){}
    }
    
    

    当然,具体的动物类不光具有吃的功能,比如有些动物还会飞,而有些动物却会游泳,那么该如何设计这个抽象的动物类呢?可以别在接口和抽象类中增加飞的功能,其中定义接口的示意代码如下:

    public interface Animal
    {
        //所有动物都会吃
        public void eat();
        //所有动物都会飞
        public void fly();
    }
    
    定义抽象类的示意代码如下:
    public abstract class Animal
    {
        //所有动物都会吃
        public abstract void eat();
    
        //所有动物都会飞
        public void fly(){};
    }
    
    

    这样一来,不管是接口还是抽象类的实现类,都具有飞的功能,这显然不能满足要求,因为只有一部分动物会飞,而会飞的却不一定是动物,比如飞机也会飞。那该如何设计呢?有很多种方案,比如再设计一个动物的接口类,该接口具有飞的功能,示意代码如下:

    public interface AnimaiFly
    {
        //所有动物都会飞
        public void fly();
    }
    

    那些具体的动物类,如果有飞的功能的话,除了实现吃的接口外,再实现飞的接口,示意代码如下:

    public class concreteAnimal implements Animal,AnimaiFly
    {
        //所有动物都会吃
        public void eat(){}
        //动物会飞
        public void fly();
    }
    

    那些不需要飞的功能的具体动物类只实现具体吃的功能的接口即可。另外一种解决方案是再设计一个动物的抽象类,该抽象类具有飞的功能,示意代码如下:

    public abstract class AnimaiFly
    {
        //动物会飞
        public void fly();
    }
    

    但此时没有办法实现那些既有吃的功能,又有飞的功能的具体动物类。因为在 Java 中具体的实现类只能实现一个抽象类。一个折中的解决办法是,让这个具有飞的功能的抽象类,继承具有吃的功能的抽象类,示意代码如下:

    public abstract class AnimaiFly extends Animal
    {
        //动物会飞
        public void fly();
    }
    

    此时,对那些只需要吃的功能的具体动物类来说,继承 Animal 抽象类即可。对那些既有吃的功能又有飞的功能的具体动物类来说,则需要继承 AnimalFly 抽象类。

    但此时对客户端有一个问题,那就是不能针对所有的动物类都使用 Animal 抽象类来进行编程,因为 Animal 抽象类不具有飞的功能,这不符合面向对象的设计原则,因此这种解决方案其实是行不通的。

    还有另外一种解决方案,即具有吃的功能的抽象动物类用抽象类来实现,而具有飞的功能的类用接口实现;或者具有吃的功能的抽象动物类用接口来实现,而具有飞的功能的类用抽象类实现。

    具有吃的功能的抽象动物类用抽象类来实现,示意代码如下:

    public abstract class Animal
    {
        //所有动物都会吃
        public abstract void eat();
    }
    
    具有飞的功能的类用接口实现,示意代码如下:
    public interface AnimaiFly
    {
        //动物会飞
        public void fly();
    }
    

    既具有吃的功能又具有飞的功能的具体的动物类,则继承 Animal 动物抽象类,实现 AnimalFly 接口,示意代码如下:

    public class concreteAnimal extends Animal implements AnimaiFly
    {
        //所有动物都会吃
        public void eat(){}
        //动物会飞
        public void fly();
    }
    

    或者具有吃的功能的抽象动物类用接口来实现,示意代码如下:

    public interface Animal
    {
        //所有动物都会吃
        public abstract void eat();
    }
    

    具有飞的功能的类用抽象类实现,示意代码如下:

    public abstract class AnimaiFly
    {
        //动物会飞
        public void fly(){};
    }
    

    既具有吃的功能又具有飞的功能的具体的动物类,则实现 Animal 动物类接口,继承 AnimaiFly 抽象类,示意代码如下:

    public class concreteAnimal extends AnimaiFly implements Animal
    {
        //所有动物都会吃
        public void eat(){}
        //动物会飞
        public void fly();
    }
    

    这些解决方案有什么不同呢?再回过头来看接口和抽象类的区别:抽象类是对一组具有相同属性和方法的逻辑上有关系的事物的一种抽象,而接口则是对一组具有相同属性和方法的逻辑上不相关的事物的一种抽象,因此抽象类表示的是“is a”关系,接口表示的是“like a”关系。

    假设现在要研究的系统只是动物系统,如果设计人员认为对既具有吃的功能又具有飞的功能的具体的动物类来说,它和只具有吃的功能的动物一样,都是动物,是一组逻辑上有关系的事物,因此这里应该使用抽象类来抽象具有吃的功能的动物类,即继承 Animal 动物抽象类,实现 AnimalFly 接口。

    如果设计人员认为对既具有吃的功能,又具有飞的功能的具体的动物类来说,它和只具有飞的功能的动物一样,都是动物,是一组逻辑上有关系的事物,因此这里应该使用抽象类来抽象具有飞的功能的动物类,即实现 Animal 动物类接口,继承 AnimaiFly 抽象类。

    假设现在要研究的系统不只是动物系统,如果设计人员认为不管是吃的功能,还是飞的功能和动物类没有什么关系,因为飞机也会飞,人也会吃,则这里应该实现两个接口来分别抽象吃的功能和飞的功能,即除实现吃的 Animal 接口外,再实现飞的 AnimalFly 接口。

    从上面的分析可以看出,对于接口和抽象类的选择,反映出设计人员看待问题的不同角度,即抽象类用于一组相关的事物,表示的是“is a”的关系,而接口用于一组不相关的事物,表示的是“has a”的关系。

  • 相关阅读:
    C# ref与out区别
    天气预报
    全面理解javascript的caller,callee,call,apply概念(修改版)
    SQL注入案例曝光,请大家提高警惕
    sql字段保存值[1,2,3,4,5]复杂取法,收藏sql函数
    MySQL故障诊断常用方法手册(含脚本、案例)
    2021 年 8 月国产数据库排行榜:秋日胜春朝
    【墨天轮专访第二期】巨杉数据库萧少聪:重视企业长期需求,打造中国的世界级产品
    【我和达梦的故事】 有奖征文活动开始啦,万元奖品池+现金奖励等你拿!
    2021年8月国产数据库排行榜:TiDB稳榜首,达梦返前三,Kingbase进十强,各厂商加速布局云生态
  • 原文地址:https://www.cnblogs.com/lqhhome/p/10746615.html
Copyright © 2011-2022 走看看