Java抽象类


273

什么是Java中的“抽象类”?


35
+1这个问题是如此基础和根本,这是SO的经典。我很惊讶以前没有在这里问过。
尤瓦

6
-1表示克莱门特的评论(如果可以的话);lmgtfy对您没有帮助。至于原因,请阅读此meta.stackexchange.com/questions/5280/embrace-the-non-googlers
Jonik,2009年

26
@tuergeist。对于Google来说,是否容易与否无关紧要,只要以前没有在SO上被要求。还有,谁说关于编程语言的初学者问题不属于SO?
约尼克,

12
我喜欢SO的一件事是,您会得到一个简洁,正确且明确的答案,而在网络的其余部分都找不到任何普通的BS。。。+1问题!
安德斯·汉森,2009年

1
所以,不只是尾巴长!强生甚至在播客56周围谈论这件事……
kwutchak

Answers:


341

抽象类是无法实例化的类。通过创建可以实例化的继承子类来使用抽象类。抽象类为继承的子类做一些事情:

  1. 定义继承的子类可以使用的方法。
  2. 定义继承子类必须实现的抽象方法。
  3. 提供一个公共接口,该接口允许子类与所有其他子类互换。

这是一个例子:

abstract public class AbstractClass
{
    abstract public void abstractMethod();
    public void implementedMethod() { System.out.print("implementedMethod()"); }
    final public void finalMethod() { System.out.print("finalMethod()"); }
}

请注意,“ abstractMethod()”没有任何方法主体。因此,您不能执行以下操作:

public class ImplementingClass extends AbstractClass
{
    // ERROR!
}

没有实现的方法abstractMethod()!因此,当JVM收到类似的信息时,JVM将无法知道它应该做什么new ImplementingClass().abstractMethod()

这是正确的ImplementingClass

public class ImplementingClass extends AbstractClass
{
    public void abstractMethod() { System.out.print("abstractMethod()"); }
}

请注意,您不必定义implementedMethod()finalMethod()。它们已经由定义AbstractClass

这是另一个正确的说法ImplementingClass

public class ImplementingClass extends AbstractClass
{
    public void abstractMethod() { System.out.print("abstractMethod()"); }
    public void implementedMethod() { System.out.print("Overridden!"); }
}

在这种情况下,您已覆盖implementedMethod()

但是,由于存在final关键字,因此无法执行以下操作。

public class ImplementingClass extends AbstractClass
{
    public void abstractMethod() { System.out.print("abstractMethod()"); }
    public void implementedMethod() { System.out.print("Overridden!"); }
    public void finalMethod() { System.out.print("ERROR!"); }
}

您不能这样做,因为finalMethod()in 的实现AbstractClass被标记为的最终实现finalMethod():永远不允许其他实现。

现在,您还可以实现两次抽象类:

public class ImplementingClass extends AbstractClass
{
    public void abstractMethod() { System.out.print("abstractMethod()"); }
    public void implementedMethod() { System.out.print("Overridden!"); }
}

// In a separate file.
public class SecondImplementingClass extends AbstractClass
{
    public void abstractMethod() { System.out.print("second abstractMethod()"); }
}

现在您可以在其他地方编写另一种方法。

public tryItOut()
{
    ImplementingClass a = new ImplementingClass();
    AbstractClass b = new ImplementingClass();

    a.abstractMethod();    // prints "abstractMethod()"
    a.implementedMethod(); // prints "Overridden!"     <-- same
    a.finalMethod();       // prints "finalMethod()"

    b.abstractMethod();    // prints "abstractMethod()"
    b.implementedMethod(); // prints "Overridden!"     <-- same
    b.finalMethod();       // prints "finalMethod()"

    SecondImplementingClass c = new SecondImplementingClass();
    AbstractClass d = new SecondImplementingClass();

    c.abstractMethod();    // prints "second abstractMethod()"
    c.implementedMethod(); // prints "implementedMethod()"
    c.finalMethod();       // prints "finalMethod()"

    d.abstractMethod();    // prints "second abstractMethod()"
    d.implementedMethod(); // prints "implementedMethod()"
    d.finalMethod();       // prints "finalMethod()"
}

请注意,即使我们声明bAbstractClass类型,它也会显示"Overriden!"。这是因为我们实例化的对象实际上是一个ImplementingClass,它implementedMethod()当然会被覆盖。(您可能已经将其称为多态。)

如果我们希望访问特定于特定子类的成员,则必须首先将其转换为该子类:

// Say ImplementingClass also contains uniqueMethod()
// To access it, we use a cast to tell the runtime which type the object is
AbstractClass b = new ImplementingClass();
((ImplementingClass)b).uniqueMethod();

最后,您不能执行以下操作:

public class ImplementingClass extends AbstractClass, SomeOtherAbstractClass
{
    ... // implementation
}

一次只能扩展一个类。如果需要扩展多个类,则它们必须是接口。你可以这样做:

public class ImplementingClass extends AbstractClass implements InterfaceA, InterfaceB
{
    ... // implementation
}

这是一个示例界面:

interface InterfaceA
{
    void interfaceMethod();
}

这基本上与以下内容相同:

abstract public class InterfaceA
{
    abstract public void interfaceMethod();
}

唯一的区别是第二种方法不让编译器知道它实际上是一个接口。如果您希望人们仅实现您的界面而没有其他人,则这将很有用。但是,作为一般初学者的经验法则,如果抽象类仅具有抽象方法,则可能应将其作为接口。

以下是非法的:

interface InterfaceB
{
    void interfaceMethod() { System.out.print("ERROR!"); }
}

您不能在接口中实现方法。这意味着,如果您实现两个不同的接口,则这些接口中的不同方法不会冲突。由于接口中的所有方法都是抽象的,因此您必须实现该方法,并且由于您的方法是继承树中的唯一实现,因此编译器知道它必须使用您的方法。


5
@Imagist -1对语句c.implementedMethod()的错误描述 //打印“ implementedMethod()”,它将打印“ Overriden!” 永远
Sachin Kumar 2015年

2
@Sachin我浪费了半个小时的时间来弄明白为什么它会打印“ implementedMethod()”,然后我看到了您的评论。Java发生了什么变化还是其他人只是忽略了错误?
Rounak 2015年

@SachinKumar由于作者缺乏回应,因此我自己解决了该错误。CMIIW。
Mateen Ulhaq '16

@SachinKumar我在这里玩游戏有些迟了,但是您能说一个很好的类比是C ++头文件中的方法声明(但没有实现)吗?
Schwaitz

5
@SachinKumar为什么会c.implementedMethod()打印“ Overriden!”?SecondImplementingClass不覆盖implementedMethod()
约翰·雷德

75

在以下情况下,Java类变为抽象:

1.至少一种方法被标记为抽象:

public abstract void myMethod()

在这种情况下,编译器会迫使您将整个类标记为抽象。

2.该类被标记为抽象:

abstract class MyClass

如前所述:如果您有抽象方法,则编译器会强制您将整个类标记为抽象。但是,即使您没有任何抽象方法,也仍然可以将类标记为抽象。

常用:

抽象类的常见用法是提供类似于接口的类的概述。但是与接口不同,它已经可以提供功能,即实现了该类的某些部分,而某些部分仅使用方法声明进行了概述。(“抽象”)

抽象类不能被实例化,但是您可以基于抽象类创建一个具体的类,然后可以对其进行实例化。为此,您必须继承抽象类并覆盖抽象方法,即实现它们。


1
Nitpick:第二个“条件”是多余的,因为您只能在显式声明为abstract的类中声明一个abstract方法。
Stephen C

2
同意,该建议不是真的正确或写得很好,只是格式很好。
午间丝绸

但是您的“使班级具体化”的建议也被错误地表述。您不必根据具体的类是否抽象来使类具体化,它是不是。
中午丝绸

1
这是完全错误的。抽象类不必具有任何抽象方法。您可以不使用方法或仅使用具体方法来创建抽象类。
乔恩,

1
游戏晚了10年,但这是最精确的答案。@乔恩,我对答案感到困惑。我敢肯定,这暗示着abstract关键字是使类抽象的必要条件。但是具体的类不能包含abstract 方法。因此,如果您的类具有abstract方法,则必须将其声明为abstract编译器的类。
拉基布

24

使用abstract关键字声明的类称为abstract class。抽象是隐藏数据实现细节并仅向用户显示功能的过程。抽象使您可以专注于对象的功能,而不是对象的功能。

抽象类的主要内容

  • 抽象类可以包含抽象方法,也可以不包含抽象方法。

    抽象方法是这样的一种方法,该方法无需实现即可声明(不带花括号,后跟分号),如下所示:

    例如: abstract void moveTo(double deltaX, double deltaY);

  • 如果一个类至少具有一个抽象方法,则该类必须是抽象的

  • 不能实例化Abstract类(不允许创建Abstract类的对象)

  • 要使用抽象类,您必须从另一个类继承它。提供其中所有抽象方法的实现。

  • 如果继承一个抽象类,则必须为其中的所有抽象方法提供实现。

声明抽象类abstract在声明期间,在类之前 指定关键字使它抽象。看下面的代码:

abstract class AbstractDemo{ }

声明抽象方法abstract在声明过程中在方法之前 指定关键字使其抽象。看下面的代码,

abstract void moveTo();//no body

为什么我们需要抽象类

在面向对象的绘图应用程序中,您可以绘制圆形,矩形,直线,贝塞尔曲线和许多其他图形对象。这些对象都有共同的某些状态(例如--:位置,方向,线条颜色,填充颜色)和行为(例如--moveTo,旋转,调整大小,绘制)。所有图形对象的某些状态和行为都是相同的(例如:填充颜色,位置和moveTo)。其他要求不同的实现(例如:调整大小或绘制)。所有图形对象必须能够绘制或调整其大小,只是它们的操作方式不同。

对于抽象超类来说,这是一个完美的情况。您可以利用这些相似之处,并声明所有图形对象都将从同一个抽象父对象(例如ex:)继承,GraphicObject如下图所示。 在此处输入图片说明

首先,您声明一个抽象类,GraphicObject以提供所有子类完全共享的成员变量和方法,例如当前位置和moveTo方法。GraphicObject还声明了抽象方法,例如draw或resize,它需要由所有子类实现,但必须以不同的方式实现。本GraphicObject类可以是这个样子:

abstract class GraphicObject {

  void moveTo(int x, int y) {
    // Inside this method we have to change the position of the graphic 
    // object according to x,y     
    // This is the same in every GraphicObject. Then we can implement here. 
  }

  abstract void draw(); // But every GraphicObject drawing case is 
                        // unique, not common. Then we have to create that 
                        // case inside each class. Then create these    
                        // methods as abstract 
  abstract void resize();
}

子类中抽象方法的用法的 每个非抽象子类(GraphicObject例如CircleRectangle)必须提供drawresize方法的实现。

class Circle extends GraphicObject {
  void draw() {
    //Add to some implementation here
  }
  void resize() {
    //Add to some implementation here   
  }
}
class Rectangle extends GraphicObject {
  void draw() {
    //Add to some implementation here
  }
  void resize() {
    //Add to some implementation here
  }
}

main方法内部,您可以像这样调用所有方法:

public static void main(String args[]){
   GraphicObject c = new Circle();
   c.draw();
   c.resize();
   c.moveTo(4,5);   
}

用Java实现抽象的方法

有两种方法可以在Java中实现抽象

  • 抽象类(0到100%)
  • 界面(100%)

具有构造函数,数据成员,方法等的抽象类

abstract class GraphicObject {

  GraphicObject (){
    System.out.println("GraphicObject  is created");
  }
  void moveTo(int y, int x) {
       System.out.println("Change position according to "+ x+ " and " + y);
  }
  abstract void draw();
}

class Circle extends GraphicObject {
  void draw() {
    System.out.println("Draw the Circle");
  }
}

class TestAbstract {  
 public static void main(String args[]){

   GraphicObject  grObj = new Circle ();
   grObj.draw();
   grObj.moveTo(4,6);
 }
}

输出:

GraphicObject  is created
Draw the Circle
Change position according to 6 and 4

记住两个规则:

  • 如果该类几乎没有抽象方法,而几乎没有具体方法,则将其声明为一个abstract类。

  • 如果该类只有抽象方法,则将其声明为interface

参考文献:


为什么上例,下例以及下例的输出中moveTo中的参数x和y的顺序不同?如果我们试图说明诸如接口和抽象类之类的概念的重要性,我们是否不应该使用与正在实现或一致扩展的接口或抽象类相同的函数签名?
乔纳森·赖斯

这两个规则将其
释放

4

这是一个无法实例化的类,它会强制实现类以可能实现其概述的抽象方法。


3

简而言之,您可以将抽象类视为具有更多功能的接口。

您不能实例化一个接口,该接口也适用于抽象类。

在您的界面上,您只需定义方法标头,所有实现者都将被强制实现所有 方法。在抽象类上,您还可以定义方法标头,但是在这里-与接口的不同-您还可以定义方法的主体(通常是默认实现)。此外,当其他类扩展(注意,没有实现,因此,你也可以只是一个抽象类,每个子类)的抽象类,他们并不是一定要去实现你的所有的抽象类的方法,除非你指定的抽象方法(在这种情况下,它的作用类似于接口,您无法定义方法主体)。

public abstract class MyAbstractClass{
  public abstract void DoSomething();
}

否则,对于抽象类的常规方法,“继承器”可以仅使用默认行为,也可以像往常一样覆盖它。

例:

public abstract class MyAbstractClass{

  public int CalculateCost(int amount){
     //do some default calculations
     //this can be overriden by subclasses if needed
  }

  //this MUST be implemented by subclasses
  public abstract void DoSomething();
}

如果OP不知道接口是什么,则此答案无济于事。由于抽象类和接口是相互关联的,因此OP在不了解另一个的情况下就不太可能知道一个。
Imagist

但这可能是。可能是他只知道接口是什么以及它如何工作,然后遇到了抽象类,并想知道为什么要使用它们。难道不是吗?
朱里(Juri)

3

从oracle 文档

抽象方法和类:

抽象类是被声明为抽象的类,它可能包含也可能不包含抽象方法

抽象类不能被实例化,但是可以被子类化

抽象方法是这样一种方法,该方法无需实现即可声明(不带花括号,后跟分号),如下所示:

abstract void moveTo(double deltaX, double deltaY);

如果类包含抽象方法,则必须将类本身声明为抽象,如:

public abstract class GraphicObject {
   // declare fields
   // declare nonabstract methods
   abstract void draw();
}

当抽象类被子类化时,子类通常为其父类中的所有抽象方法提供实现。但是,如果没有,则子类也必须声明为abstract

由于abstract classesinterfaces相关,请查看以下SE问题:

接口和抽象类之间有什么区别?

我应该如何解释接口和抽象类之间的区别?


3

在这里获取答案:

Java中的抽象类与接口

抽象类可以有final方法吗?

顺便说一句-这些是您最近提出的问题。考虑建立声誉的新问题...

编辑:

刚刚意识到,此问题和所引用问题的发布者具有相同或至少相似的名称,但用户ID始终不同。因此,或者有一个技术问题,就是keyur在再次登录并找到他的问题的答案时遇到了问题,或者这是一种娱乐SO社区的游戏;)


这就是为什么我检查了“社区Wiki”的原因-不应通过对这些问题做出反应来增加声誉;)
Andreas Dolk,2009年

1

除了所有这些职位。

有时您可能想声明一个类,却不知道如何定义属于该类的所有方法。例如,您可能想要声明一个名为Writer的类,并在其中包含一个称为write()的成员方法 。但是,您不知道如何编写write()的代码,因为每种Writer设备的类型都不同。当然,您计划通过派生Writer的子类来处理此问题,例如打印机,磁盘,网络和控制台。


1

抽象类不能直接实例化,但必须派生自类才能使用。如果一个类包含抽象方法,则它必须是抽象的:直接

abstract class Foo {
    abstract void someMethod();
}

或间接

interface IFoo {
    void someMethod();
}

abstract class Foo2 implements IFoo {
}

但是,一个类可以是抽象的,而不包含抽象方法。它是防止直接实例化的一种方法,例如

abstract class Foo3 {
}

class Bar extends Foo3 {

}

Foo3 myVar = new Foo3(); // illegal! class is abstract
Foo3 myVar = new Bar(); // allowed!

后者的抽象类样式可用于创建“类接口”类。与接口不同,抽象类允许包含非抽象方法和实例变量。您可以使用它为扩展类提供一些基本功能。

另一个常见的模式是在抽象类中实现主要功能,并在要由扩展类实现的抽象方法中定义算法的一部分。愚蠢的例子:

abstract class Processor {
    protected abstract int[] filterInput(int[] unfiltered);

    public int process(int[] values) {
        int[] filtered = filterInput(values);
        // do something with filtered input
    }
}

class EvenValues extends Processor {
    protected int[] filterInput(int[] unfiltered) {
        // remove odd numbers
    }
}

class OddValues extends Processor {
    protected int[] filterInput(int[] unfiltered) {
        // remove even numbers
    }
}

1

解决方案-基类(抽象)

public abstract class Place {

String Name;
String Postcode;
String County;
String Area;

Place () {

        }

public static Place make(String Incoming) {
        if (Incoming.length() < 61) return (null);

        String Name = (Incoming.substring(4,26)).trim();
        String County = (Incoming.substring(27,48)).trim();
        String Postcode = (Incoming.substring(48,61)).trim();
        String Area = (Incoming.substring(61)).trim();

        Place created;
        if (Name.equalsIgnoreCase(Area)) {
                created = new Area(Area,County,Postcode);
        } else {
                created = new District(Name,County,Postcode,Area);
        }
        return (created);
        }

public String getName() {
        return (Name);
        }

public String getPostcode() {
        return (Postcode);
        }

public String getCounty() {
        return (County);
        }

public abstract String getArea();

}

1
请尝试将所有代码格式化为代码,并请添加一些说明,目前几乎不能将其视为答案。
NomeN

3
直到并且除非您不对代码进行解释。您将被视为错误的回答者。因此,请在此处给出解释
devsda 2012年

0

抽象类是被声明为抽象的类-它可能包含也可能不包含抽象方法。抽象类不能被实例化,但是可以被子类化。

换句话说,用abstract关键字声明的类在Java中被称为abstract class。它可以有抽象方法(无主体的方法)和非抽象方法(有主体的方法)。

重要说明:- 抽象类不能用于实例化对象,它们可以用于创建对象引用,因为Java的运行时多态性方法是通过使用超类引用来实现的。因此,必须有可能创建对抽象类的引用,以便可以将其用于指向子类对象。您将在以下示例中看到此功能

abstract class Bike{  
  abstract void run();  
}  

class Honda4 extends Bike{  
    void run(){
        System.out.println("running safely..");
    }  

    public static void main(String args[]){  
       Bike obj = new Honda4();  
       obj.run();  
    }  
} 

0

抽象类是一个尚未完全实现的类,但为子类提供了一些蓝图。它可以部分实现,因为它包含完全定义的具体方法,但也可以包含抽象方法。这些是带有签名但没有方法主体的方法。任何子类都必须为每个抽象方法定义一个主体,否则也必须声明为抽象。由于不能实例化抽象类,因此必须将它们扩展至少一个子类才能使用。将抽象类视为泛型类,子类在那里填充丢失的信息。


0

可以同时具有具体方法和非具体方法的类,即带有和不带有主体的类。

  1. 没有实现的方法必须包含“抽象”关键字。
  2. 抽象类无法实例化。

-1

它什么都不做,只提供一个通用模板即可为其子类共享

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.