1. 接口
接口是 Java 中用于定义对象行为规范的重要机制。它仅包含抽象方法(没有方法体)和常量,不包含方法的具体实现。接口强调的是对象所能提供的功能,任何实现该接口的类都必须提供这些方法的具体实现。例如,“动物接口”可能定义了诸如“移动”、“进食”等抽象方法。
public interface Animal { String HOMELAND = "地球";//为了确保接口提供的行为和状态的一致性,接口中的“属性”实际上是常量,它们必须在声明时被初始化,因为它们具有final的性质。 public void eat(); public void move(); }
2. 抽象类
抽象类介于接口和具体类之间,它是对一类具体事物的抽象。它可以包含抽象方法,也可以包含有具体实现的方法和属性。抽象类为相关的子类提供了部分通用的实现和模板,子类继承抽象类后,必须实现其中的抽象方法。比如“狗”这个抽象类,可能定义了一些狗共有的属性和部分方法的实现,同时包含一些抽象方法供具体的狗种类去实现。
public abstract class Dog { String name; String age; public void call(){ System.out.println("汪汪"); } public abstract void run(); }
3. 类
类是对一类具体事物的全面抽象。它既包含了事物的属性(成员变量),也包含了事物的行为(成员方法)。通过类,可以创建具体的对象实例。例如“动物类”可能定义了动物的基本属性和通用行为,而“狗类”则是对狗这一具体动物的完整抽象,包括狗特有的属性和行为。
public class Dog { String name; String age; public void call(){ System.out.println("汪汪"); }; }
4. 接口和类
接口主要用于定义一类事物应该具备的行为规范,其中的方法默认是抽象的,旨在明确实现该接口的类需要提供的功能。而添加了
default
关键字的方法虽然有了实现,但仍可被重写,且不能被重载,同时接口也可以拥有静态方法。类则是对具体事物的全面抽象,包括属性和具体的方法实现。当一个类实现了一个接口,就必须实现接口中定义的抽象方法,以满足接口所规定的行为规范。
接口不能被实例化,但可以通过实现了这个接口的子类来实例化即向上转型。
List list = new ArrayList();//上转型对象list拥有List的所有方法(这是ArrayList重写的)但是失去了ArrayList新增的方法
通过上面可以看出来接口用于定义一个类可以做什么,而类用来定义一个类是什么。
5. 抽象类和类
抽象类定义了一类事物的规范框架包括了属性和方法,在抽象类中可以有抽象方法也可以有普通方法(方法头有abstract关键字的一定是抽象类,抽象类不一定有抽象方法),而类是这些事物的具体实现它实现了抽象类的所有规范。如果一个继承了抽象类的类没有实现该抽象类的所有规范那么这个类也必须是抽象类。
抽象类的主要目的是为了被继承,它定义了子类的共同属性和行为,但不提供所有行为的完整实现。这有助于代码的复用和模块化。
抽象类不能直接实例化,抽象类的实例化是通过其非抽象的子类来间接完成的。
总之抽象类定义了一个类最基本的要求,即它必须做什么,必须有什么。这些要求通过抽象方法来体现,而具体的实现细节则留给子类处理。
6. 什么时候用接口什么时候用抽象类
在实际的编程实践中,决定使用接口还是抽象类,取决于众多因素。
接口通常更适宜于界定一组毫无关联的类所共有的行为准则。举例来说,若存在多个完全不同类型的类,像“打印机”“扫描仪”以及“传真机”,它们皆需实现“开启”与“关闭”的功能,此时便可定义一个“可开关设备”的接口来明确此类行为规范。
此外,接口还适用于需要达成多重继承效果的情形。由于 Java 不支持类的多重继承,然而一个类能够实现多个接口,这便极大地增强了代码的灵活性与可扩展性。
另一方面,抽象类则在具有层次结构关联的类族群中更能发挥优势。当存在一组在概念上紧密相连,并且共享部分基本属性和方法的类时,运用抽象类能够提供部分默认的实现方式,从而降低子类中的重复代码量。
当需要定义一组类应该遵循的行为规范,尤其是当这些类在概念上没有继承关系,但需要共享某种行为时,使用接口是合适的。
当有一组类需要共享部分实现,同时它们之间存在继承关系时,使用抽象类更合适,因为它可以提供默认实现和部分抽象方法。
总的来讲,接口重点在于定义行为规范,突出的是“能够做什么”;而抽象类侧重于定义共同的属性和部分实现,强调的是“是什么以及部分怎么做”。在实际应用时,需要依据具体的业务需求和设计原则来审慎抉择。