当描述一批对象的时候,我们通常会将一些共有的方法和属性进行向上抽取,抽取出它们的父类,通过继承达到代码复用的目的。
但是,每个子类的方法某些方法业务都是独一无二的,它们都重写了父类中定义的该方法。而这个时候,父类就没有必要提供该方法的具体实现了,只需要将该方法定义为抽象方法,而且该类的存在也仅仅是为了让子类继承,实现代码的复用,那么我们就可以将该类定义为抽象类。
抽象类必须使用abstract修饰符来修饰类名,有抽象方法的类只能被定义成抽象类,抽象类里可以没有抽象方法,也可以有具体实现的方法。
1、 抽象类必须使用abstract修饰符来修饰类,抽象方法也必须使用abstract来修饰,抽象方法不能有方法体。
2、 抽象类不能被实例化,不能使用new关键字来调用抽象类的构造器创建抽象类的实例。即使抽象类里不包含抽象方法,也不能被创建实例。
3、 抽象类可以包含成员变量、方法(普通方法和抽象方法都可以)、构造器、初始化块、内部类5种成分。抽象类的构造器不能用来创建实例,主要是用于被其子类调用。
abstract class Fu{ publicFu(String str){ System.out.PRintln(str); }}class Zi1 extends Fu{ publicZi1(){ super("abcd"); }}4、含有抽象方法的类只能被定义为抽象类。
定义抽象方法只需要在普通方法上增加abstract修饰符,并且把方法体去掉,在后面加分号即可。
注意:抽象方法和空方法完全不是同一个概念。一个是抽象方法,一个是普通方法,只不过普通方法没有具体代码。
定义抽象类
只需要在普通类上增加abstract修饰符即可。甚至一个普通类(没有抽象方法)增加abstract修饰符后也将变成抽象类。
//定义Shape抽象类和实现类三角形,圆形求周长方法。获取形状方法
class AbstractTest{ //使用抽象类,进行解耦操作 static XingZhuang xz; public static void main(String[] args){ if(true){ xz= new Circle(12.12 ,"红色"); }else{ xz= new Rect(12,12,"黑色"); } double zc= xz.zhouChang(); String info = xz.getInfo(); }}abstract class XingZhuang //形状{ abstract double zhouChang(); abstract String getInfo(); String color; } class Circle extends XingZhuang{ public Circle(double radius , String color){ this.radius= radius; this.color= color; } double radius; double zc= 0; double zhouChang(){ zc =Math.PI * 2 * radius; return zc; } String getInfo(){ return color + "圆形的周长是:"+ zc; }}class Rect extends XingZhuang{ double chang; double kuang; double zc= 0; public Rect(double chang , double kuang,String color){ this.chang= chang; this.kuang= kuang; this.color= color; } double zhouChang(){ zc = (chang + kuang) * 2; return zc; } String getInfo(){ return color + "矩形的周长是:"+ zc; }}
抽象类的作用
抽象类不能被创建实例,只能当成父类被继承。普通类描述对象是对某一类事物的抽象,而抽象类则是更高层次的抽象。从多个具有相同特征的类中抽象出一个抽象类,以这个抽象类作为其子类的模板,从而避免了子类设计的随意性。
抽象类体现的就是一种模板的设计,抽象类作为多个子类的模板,子类建立在抽象类的基础上进行扩展,改造,但子类总体上大致会保留这些抽象类的行为。
如果编写一个抽象类,父类可以提供多个子类通用的抽象方法,并把具体实现推迟到子类去实现。当然,父类也可以提供通用的普通方法,这些普通方法可以作为辅助方法用来辅助子类。
接口
抽象类是从多个类中抽象出来的模板,如果将这种抽象进行的更加彻底,则可以提炼出一种更加特殊的抽象类--接口(interface),接口里不能包含普通方法,接口里所有的方法都是抽象方法。
接口的概念:
在计算机主板上,有N多个接口插槽,供我们拓展计算机的功能,如显卡、内存、硬盘、声卡等。主板只是定义了一下接口规则,这些生产显卡、内存等厂家生产的产品必须实现该接口的规则,才可以将它们的产品插到主板上。
那么,在java中,接口就是定义了一种规范,接口定义的某一批类所需要遵守的规范,接口并不关心这些类的内部状态数据,也不关心这些类里的方法实现的细节,它只是规定了这批类里必须提供某些方法,提供这些方法的类就可以满足实际需要。
可见,接口是从多个类中抽象出来的规范,接口不提供任何实现。接口体现的是规范和实现分离的设计哲学。
class JieKou{ public static void main(String[] args){ DianNao dn = new DianNao(); //调用方法,传入实现Input接口的实现类 dn.getInput(new JianPan()); //调用方法,传入实现Input接口的实现类 dn.getInput(new ShouXie()); }}class DianNao{ //定义了获取信息需要的组件,只需要遵循Input接口就OK void getInput(Input input){ System.out.println("输入了" + input. inputMethod()); }}//定义了输入设备的规范
interface Input{ String inputMethod();}//遵循了输入设备的规范的键盘class JianPan implements Input{ publicString inputMethod(){ return"键盘"; }}//遵循了输入设备的规范的手写板class ShouXie implements Input{ public String input Method(){ return "手写"; }}
让规范和实现分离正是接口的好处,弱化了各个组件之间的依赖性,即松耦合设计。例如主机上的各种插槽,只要显卡遵守规范,就可以与主机进行通信,至于显卡是如何设计,内部怎么实现,主板无需关心。
类似的,软件系统之间的各个模块之间也应该采用这种面向接口的耦合,从而降低各个模块之间的耦合,为系统提供了更好的可扩展性和可维护性。
因此,接口定义的是多个类共同的公共行为的规范,这些行为是与外部交流的通道,这就意味着接口里通常是定义一组公用方法。
定义规则:
interface 接口名 extends 父接口1、父接口2
{
0到多个抽象方法的定义
0到多个内部类、接口、枚举
0到多个默认方法或类方法
}
详细说明:
1、命名规范与类名一致。大驼峰。
2、接口可以有多个直接父接口,但是接口只能继承接口,不可以继承类。
由于接口定义的是一种规范,因此接口里不能包含构造器和初始化块。接口可以包含成员变量(只能是静态常量)、方法(只能是抽象实例方法、类方法或默认方法)、内部类(包括内部接口、枚举)定义。
对比接口和类的定义方式,不难发现接口的成员比类里的成员少了两种,而且接口里的成员变量只能是静态常量,接口里的方法只能是抽象方法。
class JieKou{ public static void main(String[] args){ DaoDian daoDian = new DaoDianImpl(); String[] cusInfo = daoDian.cusInfo(); String mrs = daoDian.mrs(cusInfo[0]); System.out.println(mrs); daoDian.zhixing(mrs, cusInfo[1]); }}
//顾客到店服务
interface DaoDian{ //获取预约顾客的信息 String[] cusInfo(); //安排顾客预约的美容师 String mrs(String name); //执行预约的服务 void zhixing(String name , String fuwu);}class DaoDianImpl implements DaoDian{ //获取预约顾客的信息 public String[] cusInfo(){String[] arr = new String[]{"顾客小红" , "Spa"}; retur narr; } //安排顾客预约的美容师 public String mrs(String name){ if(name.equals("顾客小红")){ return "小兰"; } return "小明"; } //执行预约的服务 public void zhixing(String name , String fuwu){ System.out.println(name+"美容师执行了" +fuwu+"项目"); }}前面已经说了,接口里定义的是多个类共同的公共行为规范,因此接口里的所有成员,包括常量、方法、内部类都是public访问权限。定义接口成员时,可以省略访问控制修饰符,如果指定访问控制修饰符,则只能使用public访问控制修饰符。
对于接口里的定义的静态常量而言,他们是接口相关的,因此系统会自动为这些成员变量增加static和final两个修饰符。也就是说,在接口中定义成员变量时,不管是否使用public static final修饰符,接口里的成员变量总是使用者3个修饰符来修饰,而且接口里没有构造器和初始化块,因此接口里定义的成员变量只能是在定义时指定默认值。
如下两行代码的结果是完全一样的。
int MAX_SIZE = 50;
public static final int MAX_SIZE = 50;
接口里定义的方法只能是抽象方法,因此系统会将自动为普通方法增加public abstract修饰符。
1、 属性一律使用静态修饰符:
如下,接口中定义属性没有使用static修饰,但是在测试类中,直接使用接口.name就可以访问该属性,说明该属性是静态的。
属性权限修饰符是:public
如下,属性是默认修饰符,但是在不同包下的类中,也可以访问。
属性默认使用final修饰:
2、 方法默认使用abstract、public
在接口中定义方法,使用默认权限修饰,子类也使用默认权限修饰,报错,说接口的该方法是public,证明了,接口方法默认使用public修饰。
接口的继承:
接口的继承和类的继承不一样,接口是完全支持多继承的,即一个接口可以有多个直接父接口。和类继承想死,接口扩展某个父接口,将会获得负借口定义的所有抽象方法、常量。
一个接口继承多个父接口时,多个父接口排在extends关键字之后,多个父接口之间以英文逗号隔开。
//TODOclass JieKou implements JieKouC,JieKouB,JieKouA{ public void methodc(){} public void methodb(){} public void methoda(){}}interface JieKouA{ void methoda();}interface JieKouB{ void methodb();}interface JieKouC{ void methodc();}接口的多层继承,实现接口,如果接口还有父类接口,那么实现类必须实现包括接口和其父接口的所有抽象方法:
class JieKou implements JieKouC{ public void methodc(){} public void methodb(){} public void methoda(){}}interface JieKouA{ void methoda();}interface JieKouB extends JieKouA{ void methodb();}interface JieKouC extends JieKouB{ void methodc();}使用接口
接口不能用于创建实例,但接口可以用于声明引用类型变量。当使用接口来声明引用类型变量时,这个引用类型变量必须引用到其实现类的对象,这也是属于多态特性。
class JieKou{ public static void main(String[] args){ Person person = new Man(); }}interface Person{ }class Man implements Person{}
接口的主要用途就是被实现类实现,除此之外,,接口主要用途如下:
1、 定义变量,也可以用于进行强制类型转换。
class JieKou{ public static void main(String[] args){ Manman = new Man(); Person person = (Person)man; }}2、 调用接口中定义的常量。
3、被其他类实现,实现功能的扩展
一个类可以实现一个或多个接口,继承只能使用extends关键字实现单继承,实现接口使用implements关键字多实现。
//TODOclass JieKou{ public static void main(String[] args){ Man man = new Man(); Person person = (Person)man; }}class Person{ }class Man extends Person implements Working,ShanYang{ void zhengqian(){} void xiwan(){} void shanYang(){}}class WoMan extends Person implements ShanYang{ void shanYang(){}}interface Working{ void zhengqian(); void xiwan();}interface ShanYang{ void shanYang();}实现接口与继承父类相似,一样可以获得所实现接口里定义的常量(成员变量)、方法(包括抽象方法和默认方法)
让类实现接口需要定义后增加implements部分,当需要实现多个接口时,使用逗号隔开,一个类可以继承一个父类,并且同时实现多个接口。implements必须放在extends之后。
一个类实现一个或多个接口后,这个类必须实现这些接口里所定义的全部抽象方法(也就是重写),否则就定义为抽象类。
一个类实现某个接口,就会拥有该接口的定义的常量、方法等。因此也可以将实现接口理解为一种特殊的继承。相当于一个更加彻底的抽象类。
//TODO实现两个接口,两个接口都可以接受该对象
class JieKou{ public static void main(String[] args){ Working working = new Man(); ShanYang shanYang = (ShanYang)working; }}class Person{ }class Man extends Person implements Working,ShanYang{ public void zhengqian(){} public void xiwan(){} public void shanYang(){}} interface Working{ void zhengqian(); void xiwan();}interface ShanYang{ void shanYang();}接口不能继承任何类,但是所有接口类型的变量都可以直接赋值给Object类型的引用变量。因为既然是存在的对象,那么肯定是Object对象的子类对象。
class JieKou{ public static void main(String[] args){ Working working = new Man(); Object obj =working; }}接口和抽象类区别
接口和抽象类很相似,都具有如下特征:
1、接口和抽象类都不能被实例化,他们都需要被继承或实现。
2、接口和抽象类都可以包含抽象方法,实现或继承他们的普通类必须重写抽象方法。
但接口和抽象差别也是非常大,这种差别主要体现在二者的设计目的上。
接口作为系统与外界的交互的窗口,接口体现的是一种规范,对于接口的实现者而言,接口规定了实现者必须向外提供哪些服务,对于接口调用者而言,接口规定了调用者可以调用哪些服务,以及如何调用。当一个程序中使用了接口时,接口是多个模块间的耦合标准。当多个程序之间使用了接口,那么该接口就是多个程序之间的通讯标准。
抽象类则不一样,抽象类作为多个子类共同父类,他所体现的是一种模板设计。抽象类作为多个子类的抽象父类,可以被当成系统实现过程中的中间产品,必须有更进一步的完善。
除此之外,在使用用法上也有差别:
1、接口里只能包含抽象方法,抽象类则完全可以包含普通方法。
2、接口不能定义静态方法,抽象类可以。
3、接口只能定义静态常量,不能定义普通成员变量,抽象类则可以定义静态常量,也可以定义普通成员变量。
4、接口不含构造器,抽象类可以包含构造器和构造代码块。
抽象类是可以被实例化,只不过是子类对象创建的时候,创建抽象类父类对象。所以,抽象类是可以有静态代码块,构造代码块,构造方法,抽象类就既可以有实例变量、也可以有静态变量。 接口没有构造器、构造代码块,所以,接口不能被实例化,也不可能有实例变量。
接口没有静态代码块,那么,就无法为接口定义的常量赋值,那么接口就规定:定义接口属性是需要在声明时直接赋值。
4、 一个类最多只能有一个直接父类,包括抽象类;但一个类可以多个实现接口,通过实现多个接口可以弥补java单继承的不足。
当新增功能时,发现,该功能并不属于该类的父类,就需要使用接口,来扩展一下该功能。
面向接口编程:
接口体现的是一种规范和实现分离的设计哲学,充分利用接口可以降低各个模块之间的耦合性,从而提高系统的扩展性和维护性。
面向接口编程设计在这里不做太多解释,这个不属于现阶段内容,如果有兴趣可以参阅:
http://baike.baidu.com/link?url=cjLmsENJgShmA6DGWi2RBh9gccRbn7dAXgOoaEuRrBiM9voyhUof8PvFSHI-HhxEp-ueDyacewd1ZwQdDWdsxfFMOZCS4UkojZkZr5P1vDDc3kQvMJr_vbJBx3bUPWIovM1oKESe9CJAf3fvj4eAca
基于这种原则,很多软件架构设计都倡导“面向接口”编程。而不是面向实现类编程。希望通过面向接口编程来降低程序的耦合。
工厂模式
工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。
介绍
意图:定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行。
主要解决:主要解决接口选择的问题。
何时使用:我们明确地计划不同条件下创建不同实例时。
如何解决:让其子类实现工厂接口,返回的也是一个抽象的产品。
关键代码:创建过程在其子类执行。
应用实例: 1、您需要一辆汽车,可以直接从工厂里面提货,而不用去管这辆汽车是怎么做出来的,以及这个汽车里面的具体实现。 2、Hibernate 换数据库只需换方言和驱动就可以。
优点: 1、一个调用者想创建一个对象,只要知道其名称就可以了。 2、扩展性高,如果想增加一个产品,只要扩展一个工厂类就可以。 3、屏蔽产品的具体实现,调用者只关心产品的接口。
缺点:每次增加一个产品时,都需要增加一个具体类和对象实现工厂,使得系统中类的个数成倍增加,在一定程度上增加了系统的复杂度,同时也增加了系统具体类的依赖。这并不是什么好事。
使用场景: 1、日志记录器:记录可能记录到本地硬盘、系统事件、远程服务器等,用户可以选择记录日志到什么地方。 2、数据库访问,当用户不知道最后系统采用哪一类数据库,以及数据库可能有变化时。 3、设计一个连接服务器的框架,需要三个协议,"POP3"、"IMAP"、"HTTP",可以把这三个作为产品类,共同实现一个接口。
注意事项:作为一种创建类模式,在任何需要生成复杂对象的地方,都可以使用工厂方法模式。有一点需要注意的地方就是复杂对象适合使用工厂模式,而简单对象,特别是只需要通过 new 就可以完成创建的对象,无需使用工厂模式。如果使用工厂模式,就需要引入一个工厂类,会增加系统的复杂度。
http://www.runoob.com/design-pattern/factory-pattern.html
//TODO
class Test{ public static void main(String[] args){ //调用工厂类获取对象方法,传递对象信息 HuaTu huatu = Factory.getHuaTu("Rect"); //调用返回对象的画图方法 huatu.huaTu(); }} interface HuaTu{ void huaTu();}class Circle implements HuaTu{ public void huaTu(){ System.out.println("绘制圆形"); }}class Rect implements HuaTu{ public void huaTu(){ System.out.println("绘制矩形"); }}class Factory{ //接收传入信息 static HuaTu getHuaTu(String str){ //根据传入信息判断返回什么类型对象 if(str.equals("Circle")){ return new Circle(); }elseif(str.equals("Rect")){ return new Rect(); } return null; }}
新闻热点
疑难解答