Java의 String(feat. StringBuilder, StringBuffer)

이건희·2025년 3월 11일
post-thumbnail

오늘은 자바의 String에 대해서 알아보자 !

String의 특징

불변 객체(Imumutable)

String 객체는 한번 생성되면 변경할 수 없다. 즉, 문자열을 수정하는 것처럼 보여도 내부적으로는 새로운 객체를 생성한다.

String str1 = "Hello";
str1 = str1 + "World!"; //새로운 String 객체 생성

위 코드에서 str1은 "Hello"에서 "HelloWorld!"로 변경된 것처럼 보이지만, 실제로는 새로운 String 객체가 생성되고 기존 객체는 가비지 컬렉션 대상이 된다.

String Pool

자바에서는 String 객체의 효율적인 메모리 관리를 위해 String Pool을 사용한다. 동일한 문자열이 여러 번 사용될 경우, 새로운 객체를 생성하는 대신 기존 객체를 재사용한다.

String str1 = "hello";
String str2 = "hello";
System.out.println(str1 == str2) //true - 같은 객체를 참조

하지만 new 키워드를 사용할 시 별도의 객체가 생성된다 == Heap 영역에 생성된다.

String str3 = new String("hello");
System.out.println(str1 == str3); //false - 새로운 객체 생성

즉,

  • String literal로 생성하면(String str1 = "Hello") Heap 영역 내 String Pool에 저장되어 재사용 된다.

  • new 연산자로 생성 시, Heap 영역 내에 생성되어 재사용되지 않는다.


왜 String은 객체로 다룰까?

int, double 등과 같은 기본 타입(primitive type)은 Wrapper 클래스(Integer, Double)가 있긴 하지만 그래도 객체가 아니다. 하지만 왜 String은 리터럴 취급 되면서 객체일까?

메모리 효율성

  • 문자열은 프로그램 전반에서 많이 쓰이고, 같은 문자열이 반복되는 경우가 많다.

  • 자바는 위에서 설명했듯이 String Pool을 사용해 동일한 문자열을 공유해서 메모리 낭비를 방지한다.

내장 메서드를 활용한 편의성

  • 문자열 조작을 쉽게 하기 위해 String을 객체로 만들고, .length(), .substring() 같은 메서드를 제공한다.

왜 기본 타입과 Wrapper 클래스로 나누지 않는가

아니 근데 그러면, String도 기본 타입과 Wrapper 클래스 둘다 만들면 되지 않나?란 의문이 들 수도 있다.

문자열은 크기가 동적이다.

  • 기본 타입(int, double)들은 크기가 고정되어 있어 메모리에 바로 값을 저장할 수 있다.

  • 하지만 문자열은 길이가 너무 가변적이라, 기본 타입처럼 만들려면 고정된 크기를 할당해야 하는데, 이는 비효율적이다.

int a = 100; // 4바이트 메모리 사용
String s = "Hello, World!"; // 길이가 가변적이라 메모리 크기 고정 불가능

String은 불변해야 하는데, 기본 타입은 불변이 아님

  • 기본 타입은 값을 변경할 수 있지만, String은 불변이라 새로운 객체를 만들어야 한다.
int x = 10;
x = 20; // x의 값이 변경됨

String str = "Hello";
str = "World"; // 새로운 String 객체가 생성됨 (변경 아님)

왜 String은 불변이어야 할까?

  1. 보안
  • String이 불변이 아니라면, 해커가 정보를 조작할 수 있다.
  • 예시로, 데이터베이스 연결 URL이 문자열로 저장되는데, 불변이 아니면 악의적인 변경이 가능하다.
String url = "jdbc:mysql://secure-db";
connectToDB(url);
url.replace("secure-db", "hacked-db");  // 만약 가능하다면 보안 취약점 발생!
  1. 멀티스레드 환경에서 안전
  • String이 불변이면 여러 스레드에서 동시에 사용해도 안전
  • 만약 변경 가능했다면, 동기화를 추가해야 하고, 이는 성능 저하를 일으킨다.
  1. 해시코드 캐싱(빠른 성능)
  • String은 한번 생성되면 해시코드를 캐싱할 수 있다.
  • 변경 가능하면 매번 해시코드를 다시 계산해야 해서 성능이 저하된다.
String key = "username";
int hash = key.hashCode();  // 해시 값 한 번 계산 후 캐싱됨

String vs StringBuilder vs StringBuffer

String은 불변 객체이므로 문자열 변경이 많을 경우 StringBuilder나 StringBuffer를 사용하는 것이 성능 면에서 유리하다.

클래스불변성스레드 안정성성능
String불변O느림(새 객체 생성)
StringBuilder불변X빠름
StringBuffer불변O비교적 느림

StringBuilder vs StringBuffer

  • StringBuilder는 싱글 스레드 환경에서 사용하기 적합하며, StringBuffer보다 성능이 뛰어남.

  • StringBuffer는 멀티 스레드 환경에서 사용하기 적합하며, 동기화(synchronized)가 적용되어 안전하게 사용 가능함.

// StringBuilder 예제 (싱글 스레드 환경에서 성능 우수)
StringBuilder sb = new StringBuilder("Hello");
sb.append(" World");
System.out.println(sb.toString());  // "Hello World"

// StringBuffer 예제 (멀티 스레드 환경에서 안전)
StringBuffer sbf = new StringBuffer("Hello");
sbf.append(" World");
System.out.println(sbf.toString());  // "Hello World"
profile
백엔드 개발자가 되겠어요

0개의 댓글