상속받은 메소드와 오버라이딩되는 메소드는 어떻게 참조 되는 것일까? 라는 의문으로 시작된 글입니다.
오버로딩과 오버라이딩이 실제 메모리 상에 어떻게 올라가고 어떻게 참조되어 동작하는지 궁금했는데요!
아래 정리가 잘 된 링크의 블로그 글을 참고하면서 이해해 보려고 합니다.
https://lordofkangs.tistory.com/21
https://velog.io/@ghkvud2/정적-바인딩과-동적-바인딩
제가 생각하는 핵심 키워드는 이럴 것 같네요.
- 가상 메소드 테이블(vtable)
- JVM의 동적 바인딩 기술
여기서,
정적 바인딩과 동적 바인딩의 개념을 먼저 알아야 될 것 같아요.
정적 바인딩 (static binding) |
1. 컴파일 타임 바인딩(compile-time binding) 2. 말에서 유추해 볼 수 있듯이 컴파일 시점에서 메소드 호출이나 변수 접근 등의 바인딩이 결정되는 방식입니다. 3. 즉, 프로그램이 실행되기 전에 코드의 구조와 메소드 호출이 이미 결정되기 때문에 실행 속도가 빠르고 안정성이 높다는 장점이 있습니다. 4. 오버로딩 메소드 호출에서 사용됩니다. |
동적 바인딩 (dynamic binding) |
1. 프로그램의 런타임 상태에서 메소드 호출이나 변수 접근이 결정되는 방식입니다. 2. 리플렉션(reflection) 이나 오버라이딩된 메소드 호출에서 사용됩니다. |
정적 바인딩과 동적 바인딩의 특징을 비교하자면 다음과 같습니다.
특징 | 정적 바인딩(Static Binding) | 동적 바인딩(Dynamic BInding) |
결정 시점 | 컴파일 타임 | 런타임 |
성능 | 빠름 | 상대적으로 느림 |
타입 확인 | 컴파일 타임에 타입 확인 | 런타임에 객체의 실제 타입을 기반으로 결정 |
사용 | 메소드 오버로딩 | 메소드 오버라이딩, 리플렉션 |
코드 예시 | Animal myAnimal = new Animal(); myAnimal.makeSound(); (컴파일 시점에 Animal의 makeSound 결정) |
Animal myAnimal = new Dog(); myAnimal.makeSound(); (실행 시점에 Dog의 makeSound 결정) |
유연성 | 낮음 (미리 결정된 메소드 호출) | 높음 (실행 시점에 결정됨으로 유연) |
타입 안정성 | 높음 (컴파일 시점에 타입 체크) | 다형성으로 인해 타입 안정성은 떨어질 수 있음 |
new 연산자를 통해 객체를 생성했던 정적바인딩 방식 말고,
아래와 같이 리플렉션을 통해 객체를 생성하는 동적바인딩 예시를 만들어서 테스트 해봅시다!
public class Person {
private String name;
private int age;
public Person() {
this.name = "Unknown";
this.age = 0;
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public void sayHello() {
System.out.println("안녕하세요, 저의 이름은 " + name + " 이고, 나이는 " + age + " 입니다.");
}
private void secretMethod() {
System.out.println("private secretMethod 호출!");
}
// Getter and Setter
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
위에 작성된 Person 클래스를 동적 바인딩 방식(리플렉션)을 통해서 객체를 생성하는 예시입니다!
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class ReflectionTest {
public static void main(String[] args) {
try {
// 클래스 정보 가져오기
Class<?> personClass = Class.forName("Person");
// 생성자 정보 가져오기 및 인스턴스 생성
Constructor<?> constructor = personClass.getConstructor(String.class, int.class);
Object personInstance = constructor.newInstance("홍길동", 31);
// 필드 정보 가져오기 및 값 설정
Field nameField = personClass.getDeclaredField("name");
nameField.setAccessible(true); // private 필드 접근 허용
nameField.set(personInstance, "홍길동2");
// 메서드 정보 가져오기 및 호출
Method sayHelloMethod = personClass.getMethod("sayHello");
sayHelloMethod.invoke(personInstance);
// private 메서드 호출
Method secretMethod = personClass.getDeclaredMethod("secretMethod");
secretMethod.setAccessible(true); // private 메서드 접근 허용
secretMethod.invoke(personInstance);
if(personInstance instanceof Person) {
System.out.println("Person class 입니다..");
Person person = (Person)personInstance;
System.out.println(person.toString());
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
다음과 같은 결과가 확인됩니다.
어떨결에 동적바인딩 기술 중에 리플렉션이라는 기능을 이용해서 객체를 생성해보는 예제까지 알아봤는데요.
이 글의 핵심을 다시 정리해보면,
정적 바인딩은 컴파일 타임에 메서드 호출이 결정되는 방식으로, 주로 오버로딩에 사용된다라는 점.
동적 바인딩은 런타임에 메서드 호출이 결정되며, 주로 오버라이딩과 다형성에 사용되는 점.
리플렉션은 런타임에 클래스의 메타데이터에 접근하고 조작할 수 있는 기능으로, 객체의 내부 구조를 동적으로 탐색하고 수정할 수 있는 것까지.
정적 바인딩은 성능이 우수하고, 동적 바인딩과 리플렉션은 유연성이 높아 다양한 상황에서 유용하게 사용된다고 합니다.
각각의 개념이 나중에 애플리케이션을 개발한다면..
성능, 유지보수성, 유연성 등에 영향을 미칠 수 있기 때문에 어느정도 머리속에 정리가 필요해 보입니다!!
글 정리 마치겠습니다.
'Java' 카테고리의 다른 글
switch문에 대해.. (0) | 2023.06.23 |
---|---|
java NullPointerException(NPE) (0) | 2023.06.23 |
String 메소드의 .equals와 .equalsIgnoreCase 의 차이점 (0) | 2023.06.23 |