프로그래밍의 큰 흐름을 이해하려면 먼저 절차적 프로그래밍과의 차이를 알아야 한다.
new 키워드를 사용해 컴퓨터 메모리에 실제로 생성된 실체이다.전사 무기 공격 문제에서 처음 막혔던 부분이다.
void 공격() { ... })void 공격(String 무기이름))a전사.공격("칼"); 에서 "칼"이 인자이다.)생성자는 객체의 '탄생'과 관련된 아주 중요한 개념이다.
생성자의 정의와 특징
new 키워드를 통해 메모리에 생성될 때 딱 한 번, 가장 먼저 자동으로 실행되는 특수한 메서드이다.void, int 등)을 적지 않는다.class Student {
String name; // 학생의 이름 (데이터)
// [생성자] : 학생이 태어날 때 이름을 무조건 지어주도록 강제함!
Student(String inputName) {
this.name = inputName; // 전달받은 이름을 name 변수에 저장(초기화)
System.out.println(inputName + " 학생이 입학했습니다!");
}
}
public class Main {
public static void main(String[] args) {
// 'new' 하는 순간 Student 생성자가 자동으로 팍! 실행됩니다.
Student s1 = new Student("철수");
// 출력: 철수 학생이 입학했습니다!
}
}
생성자 오버로딩 (Constructor Overloading)
사람()으로 호출하면 기본값으로 세팅되고, 사람("홍길동", 20)으로 호출하면 전달한 값으로 세팅되게끔 여러 선택지를 주는 것이다.class Coffee {
String name;
int price;
// 1. 이름과 가격을 모두 받는 생성자
Coffee(String name, int price) {
this.name = name;
this.price = price;
}
// 2. 이름만 받는 생성자 (가격은 3000원 고정) -> 오버로딩
Coffee(String name) {
this(name, 3000); // 1번 생성자를 호출해 처리
}
}
// [사용]
Coffee c1 = new Coffee("라떼", 5000); // 1번 실행
Coffee c2 = new Coffee("생수"); // 2번 실행 (가격은 3000원)
this와 this()
this: 메모리에 생성된 객체 자기 자신을 가리키는 키워드이다. 메서드 안에서 객체가 가진 변수(this.이름)를 명확히 지칭할 때 쓴다.class Person {
String name; // (A) 클래스 멤버 변수
Person(String name) { // (B) 들어오는 매개변수
this.name = name;
// 왼쪽 this.name은 (A)를 의미하고,
// 오른쪽 name은 (B)를 의미합니다.
}
}
this(): 내 클래스 안에 있는 다른 생성자를 호출할 때 쓴다. 생성자 오버로딩을 여러 개 해두었을 때, 코드가 중복되는 것을 막기 위해 특정 생성자에게 초기화 작업을 넘겨버리는(위임하는) 역할이다.class Person {
String name;
int age;
// (1) 이름만 받는 생성자
Person(String name) {
this(name, 20); // "야, 내 이름이랑 나이(20) 들고 밑에 (2)번 생성자한테 가서 만들어달라 해!"
}
// (2) 이름과 나이를 모두 받는 생성자
Person(String name, int age) {
this.name = name;
this.age = age;
}
}
생성자 연쇄 호출과 super()
super(); 라는 코드를 자동으로 몰래 끼워 넣는다.super()는 부모 클래스의 생성자를 호출하는 명령어이다. 이를 통해 부모의 데이터가 먼저 초기화(세팅)된 후, 다시 자식 생성자로 돌아와 자식의 데이터가 초기화되는 흐름을 생성자 연쇄 호출이라고 부른다.이름이 비슷해서 가장 많이 헷갈리는 두 용어이므로 완전히 다른 개념임을 정확히 구분해야 한다.
메서드 오버로딩 (Overloading - 과적하다)
자기소개(), 자기소개(String 이름))메서드 오버라이딩 (Overriding - 덮어쓰다, 재정의하다)
{ }만 바꿀 수 있다.식당 메뉴판을 생각하세요. '김밥'이라는 이름은 하나지만, 옵션이 여러 개인 상황입니다.
핵심: 손님이 "김밥 주세요"라고 할 때, 뒤에 무슨 말을 덧붙이느냐에 따라 주방장이 알아서 다른 요리를 내오는 것입니다. (같은 클래스 안에서 일어남)
대를 이어 하는 식당을 생각하세요. 부모님이 물려준 레시피가 마음에 안 들어서 내 식대로 바꾸는 겁니다.
핵심: 이름(김밥)과 재료 구성은 똑같아야 하지만, 안에 들어가는 내용(실행 코드)만 내 마음대로 싹 바꾸는 것입니다. (상속 관계에서 일어남)
| 구분 | 오버로딩 (Overloading) | 오버라이딩 (Overriding) |
|---|---|---|
| 한마디 | "이름은 같은데 여러 개 만들기" | "물려받은 거 내 맘대로 바꾸기" |
| 발생 장소 | 한 클래스 안에서 (나 혼자) | 부모와 자식 사이에서 (상속) |
| 차이점 | 매개변수가 달라야 함 (옵션 차이) | 내용물 { }만 달라야 함 (재정의) |
| 비유 | 폰 살 때 (128GB vs 256GB 선택) | 폰 업데이트 (기능은 같은데 성능 개선) |
페라리와 자동차, 그리고 사람과 팔 문제를 풀 때 사용되는 두 가지 구조이다.
상속 관계 (Inheritance - IS-A 관계)
extends)하는 방법이다.(페라리)a자동차)포함 관계 (Composition - HAS-A 관계)
둘 다 "자식 클래스야, 이 기능은 네가 반드시 오버라이딩(재정의)해서 완성해라"라고 규칙을 강제하기 위해 만들어진 부모용 설계도이다.
{ }을 가지고 있는 온전한 메서드이다.abstract 키워드가 붙으며, 실행될 코드 블록 { } 없이 선언만 되어 있는 껍데기 메서드이다. (예: abstract void 작동();)추상 클래스 (Abstract Class)
abstract가 붙는다. 미완성 설계도라서 new를 통해 직접 객체를 생성할 수 없다.extends) 받을 수 있다.인터페이스 (Interface)
interface 키워드를 사용한다. 순도 100%의 추상 클래스라고 보며, 객체 생성은 불가능하다.implements)이 가능하다는 것이다. 하나의 클래스가 여러 개의 인터페이스를 콤마(,)로 연결해서 동시에 상속받아 다양한 규칙을 부여받을 수 있다.| 특징 | 추상 클래스 (Abstract Class) | 인터페이스 (Interface) |
|---|---|---|
| 추상성 | 0% ~ 100% (일반 기능도 섞일 수 있음) | 100% (오직 추상적인 껍데기만 존재) |
| 구성 요소 | 일반 변수, 구상 메서드, 추상 메서드 혼용 가능 | 오직 상수와 추상 메서드만 허용 |
| 상속 키워드 | extends (확장) | implements (구현) |
| 다중 상속 | 불가능 (단일 상속만 허용) | 가능 (여러 개 동시 상속 허용) |
추상은 어떤 사물이나 개념의 공통적인 특징(이름, 기능의 정의)만 뽑아내고, 실제 어떻게 작동할지는 정하지 않은 상태를 말한다.
{ }이 없다.abstract 키워드를 사용한다.abstract class 무기 {
abstract void 작동(); // 추상 메서드: 이름만 있고 몸통{ }이 없음
}
구상은 추상적인 개념을 바탕으로 실제로 어떻게 움직일지 코드를 꽉 채워 넣은 상태를 말한다. 우리가 지금까지 써온 일반적인 클래스와 메서드가 모두 '구상'에 해당한다.
{ }가 붙고, 그 안에 실제 실행될 코드(System.out.println 등)가 들어있다.class 칼 extends 무기 {
void 작동() { // 구상 메서드: 부모의 추상을 이어받아 몸통{ }을 채움
System.out.println("칼로 베기!");
}
}
| 구분 | 추상 (Abstract) | 구상 (Concrete) |
|---|---|---|
| 완성도 | 미완성 (설계도 단계) | 완성 (제품 단계) |
| 코드 블록 | 없음 ( ; 으로 끝남 ) | 있음 ( { ... } ) |
| 객체 생성 | 불가능 (new 못함) | 가능 (new 가능) |
| 목적 | 표준화, 규칙 강제, 공통점 추출 | 실제 기능 구현, 데이터 처리 |
| 위치 | 주로 부모 클래스(추상클래스, 인터페이스) | 주로 자식 클래스(일반 클래스) |
질문했던 코드에서 "추상 메서드를 구상 메서드로 바꾸고 생성자를 활용하라"는 말의 의미는 다음과 같다.
무기라는 공통 개념만 잡기 위해 abstract void 작동();으로 시작한다.{ ... })로 내용을 채워버리는 것이다.정리하자면:
이 '추상'과 '구상'의 개념이 이해되면 자바에서 클래스 구조를 복잡하게 나누는 이유를 알 수 있게 된다. "나(부모)는 큰 틀(추상)만 잡을 것이니 너(자식)는 구체적인 내용(구상)을 채워라" 또는 "내가 다 짜놨으니(구상) 너는 이름만 알려줘" 같은 대화가 코드 속에서 이루어지는 것이다.