Java - String

develkkm·2025년 12월 2일

0부터 시작하는 Java

목록 보기
23/27

자바에서 문자열을 다루는 핵심 타입은 String이다. 문자열은 프로그램 전반에서 매우 자주 사용되기 때문에 자바는 이를 위해 특별한 문법적 지원과 내부 최적화를 제공한다. 이 글에서는 String의 구조 생성 방식 비교 불변성 문자열 풀 그리고 가변 문자열인 StringBuilder까지 한 흐름으로 정리한다.


기본 구조

String은 기본형이 아니라 참조형이다.
문자열 리터럴은 사실 String 객체를 생성한다.

String str1 = "hello";
String str2 = new String("hello");

String 클래스는 내부적으로 문자를 배열에 보관하며 다음과 같은 형태로 구성된다.

public final class String {

    private final byte[] value; // Java 9 이후

    public String concat(String str) { ... }
    public int length() { ... }
}

value 필드는 final이며 생성 이후 절대 변경되지 않는다. 이것이 불변성의 출발점이다.


생성 방식

리터럴 방식은 문자열 풀을 사용해 중복 객체 생성을 방지한다.

String s1 = "hello";

new 키워드를 사용하면 매번 새로운 객체가 생성된다.

String s2 = new String("hello");

비교

String 비교에서 가장 중요한 점은 == 과 equals의 차이를 이해하는 것이다.

String a = new String("hello");
String b = new String("hello");

System.out.println(a == b);       // false
System.out.println(a.equals(b));  // true

리터럴은 같은 문자열을 문자열 풀에서 공유하기 때문에 == 결과가 true가 될 수 있다.

String x = "hello";
String y = "hello";

System.out.println(x == y); // true

따라서 문자열 비교는 항상 equals를 사용해야 한다.

String Pool

리터럴을 사용하면 JVM은 문자열 풀에서 동일한 문자열이 이미 존재하는지 먼저 확인한다.
있다면 그 객체를 재사용하고 없으면 새로운 객체를 생성한다.

문자열 풀을 사용하는 이유는 메모리 최적화 뿐 아니라 빠른 비교를 가능하게 하기 때문이다.


불변 객체

String은 내부 상태가 절대 변경되지 않는 불변 객체다.

다음 코드를 보면 concat이 기존 문자열을 수정하지 않는다는 점을 알 수 있다.

String s = "hello";
s.concat(" java");

System.out.println(s);  // hello

값을 변경하려면 새로운 객체를 받아야 한다.

String s1 = "hello";
String s2 = s1.concat(" java");

System.out.println(s1); // hello
System.out.println(s2); // hello java

String이 불변으로 설계된 이유

불변 설계는 여러 이유로 필요하다.

  • 문자열 풀의 안정성 확보
    같은 리터럴을 참조하는 변수들이 있는 상황에서 만약 문자열이 변경 가능하다면 모든 참조하는 값이 동시에 변경되는 문제가 발생한다.

  • 스레드 안전성
    값이 바뀌지 않기 때문에 여러 스레드가 동시에 접근해도 동기화가 필요 없다.

  • 보안성
    URL 경로 SQL 쿼리 네트워크 프로토콜과 같은 정보가 변경 가능하면 보안상 취약해질 수 있다.


StringBuilder

문자열을 여러 번 변경해야 하는 경우 String의 불변성은 성능에 불리하게 작용한다.
문자열을 반복해서 변경하려고 하면 매번 새로운 객체가 생성되기 때문이다.

String result = "";
for (int i = 0; i < 100000; i++) {
    result += "Hello Java ";
}

문자열 변경이 잦을 때는 StringBuilder를 사용해야 한다.

StringBuilder sb = new StringBuilder();
sb.append("A").append("B").append("C").append("D");
String result = sb.toString();

StringBuilder는 내부 배열을 직접 변경하므로 객체 생성이 반복되지 않는다.
반복문에서 문자열을 합치는 작업은 StringBuilder가 압도적으로 빠르다.


메서드 체이닝

StringBuilder는 append insert delete reverse 같은 메서드가 호출될 때 자기 자신을 반환하도록 설계되어 있다.

String result = new StringBuilder()
        .append("A")
        .append("B")
        .append("C")
        .reverse()
        .toString();

메서드의 반환값이 자기 자신이기 때문에 메서드를 연속으로 연결해 호출할 수 있고 코드가 간결해진다.

profile
알던것을 더 확실하게

0개의 댓글