[2026] Java 클래스와 객체 | OOP, 상속, 인터페이스

[2026] Java 클래스와 객체 | OOP, 상속, 인터페이스

이 글의 핵심

Java 클래스와 객체: OOP, 상속, 인터페이스. 클래스와 객체·상속 (Inheritance).

들어가며

Java는 객체 지향 프로그래밍(OOP)(데이터와 동작을 객체·클래스로 묶어 설계하는 방식)을 널리 쓰는 언어입니다. 클래스는 객체를 찍어내는 설계도이고, new로 만든 인스턴스는 그 설계도를 바탕으로 만든 실물입니다. 같은 설계도로 여러 객체를 만들 수 있고, 필드·메서드는 설계도에 적힌 규격대로 동작합니다.

실무에서 마주한 현실

개발을 배울 때는 모든 게 깔끔하고 이론적입니다. 하지만 실무는 다릅니다. 레거시 코드와 씨름하고, 급한 일정에 쫓기고, 예상치 못한 버그와 마주합니다. 이 글에서 다루는 내용도 처음엔 이론으로 배웠지만, 실제 프로젝트에 적용하면서 “아, 이래서 이렇게 설계하는구나” 하고 깨달은 것들입니다. 특히 기억에 남는 건 첫 프로젝트에서 겪은 시행착오입니다. 책에서 배운 대로 했는데 왜 안 되는지 몰라 며칠을 헤맸죠. 결국 선배 개발자의 코드 리뷰를 통해 문제를 발견했고, 그 과정에서 많은 걸 배웠습니다. 이 글에서는 이론뿐 아니라 실전에서 마주칠 수 있는 함정들과 해결 방법을 함께 다루겠습니다.

1. 클래스와 객체

클래스 정의

클래스는 객체의 설계도입니다: 다음은 java를 활용한 상세한 구현 코드입니다. 함수를 통해 로직을 구현합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

public class Person {
    // 1. 필드 (Field) - 인스턴스 변수
    // private: 클래스 외부에서 직접 접근 불가 (캡슐화)
    private String name;
    private int age;
    
    // 2. 생성자 (Constructor)
    // 객체 생성 시 자동 호출되는 특수 메서드
    public Person(String name, int age) {
        // this: 현재 객체를 가리키는 참조
        // this.name: 필드 name
        // name: 매개변수 name
        this.name = name;
        this.age = age;
    }
    
    // 3. 메서드 (Method)
    public void introduce() {
        // 필드에 직접 접근 가능
        System.out.println("안녕하세요, " + name + "입니다.");
    }
    
    // 4. Getter (필드 값 읽기)
    public String getName() {
        // 외부에서 private 필드를 읽을 수 있게 함
        return name;
    }
    
    // 5. Setter (필드 값 쓰기)
    public void setName(String name) {
        // 외부에서 private 필드를 수정할 수 있게 함
        // 유효성 검사 추가 가능
        this.name = name;
    }
    
    public int getAge() {
        return age;
    }
    
    public void setAge(int age) {
        // 유효성 검사: 음수 나이 방지
        if (age > 0) {
            this.age = age;
        } else {
            System.out.println("나이는 양수여야 합니다");
        }
    }
}
// 사용 예제
public class Main {
    public static void main(String[] args) {
        // 객체 생성 (인스턴스화)
        // new Person(): 생성자 호출
        // person: 객체 참조 변수
        Person person = new Person("홍길동", 25);
        
        // 메서드 호출
        person.introduce();  // 안녕하세요, 홍길동입니다.
        
        // Getter로 필드 읽기
        System.out.println("이름: " + person.getName());  // 홍길동
        
        // Setter로 필드 수정
        person.setAge(26);
        System.out.println("나이: " + person.getAge());  // 26
        
        // person.age = 30;  // ❌ 컴파일 에러
        // private 필드는 외부에서 직접 접근 불가
        // 반드시 Getter/Setter 사용
    }
}

캡슐화의 장점: 다음은 java를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며, 에러 처리를 통해 안정성을 확보합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// ❌ 나쁜 예: public 필드
class BadPerson {
    public int age;  // 직접 접근 가능
}
BadPerson p = new BadPerson();
p.age = -10;  // 유효하지 않은 값 설정 가능!
// ✅ 좋은 예: private 필드 + Setter
class GoodPerson {
    private int age;
    
    public void setAge(int age) {
        if (age > 0 && age < 150) {  // 유효성 검사
            this.age = age;
        } else {
            throw new IllegalArgumentException("유효하지 않은 나이");
        }
    }
}
GoodPerson p = new GoodPerson();
// p.age = -10;  // 컴파일 에러 (직접 접근 불가)
p.setAge(-10);  // 예외 발생 (유효성 검사)

생성자 오버로딩

다음은 java를 활용한 상세한 구현 코드입니다. 함수를 통해 로직을 구현합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

public class Person {
    private String name;
    private int age;
    private String email;
    
    // 기본 생성자
    public Person() {
        this("익명", 0, "");
    }
    
    // 이름만
    public Person(String name) {
        this(name, 0, "");
    }
    
    // 이름과 나이
    public Person(String name, int age) {
        this(name, age, "");
    }
    
    // 모든 필드
    public Person(String name, int age, String email) {
        this.name = name;
        this.age = age;
        this.email = email;
    }
}

2. 상속 (Inheritance)

기본 상속

상속은 기존 클래스를 확장하여 새로운 클래스를 만드는 기법입니다: 다음은 java를 활용한 상세한 구현 코드입니다. 함수를 통해 로직을 구현합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// 부모 클래스 (Super Class, Base Class)
public class Animal {
    // protected: 자식 클래스에서 접근 가능
    // private보다 넓고, public보다 좁은 접근 범위
    protected String name;
    
    // 생성자
    public Animal(String name) {
        this.name = name;
    }
    
    // 메서드
    public void makeSound() {
        System.out.println("동물 소리");
    }
    
    public void sleep() {
        // protected 필드 name 사용
        System.out.println(name + "이(가) 잠을 잡니다.");
    }
}
// 자식 클래스 (Sub Class, Derived Class)
public class Dog extends Animal {
    // extends: 상속 키워드
    // Dog는 Animal의 모든 필드와 메서드를 물려받음
    
    // Dog만의 고유 필드
    private String breed;
    
    // 생성자
    public Dog(String name, String breed) {
        // super(name): 부모 생성자 호출 (필수)
        // 반드시 첫 줄에 위치해야 함
        super(name);
        
        // 자식 클래스의 필드 초기화
        this.breed = breed;
    }
    
    // 메서드 오버라이딩 (재정의)
    @Override  // 어노테이션: 오버라이딩임을 명시
    public void makeSound() {
        // 부모의 makeSound()를 재정의
        System.out.println("멍멍!");
    }
    
    // Dog만의 고유 메서드
    public void fetch() {
        // 부모의 protected 필드 name 사용 가능
        System.out.println(name + "이(가) 공을 가져옵니다.");
    }
}
// 사용 예제
public class Main {
    public static void main(String[] args) {
        // Dog 객체 생성
        Dog dog = new Dog("바둑이", "진돗개");
        
        // 오버라이딩된 메서드 호출
        dog.makeSound();  // 멍멍! (Dog의 메서드)
        
        // 상속받은 메서드 호출
        dog.sleep();      // 바둑이이(가) 잠을 잡니다. (Animal의 메서드)
        
        // Dog만의 메서드 호출
        dog.fetch();      // 바둑이이(가) 공을 가져옵니다.
        
        // 다형성 (Polymorphism)
        Animal animal = new Dog("멍멍이", "시바견");
        // 부모 타입 변수에 자식 객체 할당 가능
        animal.makeSound();  // 멍멍! (Dog의 메서드 호출)
        // animal.fetch();   // ❌ 컴파일 에러
        // Animal 타입이므로 Dog의 메서드는 호출 불가
    }
}

상속의 특징:

  1. 코드 재사용: 공통 기능을 부모 클래스에 정의
  2. 확장성: 새로운 기능을 자식 클래스에 추가
  3. 다형성: 부모 타입으로 자식 객체 참조 가능
  4. 단일 상속: Java는 한 클래스만 상속 가능 (다중 상속 불가)

super 키워드

아래 코드는 java를 사용한 구현 예제입니다. 함수를 통해 로직을 구현합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

public class Employee extends Person {
    private String department;
    
    public Employee(String name, int age, String department) {
        super(name, age);  // 부모 생성자
        this.department = department;
    }
    
    @Override
    public void introduce() {
        super.introduce();  // 부모 메서드 호출
        System.out.println("부서: " + department);
    }
}

3. 인터페이스 (Interface)

기본 인터페이스

다음은 java를 활용한 상세한 구현 코드입니다. 함수를 통해 로직을 구현합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

public interface Drawable {
    void draw();  // 추상 메서드
    
    default void display() {  // 기본 구현 (Java 8+)
        System.out.println("화면에 표시");
    }
    
    static void info() {  // 정적 메서드
        System.out.println("Drawable 인터페이스");
    }
}
public class Circle implements Drawable {
    private double radius;
    
    public Circle(double radius) {
        this.radius = radius;
    }
    
    @Override
    public void draw() {
        System.out.println("원 그리기: 반지름 " + radius);
    }
}
// 사용
Circle circle = new Circle(5.0);
circle.draw();
circle.display();
Drawable.info();

다중 인터페이스 구현

다음은 java를 활용한 상세한 구현 코드입니다. 함수를 통해 로직을 구현합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

public interface Movable {
    void move(int x, int y);
}
public interface Resizable {
    void resize(double scale);
}
public class Shape implements Drawable, Movable, Resizable {
    private int x, y;
    private double size;
    
    @Override
    public void draw() {
        System.out.println("도형 그리기");
    }
    
    @Override
    public void move(int x, int y) {
        this.x = x;
        this.y = y;
    }
    
    @Override
    public void resize(double scale) {
        this.size *= scale;
    }
}

4. 추상 클래스 (Abstract Class)

다음은 java를 활용한 상세한 구현 코드입니다. 함수를 통해 로직을 구현합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

public abstract class Shape {
    protected String color;
    
    public Shape(String color) {
        this.color = color;
    }
    
    // 추상 메서드
    public abstract double area();
    
    // 일반 메서드
    public void printColor() {
        System.out.println("색상: " + color);
    }
}
public class Circle extends Shape {
    private double radius;
    
    public Circle(String color, double radius) {
        super(color);
        this.radius = radius;
    }
    
    @Override
    public double area() {
        return Math.PI * radius * radius;
    }
}
public class Rectangle extends Shape {
    private double width, height;
    
    public Rectangle(String color, double width, double height) {
        super(color);
        this.width = width;
        this.height = height;
    }
    
    @Override
    public double area() {
        return width * height;
    }
}

5. 캡슐화

접근 제어자

다음은 java를 활용한 상세한 구현 코드입니다. 함수를 통해 로직을 구현합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

public class BankAccount {
    private double balance;  // private: 외부 접근 불가
    
    public BankAccount(double initialBalance) {
        this.balance = initialBalance;
    }
    
    public void deposit(double amount) {
        if (amount > 0) {
            balance += amount;
        }
    }
    
    public boolean withdraw(double amount) {
        if (amount > 0 && balance >= amount) {
            balance -= amount;
            return true;
        }
        return false;
    }
    
    public double getBalance() {
        return balance;
    }
}

6. 실전 예제

예제: 도서 관리 시스템

다음은 java를 활용한 상세한 구현 코드입니다. 함수를 통해 로직을 구현합니다, 반복문으로 데이터를 처리합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

public class Book {
    private String title;
    private String author;
    private int year;
    private boolean available;
    
    public Book(String title, String author, int year) {
        this.title = title;
        this.author = author;
        this.year = year;
        this.available = true;
    }
    
    public boolean borrow() {
        if (available) {
            available = false;
            return true;
        }
        return false;
    }
    
    public void returnBook() {
        available = true;
    }
    
    public void printInfo() {
        System.out.println("제목: " + title);
        System.out.println("저자: " + author);
        System.out.println("출판년도: " + year);
        System.out.println("대출 가능: " + (available ? "예" : "아니오"));
    }
}
public class Library {
    private List<Book> books;
    
    public Library() {
        books = new ArrayList<>();
    }
    
    public void addBook(Book book) {
        books.add(book);
    }
    
    public Book findBook(String title) {
        for (Book book : books) {
            if (book.getTitle().equals(title)) {
                return book;
            }
        }
        return null;
    }
}

정리

핵심 요약

  1. 클래스: 객체의 설계도, 필드 + 메서드
  2. 생성자: 객체 초기화, 오버로딩 가능
  3. 상속: extends, super, @Override
  4. 인터페이스: implements, 다중 구현 가능
  5. 추상 클래스: abstract, 일부 구현 가능
  6. 캡슐화: private, public, protected

다음 단계


관련 글

... 996 lines not shown ... Token usage: 63706/1000000; 936294 remaining Start-Sleep -Seconds 3