[Java] 오버라이딩과 오버로딩
Overriding
- 부모 클래스의 메서드를 자식 클래스에서 재정의 (다른 방식으로 동작하도록 변경)
- 목적 : 부모 클래스로 부터 상속받은 메서드를 자식 클래스에 맞게 기능을 변경하기 위함
- 조건
- 오버라이딩하려는 메서드는 부모 클래스와 같은 이름, 매개변수, 반환 타입을 가진다.
- 자식 클래스는 부모 클래스를 상속한다.
- 장점
- 다형성을 실현
- extends
- 부모 - 선언/정의
- 자식 - 사용/재정의
- 클래스 확장
- 다중 상속 불가
- implements
- 부모 - 선언
- 자식 - 오버라이딩 (재정의)
- interface 구현
- 다중 상속 가능
- abstract
- 추상 클래스 (Abstract Class)
- 일반 클래스와 추상 메서드를 동시에 가짐
- 인스턴스를 생성할 수 없다. 반드시 상속받는 자식 클래스에서 구현하여 사용한다.
Animal animal = new Animal();
x
- 상속을 통해 기능 공유 + 추상 메서드를 통해 자식 클래스가 특정 메서드를 구현하도록 강제
- 추상 메서드 (Abstract Method)
- 메서드 선언부만 존재하고 구현부가 없는 메서드
- 추상 클래스 내에서만 선언
- 상속받는 자식 클래스는 이 추상 메서드를 오버이딩하여 구체적인 동작을 정의한다.
abstract class Animal { // 추상 클래스 // 추상 메서드 abstract void makeSound(); // 일반 메서드 void breathe() { System.out.println("Breathing..."); } } class Dog extends Animal { @Override void makeSound() { System.out.println("Bark!"); } }
- 추상 클래스 (Abstract Class)
Overloading
- 같은 이름의 메서드를 다른 매개변수 여러 개로 정의
- 목적 : 동일한 기능을 수행하되, 입력값에 따라 다르게 동작하기 위함
- 매개 변수의 수나 타입은 달라야 한다.
- 리턴 타입만 다르면 안된다!
장점
- 유연한 메서드 사용
꼬리질문
- 오버라이딩, 오버로딩 메소드 결정 시점
- 오버라이딩 : 런타임 시점
- 자식 클래스의 객체가 부모 클래스 타입으로 선언되었더라도, 런타임 시점에 동적 바인딩(Dynamic Binding)을 통해 실제 객체의 타입에 맞는 메서드가 호출됩니다. 이를 통해 다형성을 실현한다.
- 오버로딩 : 컴파일 타임 시점
- 컴파일러는 함수가 호출될 때 매개변수의 종류와 개수를 보고 어떤 함수를 호출할지 결정한다. 그러나 반환 타입은 호출 순간에는 알 수 없기 때문에, 반환 타입만 다른 함수가 있으면 컴파일러가 어떤 함수를 사용해야 할지 모호해지는 문제가 발생한다. 그래서 반환 타입만 다른 함수는 오버로딩이 불가능합니다.
- int sum() , float sum( ) ⇒ 컴파일 타임 오류
- 오버라이딩 : 런타임 시점
프로그램 실행 단계
1. 코드 작성 (Source Code Writing)
- 개발자가 코드(소스 파일)를 작성하고 저장한다. 이 코드는 사람이 읽을 수 있는 고수준 프로그래밍 언어(high level language)로 작성된 상태입니다.
2. 컴파일 타임 (Compile Time)
- 컴파일 타임 단계는 코드가 기계어로 번역되는 단계이다.
- 구문 분석 (Syntax Analysis): 컴파일러가 소스 코드의 구문을 분석하여 코드가 문법적으로 올바른지 확인한다. 문법 오류가 있는 경우 컴파일러가 이를 감지하고 오류를 보고한다. ⇒ syntax error
- 중간 코드 생성 (Intermediate Code Generation): 코드가 여러 중간 단계로 변환된다. 이 중간 코드는 플랫폼에 독립적인 형식으로 저장되며, 이후에 최적화와 기계어 변환이 이루어진다.
- 최적화 (Optimization): 중간 코드가 실행 속도와 메모리 사용을 개선하도록 최적화된다. 불필요한 연산을 줄이고, 코드의 효율성을 높인다.
- 목적 코드 생성 (Object Code Generation): 최종적으로 목적 파일이 생성된다. 이 목적 파일은 기계어로 번역된 코드로, 직접 실행할 수 없는 상태이므로 링크 작업이 필요하다.
- 링크 (Linking): 목적 파일과 함께 사용할 라이브러리나 다른 모듈을 결합하여 하나의 실행 파일을 생성한다. 이때 모든 외부 참조가 해결되며, 실행 가능한 파일이 최종적으로 생성된다.
결과적으로, 컴파일 타임에는 코드의 오류를 검사하고 기계어로 변환하여 최종 실행 파일을 생성하게 된다.
3. 런타임 (Run Time)
- 컴파일이 완료된 후 프로그램이 실행되면서 런타임 단계가 시작된다. 이 단계에서 프로그램이 실제로 작동하게 된다.
- 메모리 할당 (Memory Allocation): 런타임에는 프로그램에 필요한 메모리가 할당된다. 특히, 객체의 인스턴스 생성, 변수 초기화 등이 이루어진다.
- 동적 바인딩 (Dynamic Binding): 런타임에는 오버라이딩 메서드 같은 동적 바인딩이 발생한다. 다형성을 지원하기 위해, 런타임에 객체의 실제 타입을 확인하고 올바른 메서드를 호출한다.
- 실행 흐름 제어: 프로그램의 흐름이 순차적으로 진행되며, 조건문, 반복문 등과 같은 제어 구문이 실행된다.
- 예외 처리 (Exception Handling): 런타임에는 예상치 못한 오류가 발생할 수 있다. 이 오류를 처리하기 위해
try-catch
블록으로 감싸는 예외 처리가 이루어진다. 예외가 발생하면 프로그램이 이를 감지하고 적절하게 대응한다. - 가비지 컬렉션 (Garbage Collection): 사용하지 않는 메모리를 해제하는 작업이 런타임에서 자동으로 수행된다. 이는 자바와 같은 언어에서 프로그램이 동작 중에 메모리를 효율적으로 관리하는 중요한 과정이다.
전체 구조 요약
- 코드 작성 ➔ 2. 컴파일 타임 (구문 분석 ➔ 중간 코드 생성 ➔ 최적화 ➔ 목적 코드 생성 ➔ 링크) ➔
- 런타임 (메모리 할당 ➔ 동적 바인딩 ➔ 실행 흐름 제어 ➔ 예외 처리 ➔ 가비지 컬렉션)