반응형
객체지향 언어란?
객체지향 언어(Object-Oriented Programming, OOP)는 소프트웨어 설계 및 구현을 객체(Object)라는 단위로 나누어 진행하는 프로그래밍 패러다임입니다. 객체는 속성(데이터)과 기능(메서드)을 갖춘 실체로, 각 객체는 독립적이고 자율적인 존재로 동작합니다. 이러한 객체들이 상호작용하면서 소프트웨어를 구성하게 됩니다.
객체지향 프로그래밍의 핵심 원칙은 추상화, 캡슐화, 상속, 다형성입니다. 이 원칙들은 소프트웨어 개발에서 재사용성, 유지보수성, 확장성을 높이고, 복잡한 시스템을 모듈화하여 관리하기 쉽게 만듭니다.
이번 글에서는 객체지향 프로그래밍에서 클래스와 객체 인스턴스의 차이, static 키워드의 사용법과 단점, 오버로딩과 오버라이딩, 추상화라는 네 가지 핵심 개념을 다루겠습니다. 이 개념들은 객체지향 프로그래밍의 기본이자 핵심으로, 이해하면 객체지향 언어를 다룰 때 강력한 기반이 됩니다.
1. 클래스와 객체 인스턴스의 차이
클래스 (Class)
클래스는 객체를 생성하기 위한 설계도입니다. 클래스는 객체가 가져야 할 속성(변수)과 기능(메서드)을 정의하며, 실제 데이터는 포함되지 않습니다. 클래스는 객체가 어떻게 동작할지에 대한 청사진을 제공합니다.
객체 인스턴스 (Object Instance)
객체 인스턴스는 클래스에서 정의한 설계도를 기반으로 실제 메모리 상에서 생성된 객체입니다. 객체 인스턴스는 클래스에 정의된 속성과 메서드를 실제로 사용하며, 클래스의 복제본이라고 할 수 있습니다.
비유
- 클래스는 설계도와 같고, 객체 인스턴스는 설계도를 기반으로 실제로 만든 집이라고 볼 수 있습니다.
예시:
class Car {
String color;
int speed;
void drive() {
System.out.println("Car is driving at " + speed + " km/h.");
}
}
// 클래스 정의
Car myCar = new Car(); // 객체 인스턴스 생성
myCar.color = "Red"; // 객체 속성 설정
myCar.speed = 100;
myCar.drive(); // 메서드 호출
- Car는 클래스, myCar는 객체 인스턴스입니다.
- myCar 객체는 Car 클래스의 설계도를 기반으로 메모리에 생성된 실제 객체입니다.
2. static 키워드의 사용과 단점
static 키워드
- static은 클래스 차원에서 공유되는 변수나 메서드를 정의하는 데 사용됩니다.
- 클래스가 인스턴스화되지 않아도 접근할 수 있습니다. 즉, 클래스의 모든 인스턴스가 static 변수를 공유합니다.
- 메서드가 static으로 정의되면 객체 생성 없이 클래스 이름으로 호출할 수 있습니다.
사용 예시
class Counter {
static int count = 0; // static 변수
void increment() {
count++; // 모든 객체가 count를 공유
}
}
Counter c1 = new Counter();
Counter c2 = new Counter();
c1.increment();
c2.increment();
System.out.println(Counter.count); // 출력: 2
static 키워드의 단점
- 상속에서의 제한: static 메서드는 오버라이딩이 불가능합니다. 즉, 자식 클래스에서 부모 클래스의 static 메서드를 재정의할 수 없습니다. 이는 다형성을 제한할 수 있습니다.
- 유연성 부족: static은 전역적으로 공유되므로 객체별 상태를 따로 관리할 수 없고, 상태 관리가 복잡해질 수 있습니다.
- 테스트 및 유지보수: static은 전역 상태를 사용하기 때문에, 객체 지향적인 원칙인 캡슐화를 위반할 수 있으며, 유지보수가 어려울 수 있습니다.
3. 오버로딩과 오버라이딩의 차이
오버로딩 (Overloading)
- 오버로딩은 같은 메서드 이름을 사용하되, 매개변수의 수나 타입을 다르게 하여 여러 개의 메서드를 정의하는 것입니다.
- 리턴 타입은 오버로딩에서 영향을 미치지 않습니다.
오버로딩 예시
class Printer {
void print(int num) {
System.out.println(num);
}
void print(String text) {
System.out.println(text);
}
}
Printer printer = new Printer();
printer.print(10); // print(int num) 호출
printer.print("Hello"); // print(String text) 호출
오버라이딩 (Overriding)
- 오버라이딩은 상속받은 부모 클래스의 메서드를 자식 클래스에서 재정의하여 사용하는 것입니다.
- 부모 클래스의 메서드 이름, 매개변수, 리턴 타입이 동일해야 하며, 자식 클래스에서 기능을 수정하려는 경우 사용됩니다.
오버라이딩 예시
class Animal {
void sound() {
System.out.println("Animal makes a sound");
}
}
class Dog extends Animal {
@Override
void sound() {
System.out.println("Dog barks");
}
}
Animal animal = new Dog();
animal.sound(); // Dog barks
차이점 요약
- 오버로딩: 메서드 이름은 같지만 매개변수가 다릅니다.
- 오버라이딩: 부모 클래스의 메서드를 자식 클래스에서 재정의합니다.
4. 추상화 (Abstraction)
추상화 (Abstraction)
- 추상화는 복잡한 시스템에서 불필요한 세부 사항을 숨기고, 중요한 기능만을 외부에 제공하는 것입니다.
- 객체 지향 프로그래밍에서 추상화는 추상 클래스나 인터페이스를 통해 구현됩니다.
- 추상화는 시스템의 복잡성을 줄이고 사용자에게 핵심 기능만 제공하는 데 중요한 역할을 합니다.
추상 클래스와 인터페이스
- 추상 클래스: 일부 메서드를 구현하고 일부 메서드를 추상 메서드(구현되지 않은 메서드)로 정의하여 자식 클래스가 이를 구현하게 합니다.
- 인터페이스: 메서드의 시그니처만 정의하고, 실제 구현은 구현 클래스가 담당합니다.
추상화의 장점
- 복잡성 관리: 시스템의 세부 구현을 숨기고, 핵심 기능만을 노출시켜 사용자나 개발자가 더 쉽게 접근할 수 있게 만듭니다.
- 유지보수 용이: 시스템의 내부 구현을 변경해도 외부에는 영향을 주지 않으므로, 유지보수가 용이합니다.
- 재사용성: 코드가 잘 분리되므로 재사용성이 증가합니다.
추상화 예시 (추상 클래스)
abstract class Animal {
abstract void sound(); // 추상 메서드
void sleep() { // 구현된 메서드
System.out.println("Animal is sleeping");
}
}
class Dog extends Animal {
@Override
void sound() {
System.out.println("Dog barks");
}
}
public class Main {
public static void main(String[] args) {
Animal dog = new Dog();
dog.sound(); // Dog barks
dog.sleep(); // Animal is sleeping
}
}
- Animal 클래스는 추상화된 클래스이며, Dog 클래스는 이를 상속받고 구체적인 소리를 구현합니다.
반응형