抽象类

抽象类是使用 abstract 关键字标记的类。与非抽象类相反,它可能包含抽象 - 无实现 - 方法。但是,在没有抽象方法的情况下创建抽象类是有效的。

无法实例化抽象类。只要子类也是抽象的,或者实现超类标记为抽象的所有方法,它就可以是子类(扩展)。

抽象类的一个例子:

public abstract class Component {
    private int x, y;
    
    public setPosition(int x, int y) {
        this.x = x;
        this.y = y;
    }

    public abstract void render();
}

当类具有至少一个抽象方法时,必须将其标记为抽象。抽象方法是一种没有实现的方法。可以在具有实现的抽象类中声明其他方法,以便为任何子类提供公共代码。

尝试实例化此类将提供编译错误:

//error: Component is abstract; cannot be instantiated   
Component myComponent = new Component();

但是,这个类扩展了 Component,并为其所有抽象方法提供了实现,并且可以实例化。

public class Button extends Component {

    @Override
    public void render() {
        //render a button
    }
}

public class TextBox extends Component {

    @Override
    public void render() {
        //render a textbox
    }
}

继承类的实例也可以转换为父类(正常继承),并且在调用抽象方法时它们提供多态效果。

Component myButton = new Button();
Component myTextBox = new TextBox();

myButton.render(); //renders a button
myTextBox.render(); //renders a text box

抽象类与接口

抽象类和接口都提供了一种定义方法签名的方法,同时需要扩展/实现类来提供实现。

抽象类和接口之间有两个主要区别:

  • 类只能扩展单个类,但可以实现许多接口。
  • 抽象类可以包含实例(非 static)字段,但接口可能只包含 static 字段。

Version < Java SE 8

在接口中声明的方法不能包含实现,因此在提供称为抽象方法的实现的其他方法时,使用抽象类。

Version >= Java SE 8

Java 8 允许接口包含默认方法 ,通常使用接口的其他方法实现 ,使得接口和抽象类在这方面同样强大。

抽象类的匿名子类

为方便起见,java 允许实例化抽象类的子类的匿名实例,这些实例在创建新对象时提供抽象方法的实现。使用上面的示例,这可能如下所示:

Component myAnonymousComponent = new Component() {
    @Override
    public void render() {
        // render a quick 1-time use component
    }
}