OOP란 클래스 vs 객체 클래스 구성 객체 생성·사용 생성자·this 캡슐화 📝 문제풀이
◀ 이전 📋 목차 CH 11 ▶
🏗️ CHAPTER 10 · 중급

클래스와 객체 — 객체지향의 첫걸음

여기서부터 자바의 진짜 심장, 객체지향(OOP)이 시작돼요. 어렵게 느껴질 수 있지만, 붕어빵 틀과 붕어빵 하나면 다 이해됩니다. 천천히, 비유와 함께 따라오세요.

🎯 이 장을 끝내면
📖
STEP 0 · 객체지향이란
프로그램을 "부품"으로 조립한다
지금까지는 코드를 위에서 아래로 쭉 나열했다면, 이제 생각의 틀을 바꿉니다.

객체지향 프로그래밍(OOP, Object-Oriented Programming)은 프로그램을 "객체(object)"라는 부품 단위로 나눠서 조립하는 방식이에요. 지금까지 우리는 변수와 메서드를 위에서 아래로 늘어놓는 식으로 코드를 짰죠. 그런데 프로그램이 커지면 이 방식은 금방 뒤엉켜요.

그래서 나온 아이디어가 "현실 세계처럼 생각하자"예요. 우리가 사는 세상은 전부 사물(객체)로 이뤄져 있잖아요. 자동차, 사람, 강아지, 계좌 하나하나가 자기만의 데이터와 기능을 가진 독립된 존재죠. 프로그램도 이렇게 객체들을 만들어 서로 협력하게 짜자는 게 OOP입니다.

🚗 왜 필요한가 — 현실 세계를 그대로 옮긴다 "쇼핑몰 프로그램"을 만든다고 해봐요. 현실엔 회원이 있고, 상품이 있고, 주문이 있어요.
OOP에서는 이걸 그대로 회원 객체, 상품 객체, 주문 객체로 만들어요. 각 객체는 자기 데이터(회원 이름, 상품 가격…)와 기능(주문하기, 결제하기…)을 스스로 가져요. 💡 핵심: 복잡한 프로그램을 "현실의 사물"에 대응하는 작은 부품들로 쪼개니, 이해하기 쉽고 고치기·재사용하기도 편해져요. 이게 OOP를 쓰는 이유예요.
OOP 객체(Object) 클래스(Class) 인스턴스 필드 메서드 new 생성자 this 캡슐화
🧭
이번 장의 마음가짐. OOP는 처음엔 낯설지만, 자바를 자바답게 쓰는 가장 중요한 사고방식이에요. 문법 하나하나보다 "클래스는 설계도, 객체는 실체"라는 큰 그림을 먼저 잡으세요.
🥮
STEP 1 · 클래스 vs 객체
붕어빵 틀(설계도)과 붕어빵(실체)
이 비유 하나면 클래스와 객체는 평생 안 헷갈려요.

OOP에서 가장 먼저 만나는 두 단어가 클래스(class)객체(object)예요. 둘의 관계는 이렇게 정리돼요.

🧇
클래스 (Class)
붕어빵 = 설계도
모양·구성만 정의
⚙️
new (찍어내기)
틀로 반죽을 찍어냄
= 객체 생성
🥮
객체 (Object)
실제 붕어빵 하나
= 인스턴스
🥮 붕어빵 비유 완전 정리 클래스 = 붕어빵 — 붕어빵의 모양·구성만 정의한 설계도예요. 틀 자체는 먹을 수 없죠. 프로그램에서 딱 하나만 있으면 돼요.
객체 = 틀로 찍어낸 붕어빵 하나하나 — 실제로 존재하고 사용할 수 있는 실체예요. 틀 하나로 붕어빵을 여러 개 찍어낼 수 있듯, 클래스 하나로 객체를 여러 개 만들 수 있어요. 그래서 객체 = 인스턴스(instance)라고도 불러요. "클래스를 인스턴스화(instantiate)한다"는 말은 "설계도로 실체를 찍어낸다"는 뜻이에요. 완전히 같은 말이니 두 단어를 편하게 오가세요.
🔤
인스턴스(instance) — 클래스(설계도)로 실제로 찍어낸 객체 하나하나를 가리키는 말이에요. 그래서 객체 = 인스턴스로 봐도 되고, new Car()로 하나 만드는 걸 "인스턴스를 만든다(인스턴스화)"라고 해요.

다른 비유도 많아요. 클래스는 "붕어빵 틀 / 자동차 설계도 / 쿠키 커터", 객체는 "붕어빵 / 실제 자동차 / 찍어낸 쿠키". 공통점은 하나예요 — 클래스는 개념(설계), 객체는 실물.

💡
한 줄 정리. 클래스는 설계도(1개), 객체는 그걸로 찍어낸 실체(여러 개). 코드로 class Car {...}를 쓰는 순간 "설계도"를, new Car()를 쓰는 순간 "실체 하나"를 만드는 거예요.
🧩
STEP 2 · 클래스 구성
필드(상태) + 메서드(행동)
설계도 안에는 딱 두 가지가 들어가요. 상태를 담는 것, 행동을 담는 것.

클래스라는 설계도 안에는 크게 두 가지가 들어가요.

🧩 클래스의 두 구성요소 ① 필드(field) = 속성 · 상태(데이터) — 그 객체가 "가지고 있는 것"이에요. 자동차라면 이름, 색깔, 속도. 변수의 형태로 클래스 안에 선언해요.
② 메서드(method) = 기능 · 행동 — 그 객체가 "할 수 있는 것"이에요. 자동차라면 달리다, 멈추다. 우리가 앞 장에서 배운 그 메서드예요. 쉽게 외우면 — 필드는 "명사(상태)", 메서드는 "동사(행동)". 강아지 객체라면 필드는 이름·나이, 메서드는 짖다·먹다인 셈이죠.

이제 실제 자동차 클래스를 만들어 볼게요. 자동차는 이름과 속도라는 상태를 갖고, 달리는 행동을 할 수 있어요.

// 자동차 "설계도" — 클래스
class Car {
    // ① 필드(상태): 이 자동차가 가진 데이터
    String name;   // 이름
    int speed;     // 속도

    // ② 메서드(행동): 이 자동차가 할 수 있는 일
    void run() {
        System.out.println(name + "이(가) " + speed + "km/h로 달립니다.");
    }
}

위 코드는 아직 설계도일 뿐이에요. 붕어빵 틀을 만든 것이지, 붕어빵을 찍어낸 게 아니죠. 실제로 쓰려면 다음 STEP에서 객체를 생성해야 해요.

📌
용어 정리. 클래스 안에 선언한 변수를 필드(field) 또는 멤버 변수라고 불러요. 메서드 안의 지역 변수와 구분하기 위한 이름이에요. 필드는 "그 객체에 계속 붙어 있는 데이터"랍니다.
🏗️
STEP 3 · 객체 생성과 사용
new로 찍어내고, 점(.)으로 다룬다
드디어 설계도로 실제 붕어빵을 찍어낼 시간이에요.

객체를 만드는 건 new 키워드가 담당해요. 그리고 만든 객체는 참조변수에 담아 두고, 점(.)으로 필드와 메서드에 접근해요.

public class Main {
    public static void main(String[] args) {
        // 1) 객체 생성 — 설계도(Car)로 실체 하나를 찍어낸다
        Car c = new Car();

        // 2) 필드에 값 넣기 — 점(.)으로 접근
        c.name = "소나타";
        c.speed = 100;

        // 3) 메서드 호출 — 이 객체의 행동 실행
        c.run();   // 출력: 소나타이(가) 100km/h로 달립니다.
    }
}
🔍 Car c = new Car(); 한 줄 완전 해부 new Car()new가 메모리에 새 Car 객체(붕어빵)를 실제로 만들어요. Car()는 뒤에서 배울 생성자 호출이에요.
Car cc참조변수(reference variable)예요. 방금 만든 객체가 있는 "위치(주소)"를 가리키는 리모컨이라고 생각하세요.
= — new로 만든 객체를 리모컨 c에 연결해요. 이제 c.name, c.run()처럼 c를 통해 그 객체를 조종할 수 있어요. 💡 즉 객체는 메모리 어딘가에 실제로 존재하고, 참조변수 c는 그 객체를 가리키는 리모컨일 뿐이에요. 이 구분이 나중에 아주 중요해져요.

객체는 여러 개 만들 수 있어요. 붕어빵 틀 하나로 붕어빵을 여러 개 찍듯이요. 그리고 각 객체의 필드는 완전히 독립적이에요.

Car c1 = new Car();
c1.name = "소나타";
c1.speed = 100;

Car c2 = new Car();
c2.name = "테슬라";
c2.speed = 200;

c1.run();   // 소나타이(가) 100km/h로 달립니다.
c2.run();   // 테슬라이(가) 200km/h로 달립니다.
// c2.speed를 200으로 바꿔도 c1.speed는 그대로 100! 서로 남남이에요.
🥮
객체는 서로 남남. c1c2는 같은 설계도(Car)에서 나왔지만 각자의 필드 값을 따로 가져요. 붕어빵 하나에 팥을 더 넣어도 옆 붕어빵은 그대로인 것과 같아요. 이걸 "객체마다 상태가 독립적"이라고 해요.
🔧
STEP 4 · 생성자와 this
객체를 만들 때 한 번에 초기화하기
매번 c.name=…, c.speed=… 쓰기 귀찮죠? 생성자가 이걸 자동으로 해줘요.

앞에서는 객체를 만든 뒤 c.name = "소나타";처럼 필드를 하나씩 채웠어요. 그런데 필드가 많으면 번거롭고 빠뜨리기 쉬워요. 생성자(constructor)객체를 만드는 순간(new) 필드를 한 번에 초기화해 주는 특별한 메서드예요.

🔧 생성자의 3가지 규칙 — 시험 단골 ① 이름이 클래스명과 똑같다 — 클래스가 Car면 생성자도 Car여야 해요.
반환 타입이 없다void조차 안 써요! 일반 메서드와 이걸로 구분돼요.
new로 객체를 만들 때 자동으로 딱 한 번 호출돼요. 한마디로 생성자는 "객체가 태어날 때 실행되는 초기화 담당자"예요. 붕어빵을 굽는 순간 팥을 미리 넣어 두는 것과 비슷해요.
class Car {
    String name;
    int speed;

    // 생성자 — 이름은 클래스명(Car)과 같고, 반환 타입이 없다!
    Car(String name, int speed) {
        this.name = name;    // this로 필드와 매개변수를 구분
        this.speed = speed;
    }

    void run() {
        System.out.println(name + "이(가) " + speed + "km/h로 달립니다.");
    }
}

// 사용 — 만들면서 바로 초기화! 한 줄로 끝.
Car c = new Car("소나타", 100);
c.run();   // 소나타이(가) 100km/h로 달립니다.

🪞 this — "나 자신"을 가리키는 말

this"지금 이 객체 자신"을 가리키는 키워드예요. 생성자 안을 보면 매개변수 이름(name)과 필드 이름(name)이 똑같아서 헷갈리죠? 이때 this.name은 필드, 그냥 name은 매개변수를 뜻해요.

🪞 this.name = name; 뜻풀이 왼쪽 this.name = 이 객체의 필드 name (그 객체에 계속 붙어 있을 데이터).
오른쪽 name = 생성자가 받은 매개변수 name (잠깐 전달받은 값).
그래서 이 문장은 "내 필드에다, 전달받은 값을 넣어라"는 뜻이에요. 💡 this가 없으면 name = name;이 되어 매개변수가 자기 자신에게 대입되고, 필드는 초기값(예: null) 그대로 남아요. 그래서 이름이 겹칠 땐 this.가 꼭 필요해요.

📦 기본 생성자 & 생성자 오버로딩

기본 생성자(default constructor). 생성자를 하나도 안 쓰면, 자바가 매개변수 없는 빈 생성자를 자동으로 넣어줘요. 그래서 STEP 3에서 new Car()가 됐던 거예요. 하지만 생성자를 하나라도 직접 만들면 자바는 기본 생성자를 더 이상 자동으로 안 만들어 줘요.

생성자 오버로딩(overloading). 앞 장에서 메서드 오버로딩을 배웠죠? 생성자도 매개변수 개수·타입만 다르면 여러 개 만들 수 있어요.

class Car {
    String name;
    int speed;

    Car() {                      // 생성자 ① 매개변수 없음
        this.name = "이름없음";
        this.speed = 0;
    }
    Car(String name) {           // 생성자 ② 이름만 받음
        this.name = name;
        this.speed = 0;
    }
    Car(String name, int speed) {// 생성자 ③ 둘 다 받음
        this.name = name;
        this.speed = speed;
    }
}

Car a = new Car();                 // ① 호출
Car b = new Car("소나타");          // ② 호출
Car c = new Car("테슬라", 200);     // ③ 호출  → 인자에 맞는 생성자가 자동 선택됨
💡
오버로딩 성립 조건. 생성자(또는 메서드) 이름은 같아도 매개변수의 개수나 타입이 달라야 해요. 개수·타입이 완전히 같고 이름만 다른 건 오버로딩이 아니에요 — 자바가 어느 걸 부를지 구분할 수 없거든요.
🔢 번외 — 필드의 "기본값"은 뭘까? (초기화 안 하면)
객체를 만들었는데 필드에 값을 안 넣으면, 필드는 비어 있는 게 아니라 타입별 "기본값"으로 자동 채워져요. (지역 변수와 다른 점! 지역 변수는 초기화 안 하면 에러예요.)
필드 타입기본값
int, long, short, byte0
double, float0.0
booleanfalse
char'\u0000' (코드값 0, 널 문자)
참조형 (String, 배열, 객체…)null
기억 포인트 숫자는 0, boolean은 false, 참조형은 null. 예를 들어 Car c = new Car(); 후 아무것도 안 넣으면 c.namenull, c.speed0이에요.
🔒
STEP 5 · 캡슐화 (맛보기)
필드를 숨기고, 정해진 통로로만 다루기
데이터를 아무나 함부로 못 바꾸게 "보호막"을 씌우는 개념이에요.

지금까지 우리는 c.speed = 200;처럼 필드를 바깥에서 직접 바꿨어요. 그런데 이렇게 열어두면 문제가 생겨요 — 누군가 c.speed = -9999;처럼 말도 안 되는 값을 넣어도 막을 방법이 없거든요.

그래서 나온 게 캡슐화(encapsulation)예요. 필드는 private으로 숨기고, 값을 읽고 쓰는 건 getter/setter라는 정해진 메서드로만 하도록 만드는 거예요.

🔤
캡슐화(encapsulation) — 데이터(필드)를 밖에서 함부로 못 건드리게 껍질로 감싸 숨기고, 정해진 통로(메서드)로만 다루게 하는 것이에요. 알약을 캡슐로 감싸 안전하게 먹이듯, 값을 안전하게 보호하려는 아이디어예요.
class Car {
    private String name;   // private → 바깥에서 직접 접근 불가!
    private int speed;

    // getter : 값을 "읽는" 통로
    public String getName() {
        return name;
    }
    // setter : 값을 "쓰는" 통로 (여기서 검사도 가능!)
    public void setSpeed(int speed) {
        if (speed < 0) {
            System.out.println("속도는 0보다 작을 수 없어요!");
            return;              // 잘못된 값이면 막아버림
        }
        this.speed = speed;
    }
    public int getSpeed() {
        return speed;
    }
}

Car c = new Car();
// c.speed = -9999;   // ❌ 컴파일 에러! private이라 직접 접근 불가
c.setSpeed(-9999);    // setter가 검사해서 막아줌 → "속도는 0보다..."
c.setSpeed(100);      // ✅ 정상 값만 통과
💊 왜 숨길까 — 캡슐화의 목적 1. 잘못된 값 차단 — setter 안에서 검사를 하니, 음수 속도 같은 이상한 값이 들어오는 걸 막을 수 있어요.
2. 안전한 통제 — 데이터를 바꾸는 통로가 메서드 하나로 정해져 있어, 어디서 어떻게 바뀌는지 관리하기 쉬워요.
3. 내부 구현 숨기기 — 바깥은 getName()만 알면 돼요. 내부에서 필드 이름을 바꿔도 바깥 코드는 안 고쳐도 돼요. 💊 "캡슐"이라는 이름처럼, 알맹이(데이터)를 껍질로 감싸 보호하는 거예요. 약을 캡슐에 넣어 안전하게 먹이는 것과 같은 발상이죠. 지금은 "필드는 private, 접근은 getter/setter로"라는 만 기억하면 충분해요.
🔑
접근 제한자 살짝 맛보기. private은 "이 클래스 안에서만 접근 가능", public은 "어디서든 접근 가능"이라는 뜻이에요. 캡슐화는 필드는 private, getter/setter는 public으로 두는 게 기본 패턴이에요. 접근 제한자는 뒤에서 더 자세히 다룹니다.
🧠 이 장 핵심 요약
📝
문제풀이 · 점검
배운 걸 가볍게 점검해봐요
시험이 아니라 "내가 이해했나" 확인용이에요. 틀려도 바로 해설이 나와요.
🧪
문제를 풀면 즉시 정답과 해설이 나오고, 위쪽 바에 점수가 쌓여요. 네 가지 유형(객관식 · O/X · 빈칸 · 코드결과)을 섞어 두었어요. 편하게 눌러보세요.
CH 11 🧬 상속(Inheritance) — 설계도를 물려받아 확장하기