抽象类
抽象类的意义何在?
表面上看抽象类就是其中的抽象方法 不写方法体 只写一个方法声明:
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(); // 调用默认方法
}
}
注意默认方法 可以在接口中有具体方法实现了!
而且更神奇的是 默认方法也允许你重写,你重写就调用重写的方法,不重写就调用接口中的默认方法。
设计者真心人性化
最后说到接口 可以去设计模式专栏里面 看看适配器设计模式