Java

JAVA 기초 정리 - 객체지향 (final, 인터페이스, 캡슐화, 상속)

chungmani 2026. 6. 8. 20:12

final

final 변수

변수에 final을 붙이면 값을 한 번만 할당할 수 있다.

final int a = 1;

a = 2; // 오류 발생

즉, 한 번 저장된 값은 변경할 수 없다.

 

final 클래스

클래스에 final을 붙이면 다른 클래스가 상속받을 수 없다.

public final class Animal {
}
public class Dog extends Animal { } // 오류

 

final 메서드

메서드에 final을 붙이면 자식 클래스에서 오버라이딩할 수 없다.

public final void move() {
    System.out.println("이동");
}

상수(Constant)

상수는 프로그램 실행 중 절대 변경되면 안 되는 값이다.

자바에서는 상수를 대문자로 작성하는 것이 관례이다.

상수는 보통 static final을 함께 사용한다.

public class Circle {

    static final double PI = 3.14159;
}

사용 예시

System.out.println(Circle.PI);

왜 static final을 같이 사용할까?

  • final : 값을 변경하지 못하게 함
  • static : 프로그램 시작 시 한 번만 생성되어 모든 객체가 같은 값을 공유함

즉, 모든 객체가 동일한 값을 사용해야 하는 상수에 적합하다.


불변 객체 (Immutable Object)

불변 객체란 생성 이후 내부 상태를 변경할 수 없는 객체를 말한다.

특징

  • 내부 상태 변경 불가
  • Setter를 만들지 않음
  • 값 변경이 필요하면 새로운 객체 생성

대표적인 예

  • String
  • Integer
  • Long
  • Double
  • 래퍼 클래스들

예시

String name = "Java";

name = "Spring";

위 코드는 기존 String 객체를 수정하는 것이 아니라 새로운 String 객체를 생성하는 것이다.


인터페이스 (Interface)

인터페이스는 설계 표준 또는 규격서라고 생각하면 된다.

클래스가 반드시 구현해야 하는 최소한의 규칙을 정의한다.

 

인터페이스 구현

public interface Flyable {
    void fly();
}
public class Bird implements Flyable {

    @Override
    public void fly() {
        System.out.println("날아간다.");
    }
}

클래스는 implements 키워드를 사용하여 인터페이스를 구현한다.

IntelliJ에서는 인터페이스 이름 위에서 Alt + Enter를 누르면 필요한 메서드를 자동 생성해 준다.

 

다중 구현 가능

클래스 상속은 하나만 가능하지만 인터페이스는 여러 개를 구현할 수 있다.

public class Duck implements Flyable, Swimmable {
}

 

인터페이스 변수

인터페이스에 선언한 변수는 자동으로

public static final

이 붙는다.

public interface Test {

    int NUM = 10;
}

실제로는

public static final int NUM = 10;

과 동일하다.


캡슐화 (Encapsulation)

캡슐화란 객체의 내부 데이터를 외부로부터 보호하는 것이다.

객체지향 프로그래밍의 핵심 개념 중 하나이다.

 

접근 제어자

public

어디서든 접근 가능

public String name;

protected

상속받은 클래스에서 접근 가능

protected String name;

default

같은 패키지 내부에서만 접근 가능

String name;

private

현재 클래스 내부에서만 접근 가능

private String name;

 

private 사용을 습관화하자

정보를 보호하기 위해 필드는 기본적으로 private 사용을 고려하는 것이 좋다.

private String name;

 

Getter와 Setter

Getter

정보를 읽기 위한 메서드

public String getName() {
    return name;
}

Getter를 사용하면 값을 읽을 수는 있지만 직접 수정할 수는 없다.

필요에 따라 읽는 방식도 제어할 수 있다.

 

Setter

정보를 수정하기 위한 메서드

public void setName(String name) {
    this.name = name;
}

Setter에서는 조건을 걸어 안전하게 수정할 수 있다.

public void setAge(int age) {

    if (age >= 0) {
        this.age = age;
    }
}

즉, 캡슐화는 데이터를 숨기고 Getter, Setter를 통해 안전하게 접근하도록 만드는 것이다.


상속 (Inheritance)

상속은 부모 클래스의 속성과 기능을 자식 클래스가 물려받는 개념이다.

클래스 간의 관계를 부모-자식 관계로 표현한다.

 

상속의 장점

  • 코드 재사용
  • 유지보수 편리
  • 확장성 증가
  • 중복 코드 감소

 

extends 사용

public class Animal {

    void eat() {
        System.out.println("먹는다.");
    }
}
public class Dog extends Animal {
}

Dog는 Animal의 기능을 사용할 수 있다.

Dog dog = new Dog();

dog.eat();

 

super 키워드

부모 클래스의 생성자 또는 멤버에 접근할 때 사용한다.

public Dog(String name) {
    super(name);
}

super(name)은 부모 생성자를 호출하는 코드이다.


오버라이딩 (Overriding)

부모 클래스의 메서드를 자식 클래스에서 재정의하는 것을 말한다.

public class Animal {

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

    @Override
    void sound() {
        System.out.println("멍멍");
    }
}

@Override는 해당 메서드가 오버라이딩된 메서드임을 알려주는 어노테이션이다.


추상 클래스 (Abstract Class)

추상 클래스는 미완성 설계도라고 생각하면 이해하기 쉽다.

abstract class Animal {
}

 

특징

  • abstract 키워드 사용
  • 객체 생성 불가
  • 속성, 생성자, 일반 메서드 작성 가능
  • 추상 메서드 작성 가능
abstract class Animal {

    String name;

    public Animal(String name) {
        this.name = name;
    }

    abstract void sound();
}

 

추상 메서드

구현부가 없는 메서드

abstract void sound();

 

자식 클래스에서 반드시 구현

class Dog extends Animal {

    public Dog(String name) {
        super(name);
    }

    @Override
    void sound() {
        System.out.println("멍멍");
    }
}

즉, 추상 클래스는 공통 기능은 제공하면서 특정 기능은 자식이 반드시 구현하도록 강제할 수 있다.


오늘의 한 줄 정리

  • final : 변경 금지
  • static final : 상수
  • 인터페이스 : 규격서
  • 캡슐화 : 데이터를 보호하는 것
  • 상속 : 부모 기능 재사용
  • 오버라이딩 : 부모 기능 재정의
  • 추상 클래스 : 미완성 설계도