当前位置: 首页 > news >正文

抽象类与接口:特征与比较

在Java面向对象编程中,抽象类和接口是两个基础且强大的概念。它们为代码设计提供了结构化和多态性的基础,但各自有不同的特点和使用场景。本文将详细介绍这两个概念的特征,并对它们进行比较分析。

抽象类的特征
抽象类是一种特殊的类,它不能被直接实例化,需要通过子类继承并实现其抽象方法才能使用。

1. 基本特征

使用abstract关键字声明
可以包含抽象方法(没有方法体的方法)
可以拥有普通方法、属性和构造方法
子类通过extends关键字继承

2. 重要特性

抽象类不能直接实例化对象
抽象方法不能是private的(因为需要被子类重写)
抽象方法不能被final和static修饰
如果类中有抽象方法,则该类必须声明为抽象类
子类必须实现父类中的所有抽象方法,否则子类也必须是抽象类

抽象类的注意的地方:
1.抽象类和抽象方法都使用abstract进行修饰
2.抽象类不能进行实例化,但是普通类是可以的!
3.抽象类不一定包含抽象方法,但是包含抽象方法的一定是抽象类。
4.抽象类当中 可以定义成员变量和成员方法!
5.当一个普通类 继承我们的抽象类了,此时在普通类当中一定要重写抽象类中的抽象方法!!!
6.抽象类存在的最大意义就是 为了被继承
7.当一个抽象类B继承了抽象类A,此时抽象类B不需要重写抽象类A的抽象方法,但是当一个普通类C,继承了抽象类B,此时就需要重写所有没有被重写的抽象方法(包含A,B抽象类的)
8.一定满足重写的要求
9.final关键字 不可能同时作用在一个方法或者类上(final和abstract天生冲突,因为abstract还需要重写,final不可以变动了)!
10.抽象类当中可以存在构造方法,在子类实例化的时候,会帮助父亲的成员进行初始化!

代码案例:
如果使用普通的继承画图像类

package demo1;


class Shape{

    public void draw(){

    }
}

class Cycle extends Shape{
    @Override
    public void draw() {
        System.out.println("⚪");
    }
}

class Rect extends Shape{
    @Override
    public void draw() {
        System.out.println("矩形");
    }
}
public class Test {
    public static void drawMap(Shape shape){
        shape.draw();
    }
    public static void main(String[] args) {
        drawMap(new Cycle());
        drawMap(new Rect());

    }
}

如果使用抽象类继承,代码

package demo2;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: laichangyang
 * Date: 2025-04-02
 * Time: 14:55
 */

//抽象类
abstract class Shape{
    public abstract void draw();
}

class Cyele extends Shape{

    @Override
    public void draw() {
        System.out.println("⚪");
    }
}

class Rect extends Shape{

    @Override
    public void draw() {
        System.out.println("矩形");
    }
}
public class Test {

    public static void drawMap(Shape shape){
        shape.draw();
    }

    public static void main(String[] args) {
        drawMap(new Cyele());
        drawMap(new Rect());
    }

}

好像和不使用抽象类实现的效果,为什么还要脱裤子放屁呢???
抽象类可以帮助我们检查是否重写了抽象方法,没有重写,就会报错,这个是它的重要作用。
还有就是,抽象类不可以实例化,但是如果不使用抽象类,父类(基类)就可以实例化了。

接口的特征

接口是一种完全抽象的类型,它只定义行为规范,不提供实现。

1. 基本特征

使用interface关键字声明
包含抽象方法(Java 8之前)
方法默认是public abstract
字段默认是public static final
类通过implements关键字实现接口

2. 重要特性

接口不能直接实例化
接口中的方法都是抽象的(Java 8前)
接口没有构造方法
接口不能包含普通变量,只能有常量
一个类可以实现多个接口
接口之间可以多继承(使用extends关键字)

接口:其实就是对一个标准的规范!!!可以算抽象类的进一步抽象

接口注意的地方:
1.接口是使用interface来进行定义的
2.接口当中 不能有实现的方法,但是有2种类型的方法需要注意:
2.1静态方法可以有具体的实现
2.2这个方法被default关键字修饰,也是可以的【1.8开始引入这个特征】
3.接口当中的方法默认是public abstract 修饰的
4.接口当中的成员变量默认是:public static final 修饰的
5.接口也不能通过关键字new 来进行实例化
6.类和接口之间 使用关键字implements来进行关联
7.当一个类实现一个接口后,这个类必须重写这个接口当中的抽象方法
8.当接口存在default方法,可以选择重写,也可以不重写,具体看业务需求
9.不管是接口还是抽象类,他们仍然是可以发生向上转型的
10.子类实现接口的方法的时候,这个方法一定是public修饰的!!!
11.接口当中不能有构造方法和代码块
12.一个类 不能实现接口当中的方法 这个类,可以定义为抽象类

一个类可以实现多个接口,可以解决多继承的问题!!!

如果继承和接口同时存在,先继承类,然后实现接口

如果使用接口实现前面的画图

package demo3;



interface Ishpe{
//    int age=1;//默认是 public static final

    void draw();//默认是public abstract

//    default public void draw2(){
//        System.out.println("1234");
//    }
//
//    public static void draw3(){
//        System.out.println("1234");
//    }

}

class Cycle implements Ishpe{

    @Override
    public void draw() {
        System.out.println("⚪");
    }

//    @Override
//    public void draw2() {
//        System.out.println("实现接口,default修饰方法的重写");
//    }
}

class Rect implements Ishpe{

    @Override
    public void draw() {
        System.out.println("矩形");
    }
}

public class Test {

    public static void drawMap(Ishpe shape){
        shape.draw();
    }

    public static void main(String[] args) {

        Ishpe ishpe= new Cycle();
        Ishpe ishpe1=new Rect();
        drawMap(ishpe);
        drawMap(ishpe1);
        drawMap(new Cycle());
        drawMap(new Rect());
    }
}

接口案例Computer类

先设计接口USB,包含打开和关闭驱动,两个抽象方法

package demo4;


public interface USB {

    void openDevice();

    void closeDevice();
}

然后设计电脑的组件,键盘和鼠标

package demo4;


public class KeyBoard implements USB{
    @Override
    public void openDevice() {
        System.out.println("打开键盘服务!");
    }

    @Override
    public void closeDevice() {
        System.out.println("关闭键盘服务!");
    }

    //键盘特有的方法
    public void input(){
        System.out.println("键盘输入文字");
    }
}

package demo4;

public class Mouse implements USB{
    @Override
    public void openDevice() {
        System.out.println("打开鼠标服务!");
    }

    @Override
    public void closeDevice() {
        System.out.println("关闭鼠标服务!");
    }

    //鼠标特有的方法
    public void click(){
        System.out.println("点击鼠标!");
    }
}

鼠标和键盘类,使用USB接口,需要重写接口的抽象方法,打开和关闭驱动,不同类又有自己特有的方法

接下来设计Computer类

package demo4;

public class Computer {

    public void open(){
        System.out.println("打开电脑!");
    }

    public void close(){
        System.out.println("关闭电脑!");
    }

    public void useDevice(USB usb){
        usb.openDevice();
        if(usb instanceof KeyBoard){
            KeyBoard keyBoard=(KeyBoard)usb;//向下转型
            keyBoard.input();
        }else if(usb instanceof  Mouse){
            Mouse mouse=(Mouse)usb;
            mouse.click();
        }
        usb.closeDevice();
    }

    public static void main(String[] args) {
        Computer computer=new Computer();
        computer.useDevice(new KeyBoard());
        System.out.println("_______________");
        computer.useDevice(new Mouse());
    }

}

使用Computer的使用,先new一个,先打开电脑,然后使用驱动,最后关闭电脑

代码的运行结果在这里插入图片描述

接口和继承一起使用的案例:

package demo5;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: laichangyang
 * Date: 2025-04-02
 * Time: 18:54
 */

interface IFlying{
    void fly();
}

interface ISwimming{
    void swim();
}

interface IRunning{
    void run();
}

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

    public Animal(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public abstract void eat();
}

class Dog extends Animal implements IRunning,ISwimming{

    public Dog(String name, int age) {
        super(name, age);
    }

    @Override
    public void swim() {
        System.out.println(this.getName()+"正在狗刨!");
    }

    @Override
    public void run() {
        System.out.println(this.getName()+"正在跑!");
    }

    @Override
    public void eat() {
        System.out.println(this.getName()+"正在吃狗粮!");
    }
}

class Bird extends Animal implements IFlying{

    public Bird(String name, int age) {
        super(name, age);
    }

    @Override
    public void fly() {
        System.out.println(this.getName()+"正在飞");
    }

    @Override
    public void eat() {
        System.out.println(this.getName()+"正在吃鸟粮");
    }
}

class Robot implements IRunning{

    @Override
    public void run() {
        System.out.println("机器人在跑");
    }
}

public class Test {

    public static void testFly(IFlying iFlying){
        iFlying.fly();
    }

    public static void testRun(IRunning iRunning){
        iRunning.run();
    }

    public static void testSwim(ISwimming iSwimming){
        iSwimming.swim();
    }

    public static void main(String[] args) {
        testFly(new Bird("小鸟",2));
        testSwim(new Dog("小狗",3));
        testRun(new Dog("小狗",5));
        testRun(new Robot());
    }
}

在这里插入图片描述

每一个接口可以代表一种属性,比起之前写死在动物类里面的优势在于,不是每一个动物都会飞和跑的,如果一个动物有这个属性,就调用这个接口就可以了。

如果给一个无序数组,大家都知道调用sort排序,那么是一个类,我们进行排序,怎么操作呢?

首先看数组的排序

package demo6;

import java.util.Arrays;

public class Test {

    public static void main(String[] args) {
        int[] array={1,8,9,6,5,4,3,2};
        Arrays.sort(array);
        System.out.println(Arrays.toString(array));
    }
}

在这里插入图片描述

我们先构建学生类,包含姓名,年龄,分数等信息,然后建立一个学生类的数组,然后对数组排序,观察排序前和排序后

package demo6;

import java.util.Arrays;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: laichangyang
 * Date: 2025-04-02
 * Time: 19:17
 */

class Student{
    public String name;
    public int age;
    public double score;

    public Student(String name, int age, double score) {
        this.name = name;
        this.age = age;
        this.score = score;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", score=" + score +
                '}';
    }
}
public class Test {

    public static void main(String[] args) {
        Student[] array= new Student[3];
        array[0]=new Student("zhangsan",19,85.5);
        array[1]=new Student("lisi",20,80.5);
        array[2]=new Student("wangwu",19,95.5);

        System.out.println("排序前:"+Arrays.toString(array));
        Arrays.sort(array);
        System.out.println("排序后:"+Arrays.toString(array));

    }

    public static void main1(String[] args) {
        int[] array={1,8,9,6,5,4,3,2};
        Arrays.sort(array);
        System.out.println(Arrays.toString(array));
    }
}

我们的程序明显是有问题的,排序的时候,我们根据什么排序,一个类有那么多属性。
输出结果:

通过观察
在这里插入图片描述
在这里插入图片描述

排序的时候是强制转换成Comparable类(一个接口),我们当然需要重写抽象的方法,那么我们使用一个接口,并且重写方法

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

最后发现还是不行,我们需要手动实现一下排序

最终的代码:

package demo6;

import java.util.Arrays;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: laichangyang
 * Date: 2025-04-02
 * Time: 19:17
 */

class Student implements Comparable<Student>{
    public String name;
    public int age;
    public double score;

    public Student(String name, int age, double score) {
        this.name = name;
        this.age = age;
        this.score = score;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", score=" + score +
                '}';
    }

    @Override
    public int compareTo(Student o) {
        int ret=this.age-o.age;
        if(ret>=0) return 1;
        else return 0;
    }
}

public class Test {

    public static void bubbleSort(Comparable[] comparables){
        for(int i=0;i<comparables.length-1;i++){
            for(int j=0;j<comparables.length-i-1;j++){
                if(comparables[j].compareTo(comparables[j+1])>0){
                    Comparable tmp=comparables[j];
                    comparables[j]=comparables[j+1];
                    comparables[j+1]=tmp;

                }
            }
        }
    }

    public static void main(String[] args) {
        Student[] array= new Student[3];
        array[0]=new Student("zhangsan",19,85.5);
        array[1]=new Student("lisi",20,80.5);
        array[2]=new Student("wangwu",19,95.5);

        System.out.println("排序前:"+Arrays.toString(array));
        //Arrays.sort(array);
        bubbleSort((array));
        System.out.println("排序后:"+Arrays.toString(array));

    }

    public static void main1(String[] args) {
        int[] array={1,8,9,6,5,4,3,2};
        Arrays.sort(array);
        System.out.println(Arrays.toString(array));
    }
}

在这里插入图片描述

还有一个接口Comparator也可以实现比较,看一下它怎么使用吧
具体使用

package demo7;

import java.util.Comparator;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: laichangyang
 * Date: 2025-04-02
 * Time: 19:54
 */

class Student{
    public String name;
    public int age;
    public double score;

    public Student(String name, int age, double score) {
        this.name = name;
        this.age = age;
        this.score = score;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", score=" + score +
                '}';
    }
}

class AgeComparator implements Comparator<Student>{

    @Override
    public int compare(Student o1, Student o2) {
        return o1.age-o2.age;
    }
}

class ScoreComparator implements Comparator<Student>{

    @Override
    public int compare(Student o1, Student o2) {
        return (int)(o1.score-o2.score);
    }
}
public class Test {

    public static void main(String[] args) {
        Student student1 = new Student("zhangsan",30,48);
        Student student2 = new Student("lisi",20,80);

        AgeComparator ageComparator= new AgeComparator();
        System.out.println(ageComparator.compare(student1,student2));

        System.out.println("------------");

        ScoreComparator scoreComparator = new ScoreComparator();
        System.out.println(scoreComparator.compare(student1,student2));
    }
}

在这里插入图片描述

抽象类和接口的比较

在这里插入图片描述

何时使用抽象类,何时使用接口?

使用抽象类的情况:

需要在多个相关类之间共享代码和状态
需要非公开的方法和成员变量
类之间有明确的继承关系和层次结构
希望修改基类后,所有派生类都受到影响

使用接口的情况:

不相关的类需要共享同一行为(如不同类实现Comparable接口)
需要多重继承
只关心对象能做什么,而不是它是什么
需要规范一组相关或不相关的类

总结

抽象类和接口是Java中实现抽象和多态的两种重要机制。抽象类更适合用于表示类之间的继承关系和共享实现,而接口则更适合定义对象的行为规范和能力。在实际开发中,我们需要根据具体需求选择合适的抽象机制,有时甚至需要结合使用两者,以创建灵活、可扩展的代码结构。
随着Java语言的发展,接口也在不断增强功能(如Java 8引入的默认方法),使得接口和抽象类之间的界限变得越来越模糊。但理解它们各自的特点和适用场景,仍然是Java开发者必须掌握的基础知识。

http://www.dtcms.com/a/107850.html

相关文章:

  • 【实用技巧】电脑重装后的Office下载和设置
  • 自动化测试(web测试)
  • vue-系统登录后进入页面【el-input】输入框无法正常显示?需要手动刷新才可以恢复设置样式?让我来瞅瞅~
  • 关于 UPDATE 语句 和 SELECT ... FOR UPDATE 的对比分析,包括语法、功能、锁机制、使用场景及示例代码
  • java知识梳理(二)
  • 【电子通识】为什么电子元件的规格书常常要看英文版本
  • 从 Credit Metrics 到 CPV:现代信用风险模型的进化与挑战
  • Windows家庭版如何开启Hyper-V与关闭Hyper-V
  • 面试常考简单操作
  • ADS7822中文技术手册
  • Burp靶场 - HTTP走私请求【Part2】
  • Elasticsearch collapse 的使用场景及作用机制
  • Linux驱动开发实战(十一):GPIO子系统深度解析与RGB LED驱动实践
  • es 集群存储字典 json字段----python实现
  • Conda安装ffmpeg
  • idea查看class字节码
  • Java高频面试题1:Java SE
  • SpaceX星舰商业载人首绕月球:私人太空旅行时代正式开启
  • mycat --分片规则--
  • [Android] 共生地球 v1.1.19 国产卫星地图
  • 详细介绍一下C++中的extern关键字
  • 搭建qemu环境
  • 【pcdet3D检测】——OPenpcdet如何进行测试文件配置?能否自定义测试数据?一文看懂pointpillar(pcdet)中的test.py
  • redis7.0搭建redis-cluster集群部署实战
  • AquaMoon and Chess_CodeForces - 1545B
  • AI前沿:资本狂潮下的技术暗战:巨头博弈、开源革命与生态重构
  • Java项目之基于ssm的简易版营业厅宽带系统(源码+文档)
  • Ubuntu 使用apt安装MySQL后的升级方法
  • Share02-小小脚本大大能量
  • 【面试篇】多线程