8장. 객체지향 핵심 개념
객체지향 프로그래밍의 4대 핵심 개념
객체지향 프로그래밍(OOP)의 핵심은 다음 4가지 개념으로 구성됩니다:
- 캡슐화 (Encapsulation): 데이터와 메서드를 하나로 묶어 관리
- 상속 (Inheritance): 기존 클래스를 확장하여 새로운 클래스 생성
- 다형성 (Polymorphism): 하나의 인터페이스로 여러 형태 구현
- 추상화 (Abstraction): 복잡한 것을 단순하게 표현
캡슐화 (Encapsulation)
캡슐화란?
캡슐화는 데이터와 그 데이터를 처리하는 메서드를 하나의 클래스로 묶는 것입니다. 외부에서 데이터에 직접 접근하지 못하도록 하고, 메서드를 통해서만 접근할 수 있게 합니다.
캡슐화의 목적
- 데이터 보호: 외부에서 데이터를 직접 변경하는 것을 방지
- 데이터 검증: setter 메서드에서 유효성 검사 가능
- 유지보수 용이: 필드 변경 시 getter/setter만 수정하면 됨
- 정보 은닉: 내부 구현을 숨기고 외부 인터페이스만 제공
캡슐화 구현 방법
1. private 필드 사용
public class Student {
// private 필드: 외부에서 직접 접근 불가
private String name;
private int age;
private double gpa;
}
2. Getter와 Setter 메서드 제공
public class Student {
private String name;
private int age;
private double gpa;
// Getter: 필드 값을 읽는 메서드
public String getName() {
return name;
}
public int getAge() {
return age;
}
// Setter: 필드 값을 설정하는 메서드 (유효성 검사 포함)
public void setName(String name) {
if (name != null && !name.trim().isEmpty()) {
this.name = name;
}
}
public void setAge(int age) {
if (age >= 0 && age <= 150) {
this.age = age;
}
}
public void setGpa(double gpa) {
if (gpa >= 0.0 && gpa <= 4.5) {
this.gpa = gpa;
}
}
}
캡슐화 예제
public class BankAccount {
private String accountNumber;
private double balance;
public BankAccount(String accountNumber, double initialBalance) {
this.accountNumber = accountNumber;
if (initialBalance >= 0) {
this.balance = initialBalance;
} else {
this.balance = 0;
}
}
// Getter
public double getBalance() {
return balance;
}
// 입금 (유효성 검사 포함)
public void deposit(double amount) {
if (amount > 0) {
balance += amount;
}
}
// 출금 (유효성 검사 포함)
public boolean withdraw(double amount) {
if (amount > 0 && amount <= balance) {
balance -= amount;
return true;
}
return false;
}
}
상속 (Inheritance)
상속이란?
상속은 기존 클래스의 속성과 메서드를 새로운 클래스가 물려받는 것입니다. 코드 재사용을 극대화하고, 계층 구조를 만들 수 있습니다.
상속의 장점
- 코드 재사용: 기존 클래스의 코드를 재사용
- 유지보수 용이: 부모 클래스 수정 시 자식 클래스에 자동 반영
- 계층 구조: 클래스 간 관계를 명확히 표현
- 확장성: 기존 기능을 확장하여 새로운 클래스 생성
상속 문법
class 자식클래스 extends 부모클래스 {
// 자식 클래스만의 추가 필드와 메서드
}
상속 예제
// 부모 클래스 (상위 클래스)
public class Animal {
String name;
int age;
public void eat() {
System.out.println(name + "이(가) 먹습니다.");
}
public void sleep() {
System.out.println(name + "이(가) 잡니다.");
}
}
// 자식 클래스 (하위 클래스)
public class Dog extends Animal {
String breed; // 자식 클래스만의 추가 필드
public void bark() { // 자식 클래스만의 추가 메서드
System.out.println(name + "이(가) 짖습니다.");
}
}
// 사용
Dog dog = new Dog();
dog.name = "멍멍이";
dog.age = 3;
dog.breed = "골든리트리버";
dog.eat(); // 부모 클래스의 메서드 사용
dog.bark(); // 자식 클래스의 메서드 사용
super 키워드
super는 부모 클래스를 가리키는 참조 변수입니다.
super로 부모 클래스의 생성자 호출
public class Animal {
String name;
int age;
public Animal(String name, int age) {
this.name = name;
this.age = age;
}
}
public class Dog extends Animal {
String breed;
public Dog(String name, int age, String breed) {
super(name, age); // 부모 클래스의 생성자 호출
this.breed = breed;
}
}
super로 부모 클래스의 메서드 호출
public class Animal {
public void makeSound() {
System.out.println("동물이 소리를 냅니다.");
}
}
public class Dog extends Animal {
@Override
public void makeSound() {
super.makeSound(); // 부모 클래스의 메서드 호출
System.out.println("멍멍!");
}
}
메서드 오버라이딩 (Method Overriding)
자식 클래스에서 부모 클래스의 메서드를 재정의하는 것입니다.
public class Animal {
public void makeSound() {
System.out.println("동물이 소리를 냅니다.");
}
}
public class Dog extends Animal {
@Override // 어노테이션 (선택사항이지만 권장)
public void makeSound() {
System.out.println("멍멍!");
}
}
public class Cat extends Animal {
@Override
public void makeSound() {
System.out.println("야옹!");
}
}
상속의 제한사항
- Java는 단일 상속만 지원 (하나의 부모 클래스만 상속 가능)
private필드와 메서드는 상속되지 않음- 생성자는 상속되지 않음
다형성 (Polymorphism)
다형성이란?
다형성은 하나의 인터페이스나 부모 클래스를 통해 여러 형태의 객체를 다룰 수 있는 것입니다. "하나의 이름으로 여러 형태를 표현"한다는 의미입니다.
다형성의 종류
1. 컴파일 타임 다형성 (오버로딩)
public class Calculator {
public int add(int a, int b) {
return a + b;
}
public double add(double a, double b) {
return a + b;
}
}
2. 런타임 다형성 (오버라이딩)
Animal animal1 = new Dog();
Animal animal2 = new Cat();
animal1.makeSound(); // "멍멍!"
animal2.makeSound(); // "야옹!"
업캐스팅 (Upcasting)
자식 클래스 객체를 부모 클래스 타입으로 참조하는 것입니다.
Animal animal = new Dog(); // 업캐스팅
animal.makeSound(); // Dog의 makeSound() 호출
다운캐스팅 (Downcasting)
부모 클래스 타입을 자식 클래스 타입으로 변환하는 것입니다.
Animal animal = new Dog();
Dog dog = (Dog) animal; // 다운캐스팅
dog.bark(); // Dog만의 메서드 호출
instanceof 연산자
객체가 특정 클래스의 인스턴스인지 확인합니다.
Animal animal = new Dog();
if (animal instanceof Dog) {
Dog dog = (Dog) animal;
dog.bark();
}
다형성 예제
public class Animal {
public void makeSound() {
System.out.println("동물이 소리를 냅니다.");
}
}
public class Dog extends Animal {
@Override
public void makeSound() {
System.out.println("멍멍!");
}
public void bark() {
System.out.println("왈왈!");
}
}
public class Cat extends Animal {
@Override
public void makeSound() {
System.out.println("야옹!");
}
}
// 사용
Animal[] animals = {
new Dog(),
new Cat(),
new Dog()
};
for (Animal animal : animals) {
animal.makeSound(); // 각 객체의 오버라이딩된 메서드 호출
}
추상 클래스 (Abstract Class)
추상 클래스란?
추상 클래스는 완전하지 않은 클래스로, 하나 이상의 추상 메서드를 포함하는 클래스입니다. 직접 객체를 생성할 수 없고, 상속을 통해 사용됩니다.
추상 클래스의 특징
- abstract 키워드 사용:
abstract class 클래스명 - 객체 생성 불가:
new로 직접 생성 불가 - 추상 메서드 포함 가능: 구현이 없는 메서드
- 일반 메서드도 포함 가능: 구현된 메서드도 포함 가능
- 상속 필수: 자식 클래스에서 추상 메서드를 구현해야 함
추상 클래스 정의
public abstract class Animal {
String name;
int age;
// 일반 메서드
public void eat() {
System.out.println(name + "이(가) 먹습니다.");
}
// 추상 메서드 (구현 없음)
public abstract void makeSound();
}
추상 클래스 상속
public class Dog extends Animal {
@Override
public void makeSound() {
System.out.println("멍멍!");
}
}
public class Cat extends Animal {
@Override
public void makeSound() {
System.out.println("야옹!");
}
}
추상 클래스 예제
public abstract class Shape {
String color;
public Shape(String color) {
this.color = color;
}
// 일반 메서드
public String getColor() {
return color;
}
// 추상 메서드: 넓이 계산
public abstract double calculateArea();
// 추상 메서드: 둘레 계산
public abstract double calculatePerimeter();
}
public class Circle extends Shape {
double radius;
public Circle(String color, double radius) {
super(color);
this.radius = radius;
}
@Override
public double calculateArea() {
return Math.PI * radius * radius;
}
@Override
public double calculatePerimeter() {
return 2 * Math.PI * radius;
}
}
public class Rectangle extends Shape {
double width;
double height;
public Rectangle(String color, double width, double height) {
super(color);
this.width = width;
this.height = height;
}
@Override
public double calculateArea() {
return width * height;
}
@Override
public double calculatePerimeter() {
return 2 * (width + height);
}
}
인터페이스 (Interface)
인터페이스란?
인터페이스는 클래스가 구현해야 하는 메서드들의 목록을 정의한 것입니다. "무엇을 해야 하는지"는 정의하지만 "어떻게 하는지"는 정의하지 않습니다.
인터페이스의 특징
- interface 키워드 사용:
interface 인터페이스명 - 객체 생성 불가: 직접 객체 생성 불가
- 모든 메서드는 추상 메서드: Java 8 이전에는 모든 메서드가 추상 메서드
- 다중 구현 가능: 여러 인터페이스를 구현 가능
- 상수만 포함 가능: 필드는 모두
public static final
인터페이스 정의
public interface Flyable {
// 추상 메서드 (abstract 키워드 생략 가능)
void fly();
// 상수
int MAX_SPEED = 100;
}
public interface Swimmable {
void swim();
}
인터페이스 구현
public class Duck implements Flyable, Swimmable {
@Override
public void fly() {
System.out.println("오리가 날아갑니다.");
}
@Override
public void swim() {
System.out.println("오리가 수영합니다.");
}
}
인터페이스 vs 추상 클래스
| 구분 | 인터페이스 | 추상 클래스 |
|---|---|---|
| 키워드 | interface |
abstract class |
| 상속/구현 | implements |
extends |
| 다중 상속 | 가능 (여러 인터페이스 구현) | 불가능 (단일 상속) |
| 필드 | 상수만 가능 | 일반 필드 가능 |
| 메서드 | 추상 메서드만 (Java 8 이전) | 추상/일반 메서드 모두 가능 |
| 생성자 | 없음 | 있음 |
| 사용 목적 | "무엇을" 정의 | "무엇을" + "어떻게" 일부 정의 |
인터페이스 예제
// 인터페이스 정의
public interface Drawable {
void draw();
}
public interface Resizable {
void resize(double factor);
}
// 클래스가 여러 인터페이스 구현
public class Circle implements Drawable, Resizable {
double radius;
public Circle(double radius) {
this.radius = radius;
}
@Override
public void draw() {
System.out.println("원을 그립니다. 반지름: " + radius);
}
@Override
public void resize(double factor) {
radius *= factor;
System.out.println("크기 조정 후 반지름: " + radius);
}
}
Java 8의 인터페이스 개선
Java 8부터 인터페이스에 default 메서드와 static 메서드를 포함할 수 있습니다.
public interface Animal {
// 추상 메서드
void makeSound();
// default 메서드 (구현 포함)
default void eat() {
System.out.println("동물이 먹습니다.");
}
// static 메서드
static void info() {
System.out.println("동물 인터페이스입니다.");
}
}
객체지향 핵심 개념 종합 예제
예제: 도형 관리 시스템
// 추상 클래스
public abstract class Shape {
String color;
public Shape(String color) {
this.color = color;
}
public abstract double calculateArea();
public abstract void draw();
}
// 인터페이스
public interface Resizable {
void resize(double factor);
}
// 클래스 구현
public class Circle extends Shape implements Resizable {
double radius;
public Circle(String color, double radius) {
super(color);
this.radius = radius;
}
@Override
public double calculateArea() {
return Math.PI * radius * radius;
}
@Override
public void draw() {
System.out.println(color + " 원을 그립니다.");
}
@Override
public void resize(double factor) {
radius *= factor;
}
}
// 다형성 활용
Shape[] shapes = {
new Circle("빨강", 5.0),
new Rectangle("파랑", 4.0, 6.0)
};
for (Shape shape : shapes) {
shape.draw();
System.out.println("넓이: " + shape.calculateArea());
}
연습 문제
캡슐화
Person클래스를 만들고, 이름과 나이를 private 필드로 선언한 후 getter/setter를 작성하세요.
상속
Vehicle클래스를 부모로 하고,Car와Motorcycle클래스를 자식으로 만들어 상속을 구현하세요.
다형성
- 여러 동물 클래스를 만들고, 부모 클래스 타입의 배열에 저장한 후 각각의
makeSound()메서드를 호출하세요.
- 여러 동물 클래스를 만들고, 부모 클래스 타입의 배열에 저장한 후 각각의
추상 클래스
Animal추상 클래스를 만들고,makeSound()추상 메서드를 정의한 후 자식 클래스에서 구현하세요.
인터페이스
Flyable인터페이스를 만들고,Bird와Airplane클래스에서 구현하세요.
종합 예제
- 도형 클래스들을 추상 클래스와 인터페이스를 사용하여 구현하고, 다형성을 활용하세요.
다음 장 예고
다음 장에서는 예외 처리(Exception Handling)를 통해 프로그램의 오류를 효과적으로 관리하는 방법을 학습합니다.
'BackEnd > Java' 카테고리의 다른 글
| 10장. 예외 처리 (0) | 2026.01.02 |
|---|---|
| 9장. 컬렉션 프레임워크 (0) | 2026.01.02 |
| 7장. 객체지향 프로그래밍(OOP) 기초 (0) | 2026.01.01 |
| 6장. 메서드(Method) (0) | 2025.12.31 |
| 5장. 배열과 문자열 (0) | 2025.12.30 |