[Java] 캡슐화의 본질, static의 메모리 구조, 그리고 효율적인 문자열 처리

icehugger·2026년 4월 10일

training

목록 보기
3/8

1. 캡슐화(Encapsulation)와 정보 은닉: 단순한 접근 제어 그 이상

보통 필드는 private, 메서드는 public으로 설정한다고 배운다. 하지만 단순히 데이터 접근을 막는 것이 캡슐화의 최종 목적은 아니다.

객체의 무결성 보장: Setter 메서드 내부에 유효성 검사(Validation) 로직을 추가하여, 객체가 스스로의 상태를 올바르게 유지하도록 만든다.

접근 제어자의 최소화 원칙: 클래스 설계 시 접근 범위는 가능한 한 가장 좁게 유지해야 한다. 외부 패키지에서 사용할 필요가 없는 클래스는 public이 아닌 package-private(default)으로 선언하여 시스템의 결합도를 낮추고 유지보수성을 높여야 한다.

2. 객체의 동일성(Identity)과 동등성(Equality)

== 연산자: 두 객체의 참조값(메모리 주소)이 같은지, 즉 물리적으로 완벽히 동일한 객체인지를 비교한다.

equals() 메서드: Object 클래스의 기본 equals()는 내부적으로 this == o를 통해 물리적 동일성을 먼저 검사한다. 이는 메모리 주소가 같다면 불필요한 필드 비교 연산을 건너뛰어 성능을 최적화하기 위함이다. 논리적인 값(동등성)을 비교하려면 해당 객체에서 equals()를 오버라이딩해야 한다.

3. 정적(static) 멤버의 올바른 이해와 사용

static 키워드가 붙은 멤버는 인스턴스(Heap 메모리)가 아닌 클래스 영역(Method Area)에 저장되며, 프로그램 실행 시 단 한 번만 메모리에 할당된다.

메모리 공유: 모든 인스턴스가 단 하나의 static 멤버를 공유하므로, 객체마다 달라져야 하는 상태를 보관해서는 안 된다.

접근 권장 방식: instance.staticField 형태로도 접근은 가능하지만, 이는 해당 멤버가 인스턴스에 종속된 것처럼 오해를 유발한다. 따라서 반드시 ClassName.staticField 형태로 접근하여 정적 멤버임을 코드 상에서 명확히 드러내야 한다.

4. String의 불변성(Immutability)과 StringBuilder의 메모리 효율

String 객체는 한 번 생성되면 내부의 값을 변경할 수 없다. trim(), replace(), substring() 등의 메서드는 원본을 수정하는 것이 아니라, 조작된 새로운 String 객체를 반환한다.

문제점: 반복문 안에서 String에 += 연산으로 문자열을 계속 추가하면, 매번 새로운 객체가 Heap 메모리에 생성되고 버려지며 심각한 메모리 낭비(GC 부하)를 초래한다.

해결책 (StringBuilder): 문자열의 잦은 변경이 필요할 때는 내부 버퍼를 수정하는 방식인 StringBuilder를 사용한다. 모든 조작(append, insert, delete 등)이 끝난 후 마지막에 단 한 번만 toString()을 호출하여 불변 객체인 String으로 변환해 반환하는 것이 성능 최적화의 핵심이다.

5. 데이터 타입 상호 변환 (String ↔ byte/char 배열)

문자 단위 조작: toCharArray()와 new String(char[])를 사용하여 단일 문자에 대한 알고리즘 처리를 수행한다.

바이트 단위 조작과 인코딩: getBytes()를 통해 문자열을 이진 데이터로 변환할 때, 시스템의 기본 인코딩에 의존하면 OS나 국가 환경(예: 한국/일본)에 따라 한글이나 한자 등 다국어가 깨지는 심각한 버그가 발생할 수 있다. 따라서 실무에서는 항상 getBytes(StandardCharsets.UTF_8) 처럼 명시적인 인코딩을 지정해야 한다.

0개의 댓글