Servlet学习笔记(五)—— java中abstract class 与 interface 的关联

一、理解抽象类

      本文的抽象类并不是从abstract class翻译来的,它表示的是一个抽象体,而abstract class 是Java语言中用于定义抽象体的一种方法。

      在面向对象的概念中,所有的对象都是通过类来描述的,但反过来不成立:不是所有的类都是用来描绘对象的。如果一个类中没有包括足够的信息来描绘一个具体的对象,那么这样的类就是抽象类。抽象类用来表征我们在对问题领域进行分析、设计中得出的抽象概念,是一系列看上去不同、但本质上相同的具体概念的抽象。

       比如:我们进行一个图形编辑软件的开发,就会发现问题领域存在着圆、三角形 这样一些具体概念,它们是不同的,但是它们又都属于形状这样一个概念,形状这个概念在问题领域是不存在的,它就是一个抽象概念。正是因为抽象的概念在问题 领域没有对应的具体概念,所以用以表征抽象概念的抽象类是不能够实例化的。   

       在面向对象领域,抽象类主要用来进行类型隐藏。我们可以构造出一个固定的一组行 为的抽象描述,但是这组行为却能够有任意个可能的具体实现方式。这个抽象描述就是抽象类,而这一组任意个可能的具体实现则表现为所有可能的派生类。

二、抽象类与接口的区别

1.相同点:

      ①抽象类与接口都包含未实现的方法;

      ②抽象类与接口都不能实例化;

      ③派生类都必须实现未实现的方法。

2.不同点:

      首先只有方法签名没有实现的函数叫做抽象方法

      ①抽象类中出了包含抽象方法,还可以有实现的方法;接口中只有抽象方法。

      ②抽象类中可以有自己的数据成员,可以是非公共属性的;接口中只能有常量,static final,只读不能更改,具有公共属性。

      ③抽象类在Java语言上表示的是一种继承关系,可以从另一个类或者一个/多个接口派生;接口不能从另外一个类派生但可以实现其他一个/多个接口。

      抽象类:

abstract class A{
        private int x;
        public abstract void doSomething();
	public void test(){
		System.out.println("test");
	}
}

class B extends A{
	public void doSomething(){
		System.out.println("dosomething");//抽象方法的实现
	}
}
       接口:

interface A{
        public static final int x=1;
        public abstract void doSomething();
	public abstract void test();
}

class B implements A{

	@Override
	public void doSomething() {
		// TODO Auto-generated method stub
		System.out.println("dosomething");
	}

	@Override
	public void test() {
		// TODO Auto-generated method stub
		System.out.println("test");
	}
	
}

三、abstract class 与 interface 的选择

      很多时候我们在进行抽象类定义时对于abstract class 和interface的选择显得比较随意,上面只是从语法定义和编程的角度论述了两者的区别,是比较低层次的、非本质的。下面将从设计理念层看两者的区别,分析理解二者的概念本质所在。

      前面提到过,abstract class在Java语言中体现了一种继承关系,要让继承关系合理,父类和派生类之间必须存在is a 的关系,即父类和派生类在概念本质上相同的。对于interface来说则不然,并不要求interface的实现者和interface本身在概念本质上是一致的,仅仅是实现了interface定义的契约而已。下面通过一个例子来理解:

      假设我们的问题领域中有一个关于Door的抽象概念,该Door具有执行两个动作open和close,此时我们可以通过abstract class 或者 interface来定义一个表示该抽象概念的类型,定义方式分别如下:

//abstract class 方式定义:
abstract class Door {     
    abstract public void open();    
    abstract public void close();    
} 

//interface方式定义:
interface Door{
	 abstract public void open();
	 abstract public void close();
}

     其他具体的Door类型可以extends使用abstract class方式定义或者implements使用interface方式定义。看起来好像没什么区别。

      如果现在要求Door还要具有报警的功能,我们该如何设计针对该例子的类结构呢?下面将罗列出可能的解决方案,并从设计理念层面对这些不同的方案进行分析。

      【解决方案一】:

//简单地在Door定义中增加一个alarm的方法
abstract class Door{
	abstract void open();
	abstract void close();
	abstract void alarm();
}
      或者

//简单地在Door定义中增加一个alarm的方法:
interface Door{
	 abstract public void open();
	 abstract public void close();
	 abstract public void alarm();
}

      那么具有报警功能的AlarmDoor的定义方式如下:

<pre name="code" class="java">class AlarmDoor extents Door{
	void open(){
		//...
	}
	void close(){
		//...
	}
	void alarm(){
		//...
	}
}

 
      或者 

class AlarmDoor implements Door{
	void open(){
		//...
	}
	void close(){
		//...
	}
	void alarm(){
		//...
	}
}
     

      这种方法违反了面向对象设计中的一个核心原则ISP(Interface Segregation Priciple),在Door的定义中把Door概念本身固有的行为方法和另外一个概念"报警器"的行为方法混在了一起。这样引起的一个问题是那些仅仅依赖于Door这个概念的模块会因为"报警器"这个概念的改变(比如:修改alarm方法的参数)而改变,反之依然。
      
      【解决方案二】:

      既然open、close和alarm属于两个不同的概念,根据ISP原则应该把它们分别定义在代表这两个概念的抽象类中。定义方式有:这两个概念都使用abstract class方式定义;两个概念都使用interface方式定义;一个概念使用abstract class方式定义,另一个概念使用interface方式定义。
      显然,由于Java语言不支持多重继承,所以两个概念都使用abstract class方式定义是不可行的。后面两种方式都是可行的,但是对于它们的选择却反映出对于问题领域中的概念本质的理解、对于设计意图的反映是否正确、合理。我们一一来分析、说明。

      如果两个概念都使用interface方式来定义,那么就反映出两个问题:1、我们可能没有理解清楚问题领域,AlarmDoor在概念本质上到底是Door还是报警器?2、如果我们对于问题领域的理解没有问题,比如:我们通过对于问题领域的分析发现 AlarmDoor在概念本质上和Door是一致的,那么我们在实现时就没有能够正确的揭示我们的设计意图,因为在这两个概念的定义上(均使用 interface方式定义)反映不出上述含义。

      如果我们对于问题领域的理解是:AlarmDoor在概念本质上是Door,同 时它有具有报警的功能。我们该如何来设计、实现来明确的反映出我们的意思呢?前面已经说过,abstract class在Java语言中表示一种继承关系,而继承关系在本质上是"is a"关系。所以对于Door这个概念,我们应该使用abstarct class方式来定义。另外,AlarmDoor又具有报警功能,说明它又能够完成报警概念中定义的行为,所以报警概念可以通过interface方式定 义。如下所示:

abstract class Door{
	abstract void open();
	abstract void close();
	abstract void alarm();
}

interface Alarm{
	void alarm();
}



class AlarmDoor extends Door implements Alarm {    
	void open(){
		//...
	}    
	void close(){
		//...
	}
	void alarm(){
		//...
	}
}
      这种实现方式基本上能够明确的反映出我们对于问题领域的理解,正确的揭示我们的设计 意图。其实abstract class表示的是"is a"关系,interface表示的是"like a"关系,大家在选择时可以作为一个依据,当然这是建立在对问题领域的理解上的,比如:如果我们认为AlarmDoor在概念本质上是报警器,同时又具有 Door的功能,那么上述的定义方式就要反过来了。
     【 总结 】:

      1、抽象类和接口都不能直接实例化,如果要实例化,抽象类变量必须指向实现所有抽象方法的子类对象,接口变量必须指向实现所有接口方法的类对象。

      2、抽象类要被子类继承,接口要被类实现。

      3、接口只能做方法申明,抽象类中可以做方法申明,也可以做方法实现。

      4、接口里定义的变量只能是公共的静态的常量,抽象类中的变量是普通变量。

      5、抽象类里的抽象方法必须全部被子类所实现,如果子类不能全部实现父类抽象方法,那么该子类只能是抽象类。同样,一个实现接口的时候,如不能全部实现接口方法,那么该类也只能为抽象类。

      6、抽象方法只能申明,不能实现。abstract void abc();不能写成abstract void abc(){}。

      7、抽象类里可以没有抽象方法

      8、如果一个类里有抽象方法,那么这个类只能是抽象类

      9、抽象方法要被实现,所以不能是静态的,也不能是私有的。

      10、接口可继承接口,并可多继承接口,但类只能单根继承。

四、为何要引入接口

         看到论坛里有一个很有意思的回答:

为什么电脑主板上还要有内存插槽,显卡插槽?多浪费机箱空间啊?
直接用电烙铁把显卡和内存的引脚一根一根焊到主板上不就得了。
如果你焊错位置了,说明你焊工不好。
每次拆装的的时候再继续用电烙铁。 

         接口编程的直接好处就是:实现多态,同一个接口,却可以调用不同的底层实现。

五、接口的用法

六、为什么接口中不允许定义变量?


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值