상속 : 수퍼 클래스의 코드가 사실상 * 복사 *되어 서브 클래스로 전달됩니까, 아니면 * 서브 클래스 *로 참조됩니까?


10

Class Sub는 class의 하위 클래스 Sup입니다. 그것은 실제로 무엇을 의미합니까? 다른 말로하면, "상속"의 실제 의미는 무엇입니까?

옵션 1 : Sup의 코드가 사실상 Sub로 복사 됩니다. (마찬가지로 '는 복사 - 붙여 넣기 "되지만 복사 코드없이 시각적 서브 클래스에서 본).

예 : methodA()Sup. Sub methodA()는 Sup를 확장하므로 (가상) Sub에 복사하여 붙여 넣습니다. 이제 Sub라는 메소드가 methodA()있습니다. 그것은 methodA()모든 코드 라인에서 Sup와 동일 하지만 전적으로 Sub에 속하며 Sup에 의존하지 않거나 Sup와 관련이 있습니다.

옵션 2 : Sup의 코드는 실제로 Sub로 복사 되지 않습니다 . 여전히 슈퍼 클래스에만 있습니다. 그러나 해당 코드는 서브 클래스를 통해 액세스 할 수 있으며 서브 클래스에서 사용할 수 있습니다.

예 : methodA()Sup. Sub는 Sup를 확장 methodA()하므로 다음과 같이 Sub를 통해 액세스 할 수 있습니다 subInstance.methodA(). 그러나 그것은 실제로 methodA()슈퍼 클래스에서 호출 될 것 입니다. 이는 서브 클래스에 의해 호출 된 경우에도 methodA ()가 수퍼 클래스의 컨텍스트에서 작동 함을 의미합니다 .

질문 : 두 가지 옵션 중 실제로 작동하는 방식은 무엇입니까? 둘 중 하나라도 없으면 실제로 어떻게 작동하는지 설명하십시오.


이것은 코드를 작성하고, 클래스 파일을 검사하고 (체크섬도 검사 할 수 있음), 수퍼 클래스를 수정하고, 다시 컴파일하고, 클래스 파일을 다시 봅니다. JVM 스펙의 3 장 . JVM ( Java Virtual Machine) 컴파일을 이해하는 데 도움이되는 내용 (특히 3.7 절)을 읽을 수도 있습니다 .

@MichaelT " 가상 복사 된"키워드입니다. 또한 코드가 문자 그대로 복사 되더라도 클래스로드 후에 만 ​​발생할 수 있습니다.

@delnan 핫스팟 (또는 다른 런타임 최적화 프로그램)이 어느 시점에서 코드를 인라인하는지 궁금해 할 것입니다. 그러나 JVM마다 구현 세부 사항이되어 JVM마다 다르므로 올바르게 대답 할 수 없습니다. 수행 할 수있는 최선의 방법은 컴파일 된 바이트 코드 (및 실제로 발생하는 작업을 설명 하는 호출 특수 opcode 사용)를 보는 것입니다.

답변:


13

옵션 2.

바이트 코드는 런타임에 동적으로 참조됩니다. 이것이 LinkageErrors 와 같은 이유 입니다.

예를 들어 두 클래스를 컴파일한다고 가정합니다.

public class Parent {
  public void doSomething(String x) { ... }
}

public class Child extends Parent {
  @Override
  public void doSomething(String x) {
    super.doSomething(x);
    ...
  }
}

이제 자식 클래스를 수정하거나 다시 컴파일하지 않고 부모 클래스 를 수정하고 다시 컴파일하십시오 .

public class Parent {
  public void doSomething(Collection<?> x) { ... }
}

마지막으로 자식 클래스를 사용하는 프로그램을 실행하십시오. NoSuchMethodError 가 수신됩니다 .

애플리케이션이 클래스의 지정된 메소드 (정적 또는 인스턴스)를 호출하려고 시도하고 해당 클래스에 더 이상 해당 메소드의 정의가없는 경우 발생합니다.

일반적으로이 오류는 컴파일러에서 발생합니다. 이 오류는 클래스 정의가 호환되지 않는 경우에만 런타임시 발생할 수 있습니다.


7

두 가지 간단한 클래스로 시작해 보겠습니다.

package com.michaelt.so.supers;

public class Sup {
    int methodA(int a, int b) {
        return a + b;
    }
}

그리고

package com.michaelt.so.supers;

public class Sub extends Sup {
    @Override
    int methodA(int a, int b) {
        return super.methodA(a, b);
    }
}

방법 A를 컴파일하고 바이트 코드를 살펴보면 다음과 같은 결과를 얻습니다.

  methodA(II)I
   L0
    LINENUMBER 6 L0
    ALOAD 0
    ILOAD 1
    ILOAD 2
    INVOKESPECIAL com/michaelt/so/supers/Sup.methodA (II)I
    IRETURN
   L1
    LOCALVARIABLE this Lcom/michaelt/so/supers/Sub; L0 L1 0
    LOCALVARIABLE a I L0 L1 1
    LOCALVARIABLE b I L0 L1 2
    MAXSTACK = 3
    MAXLOCALS = 3

그리고 Sup 클래스 methodA ()에 대한 조회를 수행하는 invokespecial 메소드를 사용하여 바로 볼 수 있습니다.

invokespecial 연산 코드는 다음과 같은 논리를 가지고 :

  • C에 해결 된 메소드와 이름 및 설명자가 같은 인스턴스 메소드에 대한 선언이 포함 된 경우이 메소드가 호출됩니다. 조회 절차가 종료됩니다.
  • 그렇지 않은 경우, C에 수퍼 클래스가있는 경우 C의 직접 수퍼 클래스를 사용하여 동일한 검색 프로 시저를 재귀 적으로 수행합니다. 호출 할 메소드는이 검색 프로 시저의 재귀 호출의 결과입니다.
  • 그렇지 않으면 AbstractMethodError가 발생합니다.

이 경우 클래스에 동일한 이름과 설명자를 가진 인스턴스 메소드가 없으므로 첫 번째 글 머리 기호가 실행되지 않습니다. 그러나 두 번째 글 머리 기호는 슈퍼 클래스가 있으며 슈퍼의 메소드 A를 호출합니다.

컴파일러는 이것을 인라인하지 않으며 클래스에 Sup 소스의 사본이 없습니다.

그러나 이야기는 아직 끝나지 않았습니다. 이것은 컴파일 된 코드일뿐입니다. 코드가 JVM에도달하면 HotSpot 이 관여 할 수 있습니다.

불행히도, 나는 그것에 대해 많이 알지 못 하므로이 문제에 대한 권위에 호소 하고 HotSpot 메소드 인라인 할 수 있다고 Java 에서 인라인으로 이동합니다 (최종이 아닌 메소드).

받는가는 문서 서브 methodA으로) 유효 섭 methodA의 코드를 (복사 () -는 그 특정 메소드 호출 대신마다, 조회 정보는 인라인 수 일의 핫 스폿지면 유의한다.

이는 애플리케이션의 동작 방식과 성능 속도를 높이는 데 필요한 최적화를 기반으로 런타임시 메모리에서 수행됩니다.

OpenJDK의 HotSpot Internals에 명시된 바와 같이 "메소드는 종종 인라인됩니다. 정적, 프라이빗, 최종 및 / 또는"특별한 "호출은 인라인하기 쉽습니다."

JVM옵션을 파헤 -XX:MaxInlineSize=35치면 인라인 될 수있는 최대 바이트 수인 (35가 기본값) 옵션 이 있습니다. Java가 쉽게 인라인 될 수 있기 때문에 많은 작은 메소드를 좋아하는 이유가 여기에 있습니다. 이러한 작은 메서드는 인라인 될 있기 때문에 더 많이 호출 될 때 더 빨라 집니다. 그리고 그 숫자로 플레이하고 더 크게 만들면 다른 최적화가 덜 효과적 일 수 있습니다. (관련 SO 질문 : HotSpot JIT 인라인 전략 은 HotSpot이 수행하는 인라인 내부를 들여다 볼 수있는 다른 여러 옵션을 지적합니다).

따라서 아니요-컴파일 타임에 코드가 인라인되지 않습니다. 그리고 그렇습니다. 성능 최적화가 보증하는 경우 런타임에 코드가 인라인 될 수 있습니다.

그리고 HotSpot 인라이닝에 대해 작성한 모든 내용은 Oracle에서 배포 한 HotSpot JVM에만 적용됩니다. Wikipedia의 Java 가상 머신 목록 을 살펴보면 HotSpot뿐만 아니라 JVM이 인라인을 처리하는 방식이 위에서 설명한 것과 완전히 다를 수 있습니다. Apache Harmony, Dalvik, ART-상황이 다르게 작동 할 수 있습니다.


0

코드는 복사되지 않으며 참조로 액세스됩니다.

  • 서브 클래스는 그 메소드와 슈퍼 클래스를 참조한다
  • 수퍼 클래스는 그 메소드를 참조합니다

컴파일러는 이것이 메모리에서 표현 / 실행되는 방식을 최적화 할 수 있지만 기본적으로 구조입니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.