Java抽象类和接口

简介: Java抽象类和接口

一、抽象类

🍑1、抽象类的概念

抽象: 所谓抽象就是抽出像的部分。是从多个事物中将共性的,本质的内容抽取出来。

抽象方法: 多个对象都具备相同的功能,但是功能具体内容有所不同,那么在抽取过程中,只抽取了功能定义,并未抽取功能主体,那么只有功能声明,没有功能主体的 方法称为抽象方法

🍑2、抽象类的定义和使用

其实抽象类的语法并不难理解,无非是增加了abstract关键字,多了一些限制:

abstract class Person {
    private String name;
    private int age;

    //抽象方法
    abstract public void doSport();//抽象方法,只声明不实现
    //普通方法
    public void eat() {
        System.out.println(name+"吃饭!");
    }
    public void sleep() {
        System.out.println(name+"睡觉!");
    }

    //构造器
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
}
class Student extends Person {

    //必须帮助抽象父类实现构造
    public Student(String name, int age) {
        super(name, age);
    }
    //必须重写抽象方法
    @Override
    public void doSport() {
        System.out.println("唱,跳,rap,打篮球!");
    }
}
//抽象类的使用
public class Test {
    public static void main(String[] args) {
        Student student=new Student("路人甲",18);
        Person person=student;//通过接收子类引用达到使用目的
        person.doSport();
        person.eat();
        person.sleep();
    }

🍑3、抽象类的特性

通过上面的例子我们大致了解了抽象类的定义和使用,下面就总结一下抽象类的特性:

  1. 抽象类不能直接实例化对象。抽象类的存在就是被继承的。
  2. 抽象方法不能被privatefinalstatic修饰,因为抽象方法要被子类重写,需要满足重写条件。
  3. 抽象类必须被继承,并且继承后子类要重写父类中的抽象方法,除非子类也是abstract修饰的抽象类。
  4. 抽象类中不一定包含抽象方法,但是有抽象方法的类一定是抽象类。
  5. 抽象类中可以有构造方法,供子类创建对象时,初始化父类的成员变量。

🍑4、抽象类的作用

抽象类本身不能被实例化,要想使用,只能创建该抽象类的子类。然后让子类重写抽象类中的抽象方法。抽象类不能实例化,这让人看上去只是相比普通父类类增加了一个不必要的限制。其实不然:

  1. 使用抽象方法,而非空方法体,子类就知道他必须要实现该方法,而不可能忽略。
  2. 使用抽象类,类的使用者创建对象的时候,就知道他必须要使用某个具体子类,而不可能误用不完整的父类。

引入抽象方法和抽象类,是Java提供的一种语法工具,对于一些类和方法,引导使用者正确使用它们,减少被误用。无论是写程序,还是平时做任何别的事情的时候,每个人都可能会犯错,减少错误不能只依赖人的优秀素质,还需要一些机制,使得一个普通人都容易把事情做对,而难以把事情做错。抽象类就是Java提供的这样一种机制。这种机制也可以配合编译器在编译期间让我们提早发现错误,防患于未然!


二、接口

🍑1、接口的概念

接口: 接口就是一种规范,(就像人间的法律一样),每个实现类都需要遵守。在Java中,接口可以看成是多个类的公共规范,是一种引用数据类型。

接口这个概念在生活中并不陌生,电子世界中一个常见的接口就是USB接口。电脑往往有多个USB接口,可以插各种USB设备,可以是键盘、鼠标、U盘、摄像头、手机等等。

接口声明了一组能力,但它自己并没有实现这个能力,它只是一个约定,它涉及交互两方对象,一方需要实现这个接口,另一方使用这个接口,但双方对象并不直接互相依赖,它们只是通过接口间接交互。

🍑2、为什么使用接口

我刚接触到接口的时,对它的表述以及相关的功能都感觉非常模棱两可,不能理解接口存在的意义,不知道什么场景下使用接口,以及为何存在接口。经过我的一番折腾,我在《Java编程的逻辑》一书中找到了如下观点:

之前我们一直在说,程序主要就是数据以及对数据的操作,而为了方便操作数据,高级语言引入了数据类型的概念,Java定义了八种基本数据类型,而类相当于是自定义数据类型,通过类的组合和继承可以表示和操作各种事物或者说对象。

但,这种只是将对象看做属于某种数据类型,并按该类型进行操作,在一些情况下,并不能反映对象以及对对象操作的本质。

为什么这么说呢?很多时候,我们实际上关心的,并不是对象的类型,而是对象的能力,只要能提供这个能力,类型并不重要。我们来看一些生活中的例子。

要拍个照片,很多时候,只要能拍出符合需求的照片就行,至于是用手机拍,还是用Pad拍,或者是用单反相机拍,并不重要,关心的是对象是否有拍出照片的能力,而并不关心对象到底是什么类型,手机、Pad或单反相机都可以。

要计算一组数字,只要能计算出正确结果即可,至于是由人心算,用算盘算,用计算器算,用电脑软件算,并不重要,关心的是对象是否有计算的能力,而并不关心对象到底是算盘还是计算器。

要将冷水加热,只要能得到热水即可,至于是用电磁炉加热,用燃气灶加热,还是用电热水壶,并不重要,重要的是对象是否有加热水的能力,而并不关心对象到底是什么类型。

在这些情况中,类型并不重要,重要的是能力。那如何表示能力呢?

Java使用接口这个概念来表示能力。

所以说接口并不依赖于类,它唯一注重的是能力或者说是功能。就比如fly-飞飞行的能力不依赖于任何一个Bird类或者说Plane类,无论是鸟还是飞机,它们都具有飞行的能力,因此我们可以将这个不依赖于类的功能抽象出来,化为一个通用的接口。

🍑3、定义接口

接口的定义格式与定义类的格式基本相同,接口的关键字是interface

public interface IUsb {
  String func = "这是一个USB接口";//变量量默认以public static final修饰
    void openDevice();  //方法默认以public abstract修饰
    void closeDevice(); //通常省略不写
}

注:

  1. 创建接口时, 接口的命名一般以大写字母 I 开头。
  2. 阿里编码规范中约定, 接口中的方法和属性不要加任何修饰符号, 保持代码的简洁性。

🍑4、实现接口

类可以实现接口,表示类的对象具有接口所表示的能力。Java中使用implements这个关键字表示实现接口,前面是类名,后面是接口名。

interface IUsb {

    String func = "这是一个USB接口";//变量量默认以public static final修饰
    void openDevice();  //方法默认以public abstract修饰
    void closeDevice(); //通常省略不写
}

public class KeyBoard implements IUsb {

    @Override
    public void openDevice() {
        System.out.println("链接键盘!");
    }

    @Override
    public void closeDevice() {
        System.out.println("断开链接!");
    }
}

Java中一个子类只能继承一个父类,即类和类之间是单继承的且不支持多继承,但是一个类可以实现多个接口,中间用,隔开。

interface IUsb {
    void openDevice();  //方法默认以public abstract修饰
    void closeDevice(); //通常省略不写
}

interface IGlow {
    void openLamp();
    void closeLamp();
}

interface IAdjust {
    void turnUp();
    void turnDown();
}


public class KeyBoard implements IUsb , IGlow , IAdjust {

    //……
}


上面的keyBoard类实现了三个接口IUsb、IGlow、IAdjust,表示一个带有USB接口并且可以发光和调节亮度的键盘。

🍑5、使用接口

与类不同,接口不能new,不能直接创建一个接口对象,对象只能通过类来创建。但可以声明接口类型的变量,通过接收引用来使用接口:

interface IFly {
    void fly();
}

class Bird implements IFly{
    private String name;

    public Bird(String name) {
        this.name = name;
    }

    @Override
    public void fly() {
        System.out.println(name+"用翅膀飞!");
    }
}

class Plane implements IFly{
    private String type;

    public Plane(String type) {
        this.type = type;
    }

    @Override
    public void fly() {
        System.out.println(type+"用机翼飞!");
    }
}


//测试
public class Test {
    public static void main(String[] args) {
      //创建接口变量,接收引用
        IFly fly1=new Bird("啄木鸟");
        IFly fly2=new Plane("战斗机");
        fly1.fly();
        fly2.fly();
    }
}

通过这个例子,我们可以简单体会到接口的强大威力,不同于抽象类需要依赖于类型,只要实现了相关接口,不管是什么类都可以使用接口,这就是接口强大的地方。

可以说,针对接口而非具体类型进行编程,是计算机程序的一种重要思维方式同一套代码可以处理多种不同类型的对象,只要这些对象都有相同的能力,更重要的是降低了耦合,提高了灵活性,使用接口的代码依赖的是接口本身,而非实现接口的具体类型,程序可以根据情况替换接口的实现,而不影响接口使用者。

🍑6、接口继承

在Java中,类和类之间是单继承的,一个类可以实现多个接口,接口与接口之间可以多继承。即:用接口可以达到多继承的目的。

interface IGlow {
    void openLamp();
    void closeLamp();
}

interface IAdjust {
    void turnUp();
    void turnDown();
}

interface IAuto extends IGlow,IAdjust {
    
}

public class KeyBoard implements IAuto {
    //……
}

上述IAuto接口同时继承了IGlowIAdjust接口,表示即可发光又可以调节亮度。此时KeyBoard实现IAuto接口相当于实现了两个接口,此时和多实现接口达到的效果相同。

注:接口间的继承相当于把多个接口合并在一起.

🍑7、接口的特性

了解了以上接口的语法,最后我们在来总结一下接口的特性:

接口类型是一种引用类型,但是不能直接new接口的对象

  1. 接口中每一个方法都是public的抽象方法, 即接口中的方法会被隐式的指定为 public abstract(只能是 public abstract,其他修饰符都会报错)
  2. 接口中可以含有变量,但是接口中的变量会被隐式的指定为 public static final 变量
  3. 接口中的方法是不能在接口中实现的,只能由实现接口的类来实现
  4. 重写接口中方法时,不能使用默认的访问权限,只能使用public(满足重写规则)
  5. 接口中不能有代码块构造方法

🍑8、再谈instanceof

在类中instanceof可以判断对象是否是类的实例,接口也可以使用instanceof关键字,用来判断一个对象是否实现了某接口:

public class Test {
    public static void main(String[] args) {
        IFly fly1=new Bird("啄木鸟");
        IFly fly2=new Plane("战斗机");
        fly1.fly();
        fly2.fly();
        //判断对象fly1是否实现了IFly接口
        if(fly1 instanceof IFly) {
            System.out.println("Yes");
        }
        //判断对象fly1是否是Bird的实例
        if(fly1 instanceof Bird) {
            System.out.println("Yes");
        }

    }
}


三、抽象类和接口

Java中抽象类和接口都是非常重要的部分,同时面试也会经常问到,下面就详细总结一下Java中的抽象类和接口:

语法层面的区别:

核心区别: 抽象类中可以包含普通方法和普通字段, 这样的普通方法和字段可以被子类直接使用(不必重写), 而接口中不能包含普通方法, 子类必须重写所有的抽象方法。

参数 抽象类 接口
声明 抽象类使用abstract关键字声明 接口使用interface关键字声明
实现 子类使用extends关键字来继承抽象类。如果子类不是抽象类的话,它需要提供抽象类中所有声明方法的实现 子类使用implements关键字来实现接口。他需要提供接口中所有声明方法的实现
构造器 抽象类可以有构造器 接口不能有构造器
访问修饰符 抽象类中的方法可以是任意访问修饰符 接口方法默认修饰符是public。并且不允许定义为private或protected
多继承 一个类最多只能继承一个抽象类 一个类可以实现多个接口
字段声明 抽象类的字段声明可以是任意的 接口的字段默认都是static和final

不管是抽象类还是普通类,我们都是在类型的基础上实现对对象相关的操作,而接口是将对象的某一种能力抽离出来,是针对对象的某一能力进行的操作。但是它们在设计的本质上又有什么区别呢?通过一番检索我在贴吧上找到了如下设计层面的区别,大家可了解一下👇

设计层面的区别:

(1)抽象类是对一种事物的抽象,即对类抽象,而接口是对行为的抽象。抽象类是对整个类整体进行抽象,包括属性、行为,但是接口却是对类局部(行为)进行抽象。

例子: 举个简单的例子,飞机和鸟是不同类的事物,但是它们都有一个共性,就是都会飞。那么在设计的时候,可以将飞机设计为一个类Plane,将鸟设计为一个类Bird,但是不能将飞行 这个特性也设计为类,因此它只是一个行为特性,并不是对一类事物的抽象描述。此时可以将 飞行设计为一个接口Fly,包含方法fly(),然后Airplane和Bird分别根据自己的需要实现Fly这个接口。从这里可以看出,继承是一个"是不是"的关系,而 接口实现则是"有没有"的关系。如果一个类继承了某个抽象类,则子类必定是抽象类的种类,而接口实现则是有没有、具备不具备的关系,比如鸟是否能飞(或者是否具备飞行这个特点),能飞行则可以实现这个接口,不能飞行就不实现这个接口。

(2)设计层面不同,抽象类作为很多子类的父类,它是一种模板式设计。而接口是一种行为规范,它是一种辐射式设计。

例子: 最简单例子,大家都用过ppt里面的模板,如果用模板A设计了ppt B和ppt C,ppt B和pptC公共的部分就是模板A了,如果它们的公共部分需要改动,则只需要改动模板A就可以了,不需要重新对pptB和pptC进行改动。而辐射式设计,比如某个电梯都装了某种报警器,一旦要更新报警器,就必须全部更新。也就是说对于抽象类,如果需要添加新的方法,可以直接在抽象类中添加具体的实现,子类可以不进行变更;而对于接口则不行,如果接口进行了变更,则所有实现这个接口的类都必须进行相应的改动。


小结

本篇文章我们只谈了两件事:抽象类-接口。抽象类和接口是Java中两个非常重要的概念,它们各有独特的优势,共同特点是都很抽象。本章主要探讨的是它们的语法,想要真正的深入理解它们的关系和功能,还需要我们多加练习和应用。Just coding!

相关文章
|
17天前
|
算法 Java 数据处理
从HashSet到TreeSet,Java集合框架中的Set接口及其实现类以其“不重复性”要求,彻底改变了处理唯一性数据的方式。
从HashSet到TreeSet,Java集合框架中的Set接口及其实现类以其“不重复性”要求,彻底改变了处理唯一性数据的方式。HashSet基于哈希表实现,提供高效的元素操作;TreeSet则通过红黑树实现元素的自然排序,适合需要有序访问的场景。本文通过示例代码详细介绍了两者的特性和应用场景。
33 6
|
17天前
|
存储 Java 数据处理
Java Set接口凭借其独特的“不重复”特性,在集合框架中占据重要地位
【10月更文挑战第16天】Java Set接口凭借其独特的“不重复”特性,在集合框架中占据重要地位。本文通过快速去重和高效查找两个案例,展示了Set如何简化数据处理流程,提升代码效率。使用HashSet可轻松实现数据去重,而contains方法则提供了快速查找的功能,彰显了Set在处理大量数据时的优势。
27 2
|
3天前
|
Java 开发者
在Java多线程编程的世界里,Lock接口正逐渐成为高手们的首选,取代了传统的synchronized关键字
在Java多线程编程的世界里,Lock接口正逐渐成为高手们的首选,取代了传统的synchronized关键字
19 4
|
10天前
|
安全 Java
在 Java 中使用实现 Runnable 接口的方式创建线程
【10月更文挑战第22天】通过以上内容的介绍,相信你已经对在 Java 中如何使用实现 Runnable 接口的方式创建线程有了更深入的了解。在实际应用中,需要根据具体的需求和场景,合理选择线程创建方式,并注意线程安全、同步、通信等相关问题,以确保程序的正确性和稳定性。
|
8天前
|
Java
Java基础(13)抽象类、接口
本文介绍了Java面向对象编程中的抽象类和接口两个核心概念。抽象类不能被实例化,通常用于定义子类的通用方法和属性;接口则是完全抽象的类,允许声明一组方法但不实现它们。文章通过代码示例详细解析了抽象类和接口的定义及实现,并讨论了它们的区别和使用场景。
|
8天前
|
Java 测试技术 API
Java零基础-接口详解
【10月更文挑战第19天】Java零基础教学篇,手把手实践教学!
16 1
|
10天前
|
Java 测试技术 开发者
Java零基础-抽象类详解
【10月更文挑战第17天】Java零基础教学篇,手把手实践教学!
10 2
|
12天前
|
Java 测试技术 开发者
Java零基础-抽象类详解
【10月更文挑战第15天】Java零基础教学篇,手把手实践教学!
17 2
|
13天前
|
Java 开发者
在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口
【10月更文挑战第20天】在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口。本文揭示了这两种方式的微妙差异和潜在陷阱,帮助你更好地理解和选择适合项目需求的线程创建方式。
13 3
|
13天前
|
Java
在Java多线程编程中,实现Runnable接口通常优于继承Thread类
【10月更文挑战第20天】在Java多线程编程中,实现Runnable接口通常优于继承Thread类。原因包括:1) Java只支持单继承,实现接口不受此限制;2) Runnable接口便于代码复用和线程池管理;3) 分离任务与线程,提高灵活性。因此,实现Runnable接口是更佳选择。
27 2