String 클래스란 ?
자바에서 문자를 다루는 대표적인 타입은 char, String 2가지가 있습니다.
기본형인 char는 문자 하나를 다룰 때 사용하며, char를 사용해 여러 문자를 나열하려면 char[]를 사용해야합니다. 하지만 이렇게 char[]을 직접 다루는 방법은 매우 불편하기 때문에 문자열을 매우 편리하게 다룰 수 있는 String 클래스를 제공합니다.
String은 클래스입니다. int, boolean 같은 기본형이 아니라 참조형입니다. 따라서 String str = new String("hello")로 인스턴스를 생성할 수 있습니다.
하지만 자바에서 String 인스턴스 생성 시 String str = "hello" new 키워드를 사용하지 않고 생성할 수 있도록 설정되어있다 !
new 키워드를 사용하지 않았다고 해서 객체가 아니라는 말은 아닙니다 ! 다른 객체와 동일하게 String은 Heap 영역에 저장되어 관리됩니다.
String 클래스의 구조
String 클래스는 다음과 같은 구조를 가지고 있습니다.
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence,
Constable, ConstantDesc {
@Stable
private final byte[] value;
... 여러 메서드
}
위 value 필드에는 String의 실제 문자열 값이 보관되고 문자 데이터 자체는 byte[]에 보관됩니다.
String 클래스는 개발자가 직접 다루기 불편한 byte[]를 내부에 감추고 String 클래스를 사용하는 개발자가 편리하게 문자열을 다룰 수 있도록 다양한 기능을 제공합니다. 그리고 메서드 제공을 넘어 자바 언어 차원에서도 여러 편의 문법을 제공합니다.
자바 9버전 부터 String 클래스에서 char[] 대신에 byte[]를 사용합니다.
자바에서 문자 하나를 표현하는 char는 2byte를 차지합니다. 그런데 영어, 숫자는 보통 1byte로 표현이 가능하여 단순 영어, 숫자로만 표현된 경우 1byte를 사용하고, 그렇지 않은 경우 2byte인 UTF-16 인코딩을 사용하여 메모리를 더 효율적으로 사용할 수 있게 변경되었습니다.
String 클래스는 불변 객체
String은 불편 객체입니다. 따라서 생성 이후에 절대로 내부의 문자열 값을 변경할 수 없습니다.
변경이 필요한 경우 기존 값을 변경하지 않고, 새로운 결과를 만들어 반환합니다. 예제를 통해 확인해보겠습니다.
public static void main(String[] args) {
String str1 = "hello";
String str2 = str1.concat(" java");
System.out.println("str1 = " + str1);
System.out.println("str2 = " + str2);
}
/////////////
// 실행 결과
str = hello
/////////////
public static void main(String[] args) {
String str1 = "hello";
String str2 = str1.concat(" java");
System.out.println("str1 = " + str1);
System.out.println("str2 = " + str2);
}
/////////////
// 실행 결과
str1 = hello
str2 = hello java
String.concat()은 내부에서 새로운 String 객체를 만들어서 반환합니다. 따라서 불변과 기존 객체의 값을 유지합니다.
String이 불변으로 설계된 이유는 문자열 풀에 있는 String 인스턴스의 값이 중간에 변경되면 같은 문자열을 참고하고 있는 다른 변수의 값도 함께 변경되기 때문입니다.
String은 자바 내부에서 문자열 풀을 통해 최적화를 합니다. 만약 String 내부의 값을 변경할 수 있다면, 기존 문자열 풀에서 같은 문자를 참조하는 변수의 모든 문자가 함께 변경되어 버리는 문제가 발생 만약 str3이 참조하는 문자를 변경하면 str4의 문자도 함께 변경되는 사이드 이펙트 문제가 발생할 수 있습니다. 하지만 String 클래스는 불변으로 설계되어 이런 사이트 이펙트 문제가 발생하지 않습니다.
가변 String, StringBuilder
String 클래스에도 분명 단점이 있습니다 ! String과 String을 더할 경우 불변인 String의 특성으로 인해 내부에서 값이 변경되는 것이 아닌 새로운 String 객체를 생성합니다. 즉 불변인 String 클래스의 단점은 문자를 더하거나 변경할 때 마다 계속해서 새로운 객체를 생성해야 한다는 점입니다. 문자를 자주 더하거나 변경해야 하는 상황이라면 더 많은 String 객체를 만들고, GC해야 합니다. 결과 적으로 CPU, 메모리 자원을 더 많이 사용하게된다는 의미입니다.
StringBuilder
String의 단점을 해결하기 위한 방법은 불변인 String이 아닌 가변의 String이 존재하면 됩니다 ! 가변은 내부의 값을 바로 변경하면 되기 때문에 새로운 객체를 생성할 필요가 없습니다. 따라서 성능과 메모리 사용면에서 불변보다 더 효율적입니다 !
이를 해결하기 위해 자바는 StringBuilder라는 가변 String을 제공합니다. 물론 가변의 경우 사이드 이펙트에 주의해서 사용해야합니다 !
String 최적화
자바 컴파일러는 아래와 같이 문자열 리터럴을 더하는 부분을 자동으로 합쳐줍니다 !
컴파일 전
String helloWorld = "Hello, " + "World!";
컴파일 후
String helloWorld = "Hello, World!";
따라서 런타임에 별도의 문자열 결합 연산을 수행하지 않기 때문에 성능이 향상됩니다 !
String 변수 최적화
문자열 변수의 경우 그 안에 어떤 값이 들어있는지 컴파일 시점에는 알 수 없기 때문에 단순하게 합칠 수 없습니다.
String result = str1 + str2;
이런 경우 다음과 같이 최적화를 수행할 수 있습니다. (자바 9버전 부터는 StringConcatFactory를 사용해서 최적화를 수행)
String result = new StringBuilder().append(str1).append(str2).toString();
위와 같이 자바가 최적화를 처리해주기 때문에 지금처럼 간단한 경우에는 StringBuilder를 사용하지 않아도 되기에 문자열 더하기 연산(+)을 사용하면 충분합니다.
하지만 String 최적화가 어려운 경우가 있습니다. 아래와 같이 루프안에서 문자열을 더하는 경우에는 최적화가 이루어지지 않습니다.
String result = "";
for (int i = 0; i < 100000; i++) {
result += "Hello Java ";
}
반목문의 루프 내부에서는 최적화가 되는 것 처럼 보이지만 반복 횟수만큼 객체를 생성해야합니다 !
이럴 경우 StringBuilder를 사용하면됩니다 !
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 100000; i++) {
sb.append("Hello Java ");
}
String result = sb.toString();
문자열을 합칠 때 대부분의 경우 최적화가 되으며 + 연산을 사용하면되지만 반복문, 조건문을 통해 동적으로 문자열을 조합할 때와 복잡한 문자열의 특정 부분을 변경해야할 때, 매우 긴 대용량 문자열을 다룰 때는 StringBuilder를 사용하여 최적화하는 것이 효율적입니다 !
'프로그래밍 > Java' 카테고리의 다른 글
[Java, 자바] Enum에 대하여 (0) | 2024.07.16 |
---|---|
[Java, 자바] Wrapper Class에 대하여 (1) | 2024.07.14 |
[Java, 자바] 불변 객체에 대하여 (0) | 2024.06.27 |
[Java, 자바] Object클래스에 관하여 (0) | 2024.06.23 |
[Java, 자바] @JsonProperty, @JsonNaming이란? (0) | 2024.06.20 |
개발의 모든 것 !
포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!