博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
八,抽象类与接口
阅读量:6335 次
发布时间:2019-06-22

本文共 7277 字,大约阅读时间需要 24 分钟。

hot3.png

1.抽象类

1.1定义

包含号一个抽象方法的类称为抽象类.需要用abstract声明.

并不是所有的类都是用来描绘对象的,如果一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类。抽象类往往用来表征我们在对问题领域进行分析、 设计中得出的抽象概念,是对一系列看上去不同,但是本质上相同的具体概念的抽象,我们不能把它们实例化(拿不出一个具体的东西)所以称之为抽象。

需要注意的是抽象类不能直接实例化,但可以声明,要想使用抽象类,必须依靠子类,抽象类是必须被子类继承的,而且被继承的子类需要实现抽象类中的全部抽象方法.

注:抽象类必须被实现,所以不能用final修饰.

示例:

public abstract class AbstractClass //里面至少有一个抽象方法{  public int t; //普通数据成员   public abstract void method1(); //抽象方法,抽象类的子类在类中必须实现抽象类中的抽象方法  public abstract void method2();  public void method3(); //非抽象方法  public int method4();  publi int method4 (){  …… //抽象类中可以赋予非抽象方法方法的默认行为,即方法的具体实现  }public void method3(){  …… //抽象类中可以赋予非抽象方法方法的默认行为,即方法的具体实现  }}

1.2抽象方法

在面向对象编程语言中抽象方法指一些只有方法声明,而没有具体方法体的方法。抽象方法一般存在于抽象类或接口中。

java中的抽象方法就是以abstract修饰的方法,这种方法只声明返回的数据类型、方法名称和所需的参数,没有方法体,也就是说抽象方法只需要声明而不需要实现。

数学中的抽象是指抽取出同类数学对象的共同的、本质的属性或特征,舍弃其他非本质的属性或特征的思维过程。即把研究对象或问题中抽取数量关系或空间形式而舍弃其它属性对其进行考察的思维方法。

1.3抽象类中的构造方法

抽象类中允许有构造方法,但构造方法是不能直接调用的,是交给子类去调用的,子类实例化过错中,永远是先调用父类的构造方法.抽象类中的构造函数是有作用的。子类继承抽象类时,构造函数不会被覆盖。 而且,在实例化子类对象时首先调用的是抽象类中的构造函数再调用子类中的. 因此,在抽象类中可以使用构造函数封装继承子类公共的东西。

2.接口(interface)

2.1定义

接口是一个特殊的类,java中接口是由抽象方法和全局常量组成.

接口也需要子类,不过子类不再是继承,而是实现接口,通过implements关键字完成.接口有多继承的能力.

一个接口不能继承一个抽象类,但是一个接口却可以同时继承多个接口.

示例:

public interface Interface{  static final int i; //接口中不能有普通数据成员,只能够有静态的不能被修改的数据成员,                       //static表示全局,final表示不可修改,                       //可以不用static final 修饰,会隐式的声明为static和final     public void method1(); //接口中的方法一定是抽象方法,所以不用abstract修饰    public void method2(); //接口中不能赋予方法的默认行为,即不能有方法的具体实现}
  简言之抽象类是一种功能不全的类,接口只是一个抽象方法声明和静态不能被修改的数据的集合,两者都不能被实例化。

  从某种意义上说,接口是一种特殊形式的抽象类,在java语言中抽象类表示的是一种继承关系,一个类只能继承继承一个抽象类,而一个类却可以实现多个接口。在许多情况下,接口确实可以代替抽象类,如果你不需要刻意表达属性上的继承的话。

2.2示例

interface A{	public void fun() ;}class B implements A{	public void fun(){		System.out.println("Hello") ;	}};public class InterPolDemo01{	public static void main(String args[]){		A a = new B() ;	// 为接口实例化		a.fun() ;	}};
接口也可以像抽象类那样通过对象多态性进行对象的实例化操作.

3.引入抽象类和接口的目的

1、从类的层次结构上看,抽象类是在层次的顶端,但在实际的设计当中,一般来说抽象类应当是后面才会出现。为什么?实际上抽象类的获取有点像数学中的提取 公因式:ax+bx,x就是抽象类,如果你没有前面的式子,你怎么知道x是不是公因式呢?在这点上,也符合人们认识世界的过程,先具体后抽象。因此在设计 过程中如果你得到大量的具体概念并从当中找到其共性时,这个共性的集合就是抽象类应当是没错的。

2、interface从表面上看,和抽象类很相似,但用法完全不同。它的基本功能就是把一些毫不相关的类(概念)集合在一起形成一个新的、可集中操作的 “新类”。我给学生的一个典型例子就是“司机”。谁可以当司机?谁都可以,只要领取了驾照。所以我不管你是学生,白领、蓝领还是老板,只要有驾照就是司 机。

interface DriverLicence {Licence getLicence();}class StudentDriver extends Student implements DriverLicence {}class WhtieCollarEmployeeDriver extends WhtieCollarEmployee implements DriverLicence {}class BlueCollarEmployeeDriver extends BlueCollarEmployee implements DriverLicence {}class BossDriver extends Boss implements Driver {}
当我定义了“汽车”类后,我就可以指定“司机”了。

class Car {setDriver(DriverLicence driver);}
这时候, Car的对象并不关心这个司机到底是干什么的,他们的唯一共同点是领取了驾照(都实现了 DriverLicence接口)。这个,应当是接口最强大的地方也是抽象类无法比拟的。
总结:抽象类是提取具体类的公因式,而接口是为了将一些不相关的类“杂凑”成一个共同的群体。通常我们平时养成良好的习惯就是多用接口,毕竟 java是单继承。

4.抽象类和接口的区别

4.1适配器设计模式

正常情况下,一个接口的子类要实现全部的抽象方法.但实际中可能是根据需要选择性的覆写,用一个类先将接口实现,所有的抽象方法都是空覆写,然后继承此类,这个类使用抽象类,因为抽象类也不能直接使用.

interface Window{	public void open() ;	// 打开窗口	public void close() ;	// 关闭窗口	public void icon() ;	// 最小化	public void unicon() ;	// 最大化}abstract class WindowAdapter implements Window{	public void open(){}	public void close(){}	public void icon(){}	public void unicon(){}};class MyWindow extends WindowAdapter{	public void open(){		System.out.println("打开窗口!") ;	}};public class AdpaterDemo{	public static void main(String args[]){		Window win = new MyWindow() ;		win.open() ;	}}
这样就实现了部分覆写的目的 ,将一个类的接口转换成客户希望的另外一个接口 .Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以在一起工作 .

优点

①通过适配器,客户端可以调用同一接口,因而对客户端来说是透明的.这样做更简单、更直接、更紧凑.
②复用了现存的类,解决了现存类和复用环境要求不一致的问题.
③将目标类和适配者类解耦,通过引入一个适配器类重用现有的适配者类,而无需修改原有代码.
④一个对象适配器可以把多个不同的适配者类适配到同一个目标,也就是说,同一个适配器可以把适配者类和它的子类都适配到目标接口.
缺点
①对于对象适配器来说,更换适配器的实现过程比较复杂.
适用场景: 系统需要使用现有的类,而这些类的接口不符合系统的接口. 或者旧的系统开发的类已经实现了一些功能,但是客户端却只能以另外接口的形式访问,但我们不希望手动更改原有类的时候. 还有使用第三方组件,组件接口定义和自己定义的不同,不希望修改自己的接口,但是要使用第三方组件接口的功能.
应用举例:使用过ADO.NET的开发人员应该都用过DataAdapter,它就是用作DataSet和数据源之间的适配器.DataAdapter通过映射Fill和Update来提供这一适配器.

备注:ADO.NET的名称起源于ADO(ActiveX Data Objects),是一个COM组件库,用于在以往的Microsoft技术中访问数据.之所以使用ADO.NET名称,是因为Microsoft希望表明,这是在NET编程环境中优先使用的数据访问接口.

在GoF的设计模式中,对适配器模式讲了两种类型,类适配器模式对象适配器模式.由于类适配器模式通过多重继承对一个接口与另一个接口进行匹配,而C#、java等语言都不支持多重继承,因而这里只是介绍对象适配器.

4.2 工厂设计模式

代码示例:

interface Fruit{	public void eat() ;}class Apple implements Fruit{	public void eat(){		System.out.println("吃苹果...") ;	}};class Orange implements Fruit{	public void eat(){		System.out.println("吃橘子...") ;	}};class Factory{	// 工厂类	public static Fruit getFruit(String className){		Fruit f = null ;		if("apple".equals(className)){			f = new Apple() ;		}		if("orange".equals(className)){			f = new Orange() ;		}		return f ;	}};public class InterDemo{	public static void main(String args[]){		Fruit f = Factory.getFruit(args[0]) ;		if(f!=null){			f.eat() ;		}	}}
所有接口的实例化都通过工厂类取得 ,客户端调用的时候传入的名称不同 ,完成的功能不同 .

工厂模式主要用一下几种形态:简单工厂(Simple Factory),工厂方法(Factory Method),抽象工厂(Abstract Factory).(详细见设计模式的博文)

4.3 代理设计模式

示例:

interface Give{	public void giveMoney() ;}class RealGive implements Give{	public void giveMoney(){		System.out.println("把钱还给我。。。。。") ;	}};class ProxyGive implements Give{	// 代理公司	private Give give = null ;	public ProxyGive(Give give){		this.give = give ;	}	public void before(){		System.out.println("准备:小刀、绳索、钢筋、钢据、手枪、毒品") ;	}	public void giveMoney(){		this.before() ;		this.give.giveMoney() ;	// 代表真正的讨债者完成讨债的操作		this.after() ;	}	public void after(){		System.out.println("销毁所有罪证") ;	}};public class ProxyDemo{	public static void main(String args[]){		Give give = new ProxyGive(new RealGive()) ;		give.giveMoney() ;	}};
代理模式对外部提供统一的接口方法 ,而代理类在接口中实现对真实类的附加操作行为 ,从而不影响外部调用情况下 ,进行系统扩展 .即修改真实角色的操作的时候 ,尽量不要修改他 ,而是在外部“包”一层进行附加行为 ,即代理类 .

例如:接口A有一个接口方法operator(),真实角色RealA实现接口A,则必须实现接口方法operator().客户端Client调用接口A的接方法operator().新需求需要修改RealA中的operator()的操作行为.但修改RealA就会影响原有系统的稳定性,还要重新测试.这是就需要代理类实现附加行为操作.创建代理ProxyA实现接口A,并将真实对象RealA注入进来.ProxyA实现接口方法operator(),另外还可以增加附加行为,然后调用真实对象的operator().从而达到了“对修改关闭,对扩展开放”,保证了系统的稳定性.客户端Client调用仍是接口A的接口方法operator(),只不过实例变为了ProxyA类了而已.也就是说代理模式实现了ocp原则.

备注:ocp原则即开闭原则(Open Closed Principle)是Java最基础的设计原则,有助于建立一个稳定的、灵活的系统.一个软件实体如类、模块和函数应该对扩展开放,对修改关闭.

4.4抽象类与接口的区别

通过对比可知,如果两者都可以使用的话,优先使用接口,因为它可以避免单继承的局限.

抽象类中允许包含接口:

abstract class A{	public abstract void fun() ;	interface B{	// 内部接口 		public void print() ;	}};class X extends A{	public void fun(){		System.out.println("****************") ;	}	class Y implements B{		public void print(){			System.out.println("===================") ;		}	};};public class TestDemo01{	public static void main(String args[]){		A a = new X() ;		a.fun() ;		A.B b = new X().new Y() ;		b.print() ;	}};
接口中允许包含抽象类 :
interface A{	public void fun() ;	abstract class B{	// 内部抽象类		public abstract void print() ;	}};class X implements A{	public void fun(){		System.out.println("****************") ;	}	class Y extends B{		public void print(){			System.out.println("===================") ;		}	};};public class TestDemo02{	public static void main(String args[]){		A a = new X() ;		a.fun() ;		A.B b = new X().new Y() ;		b.print() ;	}};

20150413

JAVA学习笔记系列

--------------------------------------------

                    联系方式

--------------------------------------------

        Weibo: ARESXIONG

        E-Mail: aresxdy@gmail.com

------------------------------------------------

转载于:https://my.oschina.net/u/2288529/blog/399349

你可能感兴趣的文章
clickhouse修改时区
查看>>
CSS_定位
查看>>
第二十四章:页面导航(六)
查看>>
百度、长沙加码自动驾驶,湖南阿波罗智行科技公司成立 ...
查看>>
10 个 Linux 中方便的 Bash 别名
查看>>
[Server] 服务器配置SSH登录邮件通知
查看>>
全新 DOCKER PALS 计划上线,带给您不一样的参会体验! ...
查看>>
Android开发之自定义View(二)
查看>>
python爬虫之微打赏(scrapy版)
查看>>
自制操作系统Antz day08——实现内核 (中) 扩展内核
查看>>
poj-1056-IMMEDIATE DECODABILITY(字典)
查看>>
阿里云容器Kubernetes监控(二) - 使用Grafana展现Pod监控数据
查看>>
区块链应用 | 不知道什么时候起,满世界都在谈区块链的事情
查看>>
小程序爆红 专家:对简单APP是巨大打击
查看>>
FarBox--另类有趣的网站服务【转】
查看>>
在非纯色背景上,叠加背景透明的BUTTON和STATIC_TEXT控件
查看>>
Distributed2:Linked Server Login 添加和删除
查看>>
Python-time
查看>>
Java中取两位小数
查看>>
RTX发送消息提醒实现以及注意事项
查看>>