실기 Java: 생성자·오버라이딩·static 추적
쉽게 말하면
실기 Java 문제는 주로 '누가 먼저 실행되나'와 '어느 클래스의 메서드가 불리나'를 물어요. 자식 객체를 만들면 부모 생성자가 먼저 실행돼요. 부모 형 변수에 자식 객체를 담고 메서드를 부르면, 자식이 다시 정의한(오버라이딩) 메서드가 불려요. 반대로 필드(변수)와 static 메서드는 변수의 형(왼쪽에 적힌 클래스)을 따라가요. 이 세 가지만 확실히 해도 Java 문제 대부분이 풀려요.
시험용 정의
생성자 호출 순서: 자식 생성자는 첫 줄에서 super(...)로 부모 생성자를 먼저 부른다. 적지 않으면 컴파일러가 super()를 넣는다. this(...)는 같은 클래스의 다른 생성자를 부른다. 오버라이딩(재정의)은 자식이 부모 메서드를 같은 이름·매개변수로 다시 정의하는 것이고, 부모 형 변수에 자식 객체를 담아도 실제 객체의 메서드가 실행된다(동적 바인딩). 단, 필드와 static 메서드는 오버라이딩되지 않아 변수의 형을 따른다. 오버로딩(중복 정의)은 이름은 같고 매개변수의 개수나 형이 다른 메서드를 여러 개 두는 것으로, 인자의 형으로 어느 것을 부를지 컴파일할 때 정한다. static 변수는 클래스에 하나만 있어 모든 객체가 함께 쓴다. finally 블록은 예외가 나든 return을 만나든 항상 실행된다. 문자열 ==는 같은 객체인지, equals()는 내용이 같은지 비교한다.
비유로 이해하기
생성자 순서는 집 짓기와 같다. 2층(자식)을 올리려면 1층(부모)이 먼저 있어야 한다. 동적 바인딩은 '직원'이라는 명찰을 단 사람에게 일을 시켜도, 실제로 그 사람이 '요리사'면 요리사 방식으로 일하는 것이다.
자세히
먼저 알아 둘 말
| 말 | 뜻 |
|---|---|
| super | 부모 클래스. super()는 부모 생성자, super.메서드()는 부모 메서드 |
| this | 자기 자신 객체. this()는 같은 클래스의 다른 생성자 |
| 오버라이딩 | 부모 메서드를 자식이 같은 모양으로 다시 정의 |
| 오버로딩 | 같은 이름, 다른 매개변수로 메서드를 여러 개 정의 |
| 동적 바인딩 | 실행할 때 실제 객체를 보고 부를 메서드를 정하는 것 |
| static | 객체가 아니라 클래스에 속함. 모든 객체가 하나를 함께 씀 |
예제 1: 생성자 순서
class P {
P() { System.out.print("P"); }
P(String s) { this(); System.out.print(s); }
}
class C extends P {
C() { System.out.print("C"); } // 첫 줄에 super()가 숨어 있음
C(int n) { super("#"); System.out.print(n); }
}
// new C(); → PC
// new C(7); → P#7C(7)은 super("#")로 P(String)을 부르고, P(String)은 this()로 P()를 먼저 불러요. 그래서 P → # → 7.
예제 2: 메서드는 객체, 필드와 static은 변수 형
class A {
int x = 1;
void show() { System.out.print("A" + x); }
static void st() { System.out.print("sA"); }
}
class B extends A {
int x = 2;
void show() { System.out.print("B" + x + super.x); }
static void st() { System.out.print("sB"); }
}
A o = new B();
o.show(); // B21 (실제 객체 B의 메서드)
System.out.print(o.x); // 1 (필드는 변수 형 A 기준)
o.st(); // sA (static은 변수 형 A 기준)예제 3: 오버로딩과 문자열 더하기
static String f(int a) { return "int"; }
static String f(double a) { return "double"; }
static String f(String a) { return "String"; }
// f(5) → int, f(5.0) → double, f("5") → String
System.out.println(1 + 2 + "3" + 4 + 5); // 3345+ 는 왼쪽부터 계산해요. 1 + 2 = 3(숫자)까지는 덧셈이고, 문자열 "3"을 만난 뒤부터는 이어 붙이기라 "33", "334", "3345".
예제 4: finally와 문자열 비교
static int t() {
try { return 1; }
finally { System.out.print("F"); }
}
System.out.println(t()); // F1 (return 전에 finally 실행)
String a = "java", b = "java", c = new String("java");
// a == b → true (같은 리터럴은 하나를 공유)
// a == c → false (new는 새 객체)
// a.equals(c) → true (내용 비교)
"Hello".substring(1, 3); // "el" (1번부터 3번 앞까지)
"Hello".indexOf('l'); // 2시험에 이렇게 나와요
- 자식 생성자 첫 줄의 super(), this()로 생성자 연결
- 오버라이딩된 메서드는 실제 객체 기준(동적 바인딩)
- 필드와 static 메서드는 변수 형 기준
- 오버로딩은 인자 형으로 선택, static 변수는 공유
- finally는 항상 실행, == 와 equals 차이
출제기준 기반
확인 문제
Q1. class P { P() { System.out.print("P"); } }, class C extends P { C() { System.out.print("C"); } }일 때 new C();의 출력은?
- C
- P
- CP
- PC
정답 보기
4번 PC
자식 생성자의 첫 줄에는 보이지 않는 super()가 있어 부모 생성자가 먼저 실행돼요. 그래서 P → C.
Q2. Parent p = new Child();에서 두 클래스 모두 void run()을 정의했을 때 p.run()이 실행하는 것은?
- 둘 다 차례로
- Parent의 run
- Child의 run
- 컴파일 오류
정답 보기
3번 Child의 run
오버라이딩된 메서드는 실제 객체(Child)의 것이 불려요.
Q3. System.out.println("A" + 1 + 2);의 출력은?
- 3A
- 오류
- A12
- A3
정답 보기
3번 A12
문자열을 먼저 만나 "A1", 이어서 "A12". 1 + 2 + "A"라면 3A.
Q4. 이름은 같지만 매개변수의 개수나 형이 다른 메서드를 여러 개 정의하는 것은?
- 오버로딩
- 추상화
- 캡슐화
- 오버라이딩
정답 보기
1번 오버로딩
오버로딩(중복 정의). 오버라이딩은 상속 관계에서 재정의예요.
Q5. try 블록에서 return을 만났을 때 finally 블록은?
- 예외가 날 때만 실행된다
- 실행되지 않는다
- return 전에 실행된다
- 다음 호출 때 실행된다
정답 보기
3번 return 전에 실행된다
finally는 return이나 예외와 상관없이 항상 실행돼요.
헷갈리기 쉬운 O·X
부모 형 변수로 자식 객체를 가리킬 때 필드 값은 자식 클래스의 필드를 따른다.
정답 보기
X 필드는 오버라이딩되지 않아 변수 형(부모)의 필드를 써요.
자식 생성자에 super()를 적지 않아도 부모의 기본 생성자가 먼저 실행된다.
정답 보기
O
static 변수는 객체를 만들 때마다 하나씩 새로 생긴다.
정답 보기
X 클래스에 하나만 있고 모든 객체가 함께 써요.