




已阅读5页,还剩6页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
深刻理解OOP 的抽象类和接口(仅适合初学者)面向对象的四大特征:继承 封装 抽象 多态 概念在这里就不赘述了我想把抽象类和接口还有下面的方法的重写和重载理解透彻了 再去看感念应该更好理解了吧!抽象类与接口紧密相关,它们不能实例化,并且常常部分实现或根本不实现。抽象类和接口之间的一个主要差别是:类可以实现无限个接口,但仅能从一个抽象(或任何其他类型)类继承。从抽象类派生的类仍可实现接口。可以在创建组件时使用抽象类,因为它们使您得以在某些方法中指定不变级功能,但直到需要该类的特定实现之后才实现其他方法。抽象类也制定版本,因为如果在派生类中需要附加功能,则可以将其添加到基类而不中断代码。在实现抽象类时,必须实现该类中的每一个抽象方法,而每个已实现的方法必须和抽象类中指定的方法一样,接收相同数目和类型的参数,具有同样的返回值。抽象类不能被实例化,也就是不能用new关键字去产生对象 抽象方法只需声明,而不需实现 抽象类的子类必须覆盖所有的抽象方法后才能被实例化,否则这个子类还是个抽象类.理解抽象类 abstract class和interface在Java语言中都是用来进行抽象类(本文中的抽象类并非从abstract class翻译而来,它表示的是一个抽象体,而abstract class为Java语言中用于定义抽象类的一种方法,请读者注意区分)定义的,那么什么是抽象类,使用抽象类能为我们带来什么好处呢? 在面向对象的概念中,我们知道所有的对象都是通过类来描绘的,但是反过来却不是这样。并不是所有的类都是用来描绘对象的,如果一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类。抽象类往往用来表征我们在对问题领域进行分析、设计中得出的抽象概念,是对一系列看上去不同,但是本质上相同的具体概念的抽象。比如:如果我们进行一个图形编辑软件的开发,就会发现问题领域存在着圆、三角形这样一些具体概念,它们是不同的,但是它们又都属于形状这样一个概念,形状这个概念在问题领域是不存在的,它就是一个抽象概念。正是因为抽象的概念在问题领域没有对应的具体概念,所以用以表征抽象概念的抽象类是不能够实例化的。 在面向对象领域,抽象类主要用来进行类型隐藏。我们可以构造出一个固定的一组行为的抽象描述,但是这组行为却能够有任意个可能的具体实现方式。这个抽象描述就是抽象类,而这一组任意个可能的具体实现则表现为所有可能的派生类。模块可以操作一个抽象体。由于模块依赖于一个固定的抽象体,因此它可以是不允许修改的;同时,通过从这个抽象体派生,也可扩展此模块的行为功能。熟悉OCP的读者一定知道,为了能够实现面向对象设计的一个最核心的原则OCP(Open-Closed Principle),抽象类是其中的关键所在。 从语法定义层面看abstract class和interface在语法层面,Java语言对于abstract class和interface给出了不同的定义方式,下面以定义一个名为Demo的抽象类为例来说明这种不同。 使用abstract class的方式定义Demo抽象类的方式如下:abstract class Demo abstract void method1(); abstract void method2(); 使用interface的方式定义Demo抽象类的方式如下: interface Demo void method1(); void method2(); 在abstract class方式中,Demo可以有自己的数据成员,也可以有非abstarct的成员方法,而在interface方式的实现中,Demo只能够有静态的不能被修改的数据成员(也就是必须是static final的,不过在interface中一般不定义数据成员),所有的成员方法都是abstract的。从某种意义上说,interface是一种特殊形式的abstract class。从编程的角度来看,abstract class和interface都可以用来实现design by contract的思想。但是在具体的使用上面还是有一些区别的。首先,abstract class在Java语言中表示的是一种继承关系,一个类只能使用一次继承关系。但是,一个类却可以实现多个interface。也许,这是Java语言的设计者在考虑Java对于多重继承的支持方面的一种折中考虑吧。 其次,在abstract class的定义中,我们可以赋予方法的默认行为。但是在interface的定义中,方法却不能拥有默认行为,为了绕过这个限制,必须使用委托,但是这会 增加一些复杂性,有时会造成很大的麻烦。 在抽象类中不能定义默认行为还存在另一个比较严重的问题,那就是可能会造成维护上的麻烦。因为如果后来想修改类的界面(一般通过abstract class或者interface来表示)以适应新的情况(比如,添加新的方法或者给已用的方法中添加新的参数)时,就会非常的麻烦,可能要花费很多的时间(对于派生类很多的情况,尤为如此)。但是如果界面是通过abstract class来实现的,那么可能就只需要修改定义在abstract class中的默认行为就可以了。 同样,如果不能在抽象类中定义默认行为,就会导致同样的方法实现出现在该抽象类的每一个派生类中,违反了one rule,one place原则,造成代码重复,同样不利于以后的维护。因此,在abstract class和interface间进行选择时要非常的小心。从设计理念层面看abstract class和interface 上面主要从语法定义和编程的角度论述了abstract class和interface的区别,这些层面的区别是比较低层次的、非本质的。本小节将从另一个层面:abstract class和interface所反映出的设计理念,来分析一下二者的区别。作者认为,从这个层面进行分析才能理解二者概念的本质所在。 前面已经提到过,abstarct class在Java语言中体现了一种继承关系,要想使得继承关系合理,父类和派生类之间必须存在is a关系,即父类和派生类在概念本质上应该是相同的(参考文献3中有关于is a关系的大篇幅深入的论述,有兴趣的读者可以参考)。对于interface 来说则不然,并不要求interface的实现者和interface定义在概念本质上是一致的,仅仅是实现了interface定义的契约而已。为了使论述便于理解,下面将通过一个简单的实例进行说明。 考虑这样一个例子,假设在我们的问题领域中有一个关于Door的抽象概念,该Door具有执行两个动作open和close,此时我们可以通过abstract class或者interface来定义一个表示该抽象概念的类型,定义方式分别如下所示: 使用abstract class方式定义Door:abstract class Door abstract void open(); abstract void close(); 使用interface方式定义Door: interface Door void open(); void close(); 其他具体的Door类型可以extends使用abstract class方式定义的Door或者implements使用interface方式定义的Door。看起来好像使用abstract class和interface没有大的区别。如果现在要求Door还要具有报警的功能。我们该如何设计针对该例子的类结构呢(在本例中,主要是为了展示abstract class和interface反映在设计理念上的区别,其他方面无关的问题都做了简化或者忽略)?下面将罗列出可能的解决方案,并从设计理念层面对这些不同的方案进行分析。 解决方案一: 简单的在Door的定义中增加一个alarm方法,如下: abstract class Door abstract void open(); abstract void close(); abstract void alarm(); 或者interface Door void open(); void close(); void alarm(); 那么具有报警功能的AlarmDoor的定义方式如下: class AlarmDoor extends 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(); 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的功能,那么上述的定义方式就要反过来了。 -多态性的理解-接口的出现是为了更好的实现多态,而多态的实现不一定需要依赖于接口(这一句蛮好的)举个例子,多态肯定知道,一般有三种,接口的多态,类的多态,方法的多态方法的多态就类似于我们方法的重载或重写,这里不赘述类的多态无非就是子类继承父类,并重写父类的方法,从而获得不同的实现那么再来看接口,接口跟类基本是一样,实现接口并实现接口的方法。不同的类实现接口可以有不同的方式从而表现不同的行为,就是接口的多态性啊。-多态性的理解-方法的重写与重载-方法的重写Overriding和重载Overloading是Java多态性的不同表现.重写Overriding是父类和子类之间多态性的一种表现;如果在子类中定义某方法和其父类有相同的名称和参数,我们说该方法被重写 (Overriding).子类的对象使用这个方法时,将调用子类中的定义,对他而言,父类中的定义如同被屏蔽了.重载Overloading是个类中多态性的一种表现;如果在一个类中定义了多个同名的方法,他们或有不同的参数个数或有不同的参数类型,则称为方法的重载(Overloading).重载的方法是能改动返回值的类型.-方法的重写与重载-OOP在继承和实现关系上总结: 实体类和抽象类是一样的可以互相继承都可以实现接口,接口可以继承接口但不能继承实体类或抽象类 (自己总结的)=简单区别匿名类和内部类=说实话 匿名类和内部类在做java web 开发中用的确实不是很多,做swing开发的话会多一点。匿名类详解-简单复制下来就可测试: 匿名类是不能有名称的类,所以没办法引用它们。必须在创建时,作为new语句的一部分来声明它们。 这就要采用另一种形式的new语句,如下所示: new 这种形式的new 语句声明一个新的匿名类,它对一个给定的类进行扩展,或者实现一个给定的接口。它还创建那个类的一个新实例,并把它作为语句的结果而返回。要扩展的类和要实现的接口是new语句的操作数, 后跟匿名类的主体。 如果匿名类对另一个类进行扩展,它的主体可以访问类的成员、覆盖它的方法等等,这和其他任何标准的类都是一样的。如果匿名类实现了一个接口,它的主体必须实现接口的方法java 代码interface pr /首先要定义一个接口 void print1(); public class noNameClass public pr dest() /方法返回接口类型 return new pr() /这个地方实现这个接口 public void print1() System.out.println(Hello world!); ; public static void main(String args) noNameClass c=new noNameClass(); pr hw=c.dest(); hw.print1(); 内部类详解:简单的分四种:成员内部类、局部内部类、静态内部类和匿名内部类。 (暂时不对匿名内部类做解释,参考上面的匿名类就知道了) 1.成员内部类详解-复制下来就可以测试:package ject.init;/* * 成员内部类练习 * 成员内部类: 即作为外部类的一个成员存在,与外部类的属性、方法并列。 * 注意:成员内部类中不能定义静态变量,但可以访问外部类的所有成员。 * author Administrator * */public class Outerprivate static int i = 1;private int j=10;private int k=20;public static void outer_f1() /do more somethingpublic void outer_f2() /do more something/成员内部类class Inner/static int inner_i =100; /内部类中不允许定义静态变量int j=100;/内部类中外部类的实例变量可以共存int inner_i=1;void inner_f1() System.out.println(i);/外部类的变量如果和内部类的变量没有同名的,则可以直接用变量名访问外部类的变量 System.out.println(j);/在内部类中访问内部类自己的变量直接用变量名 System.out.println(this.j);/也可以在内部类中用this.变量名来访问内部类变量 /访问外部类中与内部类同名的实例变量可用外部类名.this.变量名。 System.out.println(k);/外部类的变量如果和内部类的变量没有同名的,则可以直接用变量名访问外部类的变量 outer_f1(); /不是静态的也可以直接访问 outer_f2();/外部类的非静态方法访问成员内部类public void outer_f3() Inner inner = new Inner(); inner.inner_f1();/外部类的静态方法访问成员内部类,与在外部类外部访问成员内部类一样public static void outer_f4() /step1 建立外部类对象 Outer out = new Outer(); /*step2 根据外部类对象建立内部类对象* Inner inner=out.new Inner(); /step3 访问内部类的方法 inner.inner_f1();public static void main(String args) outer_f4();/*总结:1.非静态内部类中不能定义静态变量或静态方法 2.非静态内部类可以直接访问外部类的非静态的变量或方法*/*成员内部类的优点: 内部类作为外部类的成员,可以访问外部类的私有成员或属性。(即使将外部类声明为PRIVATE,但是对于处于其内部的内部类还是可见的。) 用内部类定义在外部类中不可访问的属性。这样就在外部类中实现了比外部类的private还要小的访问权限。 注意:内部类是一个编译时的概念,一旦编译成功,就会成为完全不同的两类。对于一个名为outer的外部类和其内部定义的名为inner的内部类。编译完成后出现outer.class和outer$inner.class两类*/ 2.局部内部类详解-复制下来就可以测试:package ject.init;/* * 局部内部类: 即在方法中定义的内部类,与局部变量类似,在局部内部类前不加修饰符public或private,其范围为定义它的代码块。 * 注意:局部内部类中不可定义静态变量,可以访问外部类的局部变量(即方法内的变量),但是变量必须是final的。 * author Administrator * */public class JuBuIner private int s = 100; private int j = 25; private int out_i = 1; public void f(final int k) final int s = 200; int i = 1; final int j = 10; class Inner /定义在方法内部 int s = 300;/可以定义与外部类同名的变量 /static int m = 20;/不可以定义静态变量 Inner(int k) inner_f(k); int inner_i = 100; void inner_f(int k)System.out.println(j +j); /方法内的变量比外部类的变量成员优先 System.out.println(out_i + out_i);/如果内部类没有与外部类同名的变量,在内部类中可以直接访问外部类的实例变量 (注意:如果局部内部类在静态方法中的话 就访问不到外部类的成员变量) System.out.println(k +k);/*可以访问外部类的局部变量(即方法内的变量),但是变量必须是final的*/ System.out.println(i); /*可以访问外部类的局部变量(即方法内的变量),但是变量必须是final的* System.out.println(s +s);/如果内部类中有与外部类同名的变量,直接用变量名访问的是内部类的变量 System.out.println(this.s +this.s);/用this.变量名 访问的也是内部类变量 System.out.println(JuBuIner.this.s +JuBuIner.this.s);/用外部外部类类名.this.变量名 访问的是外部类变量 /内部类结束 new Inner(k); /方法的结束 public static void main(String args) /访问局部内部类必须先有外部类对象 JuBuIner out = new JuBuIner(); out.f(3); /*总结: 1.局部内部类访问外部成员时局部内部类优先于方法内部成员优先于外部类成员 2.局部内部类只能访问方法内的final修饰的成员变量 */* 注意: 在类外不可直接生成局部内部类(保证局部内部类对外是不可见的)。要想使用局部内部类时需要生成对象,对象调用方法,在方法中才能调用其局部内部类。 通过内部类和接口达到一个强制的弱耦合,用局部内部类来实现接口,(感觉和匿名类有点像 ? 不过我测试了一下 好像不能实现接口或继承抽象方法 郁闷了 )并在方法中返回接口类型,使局部内部类不可见,屏蔽实现类的可见性。*/ 3.静态内部类-复制下来就可以测试:package ject.init;/* * 静态内部类练习 * 静态内部类: 静态内部类定义在类中,任何方法外,用static定义。 * 注意:静态内部类中可以定义静态或者非静态的成员 * author Administrator * */public class StaticOuter private static int i = 1; private int j = 10; public static void outer_f1() public void outer_f2() / 静态内部类可以用public,protected,private修饰/ 静态内部类中可
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 红酒相关知识培训课件
- 红酒定制知识培训课程表课件
- 红色基因代代传课件
- 企业年度法律顾问服务合同协议
- 诗经国风郑风子衿课件
- 红楼梦第五十八回课件
- 红楼梦第九回课件
- 健康咨询与管理协议
- 诗经中齐风鸡鸣课件
- 诗经中的植物课件
- 人美版《书法练习与指导》四年级上册整册教案
- 汉字形旁分类及其组字表
- NY-T 4251-2022 牧草全程机械化生产技术规范
- 代建项目安全生产管理办法20191226
- YS/T 690-2009天花吊顶用铝及铝合金板、带材
- GB/T 26463-2011羰基合成脂肪醇
- 静脉治疗护理质量评价标准
- 连铸坯质量控制与缺陷控制课件
- 社会调查研究方法-课件
- 雕塑基础教学课件
- 沥青混合料低温弯曲试验2002363
评论
0/150
提交评论