Java abstract和interface的区别

1. 抽象类(Abstract Class)

定义:抽象类是包含一个或多个抽象方法(没有实现的方法)的类,不能直接实例化,必须由子类继承并实现抽象方法。
使用abstract关键字:类和方法都需要用abstract关键字声明。
继承方式:抽象类可以使用普通的类继承机制,支持单继承(一个类只能继承一个抽象类)。
成员变量:可以包含实例变量(字段)、构造方法和已实现的方法(非抽象方法)。
访问修饰符:抽象类中的方法和成员变量可以使用所有的访问修饰符(public、protected、private)。
适用场景:当多个类有一些相同的功能,并且还需要一些差异化的实现时,可以使用抽象类。

interface Animal {
void sound();

// Java 8 开始的默认方法
default void sleep() {
System.out.println("Sleeping...");
}
}

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

2. 接口(Interface)

定义:接口是一种纯抽象类型,只能定义方法签名(方法没有具体实现),所有方法默认是public abstract。
使用interface关键字:接口使用interface关键字声明。
继承方式:接口支持多继承(一个类可以实现多个接口),这使得接口比抽象类更加灵活。
成员变量:接口中只能定义public static final的常量,不能有普通成员变量。
默认方法:从 Java 8 开始,接口可以有默认方法(default),这意味着接口可以提供方法的默认实现。
静态方法:从 Java 8 开始,接口中也可以定义静态方法。
适用场景:当你需要定义类的行为,而不需要关注类的实现细节时,适合使用接口。

interface Animal {
void sound();

// Java 8 开始的默认方法
default void sleep() {
System.out.println("Sleeping...");
}
}

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

具体区别
1. 继承 vs 实现

抽象类通过继承,接口通过实现。

抽象类的继承:

abstract class Animal {
public abstract void sound(); // 抽象方法
}

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

接口的实现:

interface Animal {
void sound(); // 接口中的方法默认是 public abstract
}

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

区别在于:Dog类通过extends关键字继承抽象类,而通过implements关键字实现接口。

2. 方法实现

抽象类可以有方法的实现,而接口(在 Java 8 之前)只能有方法声明。从 Java 8 开始,接口可以有默认方法。

抽象类的实现:

abstract class Animal {
public abstract void sound(); // 抽象方法

// 普通方法
public void eat() {
System.out.println("Eating...");
}
}

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

接口中的默认方法(Java 8+):

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

// 默认方法
default void eat() {
System.out.println("Eating...");
}
}

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

区别:抽象类中的eat()方法是可以直接在类中实现的,而接口中的eat()是通过default关键字实现的默认方法。

3. 成员变量

抽象类可以有实例变量,接口只能有public static final常量。

抽象类中的实例变量:

abstract class Animal {
String name; // 实例变量

public abstract void sound();

public void printName() {
System.out.println("Animal name is " + name);
}
}

class Dog extends Animal {
public Dog(String name) {
this.name = name;
}

@Override
public void sound() {
System.out.println("Dog barks");
}
}

接口中的常量:

interface Animal {
// 接口中的常量
String TYPE = "Mammal";

void sound();
}

class Dog implements Animal {
@Override
public void sound() {
System.out.println("Dog barks, Type: " + TYPE);
}
}

区别:抽象类可以有非final的实例变量,而接口中的成员变量默认是public static final,即常量。

4. 构造方法

抽象类可以有构造方法,接口不能有构造方法。

抽象类中的构造方法:

abstract class Animal {
String name;

// 抽象类可以有构造方法
public Animal(String name) {
this.name = name;
}

public abstract void sound();
}

class Dog extends Animal {
public Dog(String name) {
super(name); // 调用父类构造方法
}

@Override
public void sound() {
System.out.println("Dog barks, Name: " + name);
}
}

接口不能有构造方法:

interface Animal {
void sound();
}

// 无法在接口中定义构造方法

区别:抽象类可以定义构造方法,用于初始化类的状态,而接口不能定义构造方法,因为接口不能被实例化。