Java SE 핵심 개념 요약: 기초부터 객체지향, 컬렉션까지

기본 데이터 형변환과 연산자 규칙

Java에서는 데이터 타입 간의 자동 형변환이 발생할 수 있으며, 이는 작은 범위의 타입이 큰 범위의 타입으로 대입될 때 적용된다. 예를 들어 byte 값이 int 변수에 할당되면 자동으로 승격된다.

byte a = 5;
int b = a; // 자동 승격: int로 변환됨

반대로 큰 타입을 작은 타입에 대입하려면 강제 형변환이 필요하다.

int x = 1000;
byte y = (byte) x; // 강제 캐스팅 (주의: 데이터 손실 가능성 있음)

산술 표현식에서의 타입 승격

  • byte, short, char는 산술 연산 시 항상 int로 승격된다.
  • 혼합 타입 연산에서는 가장 넓은 범위의 타입으로 전체 표현식이 승격된다.
byte c = 10;
long d = 20L;
long result = c + d; // c는 long으로 승격됨

문자열 연결 연산

+ 연산자는 문자열과 함께 사용될 경우 연결(concatenation)으로 동작하며, 피연산자 중 하나라도 문자열이면 나머지도 문자열로 변환된다.

System.out.println(1 + 2 + "안녕"); // 출력: "3안녕"
System.out.println("안녕" + 1 + 2); // 출력: "안녕12"

제어문: switch와 다중 분기

switch 문은 정수 기반 타입(byte, short, int, char), 열거형(enum), 그리고 JDK 7 이상에서는 String도 지원한다.

String day = "월요일";
switch (day) {
    case "토요일":
    case "일요일":
        System.out.println("주말입니다.");
        break;
    default:
        System.out.println("평일입니다.");
}

break 문을 생략하면 'fall-through' 현상이 발생하여 다음 case 블록까지 실행된다. 의도적인 경우가 아니라면 주의가 필요하다.

클래스 구성 요소와 메모리 구조

클래스 내부에는 다음과 같은 다섯 가지 요소를 정의할 수 있다:

  • 멤버 변수 (필드)
  • 생성자
  • 메서드
  • 코드 블록 (정적 및 인스턴스 초기화 블록)
  • 내부 클래스
public class Car {
    private String model;

    static {
        System.out.println("정적 블록: 클래스 로딩 시 한 번만 실행");
    }

    {
        System.out.println("인스턴스 블록: 객체 생성 시마다 실행");
    }

    public Car() {
        // 생성자
    }
}

정적(static) 멤버의 특성

static 키워드로 선언된 필드나 메서드는 인스턴스가 아닌 클래스 자체에 속하며, 모든 인스턴스가 동일한 메모리 위치를 공유한다. 접근은 클래스명.정적멤버 방식이 권장된다.

public class MathUtils {
    public static final double PI = 3.14159;

    public static int add(int a, int b) {
        return a + b;
    }
}
// 사용
int sum = MathUtils.add(5, 3);

싱글톤 디자인 패턴

전역에서 단 하나의 인스턴스만 존재해야 하는 클래스에 사용된다.

  • 이른 초기화(Eager Initialization): 클래스 로딩 시점에 인스턴스 생성
  • 게으른 초기화(Lazy Initialization): 최초 요청 시 인스턴스 생성 (멀티스레드 환경에서는 동기화 고려 필요)
public class Singleton {
    private static final Singleton INSTANCE = new Singleton();

    private Singleton() {}

    public static Singleton getInstance() {
        return INSTANCE;
    }
}

상속과 메서드 오버라이딩

자식 클래스는 부모 클래스의 기능을 확장하거나 재정의할 수 있다. 메서드 오버라이딩 시 @Override 어노테이션 사용은 실수 방지에 도움이 된다.

class Animal {
    public void makeSound() {
        System.out.println("소리를 냅니다.");
    }
}

class Dog extends Animal {
    @Override
    public void makeSound() {
        System.out.println("멍멍!");
    }
}

생성자 호출 순서는 자식 클래스 생성 시 먼저 부모 생성자를 호출하며, 명시하지 않으면 컴파일러가 자동으로 super()를 삽입한다.

접근 제어자

멤버의 접근 범위를 조절하는 키워드로, 네 가지 수준이 있다:

제어자동일 클래스동일 패키지서브클래스전체
private×××
package-private××
protected×
public

추상 클래스와 인터페이스

추상 클래스는 일부 메서드를 미구현 상태로 두며, 서브클래스에서 반드시 구현하도록 강제한다. 인터페이스는 JDK 8 이후로 기본 메서드(default)와 정적 메서드를 포함할 수 있다.

interface Flyable {
    void fly(); // 추상 메서드

    default void land() {
        System.out.println("착륙합니다.");
    }
}

다형성(Polymorphism)

같은 타입 참조 변수가 서로 다른 하위 타입 객체를 가리킬 수 있는 특성이다. 메서드 호출은 실제 객체 타입에 따라 결정되며, 이를 동적 바인딩이라 한다.

Animal myPet = new Dog();
myPet.makeSound(); // 출력: "멍멍!" (Dog 클래스의 구현)

참조 타입 변환 시 instanceof를 사용해 안전성을 확보하는 것이 좋다.

if (myPet instanceof Dog) {
    Dog dog = (Dog) myPet;
    dog.fetch();
}

컬렉션 프레임워크 개요

자바는 다양한 데이터 구조를 제공하는 표준 API를 갖추고 있다.

  • List: 순서 유지, 중복 허용 — ArrayList, LinkedList
  • Set: 순서 없음, 중복 불허 — HashSet, LinkedHashSet
  • Map: 키-값 쌍 저장 — HashMap
List<String> names = new ArrayList<>();
names.add("Alice");
names.add("Bob");

for (String name : names) {
    System.out.println(name);
}

equals와 hashCode의 관계

사용자 정의 클래스에서 equals()를 재정의할 경우, 해시 기반 컬렉션(HashSet, HashMap)과의 호환성을 위해 hashCode()도 함께 재정의해야 한다. 동일한 객체는 반드시 동일한 해시 코드를 반환해야 한다는 계약(contract)이 존재한다.

정밀한 수치 연산: BigDecimal

부동소수점 오차 문제를 해결하기 위해 BigDecimal을 사용한다. 특히 금융 계산 등 정확성이 중요한 곳에서 권장된다.

BigDecimal a = new BigDecimal("0.1");
BigDecimal b = new BigDecimal("0.2");
BigDecimal sum = a.add(b);
System.out.println(sum); // 정확히 0.3 출력

예외 처리 전략

예외는 크게 두 가지로 나뉜다:

  • Checked Exception: 컴파일 시 확인되며 반드시 처리해야 함 (예: IOException)
  • Unchecked Exception: 런타임 예외로, 프로그래밍 오류를 나타냄 (예: NullPointerException)

비즈니스 로직에서 의미 있는 오류 상황을 표현하고 싶다면 사용자 정의 예외를 만드는 것이 효과적이다.

public class InsufficientFundsException extends RuntimeException {
    public InsufficientFundsException(String message) {
        super(message);
    }
}

태그: Java SE 객체지향 상속 다형성 컬렉션

6월 29일 16:37에 게시됨