icon

안동민 개발노트

6장 : 객체지향과 웹프로그래밍

객체지향: 클래스, 객체, 상속, 다형성

객체지향과 웹프로그래밍 학습 절입니다.

5장 2절까지는 컴퓨터 내부와 운영체제를 봤습니다.

CPU
메모리
프로세스
스레드
스케줄링
동기화
교착상태
가상기억장치

이제 다시 프로그래밍 쪽으로 옵니다. 이번 절은 객체지향프로그래밍입니다.

이번 절의 핵심은 다음과 같습니다.

클래스 = 객체를 만들기 위한 설계도
객체 = 클래스로 만든 실제 대상
캡슐화 = 데이터와 기능을 하나로 묶는 것
정보은닉 = 내부 정보를 숨기는 것
상속 = 부모 클래스의 기능을 물려받는 것
다형성 = 같은 명령이 객체에 따라 다르게 동작하는 것

객체지향프로그래밍 출제범위에는 클래스, 객체, 메시지와 메소드, 캡슐화, 상속, 다형성, 절차지향과 객체지향 비교, 생성자, 소멸자, 접근 제한, this, super, 오버로딩, 오버라이딩, 추상 클래스, 인터페이스, 동적 바인딩, 예외 처리가 포함되어 있습니다.


이번 절의 큰 그림

이번 절에서 배울 흐름은 다음과 같습니다.

절차지향과 객체지향
→ 클래스와 객체
→ 멤버변수와 메소드
→ 메시지
→ 캡슐화와 정보은닉
→ 접근 제한자
→ 생성자와 소멸자
→ this와 super
→ 오버로딩과 오버라이딩
→ 상속
→ 다형성
→ 추상 클래스와 인터페이스
→ 정적 멤버
→ 문자열과 라이브러리
→ 예외 처리 맛보기

객체지향은 처음에는 말이 추상적입니다.

그래서 이번 절에서는 코드보다 먼저 개념을 확실히 잡습니다.


객체지향 개요

절차지향이란?

절차지향은 프로그램을 순서 중심으로 생각하는 방식입니다.

C언어가 대표적으로 절차지향에 가깝습니다.

예를 들어 은행 계좌 프로그램을 만든다고 가정하겠습니다.

절차지향에서는 이렇게 생각합니다.

1. 계좌 정보를 입력합니다.
2. 입금 함수를 실행합니다.
3. 출금 함수를 실행합니다.
4. 잔액을 출력합니다.

코드도 함수 중심으로 나뉩니다.

void deposit() { ... }
void withdraw() { ... }
void printBalance() { ... }
절차지향의 핵심은
무엇을 어떤 순서로 처리할 것인가?

입니다.

장점은 단순하고 이해하기 쉽다는 것입니다. 하지만 프로그램이 커지면 데이터와 함수가 흩어져 관리가 어려워질 수 있습니다.


객체지향이란?

객체지향은 프로그램을 객체 중심으로 생각하는 방식입니다.

객체란 현실 세계의 어떤 대상을 프로그램 안에 표현한 것입니다.

학생
자동차
계좌
회원
게시글
상품
몬스터
아이템

객체지향에서는 이렇게 생각합니다.

계좌 객체가 있습니다.
계좌 객체는 잔액을 가집니다.
계좌 객체는 입금할 수 있습니다.
계좌 객체는 출금할 수 있습니다.

즉 데이터와 기능을 함께 묶습니다.

객체 = 데이터 + 기능

예를 들어 계좌 객체는은 다음과 같습니다.

구성
데이터계좌번호, 잔액, 소유자
기능입금, 출금, 잔액조회

객체지향의 핵심 질문은 다음과 같습니다.

어떤 객체들이 있고, 그 객체들이 서로 어떻게 협력하는가?

절차지향과 객체지향 비교

구분절차지향객체지향
중심함수, 절차객체
사고방식순서대로 처리객체들이 협력
데이터와 함수따로 관리되는 경우 많음하나로 묶음
장점단순한 프로그램에 적합큰 프로그램 설계에 유리
대표 언어CJava, C++, C#
짧게 말하면
절차지향 = 일의 순서 중심
객체지향 = 객체 중심

C에서도 구조체와 함수를 이용해 객체지향 비슷한 설계를 할 수 있지만, Java나 C++는 객체지향 문법을 직접 제공합니다.


클래스와 객체

클래스란?

클래스는 객체를 만들기 위한 설계도입니다.

예를 들어 붕어빵 틀을 생각하자.

붕어빵 틀 = 클래스
실제 붕어빵 = 객체

또 자동차 설계도를 생각해도 됩니다.

자동차 설계도 = 클래스
실제로 만들어진 자동차 = 객체

프로그래밍에서는 클래스 안에 객체가 가질 데이터와 기능을 정의합니다.

class Student {
    String name;
    int score;

    void study() {
        System.out.println("공부한다");
    }
}

여기서 Student가 클래스입니다.

이 클래스는 학생 객체가 가져야 할 정보를 정의합니다.

name = 이름
score = 점수
study() = 공부하는 기능

객체지향 출제범위에서도 클래스와 객체, 멤버변수와 메소드 선언, 객체 생성과 사용이 핵심 항목으로 들어 있습니다.


객체란?

객체는 클래스를 바탕으로 실제로 만들어진 대상입니다.

Student s1 = new Student();
Student s2 = new Student();

여기서 s1, s2는 Student 클래스로 만든 객체입니다.

같은 설계도로 여러 객체를 만들 수 있습니다.

Student 클래스
 ├─ s1 객체
 ├─ s2 객체
 └─ s3 객체

각 객체는 자기만의 값을 가질 수 있습니다.

s1.name = "Kim";
s1.score = 90;

s2.name = "Lee";
s2.score = 80;

둘 다 Student 객체지만, 내부 데이터는 다릅니다.


클래스와 객체 비교

구분클래스객체
의미설계도실제 만들어진 대상
메모리클래스 자체는 설계 정보객체는 메모리에 실제 생성
Students1, s2
비유붕어빵 틀붕어빵
역할어떤 데이터와 기능을 가질지 정의실제 값을 가지고 동작
짧게
클래스 = 설계도
객체 = 설계도로 만든 실제 물건

객체는 무엇으로 구성되는가?

객체는 보통 두 가지로 구성됩니다.

상태 + 동작
프로그래밍 용어로는
멤버변수 + 메소드
구분
멤버변수객체가 가진 데이터, 상태이름, 점수, 잔액
메소드객체가 할 수 있는 기능, 동작공부하기, 입금하기, 출력하기
class Account {
    String owner;
    int balance;

    void deposit(int money) {
        balance += money;
    }

    void withdraw(int money) {
        balance -= money;
    }
}
여기서
owner, balance = 멤버변수
deposit(), withdraw() = 메소드

객체지향 예시문제에서 “객체는 클래스와 메소드로 구성됩니다”라는 설명은 틀린 설명으로 나옵니다. 더 정확히는 객체는 클래스로부터 생성된 실체이며, 상태를 나타내는 데이터와 동작을 나타내는 메소드를 가진다고 이해해야 합니다.


메시지와 메소드

객체지향에서는 객체에게 일을 시키는 것을 메시지를 보냅니다고 표현하기도 합니다.

예를 들어
account.deposit(1000);

이 코드는 account 객체에게 메시지를 보낸 것입니다.

“1000원을 입금해라.”

실제로 실행되는 기능은 deposit 메소드입니다.

용어의미
메시지객체에게 어떤 일을 요청하는 것
메소드그 요청을 처리하는 함수
객체에게 메시지를 보내면, 객체의 메소드가 실행됩니다.

캡슐화와 정보은닉

캡슐화

캡슐화는 데이터와 기능을 하나로 묶는 것입니다.

class Account {
    private int balance;

    public void deposit(int money) {
        balance += money;
    }

    public int getBalance() {
        return balance;
    }
}

이 클래스는 계좌 잔액과 입금 기능을 하나로 묶고 있습니다.

balance 데이터
deposit 기능
getBalance 기능

이것이 캡슐화입니다.

캡슐화의 목적은 단순히 묶는 것만이 아닙니다.

관련 있는 데이터와 기능을 하나로 묶고,
외부에서 함부로 내부 데이터를 건드리지 못하게 합니다.

객체지향 출제범위에도 캡슐화와 정보은닉이 주요 개념으로 포함됩니다.


정보은닉

정보은닉은 객체 내부의 세부 정보를 외부에서 직접 보지 못하게 숨기는 것입니다.

예를 들어 계좌 잔액을 아무나 직접 바꾸면 위험합니다.

account.balance = -100000;

이런 식으로 외부에서 마음대로 바꾸면 계좌가 이상해집니다.

그래서 balanceprivate으로 숨깁니다.

private int balance;

그리고 정해진 메소드를 통해서만 접근하게 합니다.

public void deposit(int money) {
    if (money > 0) {
        balance += money;
    }
}

이렇게 하면 잘못된 값이 들어가는 것을 막을 수 있습니다.

정보은닉 = 내부 데이터에 직접 접근하지 못하게 숨김

캡슐화와 정보은닉은 같이 자주 나옵니다.

개념핵심
캡슐화데이터와 기능을 하나로 묶음
정보은닉내부 구현과 데이터를 숨김

접근 제한자

정보은닉을 위해 접근 제한자를 사용합니다.

대표 접근 제한자는 다음과 같습니다.

private
protected
public
접근 제한자의미
private클래스 내부에서만 접근 가능
protected같은 클래스, 자식 클래스 등에서 접근 가능
public외부에서 접근 가능
class Student {
    private int score;

    public void setScore(int s) {
        if (s >= 0 && s <= 100) {
            score = s;
        }
    }

    public int getScore() {
        return score;
    }
}

여기서 score는 외부에서 직접 접근할 수 없습니다.

s.score = 200; // 불가능

대신 메소드를 통해 접근합니다.

s.setScore(90);

객체지향 출제범위에도 접근 제한으로 private, protected, public이 포함됩니다.


getter와 setter

객체의 private 변수에 접근하기 위해 자주 쓰는 메소드가 있습니다.

getter
setter

getter

값을 가져오는 메소드입니다.

public int getScore() {
    return score;
}

setter

값을 설정하는 메소드입니다.

public void setScore(int s) {
    if (s >= 0 && s <= 100) {
        score = s;
    }
}

setter를 쓰면 잘못된 값을 막을 수 있습니다.

s.setScore(150);

이런 값은 조건문으로 막을 수 있습니다.

즉 getter와 setter는 정보은닉을 유지하면서 안전하게 값을 읽고 바꾸는 방법입니다.


객체 생성과 멤버

생성자

생성자는 객체가 만들어질 때 자동으로 실행되는 특별한 메소드입니다.

객체를 초기화할 때 사용합니다.

Java 예
class Student {
    String name;
    int score;

    Student(String n, int s) {
        name = n;
        score = s;
    }
}
객체 생성
Student s1 = new Student("Kim", 90);

이때 생성자가 자동으로 실행됩니다.

name = "Kim"
score = 90
생성자의 특징
클래스 이름과 같습니다.
객체 생성 시 자동 호출됩니다.
주로 멤버변수 초기화에 사용됩니다.

객체지향 출제범위에도 생성자와 소멸자가 포함되어 있습니다.


기본 생성자

매개변수가 없는 생성자를 기본 생성자라고 합니다.

class Student {
    String name;
    int score;

    Student() {
        name = "unknown";
        score = 0;
    }
}
객체 생성
Student s = new Student();
초기값
name = "unknown"
score = 0

생성자를 따로 만들지 않으면 언어에 따라 기본 생성자가 자동 제공될 수 있습니다. 하지만 생성자를 직접 정의하면 기본 생성자가 자동으로 없어지는 경우도 있으므로 주의해야 합니다.


소멸자

소멸자는 객체가 사라질 때 호출되는 특별한 함수입니다.

C++에는 소멸자 개념이 명확합니다.

class Student {
public:
    ~Student() {
        // 객체 소멸 시 실행
    }
};

Java에는 C++처럼 명시적인 소멸자를 직접 호출하는 방식보다는 가비지 컬렉션이 객체 메모리를 회수합니다.

정리하면 다음과 같습니다.

언어객체 소멸 처리
C++소멸자 사용
Java가비지 컬렉션 중심

시험에서는 깊게 들어가기보다 이렇게 잡으면 됩니다.

생성자 = 객체가 만들어질 때 초기화
소멸자 = 객체가 사라질 때 정리

this

this는 현재 객체 자기 자신을 가리킵니다.

예를 살펴보겠습니다.

class Point {
    int x, y;

    Point(int x, int y) {
        this.x = x;
        this.y = y;
    }
}

여기서 왼쪽 this.x는 객체의 멤버변수입니다.

오른쪽 x는 생성자의 매개변수입니다.

this.x = 현재 객체의 x
x = 매개변수 x
this = 현재 객체 자신

객체지향 예시문제에도 Point 클래스에서 ( ).x = x; ( ).y = y;에 공통으로 들어갈 키워드를 묻고, 정답은 this입니다.


super

super는 부모 클래스를 가리킬 때 사용합니다.

Java에서 자식 클래스가 부모 클래스의 생성자나 메소드를 호출할 때 씁니다.

class Animal {
    void sound() {
        System.out.println("동물 소리");
    }
}

class Dog extends Animal {
    void sound() {
        super.sound();
        System.out.println("멍멍");
    }
}
여기서
super.sound();

는 부모 클래스 Animalsound()를 호출합니다.

정리하면 다음과 같습니다.

키워드의미
this현재 객체
super부모 객체 또는 부모 클래스 부분

객체지향 출제범위에도 this, super 키워드가 포함됩니다.


정적 멤버, static

정적 멤버는 객체마다 따로 존재하는 것이 아니라 클래스에 소속되는 멤버입니다.

class Student {
    static int count = 0;

    Student() {
        count++;
    }
}

count는 모든 Student 객체가 공유합니다.

Student s1 = new Student();
Student s2 = new Student();
이후
Student.count = 2

가 됩니다.

정리하면 다음과 같습니다.

구분의미
인스턴스 변수객체마다 따로 존재
static 변수클래스에 하나 존재, 모든 객체가 공유
인스턴스 메소드객체를 통해 호출
static 메소드클래스 이름으로 호출 가능
Math.max(3, 5);

이런 식으로 클래스 이름으로 호출하는 메소드가 static 메소드인 경우가 많습니다.


오버로딩

개념과 사용 예

오버로딩은 같은 이름의 메소드를 여러 개 정의하는 것입니다.

단, 매개변수의 개수나 자료형이 달라야 합니다.

class Calculator {
    int add(int a, int b) {
        return a + b;
    }

    double add(double a, double b) {
        return a + b;
    }

    int add(int a, int b, int c) {
        return a + b + c;
    }
}

모두 이름은 add입니다. 하지만 매개변수가 다릅니다.

add(1, 2)
add(1.5, 2.3)
add(1, 2, 3)

컴파일러가 전달된 인수를 보고 어떤 메소드를 호출할지 결정합니다.

오버로딩 = 같은 이름, 다른 매개변수

객체지향 출제범위에도 멤버함수 오버로드가 포함됩니다.


오버로딩 주의점

오버로딩은 반환형만 다르게 해서 만들 수 없습니다.

예를 들어 다음은 오버로딩으로 보기 어렵습니다.

int test(int a) {
    return a;
}

double test(int a) {
    return a;
}

매개변수가 같고 반환형만 다릅니다.

호출할 때
test(3);

라고 하면 어떤 것을 부를지 애매합니다.

그래서 오버로딩은 매개변수 목록이 달라야 합니다.

매개변수 개수, 자료형, 순서가 달라야 함

상속과 오버라이딩

상속

상속은 기존 클래스의 멤버를 새로운 클래스가 물려받는 것입니다.

예를 들어 동물 클래스가 있다고 가정하겠습니다.

class Animal {
    String name;

    void eat() {
        System.out.println("먹는다");
    }
}

강아지는 동물입니다.

class Dog extends Animal {
    void bark() {
        System.out.println("멍멍");
    }
}

DogAnimal을 상속받습니다.

그러면 Dog 객체는 Animal의 기능도 사용할 수 있습니다.

Dog d = new Dog();
d.eat();
d.bark();
정리
Animal = 부모 클래스, 상위 클래스, super class
Dog = 자식 클래스, 하위 클래스, sub class

객체지향 출제범위에도 클래스 상속, 클래스 상속 선언, 생성자와 소멸자 호출 순서, 멤버함수 재정의, 추상 클래스가 포함됩니다.


상속의 장점

상속의 장점은 재사용입니다.

동물 종류가 여러 개 있다고 가정하겠습니다.

Dog
Cat
Bird

공통 기능은 Animal에 넣습니다.

class Animal {
    void eat() { ... }
    void sleep() { ... }
}

각 동물 고유 기능은 자식 클래스에 넣습니다.

class Dog extends Animal {
    void bark() { ... }
}

class Cat extends Animal {
    void meow() { ... }
}

이렇게 하면 공통 코드를 반복해서 쓰지 않아도 됩니다.

상속 = 코드 재사용 + 계층 구조 표현

객체지향 예시문제에서도 상속은 모듈 재사용과 코드의 간결성에 관련된 개념으로 제시됩니다.


is-a 관계

상속은 보통 is-a 관계일 때 사용합니다.

Dog is an Animal.
강아지는 동물입니다.

이것은 자연스럽습니다.

class Dog extends Animal

하지만 이런 것은 이상합니다.

Car is an Engine.
자동차는 엔진입니다.

자동차는 엔진을 가지고 있는 것이지 엔진 자체는 아닙니다.

이것은 상속보다 포함 관계가 맞습니다.

has-a 관계
class Car {
    Engine engine;
}

정리하면 다음과 같습니다.

관계의미구현
is-a~는 ~입니다상속
has-a~는 ~를 가집니다포함, 멤버변수

오버라이딩

오버라이딩은 부모 클래스의 메소드를 자식 클래스에서 다시 정의하는 것입니다.

class Animal {
    void sound() {
        System.out.println("동물 소리");
    }
}

class Dog extends Animal {
    void sound() {
        System.out.println("멍멍");
    }
}

class Cat extends Animal {
    void sound() {
        System.out.println("야옹");
    }
}

Animal에는 sound()가 있습니다. 그런데 Dog와 Cat이 각자 자기 방식으로 다시 정의합니다.

오버라이딩 = 부모 메소드를 자식이 재정의

객체지향 출제범위에도 멤버함수 재정의, 즉 override가 포함되어 있습니다.


오버로딩과 오버라이딩 비교

이 둘은 이름이 비슷해서 자주 헷갈리기 쉽습니다.

구분오버로딩오버라이딩
의미같은 이름의 메소드를 여러 개 정의부모 메소드를 자식이 재정의
조건매개변수 목록이 달라야 함상속 관계가 필요
이름같음같음
매개변수달라야 함보통 같아야 함
목적다양한 입력 처리자식 클래스에 맞게 동작 변경
짧게 외우면
오버로딩 = 같은 이름, 다른 매개변수
오버라이딩 = 부모 것을 자식이 다시 정의

다형성과 바인딩

다형성

다형성은 같은 이름의 명령이 객체에 따라 다르게 동작하는 성질입니다.

말 그대로
많은 형태를 가질 수 있음
Animal a1 = new Dog();
Animal a2 = new Cat();

a1.sound();
a2.sound();
결과
멍멍
야옹

변수 타입은 둘 다 Animal처럼 보이지만, 실제 객체가 Dog냐 Cat이냐에 따라 실행되는 메소드가 다릅니다.

이것이 다형성입니다.

객체지향 예시문제에서도 다형성은 “하나의 인터페이스를 이용하여 서로 다른 구현 방법을 제공하는 개념”이며, Java에서는 상속, 추상 클래스, 동적 바인딩 등을 통해, C++에서는 가상함수까지 사용하여 구현된다고 나옵니다.


다형성이 왜 중요한가?

다형성이 없으면 객체 종류마다 따로 처리해야 합니다.

Dog d = new Dog();
Cat c = new Cat();

d.sound();
c.sound();

동물이 많아지면 코드가 복잡해집니다.

다형성을 쓰면 공통 부모 타입으로 처리할 수 있습니다.

Animal[] animals = {
    new Dog(),
    new Cat(),
    new Bird()
};

for (Animal a : animals) {
    a.sound();
}

각 객체가 자기 방식으로 sound()를 실행합니다.

같은 명령 sound()
→ Dog는 멍멍
→ Cat은 야옹
→ Bird는 짹짹

즉 다형성은 코드 확장성을 높입니다.


동적 바인딩

바인딩은 어떤 메소드 호출이 실제 어떤 메소드 실행으로 연결되는지 결정하는 것입니다.

정적 바인딩

컴파일 시점에 결정됩니다.

컴파일할 때 어떤 함수가 호출될지 결정

동적 바인딩

실행 시점에 실제 객체를 보고 결정됩니다.

Animal a = new Dog();
a.sound();

컴파일 시점에는 a가 Animal 타입입니다. 하지만 실행 시점에 실제 객체는 Dog다.

그래서 Dog의 sound()가 실행됩니다.

동적 바인딩 = 실행 중 실제 객체 타입에 따라 메소드 결정

다형성은 동적 바인딩과 밀접합니다.

객체지향 출제범위에도 메소드의 동적 바인딩이 포함됩니다.


C++의 가상함수

C++에서는 다형성을 위해 virtual 키워드를 사용합니다.

class Animal {
public:
    virtual void sound() {
        cout << "동물 소리";
    }
};

class Dog : public Animal {
public:
    void sound() override {
        cout << "멍멍";
    }
};
부모 포인터로 자식 객체를 가리킬 때
Animal* a = new Dog();
a->sound();

virtual이 있으면 Dog의 sound()가 실행됩니다.

객체지향 출제범위에서도 C++의 가상함수 선언이 다형성 항목에 포함됩니다.


추상 클래스와 인터페이스

추상 클래스

추상 클래스는 완성되지 않은 클래스로, 직접 객체를 만들 수 없고 자식 클래스가 구현해야 할 메소드를 가질 수 있습니다.

Java 예
abstract class Animal {
    abstract void sound();

    void eat() {
        System.out.println("먹는다");
    }
}
여기서
abstract void sound();

는 몸체가 없습니다.

자식 클래스에서 반드시 구현해야 합니다.

class Dog extends Animal {
    void sound() {
        System.out.println("멍멍");
    }
}
정리
추상 클래스 = 일부 메소드를 자식이 구현하도록 남겨둔 클래스

직접 객체 생성은 불가능합니다.

Animal a = new Animal(); // 불가능

하지만 자식 객체는 만들 수 있습니다.

Animal a = new Dog(); // 가능

인터페이스

인터페이스는 클래스가 반드시 구현해야 할 기능 목록을 정한 약속입니다.

Java 예
interface Flyable {
    void fly();
}

이 인터페이스를 구현하는 클래스는 fly()를 반드시 구현해야 합니다.

class Bird implements Flyable {
    public void fly() {
        System.out.println("새가 난다");
    }
}

인터페이스는 “이런 기능을 제공해야 합니다”는 규칙입니다.

인터페이스 = 기능의 약속

객체지향 출제범위에도 Java의 인터페이스 선언, 인터페이스 상속, 인터페이스 다중 상속이 포함됩니다.


추상 클래스와 인터페이스 비교

구분추상 클래스인터페이스
목적공통 기능과 미완성 기능을 함께 제공구현해야 할 기능 목록 제시
객체 생성직접 생성 불가직접 생성 불가
일반 메소드가질 수 있음언어 버전에 따라 가능
변수상태를 가질 수 있음보통 상수 중심
상속/구현extendsimplements
관계is-a 관계에 가까움can-do, 할 수 있는 기능에 가까움
Dog extends Animal
Dog implements Runnable
Bird implements Flyable
짧게
추상 클래스 = 미완성 부모 클래스
인터페이스 = 기능 약속

Java의 단일 상속과 인터페이스

Java는 클래스 다중 상속을 허용하지 않습니다.

즉 한 클래스가 여러 클래스를 동시에 상속받을 수 없습니다.

class C extends A, B // 불가능

하지만 인터페이스는 여러 개 구현할 수 있습니다.

class Bird implements Flyable, Runnable {
    ...
}

C++는 클래스 다중 상속을 지원합니다.

객체지향 출제범위에도 Java의 인터페이스 다중 상속과 C++의 클래스 다중 상속이 포함됩니다.


언어별 객체지향 요소

패키지와 네임스페이스

클래스가 많아지면 이름 충돌이 생길 수 있습니다.

예를 들어 Student라는 클래스가 여러 프로젝트에 있을 수 있습니다.

그래서 관련 클래스를 묶는 방법이 필요합니다.

Java에서는 패키지를 사용합니다.

package school;

C++에서는 네임스페이스를 사용합니다.

namespace school {
    class Student { ... };
}

정리하면 다음과 같습니다.

언어클래스 그룹핑
Java패키지
C++네임스페이스

객체지향 출제범위에도 클래스 그룹핑으로 Java의 패키지, C++의 네임스페이스가 포함됩니다.


템플릿과 제네릭

자료형이 달라도 같은 구조를 쓰고 싶을 때가 있습니다.

예를 들어 리스트를 만들 때
정수 리스트
문자열 리스트
학생 리스트

모두 구조는 비슷합니다.

C++에서는 템플릿을 사용합니다.

template <typename T>
class Box {
    T value;
};

Java에서는 제네릭을 사용합니다.

class Box<T> {
    T value;
}

객체지향 출제범위에도 C++ 템플릿과 Java 제네릭이 포함됩니다.

자료구조 라이브러리에서도 자주 나옵니다.

ArrayList<String>
HashMap<String, Integer>

객체지향 라이브러리

객체지향 언어에는 이미 만들어진 클래스 라이브러리가 많습니다.

예를 들어 Java에는
String
StringBuffer
ArrayList
LinkedList
HashMap
FileReader
FileWriter

같은 클래스가 있습니다.

C++에는
string
vector<T>
list<T>
ifstream
ofstream

같은 클래스와 라이브러리가 있습니다.

객체지향 출제범위에도 화면 입출력, 문자열, 파일 입출력, 자료구조 관련 기본 함수 및 클래스 라이브러리가 포함됩니다.


String과 StringBuffer

Java 문자열에서 자주 나오는 개념입니다.

String
StringBuffer

String

문자열을 저장합니다.

String s = "ABC";

String은 한 번 만들어진 문자열이 바뀌지 않는 성질, 즉 불변성을 가집니다.

StringBuffer

문자열을 계속 변경할 때 사용합니다.

StringBuffer sb = new StringBuffer("ABC");
sb.append("DEF");

객체지향 예시문제에서도 Java에서 상수 문자열을 사용할 때는 String, 계속 변하는 문자열을 사용할 때는 StringBuffer를 사용한다고 나옵니다.

짧게
String = 고정 문자열
StringBuffer = 자주 바뀌는 문자열

예외 처리

예외는 프로그램 실행 중 발생하는 비정상 상황입니다.

0으로 나누기
배열 범위 초과
파일 없음
입력 오류
널 포인터 접근

예외 처리는 이런 문제를 안전하게 처리하는 방법입니다.

Java 예
try {
    int result = 10 / 0;
} catch (ArithmeticException e) {
    System.out.println("0으로 나눌 수 없습니다.");
}
키워드의미
try예외가 발생할 수 있는 코드
catch예외를 잡아 처리
throw예외를 던짐
finally예외 여부와 관계없이 실행

객체지향 출제범위에도 예외 처리 개념, 예외 클래스, throw, catch 처리가 포함됩니다.


정리와 예제

객체지향 4대 핵심 개념

객체지향에서 특히 중요한 것은 보통 다음 네 가지입니다.

추상화
캡슐화
상속
다형성

추상화

중요한 특징만 뽑아 표현하는 것입니다.

예를 들어 자동차를 프로그램으로 표현할 때 모든 부품을 다 넣지는 않습니다.

속도
색상
주행 기능
정지 기능

같이 필요한 것만 뽑습니다.

캡슐화

데이터와 기능을 하나로 묶습니다.

상속

부모 클래스의 기능을 자식 클래스가 물려받습니다.

다형성

같은 메소드 호출이 객체에 따라 다르게 동작합니다.


객체지향 개념 시험형 정리

객체지향 예시문제에는 이런 형태가 나옵니다.

보기 1

다형성 - 하나의 인터페이스를 이용하여 서로 다른 구현 방법을 제공

이것은 맞습니다.

보기 2

객체 - 클래스와 메소드로 구성

이것은 부정확합니다.

객체는 클래스로부터 생성된 실체이고, 상태와 동작을 가집니다.

보기 3

상속 - 모듈 재사용과 코드 간결성

이것은 맞습니다.

보기 4

캡슐화 - 정보은닉 방법 이용

이것도 맞는 설명입니다.

따라서 “거리가 먼 것”을 고르는 문제에서는 객체를 “클래스와 메소드로 구성”한다고 한 설명이 틀립니다.


예제 코드

예제 코드: 클래스와 객체

Java 스타일로 살펴보겠습니다.

class Student {
    String name;
    int score;

    void printInfo() {
        System.out.println(name + " " + score);
    }
}

public class Main {
    public static void main(String[] args) {
        Student s = new Student();

        s.name = "Kim";
        s.score = 90;

        s.printInfo();
    }
}
실행 결과
Kim 90

분석은 다음과 같습니다.

코드의미
class StudentStudent 클래스 정의
String name이름 멤버변수
int score점수 멤버변수
printInfo()정보 출력 메소드
new Student()Student 객체 생성
s.printInfo()객체의 메소드 호출

예제 코드: 캡슐화

class Account {
    private int balance;

    public void deposit(int money) {
        if (money > 0) {
            balance += money;
        }
    }

    public int getBalance() {
        return balance;
    }
}
사용
Account acc = new Account();

acc.deposit(1000);
System.out.println(acc.getBalance());
출력
1000

여기서 balanceprivate입니다.

외부에서 직접 바꿀 수 없습니다.

acc.balance = -10000; // 불가능

이것이 정보은닉입니다.


예제 코드: 상속과 오버라이딩

class Animal {
    void sound() {
        System.out.println("동물 소리");
    }
}

class Dog extends Animal {
    void sound() {
        System.out.println("멍멍");
    }
}

class Cat extends Animal {
    void sound() {
        System.out.println("야옹");
    }
}
사용
Animal a1 = new Dog();
Animal a2 = new Cat();

a1.sound();
a2.sound();
결과
멍멍
야옹

여기에는 세 개념이 들어 있습니다.

개념코드
상속Dog extends Animal
오버라이딩sound() 재정의
다형성Animal 타입으로 Dog, Cat 객체 사용

개념 연결

객체지향과 자료구조 연결

객체지향은 자료구조와도 연결됩니다.

예를 들어 3장 3절에서 노드를 구조체로 만들었습니다.

C 스타일
struct Node {
    int data;
    struct Node *next;
};

객체지향 스타일로는 Node 클래스를 만들 수 있습니다.

class Node {
    int data;
    Node next;
}

스택도 클래스로 만들 수 있습니다.

class Stack {
    int[] data;
    int top;

    void push(int x) { ... }
    int pop() { ... }
}

즉 객체지향은 자료구조를 더 깔끔하게 묶어 표현할 수 있습니다.

Stack 객체 = 데이터 배열 + push/pop 기능
Queue 객체 = 데이터 배열 + enqueue/dequeue 기능
Tree 객체 = root + 순회 기능

객체지향과 웹 연결

웹프로그래밍에서도 객체 개념이 나옵니다.

JavaScript에서는 객체를 이렇게 만들 수 있습니다.

var customer = {
    name: "홍길동",
    age: 23
};

웹프로그래밍 예시문제에도 JavaScript 객체 선언 문제가 나오고, 속성 이름과 값은 :로 연결하며 { }를 사용한 객체 리터럴 형식이 올바른 선언입니다.

즉 객체 개념은 Java나 C++에만 있는 것이 아닙니다.

Java 객체
C++ 객체
JavaScript 객체
DOM 객체

웹의 DOM도 HTML 문서를 객체처럼 다루는 구조입니다.

이 내용은 6장 3절에서 다시 다룹니다.


자주 혼동되는 출제 포인트

혼동 포인트 1. 클래스와 객체 구분

클래스 = 설계도
객체 = 실제 생성된 대상

클래스 자체를 객체라고 착각하면 안 됩니다.


혼동 포인트 2. 객체는 클래스와 메소드로 구성된다는 표현은 부정확합니다

객체는 클래스로부터 생성된 실체이고, 상태와 동작을 가집니다. 객체지향 예시문제에서도 이 설명이 틀린 보기로 나옵니다.


혼동 포인트 3. 캡슐화와 정보은닉

캡슐화 = 데이터와 기능을 묶음
정보은닉 = 내부 정보를 숨김

둘은 관련 있지만 완전히 같은 말은 아닙니다.


혼동 포인트 4. private, protected, public 구분

private = 클래스 내부
protected = 자식 클래스 등
public = 외부 접근 가능

혼동 포인트 5. this

this = 현재 객체 자신

멤버변수와 매개변수 이름이 같을 때 자주 씁니다.

this.x = x;

혼동 포인트 6. 오버로딩과 오버라이딩

오버로딩 = 같은 이름, 다른 매개변수
오버라이딩 = 부모 메소드를 자식이 재정의

혼동 포인트 7. 상속은 is-a 관계일 때 적절합니다

Dog is an Animal → 상속 적절
Car has an Engine → 포함 관계가 적절

혼동 포인트 8. 다형성

다형성 = 하나의 인터페이스로 서로 다른 구현 제공

객체지향 예시문제에서도 다형성의 정의가 이 형태로 나옵니다.


혼동 포인트 9. 추상 클래스는 직접 객체 생성 불가

Animal a = new Animal(); // 추상 클래스면 불가능

자식 클래스가 구현해야 합니다.


혼동 포인트 10. StringBuffer

Java에서 계속 변하는 문자열을 다룰 때는 StringBuffer를 사용한다는 문제가 출제됩니다.


이번 절의 핵심 정리

클래스

객체를 만들기 위한 설계도

객체

클래스로부터 생성된 실제 대상
상태와 동작을 가짐

멤버변수

객체의 상태, 데이터

메소드

객체의 동작, 기능

캡슐화

데이터와 기능을 하나로 묶음

정보은닉

내부 데이터와 구현을 외부에서 직접 접근하지 못하게 숨김

접근 제한자

private
protected
public

생성자

객체 생성 시 자동 호출되어 초기화 수행

this

현재 객체 자신

super

부모 클래스 부분

오버로딩

같은 이름의 메소드를 매개변수를 다르게 하여 여러 개 정의

오버라이딩

부모 클래스의 메소드를 자식 클래스에서 다시 정의

상속

부모 클래스의 속성과 기능을 자식 클래스가 물려받음

다형성

같은 명령이 객체에 따라 다르게 동작
하나의 인터페이스로 서로 다른 구현 제공

추상 클래스

일부 메소드 구현을 자식에게 맡기는 미완성 클래스

인터페이스

구현해야 할 기능의 약속

동적 바인딩

실행 시점에 실제 객체 타입에 따라 호출 메소드가 결정됨

핵심 한 문장

이번 절의 핵심을 한 문장으로 정리하면 다음과 같습니다.

객체지향프로그래밍은 프로그램을 객체들의 협력으로 바라보며, 클래스라는 설계도로 객체를 만들고, 캡슐화와 정보은닉으로 데이터를 보호하며, 상속과 다형성을 이용해 코드를 재사용하고 확장성 있게 설계하는 방법입니다.
더 짧게
클래스 = 설계도
객체 = 실제 대상
캡슐화 = 묶고 숨김
상속 = 물려받음
다형성 = 같은 명령, 다른 동작

확인 문제

문제 1

클래스의 의미로 가장 알맞은 것은?

① 객체를 만들기 위한 설계도 ② 실행 중인 프로그램 ③ 메모리 주소를 저장하는 변수 ④ CPU 내부의 레지스터


문제 2

객체의 의미로 가장 알맞은 것은?

① 클래스로부터 생성된 실제 대상 ② 소스 코드를 컴파일하는 장치 ③ HTML 문서의 제목 태그 ④ 운영체제의 스케줄링 알고리즘


문제 3

객체가 가지는 데이터, 즉 상태를 나타내는 것은?

① 멤버변수 ② 패키지 ③ 인터럽트 ④ 버스


문제 4

객체가 수행할 수 있는 기능이나 동작을 나타내는 것은?

① 메소드 ② 페이지 폴트 ③ 세마포어 ④ 해시 함수


문제 5

데이터와 기능을 하나로 묶는 객체지향 개념은?

① 캡슐화 ② 교착상태 ③ 페이징 ④ 후위순회


문제 6

객체 내부의 데이터와 구현을 외부에서 직접 접근하지 못하게 숨기는 개념은?

① 정보은닉 ② 버블 정렬 ③ 캐시 미스 ④ 주소 지정


문제 7

클래스 내부에서만 접근 가능하게 하는 접근 제한자는?

① public ② private ③ static ④ final


문제 8

객체가 생성될 때 자동으로 호출되어 초기화를 수행하는 것은?

① 생성자 ② 소멸자 ③ 분기문 ④ 반복문


문제 9

현재 객체 자신을 가리키는 키워드는?

① this ② super ③ that ④ parent


문제 10

부모 클래스의 멤버나 생성자를 가리킬 때 사용하는 Java 키워드는?

① this ② super ③ self ④ main


문제 11

같은 이름의 메소드를 매개변수의 개수나 자료형을 다르게 하여 여러 개 정의하는 것은?

① 오버로딩 ② 오버라이딩 ③ 세그멘테이션 ④ 동기화


문제 12

부모 클래스의 메소드를 자식 클래스에서 다시 정의하는 것은?

① 오버로딩 ② 오버라이딩 ③ 해싱 ④ 캐싱


문제 13

상속의 의미로 가장 알맞은 것은?

① 부모 클래스의 속성과 기능을 자식 클래스가 물려받는 것 ② 메모리를 페이지로 나누는 것 ③ CPU를 시간 단위로 나누는 것 ④ 배열을 정렬하는 것


문제 14

다형성의 의미로 가장 알맞은 것은?

① 하나의 인터페이스로 서로 다른 구현 방법을 제공하는 것 ② 모든 객체가 같은 데이터만 갖는 것 ③ 클래스 이름을 반드시 하나로 고정하는 것 ④ 변수를 전부 전역변수로 만드는 것


문제 15

실행 시점에 실제 객체 타입에 따라 호출될 메소드가 결정되는 것은?

① 동적 바인딩 ② 정적 배열 ③ 직접 주소지정 ④ FIFO


문제 16

추상 클래스에 대한 설명으로 알맞은 것은?

① 직접 객체를 만들 수 없고 자식 클래스가 일부 메소드를 구현해야 할 수 있습니다 ② 항상 final 변수만 가집니다 ③ 운영체제의 파일 시스템입니다 ④ 스택의 top을 저장합니다


문제 17

인터페이스의 의미로 알맞은 것은?

① 구현해야 할 기능의 약속 ② 물리 메모리의 조각 ③ 그래프의 간선 수 ④ 2진수의 보수


문제 18

다음 중 오버로딩의 조건으로 가장 알맞은 것은?

① 메소드 이름은 같고 매개변수 목록이 달라야 합니다 ② 메소드 이름은 반드시 달라야 합니다 ③ 반환형만 다르면 무조건 가능합니다 ④ 상속 관계가 반드시 있어야 합니다


문제 19

Java에서 계속 변하는 문자열을 다룰 때 사용하는 클래스로 알맞은 것은?

① StringBuffer ② StringSet ③ StringFormat ④ StringList


문제 20

객체지향의 주요 개념과 거리가 먼 설명은?

① 다형성은 하나의 인터페이스로 서로 다른 구현을 제공합니다 ② 상속은 코드 재사용에 도움을 줍니다 ③ 캡슐화는 정보은닉과 관련됩니다 ④ 객체는 클래스와 메소드로 구성된다는 표현이 정확합니다


정답과 해설은 절별 확인문제 정답해설에서 확인합니다.

다음 6장 2절은 웹 구조, HTTP, HTML, CSS입니다. 6장 1절이 프로그램을 객체 단위로 설계하는 방법이었다면, 6장 2절은 웹페이지가 클라이언트와 서버 사이에서 어떻게 오가고, HTML과 CSS로 어떻게 구조와 디자인을 만드는지 배웁니다.

목차

이번 절의 큰 그림
객체지향 개요
절차지향이란?
객체지향이란?
절차지향과 객체지향 비교
클래스와 객체
클래스란?
객체란?
클래스와 객체 비교
객체는 무엇으로 구성되는가?
메시지와 메소드
캡슐화와 정보은닉
캡슐화
정보은닉
접근 제한자
getter와 setter
getter
setter
객체 생성과 멤버
생성자
기본 생성자
소멸자
this
super
정적 멤버, static
오버로딩
개념과 사용 예
오버로딩 주의점
상속과 오버라이딩
상속
상속의 장점
is-a 관계
오버라이딩
오버로딩과 오버라이딩 비교
다형성과 바인딩
다형성
다형성이 왜 중요한가?
동적 바인딩
정적 바인딩
동적 바인딩
C++의 가상함수
추상 클래스와 인터페이스
추상 클래스
인터페이스
추상 클래스와 인터페이스 비교
Java의 단일 상속과 인터페이스
언어별 객체지향 요소
패키지와 네임스페이스
템플릿과 제네릭
객체지향 라이브러리
String과 StringBuffer
String
StringBuffer
예외 처리
정리와 예제
객체지향 4대 핵심 개념
추상화
캡슐화
상속
다형성
객체지향 개념 시험형 정리
보기 1
보기 2
보기 3
보기 4
예제 코드
예제 코드: 클래스와 객체
예제 코드: 캡슐화
예제 코드: 상속과 오버라이딩
개념 연결
객체지향과 자료구조 연결
객체지향과 웹 연결
자주 혼동되는 출제 포인트
혼동 포인트 1. 클래스와 객체 구분
혼동 포인트 2. 객체는 클래스와 메소드로 구성된다는 표현은 부정확합니다
혼동 포인트 3. 캡슐화와 정보은닉
혼동 포인트 4. private, protected, public 구분
혼동 포인트 5. this
혼동 포인트 6. 오버로딩과 오버라이딩
혼동 포인트 7. 상속은 is-a 관계일 때 적절합니다
혼동 포인트 8. 다형성
혼동 포인트 9. 추상 클래스는 직접 객체 생성 불가
혼동 포인트 10. StringBuffer
이번 절의 핵심 정리
클래스
객체
멤버변수
메소드
캡슐화
정보은닉
접근 제한자
생성자
this
super
오버로딩
오버라이딩
상속
다형성
추상 클래스
인터페이스
동적 바인딩
핵심 한 문장
확인 문제
문제 1
문제 2
문제 3
문제 4
문제 5
문제 6
문제 7
문제 8
문제 9
문제 10
문제 11
문제 12
문제 13
문제 14
문제 15
문제 16
문제 17
문제 18
문제 19
문제 20