• [转]Java接口和Java抽象类


     

    Java接口和Java抽象类

        在没有好好地研习面向对象设计的设计模式之前,我对Java接口和Java抽象类的认识还是很模糊,很不可理解。

            刚学Java语言时,就很难理解为什么要有接口这个概念,虽说是可以实现所谓的多继承,可一个只有方法名,没有方法体的东西,我实现它又有什么用呢?我从它那什么也得不到,除了一些方法名,我直接在具体类里加入这些方法不就行了吗?

           为什么一定要有抽象类这个概念?为什么就不能把这个父类写成一个具体的类,子类再继承它不就可以了吗?何必弄一个抽象类出来,还要弄一些没有方法体的抽象方法,弄得又象接口又象类的,让人捉摸不定。

    我开始学习java设计模式,真正走进面向对象设计的大门之后,我才发现,自己对面向对象设计的理解原来是那么的片面,那么的肤浅,根本就没有真正理解面 向对象思想的精髓,在某一种程度上还受着面向过程的影响,以为弄出了一个个类,就算是面向对象了,而其实还是被过程所驱使着。

    我还是说说我现在对面向对象思想的理解吧,不一定正确全面,但我想应该还算是比以前略有进步吧。

             面向对象思想,我觉得最关键的就是抽象。

             个软件设计的好坏,我想很大程度上取决于它的整体架构,而这个整体架构其实就是你对整个宏观商业业务的抽象框架,当代表业务逻辑的高层抽象层结构合理时, 你底层的具体实现需要考虑的就仅仅是一些算法和一些具体的业务实现了。当你需要再开发另一个相近的项目时,你以前的抽象层说不定还可以再次利用呢,面对对 象的设计,复用的重点其实应该是抽象层的复用,而不是具体某一个代码块的复用,是不是一下子感觉自己对复用理解的高度又上升了一层?^_^

                说到了抽象,我就不能不提到曾让我头痛的Java接口和Java抽象类了,这也是本文我想说的重点。

              既然面向对象设计的重点在于抽象,那Java接口和Java抽象类就有它存在的必然性了。

              Java 接口和Java抽象类代表的就是抽象类型,就是我们需要提出的抽象层的具体表现。OOP面向对象的编程,如果要提高程序的复用率,增加程序的可维护性,可 扩展性,就必须是面向接口的编程,面向抽象的编程,正确地使用接口、抽象类这些太有用的抽象类型做为你结构层次上的顶层。

    Java接口和Java抽象类有太多相似的地方,又有太多特别的地方,究竟在什么地方,才是它们的最佳位置呢?把它们比较一下,你就可以发现了。

    1、  Java接口和Java抽象类最大的一个区别,就在于Java抽象类可以提供某些方法的部分实现(即抽象类中有抽象方法,也有非抽象的方法),而

    Java接口不可以,这大概就是Java抽象类唯一的优点吧,但这个优点非常有用。

                 如果向一个抽象类里加入一个新的具体方法时,那么它所有的子类都一下子都得到了这个新方法,而Java接口做不到这一点,如果向一个Java接口里加入一个 新方法,所有实现这个接口的类就无法成功通过编译了,因为你必须让每一个类都再实现这个方法才行,这显然是Java接口的缺点。

    2、一个抽象类的实现只能由这个抽象类的子类给出,也就是说,这个实现处在抽象类所定义出的继承的等级结构中,而由于Java语言的单继承性,所以抽象类作为类型定义工具的效能大打折扣。

                在这一点上,Java接口的优势就出来了,任何一个实现了一个Java接口所规定的方法的类都可以具有这个接口的类型,而一个类可以实现任意多个Java接口,从而这个类就有了多种类型

    3、从第2点不难看出,Java接口是定义混合类型的理想工具,混合类表明一个类不仅仅具有某个主类型的行为,而且具有其他的次要行为。

    4 结合12点中抽象类和Java接口的各自优势,具精典的设计模式就出来了:声明类型的工作仍然由Java接口承担,但是同时给出一个Java抽象类,且 实现了这个接口,而其他同属于这个抽象类型的具体类可以选择实现这个Java接口,也可以选择继承这个抽象类,也就是说在层次结构中,Java接口在最上 面,然后紧跟着抽象类,哈,这下两个的最大优点都能发挥到极至了。这个模式就是缺省适配模式

    Java语言API中用了这种模式,而且全都遵循一定的命名规范:Abstract +接口名。

    Java接口和Java抽象类的存在就是为了用于具体类的实现和继承的,如果你准备写一个具体类去继承另一个具体类的话,那你的设计就有很大问题了。Java抽象类就是为了继承而存在的,它的抽象方法就是为了强制子类必须去实现的。(继承抽象类,其子类必须复写抽象类的所有抽象方法)

    使用Java接口和抽象Java类进行变量的类型声明、参数是类型声明、方法的返还类型说明,以及数据类型的转换等。而不要用具体Java类进行变量的类型声明、参数是类型声明、方法的返还类型说明,以及数据类型的转换等。

    我想,如果你编的代码里面连一个接口和抽象类都没有的话,也许我可以说你根本没有用到任何设计模式,任何一个设计模式都是和抽象分不开的,而抽象与Java接口和抽象Java类又是分不开的。

    理解抽象,理解Java接口和抽象Java类,我想就应该是真正开始用面向对象的思想去分析问题,解决问题了吧。

     

    首先讲清楚类和对象的区别。

     

    类是广泛的概念,表示一个有共同性质的群体,而对象指的是具体的一个实实在在的东西。例如,是一个类,它可以表示地球上所有的人;而张三李四爱因斯坦等则是一个个的对象,或者说它们是这个类的一个个实例。在 Java 中,我们可以定义类,然后创建类的对象。

     

     

     

    例如:

    // 声明一个类“Human”

    class Human{

        private String name;

        public String getName(){

          return name;

        }

        public void setName(String value){

          this.name = value;

        }

        //......

    }

     

     

    创建一个类:

    Human human = new Human();

     

     

    其次,很多人对对象和对象的引用认识模糊

    引用是程序操作对象的句柄,相当于CC++中的指针。

    前面说了,对象是一个实实在在的东西,比如前面的代码:

    Human human = new Human();  

    程序执行到这里之后,java虚拟机将会在内存中创建一个 Human 对象,并将这个对象的引用赋给 human 变量。这里有两步,首先是创建 Human 对象然后把创建的对象的引用赋给 human 变量。

    如果声明了一个对象的引用,但没有将对象赋值给它,则这个引用指向了空的对象,或者说引用了不存在的对象。这时如果想通过这个引用访问对象,则会抛出空指针异常,例如:

    Human human;

    //......

    human.setName("张三");

     

    下面重点谈一谈类、抽象类、接口和继承之间的关系

    不少细心的初学者在论坛上问类似这样的问题:

    1、接口不实现方法,但我却在程序中可以调用接口的方法,这是为什么?比如 java.sql 包中的 ConnectionStatementResultSet 等都是接口,怎么可以调用 它们的方法呢?

     

    2、抽象类不能实例化,但是jdk中却有很多抽象类的对象,这是为什么?比如 System.in 是一个 InputStream 类型对象,但 InputStream 是抽象类,怎么可以得到它的对象呢?

     

    不管怎么样,大家应该明白一点:不管是抽象类中的抽象方法,还是接口中定义的方法,都是需要被调用的,否则这些方法定义出来就没有意义了。

     

    可能有很多书上没有提到,或者提到了而读者没有注意到这一点:

    一个子类如果继承了它的基类,则表示这个类也是其基类的一种类型,这个子类的一个对象是子类类型,并且同时也是其基类的一个对象,它也具有基其类的类型;一个类如果实现了一个接口,则表示这个类的一个对象也是这个接口的一个对象。

     

    可能这样说不太好懂,又是子类、基类、类型、接口什么的,容易搞混。其实举个现实的例子你就会觉得其实很简单:

    如果是一个基类,则男人的一个子类。如果张三是一个男人,也就是说张三男人的一个对象,那么显然张三也是这个基类的一个对象。

     

    明白了这一点,就容易理解为什么我们可以得到抽象类的对象了:原来我们得到的抽象类的对象其实是它的已经实现了抽象方法的子类或子孙类的一个对象,但我们拿它当它的抽象类的基类来用。比如这个类,每个人都会悲伤,男人悲伤的时候抽烟、喝酒,女人悲伤的时候哭泣、流泪。由于不同的子类在悲伤时所进行的动作不一样,因此这个动作(方法)在基类中不好实现,但基类中又需要有这个方法,因此,这个类就可以定义一个抽象方法悲伤,由其子类男人女人来实现悲伤这个方法。但是调用者只把男人和女人的对象当作其基类的一个对象,调用它的悲伤方法。

    读者可以去体验一下 jdk 的抽象类 java.lang.Process

    Runtime runtime = Rumtime.getRuntime();

    Process process = rumtime.exec("notepad.exe");

    Class cls = process.getClass();

    System.out.println(cls.getName());

    这时会打印出 process 类的名字,如果在 Windows 下它会是一个类似于 *Win32* 的名字,它是 Process 的一个子类。因为 process 类用于管理打开的进程,而在不同的操作系统上都有不同的实现,因此它把方法定义为 Process 的抽象方法,而具体的操作只能由对应在不同操作系统下的子实现。

     

    下面来谈接口,我们知道接口只定义了一些方法,而没有实现这些方法。而其实,接口是一个规范,它规定了实现这个接口所要做的事情,或者说规定了实现接口的类必须具备的能力(也就是方法)

    那么我们可以这样对比:

    某种类型的驾驶执照,规定了拿到这个驾照的人必须能够开小汽车开公共汽车。那么我们认为这个驾照是一个接口,它规定了实现它的类所必须有的能力。

    我们可以定义一个类 Driver,继承自 Human,然后实现驾照持有者这个接口:

    public interface DriverHolder{

        public void driverCar();

        public void driverBus();

    }

    public class Driver extends Human implements DriverHolder{

        public void driverCar(){

          // ......

        }

        public void driverBus(){

          // ......

        }

    }

     

     

     

    这样一来,一个“Driver”对象,它同时也是一个 DrivreHolder 对象。即一个司机(Driver)同时是一个驾照执持有者对象。在程序中我们可以这样:

    DriverHolder driverholder = new Driver();

    driverholder.driverCar();

     

    这样我们就解释了为什么接口没有实现方法,却可以得到接口类的对象的问题。

     

    但是这样一来,肯定有人会问:为什么要定义一个接口呢,为什么不直接把这个方法定义到 Driver 类中去,然后 Driver driver = new Driver(); 一样可以调用它的 driverCar(); driverBus() 方法,这样做岂不是方便得多?

     

    这是因为java是单继承的,它只能继承于一个类,这样它的类型就只限于其基类或者基类的基类。但是java可以实现多个接口,这样它就可以有很多个接口的类型。就象一个人,它继承自脊椎动物这个类,而脊椎动物又继承自动物这个类,因此张三是个人,他是一个脊椎动物,当然他也是一个动物。但他可以继承很多个接口,比如拿驾驶执照之后,他就是驾照持有者类型,他也可以拿英语六级证书,这样他就是一个六级证书持有者等等。

     

    明白这一点之后,我们来看一看 java 的事件机制。

    java.awt.Button 类有一个 addActionListener(ActionListener l);方法。这个方法传入的是一个接口类型:ActionListerner,在实际中,我们需要实现 ActionListener 接口,并且把实现这个接口的类的对象引用作为参数传入。这样,Button对象就得到了一个 ActionListener 对象,它知道这个 ActionListener 对象有一个 actionPerformed 方法,或者说它有处理 Action 事件的能力,当 Action 事件发生时,它就可以调用这个对象的 actionPerformed 方法。

     

    比如一般我们会这样做:

    public class TestButton extends Frame implements ActionListener{

        private Button btn1 = new Button();

        //......

     

        public TestButton(){

          btn.addActionListener(this);

    this.add(btn);

        }

     

        public void actionPerformed(ActionEvent e){

     

        }

    }

     

     

    现在我们假设 ActionListener 不是接口,而是一个类。那么我们只能继承 ActionListener 类,并且重写 actionPerformed 方法。但是java是单继承的,如果类继承了 ActionListener 类,那么它就不能继承其它的类(Frame )了,而不从 Frame 类继承的话,又怎么创建窗体,怎么把 Button 放到窗体中去呢?

     

    其实接口不完全是为了解决 java 的单继承问题,它在某种程度上可以达到调用和实现细节的分离。

    比如说,中国的民用电规范是一个接口:平均电压220V50HzSin 交流电,水力发电厂、火力发电厂、核电厂,还有小型的柴油发电机如果按这个规范发电,则表示它们实现了这个民用电源的接口;冰箱、电视、洗衣机等家用电器使用这些电源,表示它们在调用这个接口。在这里,家用电器不管电从哪里来,只要它符合民用电源的规范就好,电源也不管它发的电用于什么工作,只管提供电源。

     

    再回过头来看看 Button 的事件机制。要知道,Button 要保证所有的 action 事件发生时,程序员都可以在他的代码中处理它,图书馆管理系统、收银系统、进销存等等等等等等。而接口就可以做到这一点:找一个类实现 ActionListener 接口,并且让 Button 得到这个类的对象的引用( 调用 addActionListener 方法),从而当Action事件发生时,button 创建一个包含了事件信息的对象(ActionEvent),然后调用这个接口对象的方法,到底怎么处理这次事件,这就是实现接口的类的事情了。在这里,Button 丝毫不了解 actionPerformed 方法中到底干了什么事情,也不应该知道。Button 与具体的业务逻辑完全分离开了,它可以应用到所有的场合。

     

  • 相关阅读:
    .NET2.0基础类库中的范型——FunctionalProgramming
    原则
    080711 30℃
    关于mcp
    080714 33℃
    080715 31℃
    080716 30℃
    11号
    iOS-调用系统的短信和发送邮件功能,实现短信分享邮件分享
    UIView总结
  • 原文地址:https://www.cnblogs.com/redcoatjk/p/3562404.html
Copyright © 2020-2023  润新知