자바 개발자라면 누구나 가장 많이 사용하는 클래스 중 하나가 바로 String이다. 어떤 프로그램을 만들든 문자열은 빠지지 않는다. 로그를 남길 때도, 사용자 입력을 처리할 때도, 파일 경로나 URL을 다룰 때도 문자열은 항상 함께한다. 그런데 자바의 String은 조금 특별하다. 불변 객체, 즉 한 번 생성되면 절대 내부 값을 바꿀 수 없도록 설계돼 있기 때문이다.
이쯤 되면 자연스럽게 이런 의문이 생긴다.
“왜 하필 불변으로 만들었을까?”
문자열은 언제든 수정하고 이어붙이고 가공해야 하는 대상인데, 오히려 불편하지 않을까?
사실 이 불편함 뒤에는 중요한 이유가 숨어 있다.
사이드 이펙트(side effect)를 피하기 위한 설계
문자열은 프로그램의 전반에 걸쳐 공유 객체로 자주 사용된다. 하나의 문자열 객체가 여러 곳에서 동시에 사용될 수 있다는 말이다. 만약 String이 가변 객체였다면, 누군가가 문자열 값을 바꾸는 순간 다른 모든 코드에도 그 변화가 영향을 미칠 수 있다.
이러한 상황은 멀티스레드 환경에서 특히 위험하다. 두 개 이상의 스레드가 같은 문자열 객체를 동시에 수정한다면, 결과는 예측할 수 없다. 이처럼 하나의 상태 변경이 여러 동작에 영향을 주는 것을 우리는 사이드 이펙트라고 부른다.
문자열을 바꾸고 싶으면? 새 객체를 만들면 된다
그렇다면 문자열을 조작할 수 없다는 말일까? 물론 아니다. 자바의 String은 다양한 메서드를 제공해 문자열을 가공할 수 있게 한다. 다만 이 과정에서 기존 객체를 바꾸는 것이 아니라, 새로운 문자열 객체를 만들어서 반환한다.
String original = "hello";
String result = original.concat(" world");
System.out.println(original); // hello
System.out.println(result); // hello world
concat과 같은 함수를 사용하면 자바 내부에서 새로운 객체를 만들어서 반환을 해준다. 이처럼 String 클래스는 항상 새 인스턴스를 반환한다.
자바 String 클래스 여러 기능들
1. 문자열 정보 조회
● length() : 문자열의 길이를 반환한다.
● isEmpty() : 문자열이 비어 있는지 확인한다. (길이가 0)
● isBlank() : 문자열이 비어 있거나 공백 문자만 포함하는지 확인한다. (Java 11 이상)
● charAt(int index) : 지정한 위치의 문자를 반환한다.
2. 문자열 비교
● equals(String other) : 두 문자열이 같은지 비교한다.
● equalsIgnoreCase(String other) : 대소문자를 무시하고 문자열이 같은지 비교한다.
● compareTo(String other) : 두 문자열을 사전 순으로 비교해 정수값을 반환한다.
● compareToIgnoreCase(String other) : 대소문자를 무시하고 사전 순으로 비교한다.
● startsWith(String prefix) : 지정한 접두사로 시작하는지 확인한다.
● endsWith(String suffix) : 지정한 접미사로 끝나는지 확인한다.
3. 문자열 검색
● contains(CharSequence s) : 특정 문자열을 포함하고 있는지 확인한다.
● indexOf(String str) : 지정한 문자열이 처음 나타나는 위치를 반환한다.
● indexOf(String str, int fromIndex) : 시작 위치를 지정하여 문자열의 첫 위치를 반환한다.
● lastIndexOf(String str) : 지정한 문자열이 마지막으로 나타나는 위치를 반환한다.
4. 문자열 조작 및 변환
● substring(int beginIndex) / substring(int beginIndex, int endIndex) : 문자열의 일부를 잘라 반환한다.
● concat(String str) : 문자열 끝에 다른 문자열을 이어붙여 반환한다.
● replace(CharSequence target, CharSequence replacement) : 지정한 문자열을 새 문자열로 대체하여 반환한다.
● replaceAll(String regex, String replacement) : 정규 표현식에 맞는 모든 부분을 새 문자열로 대체하여 반환한다.
● replaceFirst(String regex, String replacement) : 정규 표현식과 일치하는 첫 번째 부분만 대체하여 반환한다.
● toLowerCase() / toUpperCase() : 문자열을 각각 소문자 또는 대문자로 변환해 반환한다.
● trim() : 문자열 양쪽 끝의 공백(ASCII)만 제거하여 반환한다.
● strip() : 문자열 양쪽 끝의 유니코드 공백까지 제거하여 반환한다. (Java 11 이상)
5. 문자열 분할 및 조합
● split(String regex) : 정규 표현식을 기준으로 문자열을 나누어 배열로 반환한다.
● join(CharSequence delimiter, CharSequence... elements) : 지정한 구분자로 문자열을 연결해 반환한다.
6. 기타 유틸리티
● valueOf(Object obj) : 객체나 기본형 값을 문자열로 변환해 반환한다.
● toCharArray() : 문자열을 문자(char) 배열로 변환해 반환한다.
● format(String format, Object... args) : 포맷 문자열과 인자를 사용해 새 문자열을 생성해 반환한다.
● matches(String regex) : 문자열이 정규 표현식과 일치하는지 확인해 boolean 값을 반환한다.
결론적으로
String 클래스는 불변이라는 특성과 동시에 강력한 문자열 처리 기능을 갖춘 자바의 대표적인 클래스다. 멀티스레드 환경에서도 안전하게 사용할 수 있도록 설계되어 있고, 메서드를 통해 가공은 얼마든지 가능하다. 실질적으로 불편함보다 안정성과 신뢰성을 확보하기 위한 선택이라 할 수 있다.
앞으로 문자열을 다룰 때, String이 왜 이렇게 설계되었는지를 떠올려 보자. 단순히 "문자열을 저장하는 클래스"를 넘어, 자바의 객체 지향 철학과 안전성에 대한 고민이 담긴 결과물이라는 사실을 이해하게 될 것이다.
'자바' 카테고리의 다른 글
| [JAVA] 날짜와 시간 (1) | 2025.07.06 |
|---|---|
| [JAVA] 열거형(ENUM)에 대하여 (1) | 2025.06.29 |
| [JAVA] 래퍼 클레스에 대하여 (3) | 2025.06.24 |
| [JAVA] 불변객체에 대하여 (0) | 2025.06.24 |
| [JAVA] Object Class에 대하여 (6) | 2025.06.24 |