Java ‐ 자바 특징과 객체 지향 원칙 - dnwls16071/Backend_Summary GitHub Wiki

📚 자바 개요

자바의 특징

  • 객체지향 프로그래밍 언어 - 클래스와 객체를 기반으로 설계되며, 추상화, 캡슐화, 상속, 다형성을 지원
  • 플랫폼 독립성 - 자바 코드는 JVM 위에서 실행되므로 OS에 관계없이 동작
  • 자동 메모리 관리 - 개발자 메모리를 해제하지 않아도 JVM이 자동으로 불필요한 객체를 분리
  • 멀티쓰레딩 지원 - 여러 작업을 동시에 실행할 수 있는 멀티 쓰레딩 기능을 제공

📚 SOLID 원칙⭐

SRP

// ❌ SRP 위반 예시
public class Computer {
    public void startComputer() {
        System.out.println("부팅 중...");
        checkHardware();
        loadOS();
        connectNetwork();
        System.out.println("컴퓨터 준비 완료");
    }
  
    private void checkHardware() {
        System.out.println("하드웨어 점검");
    }
 
    private void loadOS() {
        System.out.println("운영체제 로딩");
    }
 
    private void connectNetwork() {
        System.out.println("네트워크 연결");
    }
}
// ✅ SRP 준수한 리팩토링
class Hardware {
    void check() {
        System.out.println("하드웨어 점검");
    }
}

class OS {
    void load() {
        System.out.println("운영체제 로딩");
    }
}

class Network {
    void connect() {
        System.out.println("네트워크 연결");
    }
}

public class Computer {
    private final Hardware hardware = new Hardware();
    private final OS os = new OS();
    private final Network network = new Network();
 
    public void startComputer() {
        hardware.check();
        os.load();
        network.connect();
    }
}

OCP

// ❌ OCP 위반 예시
public class NotificationService {
    public void send(String type, String message) {
        if (type.equals("email")) {
            System.out.println("이메일 전송: " + message);
        } else if (type.equals("sms")) {
            System.out.println("문자 전송: " + message);
        }
    }
}
// ✅ OCP 준수 리팩토링
interface Notifier {
    void send(String message);
}

class EmailNotifier implements Notifier {
    public void send(String message) {
        System.out.println("이메일 전송: " + message);
    }
}

class SmsNotifier implements Notifier {
    public void send(String message) {
        System.out.println("문자 전송: " + message);
    }
}

public class NotificationService {
    public void notify(Notifier notifier, String message) {
        notifier.send(message);
    }
} 

ISP

// ❌ ISP 위반 예시
interface Machine {
    void print();
    void scan();
    void fax();
}

class BasicPrinter implements Machine {
    public void print() {
       System.out.println("문서 출력");
    }
 
    public void scan() {
        throw new UnsupportedOperationException("스캔 지원 안함");
    }
 
    public void fax() {
        throw new UnsupportedOperationException("팩스 지원 안함");
    }
}
// ✅ ISP 준수 리팩토링
interface Printable {
    void print();
}

interface Scannable {
    void scan();
}

interface Faxable {
    void fax();
}

class BasicPrinter implements Printable {
    public void print() {
        System.out.println("문서 출력");
    }
}

class MultiFunctionPrinter implements Printable, Scannable, Faxable {
    public void print() {
        System.out.println("문서 출력");
    }
 
    public void scan() {
        System.out.println("문서 스캔");
    }

    public void fax() {
        System.out.println("문서 팩스 전송");
    }
}

DIP

// ❌ DIP 위반 예시
class MySQLDatabase {
    public void connect() {
        System.out.println("MySQL에 연결");
    }
}

class DataService {
    private final MySQLDatabase database;
    
    public DataService() {
        this.database = new MySQLDatabase();
    }
 
    public void use() {
        database.connect();
    }
}
// ✅ DIP 준수 리팩토링
interface Database {
    void connect();
}

class MySQLDatabase implements Database {
    public void connect() {
        System.out.println("MySQL에 연결");
    }
}

class DataService {
    private final Database database;
    
    public DataService(Database database) {
        this.database = database;
    }

    public void use() {
        database.connect();
    }
}

LSP

// ❌ LSP 위반 예시
class Bird {
    public void fly() {
        System.out.println("날아갑니다");
    }
}

class Ostrich extends Bird {
    @Override
    public void fly() {
        throw new UnsupportedOperationException("타조는 날 수 없습니다");
    }
}

public class Zoo {
    public void makeBirdFly(Bird bird) {
        bird.fly();
    }
}

Zoo zoo = new Zoo();
zoo.makeBirdFly(new Ostrich()); // 예외 발생 → LSP 위반
// ✅ LSP 준수 리팩토링
interface Flyable {
    void fly();
}

class FlyingBird implements Flyable {
    public void fly() {
       System.out.println("날아갑니다");
    }
}

// 이제 날 수 있는 새만 fly()를 갖고, 타조는 분리됨
class Ostrich {
    public void run() {
        System.out.println("달립니다");
    }
}

public class Zoo {
    public void makeFly(Flyable bird) {
        bird.fly();
    }
}

📚 JVM 자바 가상머신 구조⭐⭐

  • JVM(Java Virtual Machine)은 자바 프로그램을 실행하기 위한 가상 머신
  • 자바 코드를 바이트 코드로 변환하여 실행하는 역할을 담당
  • 운영체제와 독립적으로 동작하며 어떤 OS에서도 실행 가능

JVM의 구성 요소

  • 클래스 로더(Class Loader)
  • 런타임 데이터 영역(Runtime Data Area)
  • 실행 엔진(Execution Engine)
  • 가비지 컬렉터(Garbage Collector)

1. 클래스 로더(Class Loader)

  • 바이트 코드를 메모리에 로드하는 역할
  • 클래스 로딩 과정 : 로딩(Loading) - 링크(Linking) - 초기화(Initialization)
    • 로딩 : 바이트 코드를 메모리에 적재하는 단계
    • 링크 : 로드된 클래스의 코드와 데이터를 사용할 수 있도록 준비
    • 초기화 : static 변수에 초기값을 할당하고 static 블록을 실행

2. 런타임 데이터 영역(Runtime Data Area)

  • JVM이 실행 중 사용하는 메모리 영역
    • Heap : 객체와 인스턴스 변수 저장
    • Stack : 메서드 호출 시 생성되는 메서드 정보와 지역변수 저장
    • Method Area : 클래스 정보, static 변수 저장
    • PC Register : 현재 실행 중인 명령어 주소 저장
    • Native Method Stack : 네이티브 코드 실행 시 사용

3. 실행 엔진(Execution Engine)

  • JVM이 바이트 코드를 실제 실행하는 역할
    • 인터프리터(Interpreter)
      • 바이트 코드를 한줄씩 읽고 실행
      • 컴파일 과정이 없어 빠르게 시작 가능
      • 같은 코드가 반복 실행될 때마다 다시 해석
    • JIT 컴파일러
      • 반복 실행되는 바이트 코드를 기계어로 변환해 캐싱
      • 인터프리터보다 빠르게 실행
      • 한 번 변환된 코드는 다시 변환되지 않고 바로 실행 가능
      • 처음 실행 시 컴파일 시간이 추가됨

4. 가비지 컬렉터(Garbage Collector)

  • 가비지 컬렉터(Garbage Collector)
    • 사용하지 않는 객체를 자동으로 메모리에서 정리
    • 메모리 누수를 방지하고 효율적인 메모리 관리를 수행

📚 GC 동작 원리⭐⭐

가비지 컬렉션(Garbage Collection, GC)

  • JVM이 필요하지 않은 객체를 자동으로 제거해 메모리를 관리하는 기능
  • 프로그래머가 직접 메모리 해제를 하지 않아도 되도록 함
  • 힙 영역(객체와 인스턴스 변수가 저장되는 곳)에 존재하는 객체 중 더 이상 참조되지 않은 객체를 정리 대상으로 판단한다.
    • 힙 영역에 있는 객체들은 언제까지 필요한지 예측하기 어렵다.
    • 반면, 스택에 있는 변수들은 메서드가 끝나면 자동으로 사라짐(수명 명확)
  • GC 대상 판단 기준
    • GC는 루트 객체에서 시작해 참조 가능한 객체를 따라가며 도달할 수 없는 객체만을 가비지로 간주하고 제거함
    • 루트 객체의 예시
      • 쓰레드 스택에 있는 지역 변수(메서드 내부 변수, 매개변수)
      • Method Area에 있는 static 변수
      • Native Method Stack에서 참조 중인 객체
  • GC 동작 구분
    • Stop-the-World
      • GC가 실행될 때 JVM이 애플리케이션 실행을 멈추는 현상
      • 모든 실행 중인 쓰레드가 중단되며 GC가 완료될 때까지 기다려야 한다.
    • Mark and Sweep
      • GC의 기본 동작 방식으로 객체를 식별하고 제거하는 과정
  • GC의 기본 동작 과정
    • 객체 생성
      • new 키워드로 객체를 생성하면 Heap 영역에 저장된다.
      • 생성된 객체는 Young Generation의 Eden 영역에 위치
    • 객체 생존 여부 확인
      • GC가 실행되면 사용 중 객체와 사용되지 않는 객체를 구분한다.
      • 참조가 없는 객체는 GC 대상으로 표시된다.
    • 사용되지 않는 객체 제거
      • 참조되지 않는 객체를 Heap에서 제거해 메모리 회수
    • 제거 후 남은 객체를 압축해 메모리 단편화를 방지

📚 가비지 컬렉터(Garbage Collector)⭐⭐

힙 메모리 내 객체 생존 흐름

  • Eden 영역에서 객체를 생성
    • 자바 애플리케이션에서 new 키워드로 객체를 생성하면 대부분 Eden 영역에 생성된다.
    • Eden은 Young Generation에 속한다.
  • Minor GC 발생
    • Eden 영역이 꽉 차면 Minor GC가 발생
    • GC는 살아있는 객체만을 Survivor 영역으로 복사
    • 살아남은 객체들은 Survivor 영역으로 이동하고 죽은 객체는 제거됨
  • Survivor ↔ Survivor
    • 이후 다시 Eden 영역에 객체가 쌓이고 또 Minor GC가 발생하면 Survivor 영역에 있던 객체들은 다른 Survivor 영역으로 복사됨
    • 복사될 때마다 age값(생존 횟수)이 증가한다.
  • Old Generation으로 Promotion
    • age값이 특정 임계값 이상이 되면 더 이상 Young Generation에 두지 않고 Old Generation으로 승격된다.
    • Old 영역은 Major GC의 대상이 된다.

❗Minor GC란?

  • Young Generation에서 발생하는 GC
  • 빠르게 실행되며 Stop-the-World 시간이 짧다.

❗Major GC란?

  • Old Generation에서 발생하는 GC
  • 힙 영역 전체를 검사하기 때문에 실행 시간이 길고 Stop-the-World 시간이 길어 성능에 영향을 준다.
  • GC 튜닝을 통해 Major GC 발생을 최소화하는 것이 중요하다.

📚 GC 알고리즘

  • GC는 상황에 따라 다른 알고리즘을 사용한다.
  • 주요 GC 알고리즘 정리
GC 종류 특징 STW 시간 사용 예시
Serial GC 단일 쓰레드, 간단 길다 작은 애플리케이션
Parallel GC 멀티 쓰레드, Throughput 높다 중간 서버, 배치 등
CMS 사용자 지연 최소화 낮다 GUI 앱
G1 GC 병렬 + 병합, Region 단위 관리 중간~낮다 대부분의 앱
ZGC / Shenandoah 초저지연 GC 매우 짧다 대규모 시스템