객체지향 프로그래밍: OOP
Java는 객체지향 프로그래밍(OOP)의 대표적인 언어로, 현실에 존재하는 사물(객체)을 속성과 기능으로 정의하고 이를 코드로 구현하는 방식입니다. 객체지향 프로그래밍의 핵심은 클래스와 객체, 그리고 이를 통해 프로그램을 설계하고 구조화하는 방법에 있습니다. 이번 포스팅에서, 객체지향에 대한 설명과 Java에서 이를 어떻게 구현하는지에 대해 설명하도록 하겠습니다.
객체지향 프로그래밍의 장점
- 코드 재사용성
이미 정의된 클래스와 객체를 여러 프로그램에서 재사용할 수 있어 개발 효율이 높아집니다. - 유지보수성
클래스와 객체를 이용해 코드를 모듈화할 수 있어 수정과 유지보수가 용이합니다. - 확장성
새로운 기능을 추가하거나 변경할 때 기존 코드를 최소한으로 수정하고 확장할 수 있습니다. - 캡슐화
데이터를 보호하고 접근을 제어하는 메커니즘으로, 외부에서 직접 접근할 수 없도록 데이터를 은닉할 수 있습니다.
클래스와 객체
1. 클래스 (Class)
클래스는 객체를 생성하기 위한 설계도입니다. 클래스는 속성(변수)과 동작(메소드)을 정의하고, 객체는 이 클래스의 인스턴스입니다.
class Car {
String model;
int speed;
void drive() {
System.out.println("Driving " + model);
}
}
2. 객체 (Object)
객체는 클래스에 정의된 속성과 동작을 실제로 사용할 수 있는 클래스로부터 생성된 실체입니다.
Car myCar = new Car(); // Car 클래스의 객체 생성
myCar.model = "Hyundai";
myCar.drive(); // "Driving Hyundai"
3. 인스턴스 (Instance)
인스턴스는 객체와 같은 의미로 사용되지만, 특정 클래스에서 생성된 객체를 명확하게 지칭할 때 사용합니다. 즉, 객체는 인스턴스의 일반적인 용어이고, 인스턴스는 특정 클래스의 구현체입니다.
변수와 메소드
1. 변수
클래스 내부에서 정의된 변수는 멤버 변수로 불리며, 객체의 상태를 저장합니다. 클래스 내에 메소드 내부에서 정의된 변수는 지역 변수입니다.
- 클래스 변수(static): 클래스 자체에 속하며 모든 객체가 공유합니다.
- 인스턴스 변수: 클래스의 인스턴스를 생성할 때 만들어지는 변수로, 각 객체마다 별도로 할당됩니다.
- 지역 변수: 메소드 내에 선언되어 메소드 내에서만 사용 가능한 변수입니다.
class Car {
static int totalCarsCount; // 클래스 변수 (static)
String model: // 인스턴스 변수
// 메소드 (인스턴스 메소드)
void drive() {
int val; // 지역 변수
}
}
2. 메소드
메소드는 객체의 동작을 정의합니다. 메소드는 클래스 내부에 정의되며, 객체의 상태를 변화시키거나 기능을 수행합니다.
- 클래스 메소드 (static): 클래스 레벨에서 호출할 수 있습니다. 객체를 생성하지 않고 사용할 수 있습니다. 클래스이름.메소드이름형식으로 사용하면 됩니다.
- 인스턴스 메소드: 객체가 생성된 후에만 사용할 수 있으며, 객체의 상태를 변화시킵니다.
class MathUtil {
// 클래스 메소드
static int add(int a, int b) {
return a + b;
}
}
int result = MathUtil.add(5, 3); // 객체 생성 없이 클래스명으로 호출 가능
메소드 호출
메소드를 정의하였다고 해서 끝이 아니라, 메소드 호출을 해주어야 구현한 메소드가 실행됩니다.
메소드를 선언시, 메소드가 작업 하기 위해 필요한 값을 입력받기 위해 괄호 안에 매개변수(parameter)를 작성해줍니다. 메소드 호출 시 괄호 안에 인자(argument)를 입력하여, 메소드 선언부에 있는 매개변수에 전달합니다. 때문에 매개변수와 인자의 타입과 일치해야합니다.
int add (int x, int y) { // 메소드 선언, 매개변수
return x + y; // 반환
}
int result = add(1, 2); // 메소드 호출, 인자
객체지향 프로그래밍 특징
캡슐화 (Encapsulation)
캡슐화는 객체의 데이터를 외부에서 직접 접근하지 못하도록 하고, 데이터를 안전하게 보호하며 필요한 경우에만 허용된 방식으로 접근하도록 하는 개념입니다. 이를 통해 객체의 내부 구현을 감추고, 외부에서는 메소드를 통해서만 객체의 상태를 변경할 수 있습니다.
아래 예시를 보면, 필드(멤버 변수)를 private으로 설정하여, 해당 필드를 외부에서 직접 접근할 수 없습니다. 외부에서 데이터를 변경하거나 조회할 수 있도록 getter와 setter 메소드를 작성하여, 이를 통해서만 접근할 수 있습니다. 이를 통해 데이터의 무결성을 유지할 수 있습니다.
class User {
private String name;
// Getter 메소드
public String getName() {
return name;
}
// Setter 메소드
public void setName(String name) {
this.name = name;
}
}
은닉화
은닉화는 객체의 내부 데이터를 보호하기 위해 외부에서 직접 접근할 수 없도록 숨기는 기법입니다. 이를 통해 자신의 내부가 어떤 방식으로 동작하는지 외부에 알릴 필요 없이, 정해진 인터페이스만 제공하여 데이터를 제어할 수 있습니다. 캡슐화와 연관된 개념이며, 접근 제어자를 사용해 구현합니다.
접근 제어자
- public: 모든 클래스에서 접근 가능
- protected: 같은 패키지와 상속받은 클래스에서 접근 가능
- (default, 접근 제어자가 없는 경우): 같은 패키지에서 접근 가능
- private: 해당 클래스 내부에서만 접근 가능
상속 (extends)
상속은 객체지향 프로그래밍에서 코드의 재사용성을 높이기 위한 핵심 개념입니다. 상속을 통해 부모 클래스의 필드와 메소드를 자식 클래스에서 받아서 사용할 수 있습니다. 이를 통해 중복 코드를 줄이고, 클래스 간의 계층 구조를 형성할 수 있습니다. 상속은 extends 키워드를 사용해 구현됩니다.
class User {
String name;
void introduce() {
System.out.println(name + "입니다");
}
}
class Student extends User {
void study() {
System.out.println(name + "은 공부합니다.");
}
}
public class Main {
public static void main(String[] args) {
Student student = new Student();
student.name = "Yeo";
student.introduce(); // 상속받은 메소드 사용
student.study(); // 자식 클래스의 메소드 사용
}
}
위 예시에서 Student 클래스는 User 클래스를 상속받아 name 필드와 introduce() 메소드를 물려받습니다. 그리고 Student 클래스에 정의된 study() 메소드는 고유 기능을 추가합니다.
다형성
다형성은 동일한 메소드나 연산자가 여러 형태로 동작할 수 있게 하는 객체지향 프로그래밍의 개념입니다. Java에서는 다형성을 오버로딩(Overloading)과 오버라이딩(Overriding) 두 가지 방식으로 구현합니다.
오버로딩 (Overloading)
오버로딩은 같은 이름의 메소드를 여러 개 정의할 수 있도록 하며, 각 메소드는 매개변수의 타입, 개수, 순서가 다릅니다. 오버로딩을 통해 같은 이름의 메소드로 여러 작업을 처리할 수 있게 되어 코드의 가독성이 높아집니다.
class Calculator {
int add(int a, int b) {
return a + b;
}
int add(int a, int b, int c) {
return a + b + c;
}
double add(double a, double b) {
return a + b;
}
}
public class Main {
public static void main(String[] args) {
Calculator calc = new Calculator();
System.out.println(calc.add(1, 2)); // 3 출력
System.out.println(calc.add(1, 2, 3)); // 6 출력
System.out.println(calc.add(1.5, 2.5)); // 4.0 출력
}
}
위 예시는 add 메소드를 오버로딩하여 매개변수에 따라 다양한 연산을 처리할 수 있는 다형성을 보여줍니다.
오버라이딩 (Overriding)
오버라이딩은 부모 클래스에서 정의한 메소드를 자식 클래스에서 재정의하는 기능입니다.
class User {
void introduce() {
System.out.println("OO입니다.");
}
}
class Student extends User {
@Override
void introduce() {
System.out.println("학생입니다.");
}
}
class Employee extends User {
@Override
void introduce() {
System.out.println("직원입니다.");
}
}
public class Main {
public static void main(String[] args) {
Student student = new Student();
student.introduce(); // "학생입니다."
Employee employee = new Employee();
employee.introduce(); // "직원입니다."
}
}
위 예시에서 User클래스의 introduce() 메소드는 자식 클래스인 Student, Employee에서 각각 오버라이딩되었습니다. 이로 인해 객체의 실제 타입에 따라 다른 동작을 합니다.
인터페이스 (Interface)
인터페이스는 클래스가 구현해야 할 메소드들의 집합을 정의하는 구조입니다. 상속(extends)는 다중 상속이 불가능하지만, 인터페이스를 통해 다중 상속 기능을 구현할 수 있습니다.
interface User {
void introduce();
}
class Student implements User {
public void introduce() {
System.out.println("학생입니다.");
}
}
class Employee implements User {
public void introduce() {
System.out.println("직원입니다.");
}
}
제어자: final, abstract, static
제어자는 클래스, 메소드, 필드에 적용되어 특정 동작을 정의합니다.
final
- 클래스: 더 이상 상속할 수 없게 만듭니다.
- 변수: 값을 변경할 수 없는 상수가 됩니다.
- 메소드: 자식 클래스에서 오버라이드할 수 없습니다.
final class User {} // 더 이상 상속 불가
abstract
abstract는 추상 클래스를 정의할 때 사용되며, 추상 클래스는 인스턴스화할 수 없습니다. 추상 클래스는 추상 메소드를 포함할 수 있으며, 이를 상속받은 자식 클래서는 반드시 구현해야 합니다.
abstract class Shape {
abstract void draw(); // 추상 메소드
}
class Circle extends Shape {
void draw() {
System.out.println("Drawing a circle");
}
}
static
static 제어자는 클래스 수준에서 변수를 선언할 때 사용하며, 객체와 상관없이 클래스 자체에 속합니다.
class Counter {
static int count = 0;
Counter() {
count++;
}
}
public class Main {
public static void main(String[] args) {
new Counter();
new Counter();
System.out.println(Counter.count); // 2 출력
}
}
'Java' 카테고리의 다른 글
Java 상속(Inheritance), 인터페이스(Interface), 추상 클래스(Abstract Class) 차이점 (0) | 2024.09.18 |
---|---|
Java 생성자, this()와 super() (0) | 2024.09.16 |
Java 조건문과 반복문: if, switch, for, while 그리고 break와 continue (0) | 2024.09.10 |
Java 변수, 데이터 타입, 리터럴과 상수 차이 (0) | 2024.09.10 |
Java란 무엇인가? (0) | 2024.09.05 |