接口(Interface)和抽象类(Abstract Class)的区别是什么?

概要:

在Java中,接口(Interface)和抽象类(Abstract Class)都是用于定义类的蓝图,并为子类提供方法实现的基础。它们在设计和使用上有一些关键区别。

补充(什么是蓝图?)

在面向对象编程(OOP)中,“蓝图”是一个比喻,用来形容类(class)的角色。蓝图通常指的是一个设计图或者计划,提供了构建或实现某物的结构和细节。在编程中,蓝图表示类定义的方式,提供了创建对象的模板或规范。

抽象类(Abstract Class)

定义和特点:

定义:抽象类是一个不能被实例化的类,用于提供其他类的共同基类。它可以包含抽象方法(没有实现的方法)和具体方法(有实现的方法)。

抽象方法:抽象类可以包含抽象方法,这些方法在抽象类中没有实现,必须在其子类中实现。一个抽象类可以包含多个抽象方法,也可以没有抽象方法。

示例:

public abstract class Animal {
    // 抽象方法
    public abstract void makeSound();
    // 具体方法
    public void breathe() {
        System.out.println("Breathing...");
    }
}

构造函数:抽象类可以有构造函数,用于初始化其字段。

字段和状态:抽象类可以包含字段(成员变量)和状态,可以在类中定义和维护状态。

访问修饰符:抽象类的方法可以使用任何访问修饰符(public、protected、private),并且可以具有具体实现。

继承:一个类可以继承一个抽象类,并实现其抽象方法。一个类只能继承一个抽象类(Java中的单继承)。

示例:

public class Dog extends Animal {

    @Override

    public void makeSound() {

        System.out.println("Bark");

    }

}

使用场景:

当多个类之间有相似的实现时,可以使用抽象类来提供共享代码。

当需要提供默认行为或状态时,抽象类是一个合适的选择。

接口(Interface)

定义和特点:

定义:接口是一个纯粹的抽象类型,用于定义类应该实现的契约。接口只能包含抽象方法(直到 Java 8)、默认方法和静态方法(Java 8 及以后)。接口不能包含字段,直到 Java 8,接口可以包含常量。

示例:

public interface Flyable {
    // 默认方法
    default void fly() {
        System.out.println("Flying...");
}
   //静态方法
   static void staticMethod() {
        System.out.println("This is a static method with an implementation.");
    }

    // 抽象方法
    void takeOff();
}

抽象方法:接口中的方法默认是抽象的,Java 8 及以后可以有默认方法和静态方法,默认方法和静态必须要有方法体。接口中的抽象方法必须在实现接口的类中实现。

字段:接口可以包含常量,但不能有实例字段。

示例:

public interface Constants {

    //需要注意的是,虽然编译器会自动添加隐式修饰符(public static final),但我们在日常开发时最好加上关键字

    int MAX_SIZE = 100;  // 常量

}

实现:一个类可以实现多个接口(Java中的多重继承),这使得接口在需要多重行为时非常有用。

示例:

public class Airplane implements Flyable {

    @Override

    public void takeOff() {

        System.out.println("Airplane taking off");

    }

}

访问修饰符:接口中的方法默认是 public 的,字段默认是 public static final 的,不能有 protected 或 private 方法。(未明确标明访问修饰符时,Java 编译器根据语言规范自动为类和接口中的成员添加隐式修饰符)

使用场景:

当需要定义一组不相关的类应该遵循的行为时,使用接口是合适的。

当需要支持多重继承行为时,接口可以提供解决方案。

主要区别

特性

抽象类(Abstract Class)

接口(Interface)

继承/实现

单继承:一个类只能继承一个抽象类。

多继承:一个类可以实现多个接口。

构造函数

可以有构造函数。

不能有构造函数。

字段

可以有实例字段和常量。

只能有 public static final 常量。

方法

可以有抽象方法和具体方法。

可以有抽象方法、默认方法

(Java 8及以后)、静态方法。

访问修饰符

方法可以有 public、protected、private 修饰符。

方法默认为 public,字段为 public static

final。

实现/扩展

适用于共享代码或提供基本实现。

适用于定义行为的契约和多重继承。

默认实现

抽象类中的具体方法可以提供默认实现。

接口中的默认方法可以提供默认实现

(Java 8及以后)。

总结

抽象类适用于需要提供默认实现、共享状态或行为的情况。它允许在类中实现和维护状态,并提供构造函数。

接口适用于定义行为的契约,可以支持多重继承,提供灵活的实现方案。它主要用于定义类应遵循的行为而不涉及具体实现。

补充

(

关于接口中默认方法和静态方法:

default 方法

定义:default 方法是在接口中定义的,可以提供具体实现的方法。这使得接口能够在不破坏现有实现的情况下添加新方法。

要求:

方法体:default 方法必须提供一个方法体。它的目的是给接口的方法提供一个默认实现,从而允许实现类继承这个实现,或者覆盖它。

示例:

public interface MyInterface {

    // 默认方法,必须有方法体

    default void defaultMethod() {

        System.out.println("This is a default method with an implementation.");

    }

}

static 方法

定义:static 方法是在接口中定义的静态方法。它们属于接口本身,而不是接口的实例,因此只能通过接口类名调用。

要求:

方法体:static 方法也必须有一个方法体。static 方法不能仅仅是方法的声明,它们需要提供具体的实现。

示例:

public interface MyInterface {

    // 静态方法,必须有方法体

    static void staticMethod() {

        System.out.println("This is a static method with an implementation.");

    }

}

总结

default 方法:

 必须提供方法体。它的目的是为接口中的方法提供默认实现。

 可以被实现该接口的类继承,或在实现类中被覆盖。

static 方法:

 必须提供方法体。它们属于接口本身,不能被实例调用,只能通过接口类名调用。

 不能被实现类覆盖或重写。

因此,无论是 default 方法还是 static 方法,在接口中都必须有方法体。它们不能仅仅是方法的声明,而必须包含具体的实现。

default关键字在Java中两种不同的含义?

default 关键字在 Java 中有两种不同的含义,适用于不同的上下文。它们涉及到不同的概念和用途,虽然名字相同,但实际功能和用途完全不同。

default 关键字在接口中的使用

定义:

在接口中,default 关键字用于定义默认方法。默认方法允许接口提供方法的具体实现,从而使得接口可以在不破坏现有实现的情况下添加新方法。

示例:

public interface MyInterface {

    // 默认方法

    default void defaultMethod() {

        System.out.println("This is a default method in an interface.");

    }

   

    // 抽象方法

    void abstractMethod();

}

特点:

方法体:default 方法有具体的实现。

访问修饰符:default 方法在接口中隐式是 public,因为接口中的方法必须是 public。

目的:提供接口的默认实现,允许接口扩展而不影响现有的实现类。

未指定访问修饰符的默认访问级别

定义:如果类中的成员(字段、方法)没有明确指定访问修饰符,它们的访问修饰符默认为包级私有(package-private)。这意味着这些成员只能在同一个包内访问。

示例:

class MyClass {

    // 包级私有字段(没有显式指定访问修饰符)

    int packagePrivateField;

    // 包级私有方法(没有显式指定访问修饰符)

    void packagePrivateMethod() {

        System.out.println("This is a package-private method.");

    }

}

特点:

访问权限:包级私有,成员只能在同一个包内访问。

访问修饰符:默认为包级私有,不需要使用 default 关键字来表示。

目的:控制类成员的访问范围,增强封装性。

关系和区别

相同点:

名字相同:虽然两者都叫 default,但它们的用法和功能完全不同。

目的不同:在接口中,default 用于定义默认实现的方法;而在类中,未指定访问修饰符的成员是包级私有的。

不同点:

用途:

接口中的 default:用于定义方法的默认实现,提供接口方法的具体行为。

类中的默认访问修饰符:没有显式指定访问修饰符时,成员的访问级别默认为包级私有,控制访问范围。

上下文:

接口中的 default:是一个关键字,用于接口中的方法定义。

类中的默认访问级别:不是关键字,而是 Java 的默认访问修饰符(即包级私有)的行为表现。

总结

接口中的 default 关键字:用于定义接口中的默认方法,允许接口提供方法实现。

类中的默认访问修饰符:指的是当类中的成员没有明确指定访问修饰符时,默认的访问级别是包级私有。

这两者虽然名字相同,但应用场景和目的完全不同,理解它们的区别有助于在 Java 编程中正确地使用访问修饰符和接口特性。

隐式修饰符是什么,该怎么实现?

隐式修饰符是指那些没有显式声明但在默认情况下应用于类、方法和变量的修饰符

实现:在 Java 中,隐式修饰符的实现涉及到编译器和语言规范的行为,确保类和接口中的某些成员自动拥有特定的修饰符。以下是这些隐式修饰符如何被编译器和语言规范处理的详细解释:

1. 编译器处理隐式修饰符

Java 编译器根据语言规范自动为类和接口中的成员添加隐式修饰符。这种处理是为了确保代码的正确性和一致性,即使开发者没有显式声明修饰符。以下是编译器如何处理不同类型的隐式修饰符:

接口中的隐式修饰符

字段

public: 接口中的字段隐式地被声明为 public,即使开发者没有显式声明。编译器将所有接口字段视为 public,以确保它们对所有实现类都可见。

static: 接口中的字段隐式地被声明为 static,因为接口不能有实例字段。编译器将所有接口字段视为 static。

final: 接口中的字段隐式地被声明为 final,因为接口字段通常用于定义常量,其值一旦初始化就不应更改。编译器将所有接口字段视为 final。

示例:

public interface MyInterface {

    int MY_CONSTANT = 10; // 隐式地是 public static final

}

方法

public: 接口中的方法隐式地被声明为 public,以确保它们可以被实现类访问。编译器将接口方法视为 public,并要求实现类覆盖这些方法。

abstract: 在 Java 8 之前,接口中的方法隐式地被声明为 abstract。编译器将这些方法视为抽象方法,要求实现类提供实现。

default: 从 Java 8 开始,接口可以定义 default 方法,允许提供默认实现。编译器处理这些方法并生成字节码,以便实现类可以选择性地覆盖它们。

static: 从 Java 8 开始,接口可以定义 static 方法,这些方法不能被实现类覆盖。编译器处理这些静态方法,并将它们与接口关联。

private: 从 Java 9 开始,接口可以定义 private 方法用于接口内部共享代码。编译器确保这些方法只能在接口内部访问。

示例:

public interface MyInterface {

    void abstractMethod(); // 隐式地是 public abstract



    default void defaultMethod() {

        // 默认实现

    }



    static void staticMethod() {

        // 静态方法实现

    }



    private void privateMethod() {

        // 私有方法实现(Java 9 及之后)

    }

}

类中的隐式修饰符

字段

package-private: 如果类中的字段没有显式声明访问修饰符,编译器将其视为包内可见(package-private)。这意味着字段只能在同一包中的其他类中访问。

instance: 如果字段没有显式声明 static 修饰符,编译器将其视为实例字段,即它属于类的实例,而不是类本身。

示例:

public class MyClass {

    int myField; // 隐式地是 package-private

}

方法

package-private: 如果类中的方法没有显式声明访问修饰符,编译器将其视为包内可见(package-private)。这意味着方法只能在同一包中的其他类中访问。

instance: 如果方法没有显式声明 static 修饰符,编译器将其视为实例方法,即它属于类的实例,而不是类本身。

示例:

public class MyClass {

    void myMethod() { // 隐式地是 package-private

        // 方法实现

    }

}

2. 语言规范

Java 语言规范定义了隐式修饰符的规则,并指导编译器如何处理这些修饰符。语言规范确保了代码的一致性和正确性,即使开发者没有显式声明所有修饰符。编译器遵循这些规范,在编译阶段自动应用隐式修饰符。

接口: 根据 Java 语言规范,接口中的字段总是 public static final,方法总是 public abstract,除非它们被显式声明为 default 或 static。这保证了接口的正确性和一致性。

类: 根据 Java 语言规范,类中的字段和方法如果没有显式声明修饰符,默认是 package-private,并且字段是实例字段,方法是实例方法。这种默认行为确保了类的访问控制和成员的使用。

总结

Java 中的隐式修饰符是由编译器根据语言规范自动应用的。编译器将这些修饰符添加到类和接口中的成员,确保代码的正确性和一致性。开发者可以专注于代码的逻辑,而不必担心隐式修饰符的细节,因为编译器会自动处理这些修饰符。

)

你可能感兴趣的:(java,开发语言,后端)