자바의 동적 바인딩(Dynamic Binding)과 정적 바인딩(Static Binding)

2026. 7. 29. 15:50·Java/정리

A a = new B();

Animal animal = new Dog();

 

"변수 타입은 Animal인데 왜 Dog의 메서드가 실행되지?"

 

이 질문의 답이 바로 동적 바인딩(Dynamic Binding)이다. 이번 포스트에서는 바인딩이 무엇인지부터 동적 바인딩과 정적 바인딩의 차이, 그리고 JVM 내부에서 어떤 일이 일어나는지까지 자세히 알아보자.


바인딩(Binding)이란?

바인딩(Binding)은 메서드 호출과 실제 실행될 메서드를 연결하는 과정을 의미한다.

dog.sound();

sound()를 호출했다.
        ▼
어떤 클래스의 sound()를 실행해야 하지?
        ▼
실제 실행할 메서드를 결정

바인딩은 언제 일어날까?

2가지가 존재실행(런타임) 시점

  • 실행 시점 (런타임) - 동적 바인딩
  • 컴파일 시점 - 정적 바인딩

정적 바인딩

컴파일할 때 어떤 메서드를 호출할지 이미 결정되는 것을 말한다.
실행중에 절때 바뀌지 않음.

 

class Animal {

    static void hello() {
        System.out.println("Animal");
    }
}

public class Main {

    public static void main(String[] args) {

        Animal.hello();

    }
}
Animal

컴파일러는 이미 Animal.hello() 를 호출한다는 것을 알고 있다.


정적 바인딩 적용 대상

  • static 메서드
    클래스 자체에 속하기 때문에 컴파일 시점에 결정 됨.
class Animal {

    static void hello() {
    }
}

 

  • private 메서드
    private은 자식 클래스가 접근 자체를 못함. 따라서 컴파일러가 바로 결정. 오버라이딩도 불가능
class Animal {

    private void eat() {

    }
}

 

  • final 메서드
    final은 오버라이딩이 금지 됨. 바뀔 가능성이 없기 때문에 정적 바인딩
class Animal {

    final void sound(){

    }
}

동적 바인딩

실행(Runtime) 중에 실제 객체를 보고 어떤 메서드를 실행할지 결정하는 것.
컴파일할 때는 Animal 타입으로 알고 있지만 실행하면 객체는 Dog객체로 인지하고 Dog 의 메서드를 실행함

 

class Animal {

    void sound() {
        System.out.println("동물");
    }

}

class Dog extends Animal {

    @Override
    void sound() {
        System.out.println("멍멍");
    }

}

public class Main {

    public static void main(String[] args) {

        Animal animal = new Dog();

        animal.sound();

    }

}
// -> "멍멍" 출력

메서드 호출 시 JVM은 아래와 같은 순서로 실행 됨. 결론적으로 실제 객체를 기준으로 메서드를 선택하게 된다.

1. 컴파일 시점에 animal.sound()는 animal 타입이다.
(컴파일러는 animal의 타입이 Animal인 것만 봄)

Animal 클래스에 sound()가 있기 때문에 컴파일 성공.


2. animal.sound() <- 동적 바인딩 수행

이 시점에서는 실제 객체는 Dog() 인것을 확인


3. 실제 객체인 Dog()에 가서 sound() 메서드를 찾고 실행

4. 메서드가 없다면 부모 클래스에 있는 sound()를 실행

오버로딩과 오버라이딩

  • 오버로딩 - 정적 바인딩
class Test {

    void print(int a){
        System.out.println("int");
    }

    void print(String s){
        System.out.println("String");
    }

}

...

// main

Test t = new Test();

t.print(10);

컴파일러는 10은 int라는 것을 확인하고, print(int)를 호출한다. 이미 컴파일 시점에 결정이 완료된 상태이다. 결론적으로 오버로딩은 정적 바인딩이다.

  • 오버라이딩 - 동적 바인딩
class Parent {

    void hello(){
        System.out.println("Parent");
    }

}

class Child extends Parent{

    @Override
    void hello(){
        System.out.println("Child");
    }

}

...

// main

Parent p = new Child();

p.hello();


//-> "Child" 출력

Parent p = new Child()에서 컴파일러는 (1)참조 변수 p의 타입이 Parent인 것을 확인한다. (2)그리고 p.hello()를 호출했을 때 Parent 클래스에 hello() 메서드가 존재하는지 확인한다. 메서드가 존재하면 (3)컴파일이 완료된다. 이후 (4)실행(Runtime) 시점에는 JVM이 p가 실제로 참조하고 있는 객체가 Child인지 확인한다. (5)실제 객체가 Child이므로 먼저 Child 클래스에서 hello() 메서드를 찾는다. 오버라이딩된 메서드가 있다면 Child의 hello()를 실행하고, 없다면 부모 클래스인 Parent로 올라가 hello()를 찾아 실행한다.


헷갈리는 부분 - 하이딩

class Parent {

    static void hello(){
        System.out.println("Parent");
    }

}

class Child extends Parent {

    static void hello(){
        System.out.println("Child");
    }

}

...

// main

Parent p = new Child();

p.hello();



// -> Parent 출력

 

static 메서드는 참조 변수의 타입을 기준으로 컴파일 시점에 결정되기 때문에 Parent 클래스의 hello가 실행이 된다.
  • Parent p = new Child();에서 컴파일러는 p의 타입이 Parent인 것을 확인한다. 그리고 p.hello()를 호출했을 때 Parent 클래스에 hello() 메서드가 존재하는지 확인한다.
  • 그런데 hello()는 static 메서드이므로 컴파일러는 실행 시점까지 기다리지 않고, 컴파일 시점에 Parent.hello()를 호출하도록 이미 결정한다.
  • 따라서 프로그램이 실행될 때 p가 실제로 Child 객체를 참조하고 있더라도, JVM은 객체를 확인하지 않고 컴파일 시점에 결정된 Parent.hello()를 그대로 실행한다.

참고

https://hyunsb.tistory.com/58

 

[JAVA] 정적(Static) 바인딩 vs 동적(Dynamic) 바인딩

바인딩 (binding)바인딩이란 컴퓨터 프로그램에서 각종 값들이 더 이상 변경되지 않는 값으로 구속 되는 것입니다. 풀어서 설명해보자면, 변수(식별자, identifier)가 각종 타입에 의해 데이터형이

hyunsb.tistory.com

https://velog.io/@hyeonseohyeon/Java-%EC%A0%95%EC%A0%81-%EB%B0%94%EC%9D%B8%EB%94%A9-%EB%8F%99%EC%A0%81-%EB%B0%94%EC%9D%B8%EB%94%A9

 

[Java] 정적 바인딩? 동적 바인딩?

변수나 함수의 각종 값이 확정되어 구속되는 것. 바인딩바인딩은 바인딩 시점에 따라 정적 바인딩과 동적 바인딩으로 나뉜다.자바에서의 각 바인딩 유형을 이해해보자!실행 이전에 값이 결정되

velog.io

 

'Java > 정리' 카테고리의 다른 글

StringTokenizer의 사용  (0) 2026.07.28
Java 문자열 입력 처리 - Scanner와 BufferedReader  (0) 2026.07.28
자바에서 int[] ↔ List<Integer> 변환 (for문은 이제 그만...)  (0) 2025.12.08
오름차순 정렬과 내림차순 정렬에 대해 알아보자  (0) 2025.12.05
ArrayList가 처음이라면 꼭 알아야 할 핵심 기능  (0) 2025.12.04
'Java/정리' 카테고리의 다른 글
  • StringTokenizer의 사용
  • Java 문자열 입력 처리 - Scanner와 BufferedReader
  • 자바에서 int[] ↔ List<Integer> 변환 (for문은 이제 그만...)
  • 오름차순 정렬과 내림차순 정렬에 대해 알아보자
나는지토
나는지토
  • 나는지토
    안녕은헬로입니다.
    나는지토
  • 전체
    오늘
    어제
    • 분류 전체보기 (32)
      • Java (8)
        • 정리 (7)
        • 코테 (1)
      • NestJS (19)
        • 개발 (9)
        • 개념과 구조 정리 (10)
      • Python (1)
        • 코테 (1)
      • Backend Design (1)
      • PostgreSQL (2)
      • Docker (1)
  • 블로그 메뉴

    • 홈
    • 태그
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    ArrayList
    nestjs/jwt
    조회 방식
    인증 가드
    토큰 검사
    PostgreSQL
    Collections
    컨트롤러
    db 연결 오류
    BullMQ
    자료구조
    Java
    Redis
    JWT
    코딩테스트
    역할 검사
    커서기반 조회
    서비스
    채팅
    nestjs
  • 최근 글

  • hELLO· Designed By정상우.v4.10.5
나는지토
자바의 동적 바인딩(Dynamic Binding)과 정적 바인딩(Static Binding)
상단으로

티스토리툴바