理解java特性:抽象类和接口

抽象类

抽象类的意义何在?
表面上看抽象类就是其中的抽象方法 不写方法体 只写一个方法声明:

public abstract void eat();

这个eat方法 在基类中是一个抽象概念 不知道动物要吃什么 动物是一个总体概念

所以继承它的子类必须实现这个方法 把抽象变为具体

public abstract class Animal {

    @Setter
    @Getter
    private String name = "动物";

    @Setter
    @Getter
    private int age;

    @Setter
    @Getter
    private String color;


    public void show(){
        System.out.println("这是一个动物");
    }

   public abstract void eat();
}

那么这里引申出几个问题:

  • 你会发现抽象类中是可以有其他非抽象的东西的 比如一般的属性 方法等等
  • 如果出现抽象方法 那么它的类必须也带上 abstract 这是一个死规则
  • 抽象类是不能实例化 不能创建对象的 比如你这样写
    Animal a = new Animal();
    是会报错的,原因也很简单 因为它里面可能有抽象方法 抽象方法里面什么都没有 就一个声明 无法被调用 因为java不允许你直接实例化抽象类

最后回到最初的问题 抽象类的意义在哪里呢? 好像你不写抽象方法eat ,继承动物的猫狗类中 也照样可以写各自的eat方法
比较官方的回答是:

提供了一种约束和规范:抽象类可以定义一些方法的签名但不提供具体的实现,这些方法留给子类去实现。这样一来,子类必须实现这些抽象方法,从而使得子类在设计和实现时具有一定的规范和约束,确保了程序的结构和行为的一致性。

实现了代码复用和继承:通过继承抽象类,子类可以继承抽象类中定义的方法和属性,从而实现了代码的复用。这种继承机制使得子类可以在基类的基础上进行扩展和定制,同时避免了重复编写相似的代码。

面向抽象编程:抽象类可以被视为一种抽象的概念,它定义了一个通用的模板或接口,而不涉及具体的实现细节。这样一来,程序员可以针对抽象类编程,而不需要关心具体的子类实现细节,从而提高了代码的灵活性和可维护性。

实现了多态性:由于抽象类可以被子类继承并实现其抽象方法,因此可以通过父类类型引用指向子类对象,从而实现多态性。这种多态性使得代码更加灵活,能够在运行时根据对象的实际类型来调用相应的方法,从而实现了更加动态和可扩展的程序设计。

接口

接口的概念很简单,很抽象类很相似 为了照顾新手 也写一个例子:

// 定义一个接口
interface Animal {
    void makeSound(); // 接口中的方法只有方法签名,没有方法体
}

// 实现接口的类
class Dog implements Animal {
    @Override
    public void makeSound() {
        System.out.println("Dog barks");
    }
}

class Cat implements Animal {
    @Override
    public void makeSound() {
        System.out.println("Cat meows");
    }
}

public class Main {
    public static void main(String[] args) {
        Animal dog = new Dog(); // 通过接口类型引用指向实现类的对象
        Animal cat = new Cat(); // 同上

        dog.makeSound(); // 调用接口中定义的方法
        cat.makeSound(); // 同上
    }
}

这里注意:

  • 我们之前讲多态的时候 说个继承关系可以多态 子类对象指向父类引用,这里发现接口也可以多态!
  • 接口是行为规则定义 所以它里面没有 成员属性 比如动物的 name age type, 只能有常量!final修饰 意味着不能改
  • 接口中没有构造方法! 因为接口是行为规则的定义 而不是某个对象的定义

正确的理解区分抽象类和接口

这部分才是重点,新手往往都会疑惑一个事情 抽象类和接口有什么区别? 好像看着差不多

这里有一个重要的理解: 抽象类是定义的抽象, 而接口是行为的抽象。

怎么理解呢?

比如基类动物 继承类 猫 狗 牛 鸟

这时候出现一个吃eat方法, 这时候应该用抽象类还是接口???

答案是抽象类 因为吃是动物的一个基础属性 是个动物都会吃 不存在 不吃东西的动物,所以它放入抽象类中

如果出现一个飞fly方法, 这时候应该用哪个

答案是接口 因为飞是不是一个基础属性 而是一个选择性的行为 有的动物会飞 有的动物不会飞 那么会飞的动物就实现飞的接口就可以了


JDK8之后接口的变化:允许写具体方法

这是一个很重要的特性 新手一定要知道,因为它是接口的一次重大改良:
从前的接口aaa: 里面定义了 3个方法 1 2 3, 这时候有100个类实现了这个接口 也就必须每个类实现这3个方法。
现在的问题是 如果aaa里面多了一个方法4,那么100个类中全部要改。

这类情况要两说:

  • 如果这个方法4 每个类实现不一样 那没办法 你加班加点也得改!
  • 如果是特别的情况 这个方法4是一个静态、默认,或者统一的方法 能不能不用重复实现100遍!

这就是新特性!default

interface Animal {
    void makeSound(); // 抽象方法

    default void sleep() {
        System.out.println("Animal sleeps");
    }
}

class Dog implements Animal {
    @Override
    public void makeSound() {
        System.out.println("Dog barks");
    }
}

public class Main {
    public static void main(String[] args) {
        Animal dog = new Dog();
        dog.makeSound();
        dog.sleep(); // 调用默认方法
    }
}

注意默认方法 可以在接口中有具体方法实现了!

而且更神奇的是 默认方法也允许你重写,你重写就调用重写的方法,不重写就调用接口中的默认方法。

设计者真心人性化


最后说到接口 可以去设计模式专栏里面 看看适配器设计模式

  • 6
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值