LALA's blog

[우테코 강의] 문자와 문자열 ( String , StringBuilder , StringBuffer ) 본문

개발/우아한테크코스

[우테코 강의] 문자와 문자열 ( String , StringBuilder , StringBuffer )

lala.seeun 2022. 2. 25. 17:29

문자와 문자열

- 문자열(String) 은 자바 프로그램이 실행되는 동안 가장 많이 생성되는 객체이다.

- 문자열은 객체이지만 각각의 문자의 나열로 구성된다.

char capitalA = 'A';  // 문자
Strinng a = "abc";   // 문자열 == 문자의 배열
String b = new String("abc"); // 이와 같이 구현하지 마라.

String 은 가장 많이 사용되는 객체이기 때문에 가장 최적화가 잘 되어있다.

 

🤔. 문자열들은 어디서 관리되는 될까?

- Constant Pool 에 저장이 돼서 계속 재사용할 수 있게 된다.

- 아래의 두 문자열은 같은 객체를 바라보고 있다.

String string1 = "string";
String string2 = "string";

String 도 객체인데 왜 new 키워드를 사용해서 생성하지 않을까?

- new 키워드를 사용하면 무조건 새로운 객체를 반환되어야 한다.

- 재사용하며 사용하기 때문에 new 키워드를 붙혀서 사용하지 않는다. (사용하지 않도록 한다.)

 

위의 두 객체는 같은 객체로 봐야한다. == 동일하다고 봐야한다.

동등성 vs 동일성 ????

 

동등성

assertThat(string1.equals(string2)).isTrue();
assertThat(sstring1).isEqualTo(string2);

동일성

assertThat(string1 == string2).isTrue();
assertThat(string1).isSameAs(string2);

 

🤔. 그럼 string1 과 string2 는 equals() 가 아닌 == 로 비교해야 하는 것이 아닌가?

- 항상 동일하다는 보장을 해주진 못한다.

String string1 = "string";
String string2 = new String("string");

- 이처럼 어떤 곳에서는 new 를 통해 새로운 객체로 생성됐을 수도 있다.

- JDK 버전에 따라서 String Pool 의 관리 방식이 다를 수 있기 때문이다.

- 8 버전 이상부터는 GC 대상이 되기 때문에 다른 객체가 될 수도 있다. (아직 몰라도 됨)

 

String Pool 이 존재하고, 이를  통해 자바가 알아서 최적화시켜주고 있다 !!

String k = new String("k");
String a = "a";
String b = "b";
String c = "c";

여기서 객체는 몇개 생성될까 ?

- 1 개 ^___^

byte code 보는 법 ⬇️

더보기

프로젝트 > build > classes > java > 해당 파일 경로

Navigate 메뉴 > Search EveryWhere > show byte 검색

 

- byte code 를 살펴보면 String k 는 NEW 로 생성되었다.

- 나머지 a, b, c 는 생성되지 않았다.

- 컴파일 시점에 고정되어 있는 값들은 최적화를 하기 때문에 런타임에 생성되는 것이 아닌 경우 대부분 컴파일 타임에 만들어 지니다고 보면 된다.

 

이 경우엔?

String a = "a";
String b = "b";
String c = "c";

System.out.println(a + b + c);

decomplied file

자바 11 에서는 StringConcatFactory.makeConcatWithConstants 를 통해 최적화를 해서 abc 를 합친 문자열로 만들어버린다.

byte code

🤔. a + b + c 를 최적화를 해준 이유가 뭘까?

최적화 하지 않으면 + 할 때 마다 새로운 객체를 만들기 때문이다.

a + b = "ab" 가 만들어지고

ab + c = "abc" 가 만들어지게 된다.

 

 

최적화 하는 다른 방법

- final 키워드를 붙혀준다.

final String a = "a";
final String b = "b";
final String c = "c";

System.out.println(a + b + c);

final 키워드를 붙혀주니 "abc" 객체가 생성된 것을 볼 수 있다.

컴파일 타임에 변하지 않는 값이라는 것을 이미 알기 때문에 최적화하기 굉장히 좋은 경우이므로 최적화가 가능하다.

decompiled file
byte code

String 을 1000 번 + 해준다면 어떻게 될까?

String text = "";

for (int i = 0; i < 1000; i++) {
    text += i;
}

- + 연산을 할 때마다 객체를 생성할 것 같다.

- 자바 8 에서는 StringBuilder 로 최적화해주는 것을 볼 수 있다.

오 JVM 완전 똑똑한데?

우리의 예상

StringBuilder text = new StringBuilder();

for (int i = 0; i < 1000; i++) {
    text.append(i);
}

이렇게 해주겠지?

 

현실..

String  text = "";

for (int i = 0; i < 1000; i++) {
    text = new StringBuilder(text).append(i).toString();
}

매번 StringBuilder 가 생성되는 것이다. 생각보다 멍청하군?

따라서 가변적인 문자열인 경우 우리가 직접 StringBuilder 를 적용하는 것이 좋다.

 

 

StringBuilder vs StringBuffer

- StringBuffer 는 thread-safe 하다는 특징이 있다.

- StringBuffer 는 자바 1 버전에 생겼다

- StringBuilder 는 1.5 버전에 생겼다. (최신 기술)

 

🤔. thread-safe 한 경우에 StringBuffer 를 쓰면 될까?

- String 을 멀티 쓰레딩 환경에서 쓸 일이 있을까? 생각해보고 써라.

- 메소드들에 synchronized 덕지덕지 붙어있어서 성능만 떨어짐. ㅎ

- 아무도 사용하지 않아서,, 생긴게 StringBuilder 이다. (만든사람도 후회중 ㅋ)

 

 

immutable

- immutable(불변)이란 객체를 생성한 후 상태를 변경할 수 없는 것을 의미한다.

- String 객체는 문자열의 상태 값을 변경할 수 없기 때문에 immutable 객체라 한다.

 

 

🤔. 아니 그래서.. String vs StringBuilder vs StringBuffer vs StringConcatFactory 뭐가 제일 좋나 ?

 

- StringBuilder 가 제일 빠르기 때문에 좋긴 하다.

- StringBuilder 는 byte[] 로 저장하고 있기 때문에 배열의 size 도 정할 수 있고, 중간 중간에 size 도 늘려주는 기능도 있다.

- StringConcatFactory 는 내부 로직이 복잡하고, 추가적인 객체들도 많은 단점이 있다.