简体   繁体   English

Java中的继承真的发生了什么?

[英]What really happens on inheritance in Java?

Suppose I have two classes Parent and Child , and Child inherits from Parent . 假设我有两个类ParentChild ,而Child继承自Parent I have three methods in Parent , out of which two are public and one is private. 我在Parent有三种方法,其中两种是公共的,一种是私有的。

Normally we say that all the non-private methods are inherited into the Child class, but I'm confused about exactly what happens. 通常我们说所有非私有方法都被继承到Child类中,但我对确切发生的事情感到困惑。 Does Java make a copy of the methods in the Child class, or does it use some kind of reference to maintain the relationship? Java是否在Child类中复制了这些方法,还是使用某种引用来维护关系?

class Parent{
    // Private method
    private void method1(){
        System.out.println("In private method of Parent class");
    }
    void method2(){
    // calling private method
        method1();
    }
    void method3(){
    // calling private method
        method1();
    }
}

class Child extends Parent{

}

class MainClass{
   public static void main(String[] args){
       Child child = new Child();
       // calling non-private method which internally calls the private method
       child.method2();
   }
}

Does Java makes copy of the methods in the Child class or it uses some kind of reference to maintain the relationship? Java是否在Child类中创建了方法的副本,还是使用某种引用来维护关系?

Latter. 后者。 Take a look at invokespecial . 看一下invokespecial Java recursively looks up methods one class at a time. Java递归地一次查找一个类的方法。 The first matching method is called. 调用第一个匹配方法。 This is why calling a virtual (default) method is slower than calling a final method. 这就是调用虚拟(默认)方法比调用final方法慢的原因。

In general, the actual code within a method is not copied when you inherit from a class containing that method. 通常,从包含该方法的类继承时,不会复制方法中的实际代码。

Neither. 都不是。

Inheritance is a programming language concept, not a real action. 继承是一种编程语言概念,而不是一种真正的行为。 When you investigate the compiled Child class, eg with javap , you won't find any artifact related to the three methods of Parent . 当您研究已编译的Child类时,例如使用javap ,您将找不到与Parent的三种方法相关的任何工件。 There will be the information that Child has the superclass Parent , but no mentioning of the inherited methods, neither as references nor as copies. 将有Child具有超类Parent ,但没有提及继承的方法,既不作为引用也不作为副本。

The fact that Child conceptionally inherits methods from Parent comes into play when you actually try to invoke one of them through a variable whose compile-time type is Child , like in your Test class. 当您实际尝试通过编译时类型为Child的变量调用其中一个时, Child概念上从Parent继承方法的事实发挥作用,就像在Test类中一样。 Then it is up to the compiler to find the inherited methods in order to compile the code containing the invocation correctly. 然后由编译器来查找继承的方法,以便正确编译包含调用的代码。 It is also up to the compiler whether it searches the class hierarchy every time it resolves a target method of an invocation or whether it collects the existing methods in certain data structures to speed up subsequent lookups and which data structures it uses. 编译器是否在每次解析调用的目标方法时搜索类层次结构,或者它是否收集某些数据结构中的现有方法以加速后续查找以及它使用哪些数据结构也取决于编译器。

This process is more complicated than you might think. 这个过程比你想象的要复杂得多。 There may be multiple candidates among which the compiler has to select one and it may even fail due to ambiguity. 可能存在多个候选者,其中编译器必须选择一个候选者,并且它甚至可能由于模糊而失败。 The result of the process will be a decision about whether the invocation is valid and, in case it is valid, a single target method and its actual signature. 该过程的结果将是关于调用是否有效以及如果它是有效的,单个目标方法及其实际签名的决定。

The compiled invocation will contain the name and signature of the target method and a reference to the compile time type of the reference on which it is invoked, ie Child . 编译的调用将包含目标方法的名称和签名,以及对其调用的引用的编译时类型的引用,即Child It will not contain the information that the actual method has been inherited from Parent . 它不包含实际方法从Parent继承的信息。 This is intentional. 这是故意的。 Whether Child declares the method itself, inherits it from Parent or overrides a method of Parent should not affect the code of the invoker Test nor the compatibility between compiled class files. 无论是Child声明的方法本身,从继承了Parent或覆盖的方法, Parent不应该影响调用的代码Test ,也不编译的类文件之间的兼容性。


This implies that at runtime a class like Test may contain a reference, given by name and signature, to a method in Child not being stored in the class file of Child , in other words, the JVM is responsible as well, for making the concept of inheritance working. 这意味着在运行时,像Test这样的类可能包含一个名称和签名给定的引用,而Child的方法没有存储在Child的类文件中,换句话说,JVM也负责制作这个概念。继承工作。 At the same time, it has to ensure that the concept of overriding works, when a method invocation is executed. 同时,它必须确保在执行方法调用时覆盖的概念起作用。

The way it implements this is also unspecified, leaving room for different strategies. 它实现这一点的方式也未明确,为不同的策略留出了空间。 Searching class hierarchies on every invocation would be legal, but lead to poor performance. 在每次调用时搜索类层次结构都是合法的,但会导致性能不佳。

A common technique is a vtable . 一种常见的技术是vtable Associated with every class is a table (array) containing references to the available methods, be it declared or inherited. 与每个类相关联的是一个表(数组),其中包含对可用方法的引用,无论是声明还是继承。 On initialization of a subclass, its table will start with a copy of the superclass' table, having entries for new methods appended at the end and entries of overridden methods changed. 在初始化子类时,它的表将以超类'表的副本开头,在末尾附加新方法的条目,并且更改了重写方法的条目。 At some time, the method invocation will be linked by finding the index of the vtable entry, appropriate to the specified name and signature. 在某些时候,方法调用将通过查找适合于指定名称和签名的vtable条目的索引来链接。 A typical strategy is to do the lookup on the first invocation. 典型的策略是在第一次调用时进行查找。 The instruction is then modified to refer to the index to avoid subsequent lookups. 然后修改指令以引用索引以避免后续查找。 From then, subsequent executions consist of fetching the vtable for an object's runtime class and getting the method reference from the table's entry. 从那时起,后续执行包括获取对象的运行时类的vtable,并从表的条目中获取方法引用。

Considering that, you could say that at runtime, inheritance is typically implemented as some kind of reference—but wait. 考虑到这一点,你可以说在运行时,继承通常被实现为某种引用 - 但等待。

Implementations like Oracle's JVM are capable of doing hotspot optimizations in which the context of often executed code is considered. 像Oracle的JVM这样的实现能够进行热点优化,其中考虑了经常执行的代码的上下文。 There might be, for example an inherited method which itself invokes methods which have been overridden in some subclasses. 例如,可能存在一个继承的方法,它本身调用在某些子类中被重写的方法。 When the JVM finds out that this method is called very often on a single concrete subclass, it may create an optimized version for that particular case. 当JVM发现在单个具体子类上经常调用此方法时,它可能会为该特定情况创建优化版本。 Then, a copy of the method's code will be the starting point for the subsequent code transformations, inlining code of the specific subclass, etc. 然后,方法代码的副本将成为后续代码转换的起点,内联特定子类的代码等。

Since such sophisticated implementations will use references for non-optimized code while using optimized copies in other cases, an alternative to the initial answer answer could be: 由于此类复杂的实现将使用非优化代码的引用,而在其他情况下使用优化副本,因此初始答案答案的替代方法可能是:

Both. 都。

Method bodies aren't copied in the undefined method body of a subclass. 方法体不会复制到子类的未定义方法体中。 Instead, when you call 相反,当你打电话

Child child1 = new Child();
child1.method1();

It will look through its hierarchy, going up a level every time it can't find an implementation. 它将查看其层次结构,每次无法找到实现时上升到一个级别。 First it will check Child which has no implementation. 首先,它将检查没有实现的Child。 One level above that there's Parent which does, so it will use that one.As correctly mentioned by @Dante above that this is achieved via the super() call in the constructor of the Child Class. 比父级更高一级,所以它将使用那个。正如@Dante上面正确提到的那样,这是通过子类的构造函数中的super()调用实现的。 This image might help you get a better picture : 此图片可能会帮助您获得更好的图片: 在此输入图像描述

I think your confusion is somehow associated to the point of non inheritance of the private methods.So,I would like to address that as well 我认为你的困惑在某种程度上与私人方法的非继承性有关。所以,我也想解决这个问题。

Why Private Members are not inherited ? 为什么私人会员不被继承?

You could say that private members are not inherited, because nowhere can Child refer explicitly to value. 你可以说私有成员不是继承的,因为Child无处可以明确地引用值。 Ie any code like this.value can't be used within Child , nor can obj.value be used from some calling code (obviously). 即任何像this.value这样的代码都不能在Child ,也不能从某些调用代码中使用obj.value (显然)。 However, in another sense, you could say that value is inherited. 但是,从另一个意义上说,你可以说价值是继承的。 If you consider that every instance of Child is also an instance of Parent , then that object must contain 'value' as defined in Parent . 如果您认为Child每个实例也是Parent的实例,那么该对象必须包含Parent定义的'value' Even if the Child class knows nothing about it, a private member name value still exists within each and every instance of Child . 即使Child类对它一无所知,私有成员名称值仍然存在于Child每个实例中。 So in this sense, you could say that value is "inherited" in Child .So without using the word "inheritance", just remember that child classes don't know about private member defined within parent classes. 所以在这个意义上,你可以说在Child没有使用“继承”这个词继承了这个值。只记得子类不知道在父类中定义的私有成员。 But also remember that those private members still exists within instances of the child class. 但是请记住,那些私有成员仍然存在于子类的实例中。

Note: The the JLS states ( http://docs.oracle.com/javase/specs/jls/se5.0/html/classes.html#8.2 ): 注意:JLS声明( http://docs.oracle.com/javase/specs/jls/se5.0/html/classes.html#8.2 ):

Members of a class that are declared private are not inherited by subclasses of that class. 声明为private的类的成员不会被该类的子类继承。 Only members of a class that are declared protected or public are inherited by subclasses declared in a package other than the one in which the class is declared. 只有声明为protected或public的类的成员才会被声明在声明类之外的包中声明的子类继承。

Java does not make a copy of the methods.The methods that are inherited stay with the parent class only . Java不会复制这些方法。继承的方法仅保留在父类中

Now you must me wondering,How does child class get access to them? 现在你必须要知道,儿童班如何获得他们的访问权限?

The answer lies in understanding the super keyword . 答案在于理解super关键字

The super keyword is used to refer to object of immediate parent class.The very first thing super does is initialize object of parent class.This means it is responsible for creating object of parent class and is used to refer to that object. super关键字用于引用直接父类的对象。超级做的第一件事是初始化父类的对象。这意味着它负责创建父类的对象并用于引用该对象。

The super(); 超级(); is implicitly the first statement inside constructor of child class,however you can do that explicitly also. 隐式地是子类的构造函数中的第一个语句,但是您也可以明确地这样做。

Now the important part: 现在重要的部分:

If you have the following : 如果您有以下内容

super();

not as your first statement inside child class constructor,then compile error is generated saying: 不是你在子类构造函数中的第一个语句,然后生成编译错误说:

call to super must be first statement in constructor.

The reason is: 原因是:

Because the child class is assumed to have the inherited methods,but do not possess a copy of them. 因为假定子类具有继承的方法,但是没有它们的副本。

The inherited methods are basically with parent class,so it is important to create object of parent class first,so that whenever following is used 继承的方法基本上与父类一起使用,因此首先创建父类的对象非常重要,这样无论何时使用以下内容

instance_of_child.method_of_parent();

The method will be actually invoked from object of parent class,which was already created by super(explicitly or implicitly used). 该方法实际上将从父类的对象调用,该对象已由super(显式或隐式使用)创建。

This is the reason why you can have child class constructor like: 这就是为什么你可以拥有子类构造函数的原因:

Child()
{
 super();
 parent_method();
}

but not like: 但不喜欢:

Child()
{
 parent_method();
 super();
}

because parent_method(), that is the inherited method requires reference to object of parent class which is provided by super(). 因为parent_method(), 即继承方法需要引用由super()提供的父类对象。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM