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!

相关文章
|
3天前
|
Java
java基础(12)抽象类以及抽象方法abstract以及ArrayList对象使用
本文介绍了Java中抽象类和抽象方法的使用,以及ArrayList的基本操作,包括添加、获取、删除元素和判断列表是否为空。
10 2
java基础(12)抽象类以及抽象方法abstract以及ArrayList对象使用
|
13天前
|
Java
Java——抽象类和接口
抽象类是一种不能被实例化的类,至少包含一个抽象方法(无实现体的方法),常用于定义一组相关类的共同特征,并强制子类实现特定方法。抽象方法不能被 `static` 或 `final` 修饰,且必须被重写。 接口则是一个完全抽象的类,用于规范类的行为。接口使用 `interface` 关键字定义,不能实例化,并且类与接口之间是实现关系。 内部类是在一个类内定义的类,分为成员内部类、静态内部类、局部内部类和匿名内部类。成员内部类可被修饰符修饰,静态内部类只能访问外部类的静态成员,局部内部类定义在方法内,匿名内部类则隐藏了名字,直接通过 `new` 关键字定义并实现接口或继承类。
13 5
Java——抽象类和接口
|
13天前
|
Java
Java——接口的使用实例
Comparable接口用于自定义类的对象比较。通过实现此接口并重写`compareTo`方法,可以定义自定义类型的比较规则。 接下来介绍了Comparator接口,它提供了一种更灵活的比较方式。通过实现Comparator接口并重写`compare`方法,可以根据不同属性定义不同的比较规则。例如,定义一个`BrandComparator`类来比较汽车的品牌。 最后,介绍了Cloneable接口,用于实现对象的克隆。实现该接口并重写`clone`方法后,可以创建对象的浅拷贝或深拷贝。浅拷贝仅复制对象本身,深拷贝则会递归复制所有成员变量。
14 4
Java——接口的使用实例
|
2天前
|
Java
Java 抽象类详解
在 Java 中,抽象类是一种特殊类,用于提供基础定义并派生具体子类,支持代码复用。它包含抽象方法(无实现)和具体方法(有实现),不能被实例化,需用 `abstract` 关键字定义。子类必须实现所有抽象方法,除非子类也是抽象类。抽象类可用于定义模板或框架,确保子类实现特定方法。通过示例展示了如何定义抽象类 `Animal` 及其子类 `Dog` 和 `Cat`,并在主类中调用相关方法。使用抽象类可以提高代码复用性和设计质量,但也可能增加维护难度和性能开销。
|
7天前
|
Java
领略Lock接口的风采,通过实战演练,让你迅速掌握这门高深武艺,成为Java多线程领域的武林盟主
领略Lock接口的风采,通过实战演练,让你迅速掌握这门高深武艺,成为Java多线程领域的武林盟主
21 7
|
4天前
|
JSON 前端开发 JavaScript
java中post请求调用下载文件接口浏览器未弹窗而是返回一堆json,为啥
客户端调接口需要返回另存为弹窗,下载文件,但是遇到的问题是接口调用成功且不报错,浏览器F12查看居然返回一堆json,而没有另存为弹窗; > 正确的效果应该是:接口调用成功且浏览器F12不返回任何json,而是弹窗另存为窗口,直接保存文件即可。
26 2
|
19天前
|
Java 数据库连接 数据库
Java服务提供接口(SPI)的设计与应用剖析
Java SPI提供了一种优雅的服务扩展和动态加载机制,使得Java应用程序可以轻松地扩展功能和替换组件。通过合理的设计与应用,SPI可以大大增强Java应用的灵活性和可扩展性。
47 18
|
17天前
|
Java 开发者
Java的接口详解
Java接口是编程中的一种重要特性,用于定义方法签名而不提供具体实现,作为类之间的契约,使不同类能以统一方式交互。接口使用`interface`关键字定义,可包含方法声明和常量。类通过`implements`关键字实现接口,并可同时实现多个接口,解决多重继承问题。接口中的方法默认为抽象方法,变量默认为`public static final`。Java 8引入了默认方法和静态方法,增强接口功能。接口广泛应用于回调机制和多态性实现,有助于构建更灵活和可维护的代码结构。
|
27天前
|
Java
盘点java8 stream中隐藏的函数式接口
`shigen`是一位坚持更新文章的博客作者,记录成长历程,分享认知见解,留住感动瞬间。本文介绍了函数式接口的概念及其在Java中的应用,包括`Comparator`、`Runnable`、`Callable`等常见接口,并详细讲解了`Function`、`Predicate`、`Consumer`、`Supplier`和`Comparator`等函数式接口的使用方法及应用场景,展示了如何利用这些接口简化代码并提高编程效率。**个人IP:shigen**,与shigen一起,每天进步一点点!
31 0
盘点java8 stream中隐藏的函数式接口
|
28天前
|
Java 编译器 开发者
Java中的Lambda表达式与函数式接口
【8月更文挑战第31天】本文将深入探讨Java 8中引入的Lambda表达式和函数式接口,它们如何改变我们编写代码的方式。通过简化集合操作、事件处理等示例,我们将看到这些特性如何提升代码可读性、减少冗余,并提高开发效率。准备好一起探索这个让Java编程更加简洁强大的新世界吧!