面向对象编程(十五)——抽象类和接口

一、抽象(abstract)类

抽象类,说白了就是包含抽象方法的类。那什么是抽象方法?抽象方法是一种特殊的方法:抽象方法只有声明,而没有具体的实现。抽象方法说白了就是只有方法的声明,没有方法体。

抽象方法必须用abstract关键字进行修饰。如果一个类含有抽象方法,则称这个类为抽象类,抽象类必须在类前用abstract关键字修饰。因为抽象类中含有无具体实现的方法,所以不能用抽象类创建对象。

上图Cat必须实现抽象方法run(),如果不实现,编译通不过。可以选择实现run()方法或者定义Cat为抽象类。

完整截图:

1. 为什么需要抽象类?如何定义抽象类?

  • 是一种模版模式。抽象类为所有子类提供了一个通用模板。子类可以在这个模版基础上进行扩展
  • 通过抽象类,可以避免子类设计的随意性。通过抽象类,我们可以做到严格限制子类的设计,使子类之间更加通用。

2. 抽象类要点

  1. 有抽象方法的类只能定义为抽象类;只要包含一个抽象方法的抽象类,该类必须要定义为抽象类,不管是否还包含其他方法;
  2. 抽象类中可以包含普通方法(有具体实现的方法),当然也可以不包含抽象方法;
  3. 抽象类不能被实例化,即不能用new来实例化抽象类;
  4. 抽象类可以包含属性、方法、构造方法。但是构造方法不能用new来实例,只能用来被子类调用;
  5. 抽象类只能用来被继承;
  6. 抽象方法必须被子类实现,即由子类来进行重写。(不实现的话,编译器会报错)

【示例】

下面通过一下的小程序深入理解抽象类

  因此在类Animal里面只需要定义这个enjoy()方法就可以了,使用abstract关键字把enjoy()方法定义成一个抽象方法,定义如下:public abstract void enjoy(); 

  从某种意义上来说,抽象方法就是被用来重写的,所以在父类声明的抽象方法一定要在子类里面重写。如果真的不想在子类里面重写这个方法,那么可以再在子类里面把这个方法再定义为抽象方法,因为子类觉得我去实现也不合适,应该让继承我的子类去实现比较合适,因此也可以在继承这个子类的下一个子类里面重写在父类里面声明的抽象方法,这是可以的。

  这里有一个规则:既然父类里面的方法是抽象的,那么对于整个类来说,它就有一个没有实现的方法,这个方法不知道怎么去实现,那么这个类是就是残缺不全的,因此这个类应该被定义为一个抽象类。所以前面这样声明的声明的class Animal应该要在class的前面加上abstract,即声明成这样:abstract class Animal,这样Animal类就成了一个抽象类了。Animal类的最终定义代码如下:

/**
 * 父类Animal
 * 在class的前面加上abstract,即声明成这样:abstract class Animal
 * 这样Animal类就成了一个抽象类了
 */
abstract class Animal {

    public String name;

    public Animal(String name) {
        this.name = name;
    }
    
    /**
     * 抽象方法
     * 这里只有方法的定义,没有方法的实现。
     */
    public abstract void enjoy(); 
    
}

 Java语言规定,当一个类里面有抽象方法的时候,这个类必须被声明为抽象类。

  子类继承父类时,如果这个父类里面有抽象方法,并且子类觉得可以去实现父类的所有抽象方法,那么子类必须去实现父类的所有抽象方法,如:

 1 /**
 2  * 子类Dog继承抽象类Animal,并且实现了抽象方法enjoy
 3  * @author gacl
 4  *
 5  */
 6 class Dog extends Animal {
 7     /**
 8      * Dog类添加自己特有的属性
 9      */
10     public String furColor;
11 
12     public Dog(String n, String c) {
13         super(n);//调用父类Animal的构造方法
14         this.furColor = c;
15     }
16 
17     @Override
18     public void enjoy() {
19         System.out.println("狗叫....");
20     }
21 
22 }

这个父类里面的抽象方法,子类如果觉得实现不了,那么把就子类也声明成一个抽象类,如:

 1 /**
 2  * 这里的子类Cat从抽象类Animal继承下来,自然也继承了Animal类里面声明的抽象方法enjoy(),
 3  * 但子类Cat觉得自己去实现这个enjoy()方法也不合适,因此它把它自己也声明成一个抽象的类,
 4  * 那么,谁去实现这个抽象的enjoy方法,谁继承了子类,那谁就去实现这个抽象方法enjoy()。
 5  * @author gacl
 6  *
 7  */
 8 abstract class Cat extends Animal {
 9 
10     /**
11      * Cat添加自己独有的属性
12      */
13     public String eyeColor;
14 
15     public Cat(String n, String c) {
16         super(n);//调用父类Animal的构造方法
17         this.eyeColor = c;
18     }
19 }

这里的子类Cat从抽象类Animal继承下来,自然也继承了Animal类里面声明的抽象方法enjoy(),但子类Cat觉得自己去实现这个enjoy()方法也不合适,因此它把它自己也声明成一个抽象的类,那么,谁去实现这个抽象的enjoy方法,谁继承了子类,那谁就去实现这个抽象方法enjoy()。如:

 1 /**
 2  * 子类BlueCat继承抽象类Cat,并且实现了从父类Cat继承下来的抽象方法enjoy
 3  * @author gacl
 4  *
 5  */
 6 class BlueCat extends Cat {
 7 
 8     public BlueCat(String n, String c) {
 9         super(n, c);
10     }
11 
12     /**
13      * 实现了抽象方法enjoy
14      */
15     @Override
16     public void enjoy() {
17         System.out.println("蓝猫叫...");
18     }
19     
20 }

完整的测试代码如下:

  1 package javastudy.summary;
  2 
  3 /**
  4  * 父类Animal
  5  * 在class的前面加上abstract,即声明成这样:abstract class Animal
  6  * 这样Animal类就成了一个抽象类了
  7  */
  8 abstract class Animal {
  9 
 10     public String name;
 11 
 12     public Animal(String name) {
 13         this.name = name;
 14     }
 15     
 16     /**
 17      * 抽象方法
 18      * 这里只有方法的定义,没有方法的实现。
 19      */
 20     public abstract void enjoy(); 
 21     
 22 }
 23 
 24 /**
 25  * 这里的子类Cat从抽象类Animal继承下来,自然也继承了Animal类里面声明的抽象方法enjoy(),
 26  * 但子类Cat觉得自己去实现这个enjoy()方法也不合适,因此它把它自己也声明成一个抽象的类,
 27  * 那么,谁去实现这个抽象的enjoy方法,谁继承了子类,那谁就去实现这个抽象方法enjoy()。
 28  * @author gacl
 29  *
 30  */
 31 abstract class Cat extends Animal {
 32 
 33     /**
 34      * Cat添加自己独有的属性
 35      */
 36     public String eyeColor;
 37 
 38     public Cat(String n, String c) {
 39         super(n);//调用父类Animal的构造方法
 40         this.eyeColor = c;
 41     }
 42 }
 43 
 44 /**
 45  * 子类BlueCat继承抽象类Cat,并且实现了从父类Cat继承下来的抽象方法enjoy
 46  * @author gacl
 47  *
 48  */
 49 class BlueCat extends Cat {
 50 
 51     public BlueCat(String n, String c) {
 52         super(n, c);
 53     }
 54 
 55     /**
 56      * 实现了抽象方法enjoy
 57      */
 58     @Override
 59     public void enjoy() {
 60         System.out.println("蓝猫叫...");
 61     }
 62     
 63 }
 64 
 65 /**
 66  * 子类Dog继承抽象类Animal,并且实现了抽象方法enjoy
 67  * @author gacl
 68  *
 69  */
 70 class Dog extends Animal {
 71     /**
 72      * Dog类添加自己特有的属性
 73      */
 74     public String furColor;
 75 
 76     public Dog(String n, String c) {
 77         super(n);//调用父类Animal的构造方法
 78         this.furColor = c;
 79     }
 80 
 81     @Override
 82     public void enjoy() {
 83         System.out.println("狗叫....");
 84     }
 85 
 86 }
 87 
 88 public class TestAbstract {
 89 
 90     /**
 91      * @param args
 92      */
 93     public static void main(String[] args) {
 94 
 95         /**
 96          * 把Cat类声明成一个抽象类以后,就不能再对Cat类进行实例化了,
 97          * 因为抽象类是残缺不全的,缺胳膊少腿的,因此抽象类不能被实例化。
 98          */
 99         //Cat c = new Cat("Catname","blue");
100         Dog d = new Dog("dogname","black");
101         d.enjoy();//调用自己实现了的enjoy方法
102         
103         BlueCat c = new BlueCat("BlueCatname","blue");
104         c.enjoy();//调用自己实现了的enjoy方法
105     }
106 }
View Code

二、接口(interface)

接口,比抽象类还要抽象的类。

接口

如:接口MyInterface

实现类:MyClass

1. 为什么需要接口?接口和抽象类的区别?

  • 接口就是比“抽象类”还“抽象”的“抽象类”,可以更加规范的对子类进行约束。全面地专业地实现了“规范和具体实现的分离。
  • 接口就是规范,定义的是一组规则,体现了现实世界中“如果你是……则必须能……”的思想。(如果你是天使,则必须能飞,如果你是汽车,则必须能跑……)
  • 接口的本质是契约,就像我们人间的法律一样,制定好后大家都遵守。
  • 项目的具体需求多变,我们必须以不变应万变才能从容开发,此处的不变就是规范。因此,我们开发项目往往都是面向接口编程!

2. 如何定义接口?

格式:

3. 接口要点

  1. 子类通过implements来实现接口中的规范;
  2. 接口不能创建实例,但是可以用于声明引用变量类型;
  3. 一个类实现了接口,必须实现接口中所有的方法,并且这些方法只能是public;
  4. 接口支持多继承
  5. 接口和接口之间可以相互继承,类和类之间可以相互继承,类和接口之间,只能是类来实现接口

   JAVA是只支持单继承的,但现实之中存在多重继承这种现象,如“金丝猴是一种动物”,金丝猴从动物这个类继承,同时“金丝猴是一种值钱的东西”,金丝猴从“值钱的东西”这个类继承,同时“金丝猴是一种应该受到保护的东西”,金丝猴从“应该受到保护的东西”这个类继承。这样金丝猴可以同时从 “动物类”、“值钱的东西类”、“应该受到保护的东西” 这三个类继承,但由于JAVA只支持单继承,因此金丝猴只能从这三个类中的一个来继承,不能同时继承这三个类。因此为了封装现实生活中存在的多重继承现象,为了实现多继承,可以把其中的两个类封装成接口。使用接口可以帮助我们实现多重继承。

  接口(interface)是一种特殊的抽象类,在这种抽象类里面,所有的方法都是抽象方法,并且这个抽象类的属性(即成员变量)都是声明成“public static final 类型 属性名”这样的,默认也是声明成“public static final”即里面的成员变量都是公共的、静态的,不能改变的。因此在接口里面声明常量的时候,可以写成“public static final 类型 常量名=value(值)”这样的形式,也可以直接写成“类型 常量名=value(值)如:“public static final int id=10”可以直接写成“int id=10”这样的形式,因为在接口里面默认的属性声明都是“public static final”的,因此“public static final”可以省略不写。在接口里面声明的抽象方法可以不写abstract关键字来标识,因为接口里面所有的方法都是抽象的,因此这个“abstract”关键字默认都是省略掉的,如在一个接口里面声明这样的三个方法:“public void start()”、“public void run()”、“public void stop()”这三个方法前面都没有使用abstract关键字来标识,可它们就是抽象方法,因为在接口里面的声明的方法都是抽象方法因此在接口里面的抽象方法都会把abstract关键字省略掉,因为默认声明的方法都是抽象的,所以就没有必要再写“abstract”字了,这一点与在抽象类里面声明抽象方法时有所区别,在抽象类里面声明抽象方法是一定要使用“abstract”关键字的,而在接口里面声明抽象方法可以省略掉“abstract。注意:在接口里面声明的抽象方法默认是“public(公共的)”的,也只能是“public(公共的)”之所以要这样声明是为了修正C++里面多重继承的时候容易出现问题的地方,C++的多继承容易出现问题,问题在于多继承的多个父类之间如果他们有相同的成员变量的时候,这个引用起来会相当地麻烦,并且运行的时候会产生各种各样的问题。JAVA为了修正这个问题,把接口里面所有的成员变量全都改成static final,成员变量是static类型,那么这个成员变量就是属于整个类里面的,而不是专属于某个对象。对于多重继承来说,在一个子类对象里面实际上包含有多个父类对象,而对于单继承来说,子类对象里面就只有一个父类对象。多继承子类对象就有多个父类对象,而这些父类对象之间可能又会存在有重复的成员变量,这就非常容易出现问题,因此在JAVA里面避免了这种问题的出现,采用了接口这种方式来实现多继承。作为接口来说,一个类可以从接口继承(或者叫实现接口),这也是多继承,接口里面的成员变量不专属于某个对象,都是静态的成员变量,是属于整个类的,因此一个类去实现多个接口也是无所谓的,不会存在对象之间互相冲突的问题。实现多个接口,也就实现了多重继承,而且又避免了多重继承容易出现问题的地方,这就是用接口实现多重继承的好处。

 

 

参数

抽象类

接口

默认的方法实现

它可以有默认的方法实现

接口完全是抽象的。它根本不存在方法的实现

实现

子类使用extends关键字来继承抽象类。如果子类不是抽象类的话,它需要提供抽象类中所有声明的方法的实现。

子类使用关键字implements来实现接口。它需要提供接口中所有声明的方法的实现

构造器

抽象类可以有构造器

接口不能有构造器

与正常Java类的区别

除了你不能实例化抽象类之外,它和普通Java类没有任何区别

接口是完全不同的类型

访问修饰符

抽象方法可以有publicprotecteddefault这些修饰符

接口方法默认修饰符是public。你不可以使用其它修饰符。

main方法

抽象方法可以有main方法并且我们可以运行它

接口没有main方法,因此我们不能运行它。

多继承

抽象方法可以继承一个类和实现多个接口

接口只可以继承一个或多个其它接口

速度

它比接口速度要快

接口是稍微有点慢的,因为它需要时间去寻找在类中实现的方法。

添加新方法

如果你往抽象类中添加新的方法,你可以给它提供默认的实现。因此你不需要改变你现在的代码。

如果你往接口中添加方法,那么你必须改变实现该接口的类。


相关链接:

java基础学习总结——抽象类

java基础学习总结——接口

posted @ 2016-02-15 16:06  萌小Q  阅读(1142)  评论(1编辑  收藏  举报