>  기사  >  Java  >  Java 메소드 호출을 사용하여 정적 및 동적 디스패치 해결

Java 메소드 호출을 사용하여 정적 및 동적 디스패치 해결

WBOY
WBOY앞으로
2023-04-20 23:22:211264검색

메서드 호출

프로그램이 실행 중일 때 메서드 호출이 가장 일반적이고 자주 발생하는 작업입니다.

메서드 호출은 메서드 실행과 동일하지 않습니다.

  • 메서드 호출 단계의 유일한 작업은 메서드를 결정하는 것입니다. 즉, 어떤 메소드가 호출되는지, 메소드 내부의 특정 실행 프로세스가 포함되지 않습니다. 클래스 파일의 컴파일 프로세스에는 클래스 파일의 모든 메소드 호출이 저장되지 않습니다. 클래스 파일에서는 모두 실제 런타임 메모리 레이아웃의 메서드 항목 주소가 아닌 기호 참조입니다. 즉, 이전 직접 참조입니다.

  • 이로 인해 Java는 더욱 강력한 동적 확장 기능을 갖게 됩니다

Java 메서드도 만듭니다. 호출 프로세스가 상대적으로 복잡해집니다.

대상 메서드에 대한 직접 참조는 클래스 로딩 중에 또는 런타임 중에도 결정되어야 합니다.
  • 메서드 분석
  • 모든 메서드 호출에서 대상 메서드는 다음과 같습니다. 클래스 파일에서도 마찬가지입니다. 상수 풀 참조

    클래스 로딩 및 구문 분석 단계에서 일부 기호 참조는 직접 참조로 변환됩니다.
  • 메서드에는 프로그램이 실제로 실행되기 전에 결정 가능한 호출 버전이 있으며, 호출 이 메소드의 버전은 실행 중입니다. 마침표는 불변입니다

    즉, 호출 대상은 프로그램 코드에서 완료되며 컴파일러가 컴파일할 때 결정되어야 합니다. 이를 메소드 분석
Java 메소드 분류

Java에서는라고도 합니다. , "알려진 컴파일 기간"을 따릅니다. "런타임 불변" 메서드에는 두 가지 주요 범주가 있습니다:

정적 메서드:

유형과 직접 관련됨

개인 메서드:

외부에서 액세스할 수 없음
  • 이 두 메소드의 각각의 특징 이 두 메소드 중 어느 것도 상속이나 다른 메소드를 통해 재정의될 수 없으므로 클래스 로딩 단계에서 구문 분석에 적합하다고 결정되었습니다.

  • 비 가상 메소드:
  • 동안 클래스 로딩 단계에서 기호 참조는 메소드

    Static Methods

Private Methods

Instance Constructors
  • Parent Class Methods
  • Vi에 대한 직접 참조로 구문 분석됩니다. 실제 방법 :
  • 기호 참조는 클래스 로딩 단계에서 메서드에 대한 직접 참조로 해석되지 않습니다.

    위의 비가상 메서드를 제외하고 다른 모든 메서드는 가상 메서드입니다.
  • 정적 디스패치

    public class StaticDispatch {
    	static abstract class Human {
    	}
    	static class Man extends Human {
    	}
    	static class Woman extends Human {
    	}
    	public static void sayHello(Human guy) {
    		System.out.println("Hello, Guy!");
    	}
    	public static void sayHello(Man guy) {
    		System.out.println("Hello, Gentleman!");
    	}
    	public static void sayHello(woman guy) {
    		System.out.println("Hello, Lady!");
    	}
    	public static void main(String[] args) {
    		Human man = new Man();
    		Human women = new Woman();
    		sayHello(man);
    		sayHello(woman);
    	}
    }

Human man = new Human ();

Human은 변수의 정적 유형입니다.

Man은 변수의 실제 유형입니다.

정적 유형과 실제 유형 모두 프로그램에서 변경됩니다:

정적 유형:

Human为变量的静态类型

Man

정적 유형은 사용될 때만 변경됩니다. 변수 자체의 정적 유형은 변경되지 않습니다. 실제 유형은 컴파일러에 알려져 있습니다. :

어서

  • 컴파일러는 컴파일 중에 객체의 실제 유형이 무엇인지 모릅니다.

  • Human human = new Man();
    sayHello(man);
    sayHello((Man)man);		// 类型转换,静态类型变化,转型后的静态类型一定是Man
    man = new woman();		// 实际类型变化,实际类型是不确定的
    sayHello(man);
    sayHello((Woman)man);	// 类型转换,静态类型变化
  • 컴파일러는 실제 유형 대신 매개변수의 정적 유형을 기반으로 사용합니다. 오버로딩 중 정적 유형은 컴파일 중에 결정됩니다. 다음을 알 수 있습니다.

    컴파일 단계에서 Javac 컴파일러는 매개변수의 정적 유형을 기반으로 사용할 오버로드된 버전을 결정합니다.
  • 정적 디스패치:

메서드의 실행 버전을 찾기 위해 정적 유형에 의존하는 모든 디스패치 작업

  • 일반적인 응용 프로그램

    : 메서드 오버로드

  • 정적 디스패치는 컴파일 단계에서 발생하므로 정적 디스패치를 ​​결정하는 작업은 그렇지 않습니다. 가상 머신에 의해 수행되지만 컴파일러에 의해 완료

리터럴이 표시되지 않기 때문에 정적 유형은 언어 규칙을 통해서만 이해하고 추론할 수 있습니다

public class LiteralTest {
	public static void sayHello(char arg) {
		System.out.println("Hello, char!");
	}
	public static void sayHello(int arg) {
		System.out.println("Hello, int!");
	}
	public static void sayHello(long arg) {
		System.out.println("Hello, long!");
	}
	public static void sayHello(Character arg) {
		System.out.println("Hello, Character!");
	}
	public static void main(String[] arg) {
		sayHello('a');
	}
}

컴파일러는 오버로드된 메서드에 위에서 아래로 주석을 달아 다른 출력을 얻습니다

컴파일러가 변환을 사용자 정의할 유형을 결정할 수 없는 경우 유형 모호성을 표시하고 컴파일을 거부합니다

public class LiteralTest {
	public static void sayHello(String arg) {	// 新增重载方法
		System.out.println("Hello, String!");
	}
	public static void sayHello(char arg) {	
		System.out.println("Hello, char!");
	}
	public static void sayHello(int arg) {
		System.out.println("Hello, int!");
	}
	public static void sayHello(long arg) {
		System.out.println("Hello, long!");
	}
	public static void sayHello(Character arg) {
		System.out.println("Hello, Character!");
	}
	public static void main(String[] args) {
		Random r = new Random();
		String s = "abc";
		int i = 0;
		sayHello(r.nextInt() % 2 != 0 ? s : 1 );	// 编译错误
		sayHello(r.nextInt() % 2 != 0 ? 'a' : false);	//编译错误
	}
}

동적 디스패치
    public class DynamicDispatch {
    	static abstract class Human {
    		protected abstract void sayHello();
    	}
    	static class Man extends Human {
    		@override
    		protected void sayHello() {
    			System.out.println("Man Say Hello!");
    		}
    	}
    	static class Woman extends Human {
    		@override
    		protected void sayHello() {
    			System.out.println("Woman Say Hello!");
    		}
    	}
    	public static void main(String[] args) {
    		Human man = new Man();
    		Human women = new Woman();
    		man.sayHello();
    		woman.sayHello();
    		man = new Woman();
    		man.sayHello();
    	}
    }
  • 이것은 정적 유형

  • 정적 유형

    Human두 변수에 따라 결정되지 않습니다. man

    and
  • woman

sayHello() 메서드를 호출하고 있습니다.

변수

man

이 두 번의 호출에서 서로 다른 메서드를 수행했습니다.

이 현상의 원인

: 실제 유형 이 두 변수는 서로 다릅니다

  • Java virtual 머신이 실제 유형에 따라 메소드의 실행 버전을 전달하는 방법: invokevirtual 명령어의 다형성 검색 프로세스에서 시작하여 Invokevirtual의 런타임 구문 분석 프로세스 명령은 대략 다음 단계로 나뉩니다.

    피연산자 스택 찾기
  • C
  • 로 표시되는 상단의 첫 번째 요소가 가리키는 객체의 실제 유형

  • 상수에 있는 설명자와 간단한 이름이 일치하는 메서드가 유형 C에서 발견되면 액세스 권한 확인이 수행되고 확인에 성공하면 이 메서드에 대한 직접 참조가 반환되고 확인되면 검색 프로세스가 종료됩니다. 실패하면 java.lang.illegalAccessErrorException

  • 발생

    아직 찾지 못한 경우 적합한 메소드를 찾으면
  • java.lang.AbstractMethodError
  • 예외가 발생합니다.

  • Java 언어 메소드 재작성의 본질:

invokevirtual

명령 실행의 첫 번째 단계 런타임 유형에서 실제 수신자를 결정하는 것이므로 두 호출의 Invokevirtual 명령은 상수 풀의 클래스 메서드 기호 참조를 다른 직접 참조로 확인합니다.

실제 유형을 기반으로 메서드 실행 버전을 결정하는 이 디스패치 프로세스 런타임 동안의 작업을 동적 디스패치라고 합니다

가상 머신의 동적 할당 구현

가상 머신의 개념을 분석하는 모드는 정적 할당과 동적 할당으로 "할당 중에 가상 머신이 수행할 작업"에 대한 질문을 이해할 수 있습니다.

가상 머신

"은 구체적으로 어떻게 작동합니까? " 다양한 가상 머신 구현에는 차이가 있습니다.

동적 디스패치는 매우 빈번한 작업이고 동적 디스패치의 메서드 버전 선택 프로세스에는 다음 검색이 필요하기 때문입니다. 런타임 시 클래스의 메서드 메타데이터에 있는 적절한 대상 메서드

  • 따라서 가상 머신의 실제 구현에서는 성능상의 이유로 대부분의 구현에서는 실제로 이러한 빈번한 검색을 수행하지 않습니다

  • 가장 일반적으로 사용되는 " 안정성 최적화' 방법은 메소드 영역에

    Virtual Method Table(vtable)을 생성하여 클래스를 제공하는 것이며,

    메타데이터 검색 대신
  • 가상 메소드 테이블 인덱스
  • 를 사용하여 성능 향상

    각 메소드의 실제 항목 주소 은 가상 메서드 테이블에 저장됩니다.

하위 클래스에서 메서드가 재정의되지 않은 경우 하위 클래스의 가상 메서드 테이블에 있는 주소 항목은 상위 클래스에 있는 동일한 메서드의 주소 항목과 일치합니다. , 둘 다 상위 클래스의 실제 항목을 가리킵니다

  • 이 메서드가 하위 클래스에서 재정의되면 하위 클래스 메서드 테이블의 주소가 하위 클래스의 실제 메서드를 가리키는 항목 주소로 대체됩니다

  • 상위 클래스와 하위 클래스에서 동일한 시그니처를 가진 메소드 가상 메소드 테이블의 인덱스 번호는 동일합니다.

이런 식으로 유형이 변경되면 조회할 메소드 테이블만 변경하면 됩니다. 필요한 항목 주소는 인덱스에 따라 다른 가상 메서드 테이블에서 변환될 수 있습니다

메서드 테이블은 일반적으로 클래스 로딩 단계의 연결 단계에서 초기화가 수행됩니다.

변수의 초기 값을 준비한 후 클래스의 가상 머신은 클래스의 메소드 테이블도 초기화합니다

위 내용은 Java 메소드 호출을 사용하여 정적 및 동적 디스패치 해결의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 yisu.com에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제