关于OOP:Java中的抽象类

Abstract class in Java

Java中的"抽象类"是什么?


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

  • 定义继承子类可以使用的方法。
  • 定义继承子类必须实现的抽象方法。
  • 提供一个公共接口,允许子类与所有其他子类交换。
  • 下面是一个例子:

    1
    2
    3
    4
    5
    6
    abstract public class AbstractClass
    {
        abstract public void abstractMethod();
        public void implementedMethod() { System.out.print("implementedMethod()"); }
        final public void finalMethod() { System.out.print("finalMethod()"); }
    }

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

    1
    2
    3
    4
    public class ImplementingClass extends AbstractClass
    {
        // ERROR!
    }

    没有实现abstractMethod()的方法!因此,当JVM得到类似于new ImplementingClass().abstractMethod()的东西时,它无法知道应该做什么。

    这是正确的ImplementingClass

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

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

    这是另一个正确的ImplementingClass

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

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

    但是,由于final关键字的存在,以下内容是不可能的。

    1
    2
    3
    4
    5
    6
    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!"); }
    }

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

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    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()"); }
    }

    现在你可以在某个地方写另一个方法。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    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()当然被重写了。(您可能已经看到这被称为多态性。)

    如果我们希望访问特定子类的特定成员,则必须首先向下转换到该子类:

    1
    2
    3
    4
    // 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();

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

    1
    2
    3
    4
    public class ImplementingClass extends AbstractClass, SomeOtherAbstractClass
    {
        ... // implementation
    }

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

    1
    2
    3
    4
    public class ImplementingClass extends AbstractClass implements InterfaceA, InterfaceB
    {
        ... // implementation
    }

    下面是一个示例界面:

    1
    2
    3
    4
    interface InterfaceA
    {
        void interfaceMethod();
    }

    这基本上与:

    1
    2
    3
    4
    abstract public class InterfaceA
    {
        abstract public void interfaceMethod();
    }

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

    以下是非法的:

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

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


    Java类在以下条件下变得抽象:

    1。至少有一个方法标记为抽象:

    1
    public abstract void myMethod()

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

    2。类标记为抽象:

    1
    abstract class MyClass

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

    常用:

    抽象类的一个常见用法是提供类的大纲,类似于接口。但与接口不同的是,它已经能够提供功能,即类的某些部分已经实现,而某些部分只是通过方法声明进行概述。(摘要)

    无法实例化抽象类,但可以基于抽象类创建具体类,然后可以实例化该类。要做到这一点,您必须从抽象类继承并重写抽象方法,即实现它们。


    使用abstract关键字声明的类称为abstract class。抽象是一个隐藏数据实现细节的过程,并且只向用户显示功能。抽象让你专注于对象做什么,而不是它如何做。

    抽象类的主要内容

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

      抽象方法是在没有实现(不带大括号,后跟分号),如下所示:

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

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

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

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

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

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

    1
    abstract class AbstractDemo{ }

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

    1
    abstract void moveTo();//no body

    为什么我们需要抽象类

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

    对于抽象超类来说,这是一个完美的情况。您可以利用这些相似性,声明所有图形对象从同一抽象父对象继承(例如:GraphicObject),如下图所示。enter image description here

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    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方法的实现。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    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方法中,可以调用以下所有方法:

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

    在Java中实现抽象的方法

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

    • 抽象类(0到100%)
    • 接口(100%)

    带有构造函数、数据成员、方法等的抽象类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    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);
     }
    }

    输出:

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

    记住两条规则:

    • 如果类的抽象方法和具体方法很少,声明为abstract类。

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

    参考文献:

    • Java抽象
    • Java抽象类方法
    • Java文档-抽象方法和类
    • Java中的抽象类


    它是一个不能实例化的类,并且强制实现类,可能的话,实现它概述的抽象方法。


    来自Oracle文档

    抽象方法和类:

    An abstract class is a class that is declared abstract—it may or may not include abstract methods

    Abstract classes cannot be instantiated, but they can be subclassed

    抽象方法是不带实现(不带大括号,后跟分号)声明的方法,如下所示:

    1
    abstract void moveTo(double deltaX, double deltaY);

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

    1
    2
    3
    4
    5
    public abstract class GraphicObject {
       // declare fields
       // declare nonabstract methods
       abstract void draw();
    }

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

    由于abstract classesinterfaces是相关的,请看下面这些问题:

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

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


    请在此处获取答案:

    Java中抽象类与接口的接口

    抽象类可以有最终方法吗?

    顺便说一句-这些是你最近问的问题。想一个新问题来建立声誉…

    编辑:

    刚刚意识到,这个问题的海报和引用的问题有相同或至少相似的名称,但是用户ID总是不同的。因此,要么是因为技术问题,要么是因为keyur无法再次登录并找到问题的答案,要么是因为这是一种娱乐SO社区的游戏;)


    简单地说,您可以将抽象类视为具有更多功能的接口。

    不能实例化接口,该接口还保存抽象类。

    在您的接口上,您只需定义方法头,所有实现者都必须实现所有方法头。在抽象类上,您也可以定义方法头,但是在这里——接口的不同之处——您还可以定义方法的主体(通常是默认实现)。此外,当其他类扩展(注意,不实现,因此每个子类只能有一个抽象类)您的抽象类时,它们不会强制实现您的抽象类的所有方法,除非您指定了一个抽象方法(在这种情况下,它的工作方式与接口类似,您不能定义方法体)。

    1
    2
    3
    public abstract class MyAbstractClass{
      public abstract void DoSomething();
    }

    否则,对于抽象类的常规方法,"继承者"可以像往常一样使用默认行为或重写它。

    例子:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    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();
    }


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

    1
    2
    3
    abstract class Foo {
        abstract void someMethod();
    }

    或间接地

    1
    2
    3
    4
    5
    6
    interface IFoo {
        void someMethod();
    }

    abstract class Foo2 implements IFoo {
    }

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    abstract class Foo3 {
    }

    class Bar extends Foo3 {

    }

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

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

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    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
        }
    }

    所有这些职位都没有增加什么。

    Sometimes you may want to declare a
    class and yet not know how to define
    all of the methods that belong to that
    class. For example, you may want to
    declare a class called Writer and
    include in it a member method called
    write(). However, you don't know how to code write() because it is
    different for each type of Writer
    devices. Of course, you plan to handle
    this by deriving subclass of Writer,
    such as Printer, Disk, Network and
    Console.


    解决方案-基类(抽象)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    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();

    }


    抽象类是一个没有完全实现但为子类提供蓝图的类。它可以部分实现,因为它包含完全定义的具体方法,但也可以包含抽象方法。这些是带有签名但没有方法体的方法。任何子类都必须为每个抽象方法定义一个主体,否则它也必须声明为抽象的。因为抽象类不能被实例化,所以必须至少扩展一个子类才能被使用。把抽象类看作泛型类,子类用来填充缺少的信息。


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

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

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    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();  
        }  
    }

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

  • 没有实现的方法必须包含"abstract"关键字。
  • 无法实例化抽象类。