Java学习之旅(四六):抽象类与接口

本文深入探讨Java中抽象类与接口的概念、定义方法及应用场景,解析二者之间的区别与联系,帮助开发者更好地理解和运用。
摘要由CSDN通过智能技术生成

通常可以说四边形具有四条边,或者也可以说平行四边形是具有对边平行且相等的特征的特殊四边形,等腰三角形是具有两条边相等的特性的特殊三角形,这些文字的描述都是和合乎情理的。但对于图形图像来说,却不能使用具体的语言进行描述,它有几条边,究竟是什么图形,没有人能够说清楚,这种类在 Java 中被定义为抽象类。

抽象类

在解决实际问题时,一般将父类定义为抽象类,需要使用父类进行继承与多态处理。在继承树中,越是在上方的类越抽象,如平行四边形类继承四边形类,四边形类继承图形类,又比如鸽子类继承鸟类,鸟类继承动物类等。但是在多态体制中,并不需要将父类初始化对象,我们需要的只是子类的对象,所以在 Java 中设置抽象类不可以实例化对象,因为图形类不能抽象出任何一种具体图形,但它的子类却可以。抽象类的定义语法如下:

public abstract class Test {
    // 定义抽象方法
    abstract void print();
}

其中, abstract 为定义抽象类的关键字。使用 abstract 关键字定义的类称为抽象类,而是用这个关键字定义的方法称为抽象方法。抽象方法没有方法体,这个方法本身没有任何意义,除非它被重写,而承载这个抽象方法的抽象类必须被继承,实际上抽象类除了被继承也没有其他意义。

反过来说就是,如果声明一个抽象的方法,就必须将承载这个抽象方法的类定义为抽象类,不可能在非抽象类中获取抽象方法。只要类中有一个抽象方法,这个类就要被标记为抽象类。

抽象类被继承后需要实现其中所有的抽象方法,也就是保证相同的方法名称、参数列表和相同的返回值类型创建出非抽象方法,当然也可以是抽象方法。

如上图所示,创建一个 Graph 的抽象类,其中有两个抽象方法 draw() 和 say(),接着又创建了 Quadrilateral 类和 Triangle 类,这两个类都继承了 Graph 类,所以都需要去重写 Graph 类中的两个抽象方法,除此之外,Quadrilateral 类中又声明了一个 doAnything() 的抽象方法,所以 Quadrilateral 类也是一个抽象类,该类中总共有三个方法,之后又创建了 Square 类和 Parallelogram 类去继承 Quadrilateral 类,自然而然需要重写 Quadrilateral 类中的抽象方法 doAnything()。

继承抽象类的所有子类需要将抽象类中的所有抽象方法进行覆盖重写。这样在多态机制中,就可以将父类修改为抽象类。如,将 draw() 方法设置为抽象方法,然后每个子类都去重写这个方法,但这样就会出现冗余代码,同时这样的父类局限性很大,也许某个子类不需要 draw() 方法,但是因为继承了抽象类,所以也不得不去重写这个 draw() 方法。如果将 draw() 方法放置在另外一个类中,让需要 draw() 方法的子类去继承该类,不需要 draw() 方法的子类只需要正常去继承抽象类即可。但是这样又有一个问题,所有的子类都需要 Graph 类,因为这些类都是从 Graph 类中导出的,同时某些子类还需要 draw() 方法,而 Java 中规定类不能同时继承多个父类。于是,接口的概念便产生了。

接口

接口是抽象类的延伸,可以将它看作是纯粹的抽象类,接口中的所有方法都没有方法体。比如我们上面说的这个问题,我们可以考虑将 draw() 方法封装到一个接口中,使需要 draw() 方法的类去实现这个接口,同时也继承图形类,这就是接口存在的必要性。

接口使用 interface 关键字进行定义,其语法为:

public interface drawTest() {

    void draw();

}

其中,interface 是用来声明接口类的关键字,public 是接口权限的修饰符,drawTest 是接口名称。一个类要实现该接口的话需要使用 implements 关键字。

在接口中,方法必须被定义为 public 或 abstract 形式,其他修饰权限不被 Java 编译器认可。或者说,即使不将该方法声明为 public 形式,它也是 public 形式。在接口中定义的任何字段都自动是 static 和 final 的。

示例:在项目中创建一个四边形类,并且在该类中创建两个继承该类的内部类,平行四边形类和正方形类;然后再另一个文件中创建 Draw 接口类,声明 draw() 方法,并使平行四边形和正方形两个内部类去实现该接口;然后在主方法中分别调用这两个内部类的 draw() 方法。

// 声明接口类
public interface Draw {

    public void draw();

}


/**
 * 四边形类
 */
public class Quadrilateral {

    public void doAnything() {

    }

    class Parallelogram extends Quadrilateral implements Draw {

        @Override
        public void draw() {
            System.out.println("平行四边形类继承了四边形类,并实现了Draw接口类中的draw()方法");
        }

        @Override
        public void doAnything() {
            // 继承了四边形类,所以需要覆盖父类的方法
        }

    }

    class Square extends Quadrilateral implements Draw {

        @Override
        public void doAnything() {
            // 继承了四边形类,需要覆盖父类的方法
        }

        @Override
        public void draw() {
            System.out.println("正方形类继承了四边形类,并实现了Draw接口类中的draw()方法");
        }

    }

    public static void main(String[] args) {
        Draw[] quadrilaterals = {
                new abstractClass.Parallelogram(),
                new abstractClass.Square()
        };
        for (int i = 0; i < quadrilaterals.length; i++) {
            quadrilaterals[i].draw();
        }
    }

}

运行 main() 方法,得到如下结果:

正方形类和平行四边形类分别实现了 Draw 接口并继承了四边形类,所以需要覆盖接口中的 draw() 方法。在调用 draw() 方法时,首先将平行四边形类对象与正方形类对象向上转型为 Draw 接口形式。在 Java 中,无论是将一个类向上转型为父类对象还是向上转型为抽象父类对象,或者向上转型为该类的实现接口,都是没有问题的。

Java 中不允许出现多继承,但使用接口就可以实现多继承。一个类可以同时实现多个接口,因此也可以将所有需要继承的接口放在 implements 关键字之后并使用逗号隔开。但这可能会在另一个类中产生庞大的代码量,因为继承一个接口时需要实现接口中所有的方法。

多重继承的语法为:

class 类名 implements 接口1,接口2,...

接口与抽象类的区别

相同点:抽象类和接口都不能被实例化;抽象类和接口都可以作为引用类型;一个类如果继承了某个抽象类或者实现了某个接口都需要对其中的抽象方法全部进行实现,否则就需要将该类声明为抽象类

不同点:

  • 抽象类中可以定义构造器,接口中不能定义构造器;
  • 抽象类中可以有抽象方法,也可以有具体方法,接口中的方法全部是抽象方法;
  • 抽象类中的成员可以是  private、default、protected、public 的,接口中的成员只能是 public 的;
  • 抽象类中可以定义成员变量,接口中定义的成员变量其实就是常量;
  • 有抽象方法的类必须被声明为抽象类,但是抽象类不一定要有抽象方法;
  • 抽象类中可以包含静态方法,接口中不能有静态方法;
  • 一个类只能继承一个抽象类,但一个类可以实现多个接口。

接口的设计目的是对类的行为进行约束,也可以理解为接口提供了一种机制,这种机制可以强制要求不同的类具有形同的行为。但是接口只约束了行为的有无,并没有对如何实现行为进行限制。也就是说只约束了你有没有这个行为,如果你有这个行为,你用来干什么我并不约束。

而抽象类设计的目的是代码复用。当不同的类具有某些相同的行为(记为行为集合 A)且其中一部分行为的实现方式一致(A 的非真子集)时,可以让这些类都派生与同一个抽象类。在这个抽象类中实现了 B,避免让所有的子类都实现 B,这就达到了代码复用的目的。而 A 减 B 的部分,留给各个子类自己实现。正是因为 A-B 在这里没有实现,所以抽象类不允许实例化出来,否则当调用 A-B 时,无法执行。

抽象类是对类本质的抽象,表达的是 is 的关系。抽象类包含并实现了子类的通用特性,将子类存在差异化的特征进行抽象,交由子类去实现。

而接口是对行为的抽象,表达的是 like 的关系,但其实本质上还是 is 的关系。接口的核心是定义行为,即实现类可以做什么,至于实现类主体是谁,如何实现,接口并不关心。

抽象类的功能要远远超过接口,但是定义抽象类的代价高。因为作为高级语言来说,每个类只能继承一个类,在这个类中,你必须继承或编写出其所有子类的所有共性。虽然接口在功能上会弱化许多,但是它只是针对一个动作的描述,而且可以在一个类中同时实现多个接口,这在设计阶段会降低难度的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值