Java中的继承如何工作?

时间:2013-03-12 16:45:15

标签: java inheritance

我们有下一堂课:

class Super {
    void foo() {
        System.out.println("Super");
    }
}

class Sub extends Super {
    void foo() {
        super.foo();
        System.out.println("Sub");
    }
}

public class Clazz {
    public static void main(String[] args) {
        new Sub().foo();
    }
}

输出是:

  

超级

     

问题:

super出现了什么?它是父类的对象,哪个孩子保持为字段?

  • 如果是,抽象类的继承如何工作?您无法创建抽象类的实例。
  • 如果不是,覆盖的方法在哪里举行?

我尝试使用Google,但我找到的只是有关如何继承类等的常用信息。

更新

你还在告诉我显而易见的事情。也许我的问题有点误导,但我会试着改写它:

  • 当我们使用super调用方法时,您说,我们正在访问父方法。但是如何在没有父对象的情况下调用此方法呢?
  • superthis相同吗?如您所知,this是对具体对象的引用。

6 个答案:

答案 0 :(得分:6)

子类不维护任何表示其父级的特殊字段。您可能正在思考内部类的内容, 维护对其外部类的引用。这是一种特殊情况,并不代表超级子类彼此之间的关系。

在内部,JVM维护一个'方法表',将其加载的每个类与该类可用的方法相关联。 JVM还知道它加载的所有类之间的关系,包括超子关系。

当你调用super函数时,JVM实际上做了几件事:

  • 确定从
  • 调用该方法的类的父级
  • 确定将被调用的父级方法
  • 使用特殊指令(invokespecial
  • 调用该方法

如果要检查Sub类的类文件,您会看到foo方法的类似内容:

void foo();
    flags: 
    Code:
        stack=2, locals=1, args_size=1
            0: aload_0       
            1: invokespecial #2        // Method Super.foo:()V
            4: getstatic     #3        // Field java/lang/System.out:Ljava/io/PrintStream;
            7: ldc           #4        // String Sub
            9: invokevirtual #5        // Method java/io/PrintStream.println:(Ljava/lang/String;)V

列表中的第1行显示了调用超类方法的特殊指令。

良好的阅读来源是Java Virtual Machine Specification,尤其是Section 2.11.8

答案 1 :(得分:3)

好。我们一行一行地通过您的代码。

您的'foo'方法中的第一个声明是

super.foo();

这是对超类foo方法的显式调用。这是:

 void foo() {
    System.out.println("Super");
}

因此,“Super”会输出到控制台,因为您已使用super关键字明确调用此方法。 super引用类的超类,与this引用当前类的方式相同。

接下来是子类中foo方法的其余部分:

 void foo() {
    super.foo();
    System.out.println("Sub");
}

super.foo()被调用之后,是时候转到下一个输出“Sub”的语句了。


你的程序首先移动到子类'方法而不是超类的原因是因为一个名为Polymorphism的原则。也就是说,子类从超类中获取一个方法,并改变它的行为。

抽象类

您无法创建Abstract类的实例,不能使用super关键字,但您仍可以访问超类的功能。

在Java虚拟机的上下文中

那么当您进行方法调用时,如果是实例方法,Java虚拟机将在本地类中查找该方法。如果找不到它,它将移动到超类。当您使用Polymorphism的原则时,JVM会在子类中找到具有正确签名的方法,并停止查找。这就是继承和Polymorphism在Java的上下文中以简单的术语工作的方式。

覆盖方法时,将具有相同方法签名的方法(方法字段的名称,编号和类型)添加到子类定义中。这就是JVM找到它的方式,这是存储重写方法的地方。

答案 2 :(得分:3)

super是一个关键字,允许您调用超类中定义的方法实现。它不是你的子类的领域。

  

如果不是,覆盖的方法在哪里举行?

我不太清楚你的意思,但是:

  • 打印“超级”的方法保存在超类的类定义中
  • 打印“Sub”的方法保存在子类的类定义中。

Sub扩展Super以来,Sub类的定义包含对Super类定义的引用。

回答更新的问题:

  

当我们用super调用方法时,你说,我们正在进行父方法。但是如何在没有父对象的情况下调用此方法呢?

方法只是一个代码块,只是我们需要执行的一系列字节码指令。当您调用方法时,JVM的任务是根据您提供的方法名称和参数确定在哪里找到此代码块。通常,正如其他人所说,它将首先查看调用该方法的对象的类的类定义。当您使用super时,您告诉JVM不要在这里查看,而是查看父类定义。

因此,您不需要SuperSub的单独实例,因为Sub Super({{1} } {是new Sub() instanceof Super),因为JVM知道true关键字意味着它应该在super的类定义中查找构成方法的代码。

  

与此超级相同吗?如你所知,这是对具体对象的引用。

不,他们不一样。 Super是对当前对象的引用,而this不是对对象的引用,而是一个关键字,它影响JVM查找定义正在调用的方法的代码的位置。

答案 3 :(得分:1)

当你写super.foo();时,你正在调用超类方法。

class sub的foo方法通过向超类方法添加指令来覆盖Super的foo方法。

答案 4 :(得分:1)

子类.super.foo()中的

方法覆盖调用print super,然后调用System.out.println(“Sub”);显示Sub。

尝试继承这个

class Super {
    Super()
    {
        System.out.println("1");
    }
    void foo() {
        System.out.println("Super");
    }
}

class Sub extends Super {
    public Sub() {
        // TODO Auto-generated constructor stub
        System.out.println("2");
    }
    void foo() {
       super.foo();
        System.out.println("Sub");
    }
}

答案 5 :(得分:0)

只有一个对象同时是Sub,Super和Object。它具有每个类的所有非静态字段。一个类实际上只需要一个代码副本用于其方法,甚至是非静态代码。