抽象类和接口

抽象类

当描述一批对象的时候,我们通常会将一些共有的方法和属性进行向上抽取,抽取出它们的父类,通过继承达到代码复用的目的。

但是,每个子类的方法某些方法业务都是独一无二的,它们都重写了父类中定义的该方法。而这个时候,父类就没有必要提供该方法的具体实现了,只需要将该方法定义为抽象方法,而且该类的存在也仅仅是为了让子类继承,实现代码的复用,那么我们就可以将该类定义为抽象类。

抽象类必须使用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关键字之后,多个父接口之间以英文逗号隔开。

//TODO
class 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关键字多实现。

//TODO
class 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;
    }
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值