112. Java 继承 - 抽象方法和类
抽象方法和类
抽象类 是一个声明为 abstract
的类,它不能被直接实例化。抽象类可能包含抽象方法,也可以没有抽象方法。抽象方法是没有具体实现的方法,它仅仅声明了方法的签名,而没有提供方法的实际代码实现。抽象类的作用是为子类提供一个模板,让子类根据自己的需要来实现这些抽象方法。
抽象方法
抽象方法的声明方式如下:
java
abstract void moveTo(double deltaX, double deltaY);
在上面的例子中,moveTo
是一个抽象方法,它没有方法体。它仅定义了方法的签名,而没有提供实现。任何继承此抽象类的子类都必须实现该方法,除非子类本身也是抽象类。
抽象类
如果一个类中包含抽象方法,那么该类也必须声明为 abstract
,如下所示:
java
public abstract class GraphicObject {
// 声明字段
// 声明非抽象方法
abstract void draw();
}
在这个例子中,GraphicObject
是一个抽象类,它包含一个抽象方法 draw()
,要求所有继承该类的子类必须实现 draw()
方法。
注意 :接口中的方法默认都是抽象的,除非明确声明为 default
或 static
。因此,接口方法不需要显式地使用 abstract
关键字。
抽象类与接口的比较
抽象类和接口有许多相似之处,都是用于描述类的行为和结构,但它们有显著的不同之处。
抽象类
- 不能实例化:不能创建抽象类的实例。
- 可以包含字段 :可以包含字段,包括非
static
和非final
字段。 - 可以包含方法 :可以包含具体实现的非
static
和非final
方法。 - 继承限制:一个类只能继承一个抽象类。
接口
- 可以多继承:一个类可以实现多个接口。
- 方法默认是
abstract
:接口中的方法默认是抽象的,除非声明为default
或static
。 - 字段 :接口中的字段默认是
public static final
。
何时使用抽象类,何时使用接口?
- 使用抽象类 :如果希望在多个相关的类之间共享代码,或者类需要多个常用的字段或方法,或者需要使用
protected
或private
等访问修饰符,那么应使用抽象类。 - 使用接口 :如果期望不同的类实现相同的行为,但这些类没有任何继承关系(例如,
Comparable
或Cloneable
接口),或者希望支持多重继承时,应使用接口。
示例:
JDK
中的 AbstractMap
类就是一个抽象类,包含了许多可以共享的操作(如 get()
, put()
方法)。具体实现如 HashMap
或 TreeMap
继承了该类并提供了具体实现。
另一方面,HashMap
类实现了多个接口,如 Serializable
,Cloneable
和 Map<K, V>
。这意味着 HashMap
的实例不仅是一个映射结构,还具备克隆和序列化的能力。
抽象类示例
假设我们正在开发一个面向对象的绘图应用程序,需要绘制不同的图形对象,如圆形、矩形、线条等。这些图形对象有一些共同的状态(如位置、颜色)和行为(如移动、旋转、调整大小、绘制)。但是,每种图形对象的绘制和调整大小方式可能不同。
定义抽象类
java
abstract class GraphicObject {
int x, y; // 图形的坐标
String color; // 图形的颜色
// 共享的行为
void moveTo(int newX, int newY) {
x = newX;
y = newY;
}
// 需要子类实现的行为
abstract void draw();
abstract void resize();
}
在上面的例子中,GraphicObject
是一个抽象类,包含了所有图形对象共有的状态(如 x
, y
, color
)和方法(如 moveTo
)。但是,draw
和 resize
方法是抽象的,因为每种图形对象的绘制和调整大小方式不同,需要在子类中实现。
子类实现抽象方法
java
class Circle extends GraphicObject {
void draw() {
System.out.println("Drawing a Circle at (" + x + ", " + y + ")");
}
void resize() {
System.out.println("Resizing the Circle.");
}
}
class Rectangle extends GraphicObject {
void draw() {
System.out.println("Drawing a Rectangle at (" + x + ", " + y + ")");
}
void resize() {
System.out.println("Resizing the Rectangle.");
}
}
在上面的代码中,Circle
和 Rectangle
是 GraphicObject
的子类,它们实现了 draw()
和 resize()
方法。每个子类根据其图形的不同特点提供了具体的实现。
当抽象类实现接口时
一个抽象类可以实现一个接口,但不需要实现接口中的所有方法。子类可以继承抽象类并实现接口中剩余的未实现方法。
例如:
java
abstract class X implements Y {
// 实现接口 Y 中的部分方法
}
class XX extends X {
// 完成实现接口 Y 中剩余的方法
}
在这个例子中,类 X
实现了接口 Y
,但是它没有实现接口中的所有方法,因此 X
被声明为抽象类。类 XX
继承了 X
并实现了接口 Y
中剩余的方法。
抽象类的静态成员
虽然抽象类不能被实例化,但它仍然可以拥有静态字段和静态方法。您可以像普通类一样使用静态方法,例如:
java
abstract class AbstractClass {
static void staticMethod() {
System.out.println("Static method in abstract class");
}
}
public class TestClass {
public static void main(String[] args) {
AbstractClass.staticMethod(); // 直接通过类名调用静态方法
}
}
静态方法和字段是类级别的,而不是实例级别的,因此您不需要实例化类就可以访问它们。
通过这些示例和扩展,抽象类和方法的概念应该更加清晰了。抽象类是面向对象编程中非常重要的一部分,它帮助我们建立类之间的共性,并在不同的子类中实现多态性。