Hey, Nice to meet You. 

必有过人之节.人情有所不能忍者,匹夫见辱,拔剑而起,挺身而斗,此不足为勇也,天下有大勇者,猝然临之而不惊,无故加之而不怒.此其所挟持者甚大,而其志甚远也.          ☆☆☆所谓豪杰之士,

夯实Java基础(十)----抽象类和接口

1、本章前言

之前介绍了Java的三大特征有封装、继承、多态,这样说其实我们少讲了一个,那就是抽象性,在平时的教程中认为只有三种特征,因为它们把抽象放到继承里了,认为抽象类是继承的一种,这也使抽象性是否是Java的一大特征具有争议。而Java语言中对抽象概念定义的机制就是抽象类和接口,正由于它们才赋予Java强大的面向对象的功能。他们两者之间对抽象概念的支持有很大的相似,但是也有区别。所以接下来我们就来学习它们两的使用和区别。(如果在平时面试时遇到问Java有几大特征,我们就说封装、继承、多态这三特征即可)。

2、抽象类

在Java面向对象的概念中,我们知道所有的对象都是通过类来描绘的,类是对象的抽象,而对象是类的具体实例。类是抽象的,不占用内存,而对象是具体的,会占用内存空间。但是有时候并不是所有的类都是用来描绘对象的,如果一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类。

抽象类是用来描述一种类型应该具备的基本特征与功能,而具体如何去完成这些行为则由其子类通过方法重写来完成。简单举个例子:比如我们创建一个Animal类,然后用这个类来创建一个动物对象,但是我们并不知道这具体是哪一种动物,只知道动物的一些基本特征和行为,比如有吃喝拉撒睡等,此时这个Animal对象是抽象的。所以我们需要一个具体的类来描述该动物,如用狗、猫来对它进行特定的描述,才知道它具体是什么动物。在描述的同时,狗和猫的特征也是不一样的,比如猫喜欢吃鱼,而狗喜欢吃骨头,所以此时不应该在动物类中将这些特征体现出来,而是要在Animal类的子类中给出一个声明即可,也就是重写父类中的方法。


在Java中用abstract关键字来修饰的类就是抽象类,当然这个关键字也可以用来修饰方法,表明该方法是抽象方法。

『以下是抽象类的一些特点(非常重要!!!)』:

  1. 抽象类不能实例化,必须要由继承它的子类来创建实例。
  2. 抽象方法只有方法的声明,没有方法体。抽象类中的抽象方法必须要在子类中重写。
  3. 抽象类中既可以有抽象方法,也可以有普通方法,普通方法可不用重写。
  4. 抽象类中不一定包含抽象方法,但是有抽象方法的类必定是抽象类。
  5. 抽象类中可以有构造方法,是供子类创建对象时,初始化父类成员变量使用的。
  6. 抽象类的子类,必须重写抽象父类中所有的抽象方法,否则,编译无法通过而报错。除非该子类也是抽象类。
  7. 只要包含一个抽象方法的类,该类必须要定义成抽象类。
  8. abstract不能用来修饰属性、构造器等结构。
  9. abstract不能与final并列修饰同一个类。
  10. abstract不能与private、static、final或native并列修饰同一个方法。

  • 抽象类:被abstract所修饰的类。
  • 抽象方法 :被abstract所修饰的方法,它是没有方法体的方法。

①、抽象类的语法格式:

【权限修饰符】 abstract class 类名{
    
}
【权限修饰符】 abstract class 类名 extends 父类{
    
}

②、抽象方法的语法格式:

【其他修饰符】 abstract 返回值类型  方法名(【形参列表】);

注意:抽象方法没有方法体

③、抽象类的简单示例,代码如下所示:

public abstract class Animal {
    //可以有属性
    String name;
    Integer age;

    //抽象方法
    public abstract void eat();

    //普通方法
    public void sleep() {
        System.out.println("动物需要睡觉...");
    }
    //抽象类可以有构造器
    public Animal() {
    }
}

class Cat extends Animal {
    @Override
    public void eat() {
        System.out.println("猫喜欢吃鱼...");
    }
    @Override
    public void sleep() {
        System.out.println("猫需要睡觉...");
    }
}

class Dog extends Animal {
    @Override
    public void eat() {
        System.out.println("狗喜欢吃骨头...");
    }
    @Override
    public void sleep() {
        System.out.println("狗需要睡觉...");
    }
}
//测试类
class Main{
    public static void main(String[] args) {
        Animal a1 = new Dog();
        a1.eat();
        a1.sleep();

        Animal a2 = new Cat();
        a2.eat();
        a2.sleep();
    }
}

3、接口

在我们的生活中,也常常会接触到接口这个词,比如我们的电脑边上提供了USB接口插槽,只要其它设备也是遵循USB接口的规范,那么就可以互联,并正常通信。至于这个电脑、以及其他设备是哪个厂家制造的,内部是如何实现的,我们都无需关心。

这种设计是将规范和实现分离,这也正是Java接口的好处。Java的软件系统会有很多模块组成,那么各个模块之间也应该采用这种面向接口的低耦合设计,为系统提供更好的可扩展性和可维护性。

接口的本质是契约,标准规范 ,其实 准确来说接口定义的就是一种规范。体现了现实世界中“如果你是/要...则必须能...”的思想。继承是一个"是不是"的is-a关系,而接口实现则是 "能不能"的has-a关系。

  • 例如:你能不能用USB接口进行连接,或是否具备USB通信功能,就看你是否遵循USB接口规范
  • 例如:Java程序是否能够连接使用某种数据库产品,那么要看该数据库产品有没有实现Java设计的JDBC规范

接口的英文名称是 interface。接口是属于和类类型同等级别的结构,但是它不是类,却和类类型有着共同点,在接口中可以含有变量、方法。接口使用interface这个关键字来进行修饰。

接口:用interface关键字修饰的类。

接口中的变量接口中的变量会被隐式地指定为public static final变量,并且只能是public static final变量,用private修饰会报编译错误。

接口中的方法接口中的方法会被隐式地指定为public abstract方法,且只能是public abstract方法,如果用其他关键字,如private、protected、static、 final等修饰都会导致报编译错误。

接口的实现:使用implement关键字。

也就是说接口中的变量全都是常量,方法都是抽象方法(JDK1.8以前)。从这里可以隐约看出接口和抽象类的区别,接口是一种极度抽象的类型,它比抽象类更加“抽象”。

Java8中的接口:在jdk8之前,interface之中可以定义变量和方法,变量必须是public、static、final的,方法必须是public、abstract的。在jdk8及以后,允许我们在接口中定义static方法和default方法。

  • default方法:必须用default关键字修饰,而且可以被实现类重写,它只能通过接口实现类的对象来调用。
  • static方法:只能通过接口名调用,不可以通过实现类的类名或者实现类的对象调用。

Java9中的接口:jdk9后接口中允许定义private的方法,用于服务于本接口其他方法。

①、接口的声明格式:

【修饰符】 interface 接口名{
    //接口的成员
    [public static final] 数据类型 变量名;//静态常量,默认修饰符public static final
    [public abstract] 返回值类型 方法名(形参列表);//抽象方法,默认自带修饰符public abstract
    default 方法名(形参列表){}//jdk8后,缺省方法,扩展方法,默认public修饰,实现类可以自行选择是否实现此方法
    static void testStatic(){}//jdk8后,默认public修饰,通常定义服务于此接口的实现类的一些工具类方法
    private void testPrivate(){}//jdk9后出现,服务于本接口其他方法。
}

接口的实现格式(接口可以多实现,类只能单继承):

修饰符 class 实现类名 implement 接口{
    //重写接口中的方法
    public 返回值类型 方法名(形参列表){
        方法体
    }
}
修饰符 class  实现类名 extends 父类 implements 接口名1,接口名2,...{
    //重写接口和抽象类中的抽象方法
}

接口的一些特点(都非常重要!!!):接口定义的是多个类共同的公共行为规范,这些行为规范是与外部交流的通道,这就意味着接口里通常是定义一组公共方法。

  • 接口没有构造方法,不能创建对象。
  • 成员变量默认自带修饰符public static final,即为静态常量
  • 抽象方法默认自带修饰符public abstract(jdk8之前版本接口中方法只能是抽象方法)
  • 接口是用来被实现的,其实现类必须重写它的所有抽象方法,除非实现类是个抽象类
  • 接口可以多实现,一个类可以同时实现多个接口
  • 接口可以继承接口,接口之间支持多继承
  • 在JDK1.8时,接口中允许声明默认方法和静态方法:
    • 公共的默认的方法:其中public 可以省略,建议保留,但是default不能省略
    • 公共的静态的方法:其中public 可以省略,建议保留,但是static不能省略
  • 在JDK1.9时,接口又增加了私有方法,用于服务于本接口其他方法
  • 如果子类(或实现类)继承的父类和实现的接口中声明了同名的成员变量,那么在调用的时候会报错,模糊不清。
  • 如果子类(或实现类)继承的父类和实现的接口中声明了同名同参数的方法,那么子类在没有重写该方法的情况下,默认调用父类中的同名同参数的方法。-->类优先原则。
  • 如果实现类实现了多个接口(没有继承),而多个接口中定义了同名同参数的默认方法,那么在实现类没有重写此方法的情况下,会报错。-->接口冲突。这就需要我们在实现类中重写此方法。
  • 如果需要调用父类中方法或接口中默认方法,父类使用 super . 方法名,接口使用 接口名.super.方法名。

这里讲的非常的细,其实我们只需要了解即可。

接口的简单举例,代码如下:

/**
 * 定义接口
 */
public interface InterfaceDemo {
    //接口成员变量(前面默认加了public static final)
    int MAX_VALUES = 1000;
    //接口抽象方法(前面默认加了public abstract)
    void abstractMethod();
    //接口默认方法(必须加default)
    default void defaultMethod() {
        System.out.println("接口default方法...");

    }
    //接口静态方法
    static void staticMethod() {
        System.out.println("接口static方法...");
    }
}
//定义接口的实现类
class InterfaceImpl implements InterfaceDemo {
    @Override
    public void abstractMethod() {
        System.out.println("重写接口中的abstract方法...");
    }
    @Override
    public void defaultMethod() {
        System.out.println("重写接口中的default方法...");
    }
}
//定义测试类
class Main {
    public static void main(String[] args) {
        InterfaceImpl ifi = new InterfaceImpl();
        ifi.abstractMethod();
        ifi.defaultMethod();
        System.out.println("接口中的常量值:" + InterfaceDemo.MAX_VALUES);
        InterfaceDemo.staticMethod();
    }
}

运行结果如下所示:

image

4、抽象类与接口的对比

抽象类和接口的主要区别:

  • 从设计层面上:抽象类是对类的抽象,是一种模板设计,所以抽象类常用作当做模板类使用。接口是行为的抽象,是一种行为的规范。
  • 从应用层面上:接口更多的是在系统架构设计方法发挥作用,主要用于定义模块之间的通信契约。而抽象类在代码实现方面发挥作用,可以实现代码的重用。
特点 抽象类 接口
继承限制 单继承 一个类可以实现多个接口,而且接口也可以继承多个接口
成员变量 只能是公共的静态的常量【public static final】(不写默认会加上)
构造器
代码块 可以有
抽象方法 可以有 只能是公共的抽象方法【public abstract】
静态方法 可以有 JDK1.8之后可以有公共的静态方法
默认方法 可以有 JDK1.8之后可以有公共的默认方法,必须用default修饰
私有方法 可以有 JDK1.9之后可以有私有方法
访问修饰符 抽象方法可以有publicprotecteddefault 接口方法默认修饰符是public,不可以使用其它修饰符
相同点 都不能直接实例化 都不能直接实例化
posted @ 2019-07-27 14:55  唐浩荣  阅读(402)  评论(0编辑  收藏  举报