본문 바로가기
Language/Java

[Java] String, StringBuffer, StringBuilder 클래스

by 계범 2022. 3. 7.

String클래스

String클래스란

String클래스는 문자열을 저장하고 이를 다루는 필요한 메서드를 함께 제공한다.

 

변경 불가능한(immutable) 클래스

String클래스에는 인스턴스 생성 시 생성자의 매개변수로 입력받는 문자열은 이 인스턴스변수(char[] value)에 문자형 배열로 저장된다.

 

한번 생성된 String인스턴스가 갖고 있는 문자열은 읽어 올 수만 있고, 변경할 수는 없다.

 

'+'연산자를 이용해 문자열을 결합하는 경우 새로운 String인스턴스가 생성되는 것이다.

즉, 결합할때마다 메모리공간을 차지하게 되므로 이러한 작업을 할때는 StringBuffer클래스를 이용하는 것이 좋다.

 

문자열의 비교

문자열을 만들 때는 두가지 방법이 있다.

  1. 문자열 리터럴을 지정하는 방법
  2. String생성자를 사용하는 방법
String str1 = "abc"; //문자열 리터럴 "abc"의 주소가 str1에 저장됨
String str2 = "abc"; //문자열 리터럴 "abc"의 주소가 str2에 저장됨
String str3 = new String("abc"); // 새로운 String인스턴스 생성
String str4 = new String("abc"); // 새로운 String인스턴스 생성

str1과 str2는 같은 주소의 문자열 리터럴을 참조한다.("abc" 리터럴 주소를 둘다 참조)

str3과 str4는 서로 다른 주소를 가진 String인스턴스이다.

문자열 리터럴은 클래스가 메모리에 로드될 때 자동적으로 미리 생성

만약 두 인스턴스의 내용을 비교할때 '=='을 쓴다면 주소값을 비교하므로 false가 나오기에,

이럴때 안의 내용을 비교해주는 equals() 메서드가 존재한다.(String클래스에 equals는 내부 내용을 비교하게 오버라이딩 되어있음)

str3 == str4 // 결과 false
str3.equals(str4) // 결과 true

 

문자열 리터럴

자바 소스파일에 포함된 모든 문자열 리터럴은 컴파일 시에 클래스 파일에 저장된다.

이 때 같은 내용의 문자열 리터럴은 한번만 저장된다. 문자열 리터럴도 String인스턴스이고, 한번 생성하면 내용을 변경할 수 없으니 하나의 인스턴스를 공유하면 되기 때문이다.

 

클래스 파일에는 소스파일에 포함된 모든 리터럴의 목록이 있다. 해당 클래스 파일이 클래스로더에 의해 메모리에 올라갈 때, 이 리터럴의 목록에 있는 리터럴들이 JVM내에 있는 '상수 저장소(constant pool)'에 저장된다. 이 때, 이곳에 'AAA'와 같은 문자열 리터럴이 자동적으로 생성되어 저장되는 것이다.

 

빈 문자열(empty string)

String s = "";

참조변수 s가 참조하고 있는 String 인스턴스는 내부에 'new char[0]'과 같이 길이가 0인 char형 배열을 저장하고 있는 것이다.

 

String클래스의 생성자와 메서드

추후 적겠음!

 

join()과 StringJoiner

join()은 여러 문자열 사이에 구분자를 넣어서 결합한다.

java.util.StringJoiner클래스를 사용해서도 문자열을 결합할 수 있다.

 

// join()
String[] animals = {"dog","cat","bear"};
String str = String.join("-",animals); // 배열의 문자열을 '-'를 구분해서 결합
System.out.println(str); // dog-cat-bear 출력됨

// StringJoiner
StringJoiner sj = new StringJoiner(",","[","]"); // (구분문자,시작,끝)
String[] strArr = { "aaa","bbb","ccc" };

for(String s: strArr){
	sj.add(s);
}

System.out.println(sj.toString()); // [aaa,bbb,ccc] 출력됨

 

문자 인코딩 변환

getByte(String charsetName)를 사용하면, 문자열의 문자 인코딩을 다른 인코딩으로 변경 가능하다.

자바는 UTF-16을 사용하지만, 문자열 리터럴에 포함되는 문자들은 OS의 인코딩을 사용한다.

한글 윈도우즈의 경우 문자 인코딩으로 CP949를 사용하며, UTF-8로 변경하려면, 아래와 같다.

byte[] utf8_str = "가".getByte("UTF-8"); // 문자열을 UTF-8로 변환
String str = new String(utf8_str,"UTF-8"); // byte배열을 문자열로 변환

서로 다른 문자 인코딩을 사용하는 컴퓨터 간에 데이터를 주고 받을 때 필요하다.

 

 

StringBuffer클래스와 StringBuilder클래스

StringBuffer

StringBuffer클래스는 String과 달리 내부적으로 문자열 편집을 위한 버퍼(buffer)를 가지고 있으며,

StringBuffer인스턴스를 생성할 때 그 크기를 지정할 수 있다.

 

StringBuffer클래스는 String클래스와 동일한점은 인스턴스가 생성될 때, char형 배열이 생성되며 이 때 생성된 char형 배열을 인스턴스변수 value가 참조하게 된다.

 

StringBuffer생성자

StringBuffer는 인스턴스를 생성할 때, 내부 버퍼의 길이를 지정해줄 수 있으며, 지정해주지 않으면 16개의 문자를 저장할 수 있는 크기의 버퍼가 생긴다.

 

public StringBuffer(int length){
	value = new char[length];
    	shared = false;
}

public StringBuffer(){
	this(16); //버퍼의 크기
}

public StringBuffer(String str){
	this(str.length() + 16); // 지정한 문자열의 길이보다 16이 더 크게 버퍼 생성
    	append(str);
}

 

StringBuffer인스턴스로 문자열을 다루다가 버퍼의 크기가 문자열의 길이보다 작아지면,

내부적으로 버퍼의 크기를 증가시키는 작업이 수행된다.

배열의 길이는 변경될 수 없으므로 새로운 길이의 배열을 생성한 후 이전 배열의 값을 복사해야 한다.

 

//새로운 길이(newCapacity)의 배열을 생성한다. newCapacity는 정수값이다.
char newValue[] = new char[newCapacity];

// 배열 value의 내용을 배열 newValue로 복사한다.
System.arraycopy(value, 0, newValue, 0, count); // count는 문자열의 길이
value = newValue; // 새로 생성된 배열의 주소를 참조변수 value에 저장

 

StringBuffer의 변경

StringBuffer는 내용을 변경할 수 있다.

 

StringBuffer sb = new StringBuffer("abc");

// sb엔 "abc"가 들어 있음.

sb.append("123");

// 버퍼안의 "abc" 뒤에 "123"을 추가함. "abc123"

// append의 반환타입은 자신의 주소를 반환
// 아래와 같이 쓰는 게 가능해짐.(void였으면 불가능)

sb.append("abc");
sb.append("123");
// 위와 아래 동일
sb.append("abc").append("123");

 

StringBuffer의 비교

String의 equals는 오버라이딩해서 문자열의 내용을 비교하도록 구현되어 있지만, StringBuffer클래스의 equals는 Object의 equals와 동일하다.

 

그렇기 때문에 내부의 내용을 비교하고 싶으면 toString()(오버라이딩 되어있음)을 통해 내부에 담고 있는 문자열을 String으로 반환하여 비교해준다.

 

StringBuffer sb = new StringBuffer("abc");
StringBuffer sb2 = new StringBuffer("abc");

sb.equals(sb2); // false

String s = sb.toString();
String s2 = sb2.toString();

s.equals(s2); // true

 

StringBuffer클래스의 생성자와 메서드

추후 내용 정리 하겠음!

 

StringBuilder

StringBuffer는 멀티쓰레드에 안전(thread safe)하도록 동기화되어 있다.

멀티쓰레드로 작성된 프로그램이 아닌 경우, StringBuffer의 동기화는 성능만 떨구기 때문에 동기화만 뺀 StringBuilder가 등장했다.

 

StringBuffer에서 동기화만 뺀 것이 StringBuilder이다.

 

그래서 연산만 놓고보면 많은 연산이 이뤄진다는 가정하에

StringBuilder > StringBuffer >> String 순으로 성능이 좋다.

 

String, StringBuffer, StringBuilder 차이요약

클래스 저장공간 변경여부 멀티 쓰레드
String Heap or Constant Pool(상수풀) 불가능(immutable) YES
StringBuffer Heap 가능(mutable) YES
StringBuilder Heap 가능(mutable) NO

 

참조

'Java의 정석' 책

교글참조

댓글