자바에서 문자열
문자 하나: char
자바에서 문자를 다루는 가장 기본적인 타입.
1
2
char [] arr = { 'h' , 'e' , 'l' , 'l' , 'o' };
System . out . println ( arr ); // hello
여러 문자를 다루려면 char[]
을 사용한다.
하지만 char[]
는 직접다루기 불편하기에 자바에서는 String
클래스를 제공한다.
문자열: String
1
2
String str1 = "hello" ; //리터럴 방식
String str2 = new String ( "hello" ); // 객체 생성 방식
문자열 리터럴은 문자열 풀에 저장되어 중복 방지 및 성능 최적화가 된다.
생성자 방식은 매번 새로운 객체가 생성된다.
String
클래스는 참조형
String
은 클래스 즉 참조형
내부적으로 문자 배열(char[]
또는 Java 9 이후 byte[]
)을 사용해 문자열 저장
문자열 더하기는 concat()
또는 +
연산자 사용 가능
1
2
3
4
5
String a = "hello" ;
String b = " java" ;
System . out . println ( a . concat ( b )); // hello java
System . out . println ( a + b ); // hello java
//+ 연산자는 자바가 내부적으로 StringBuilder 를 사용해 최적화함
문자열 비교 ==
vs equals()
1
2
3
4
5
6
7
8
9
10
11
String s1 = new String ( "hello" );
String s2 = new String ( "hello" );
System . out . println ( s1 == s2 ); // false: 참조값 비교
System . out . println ( s1 . equals ( s2 )); // true: 문자열 내용 비교
String s3 = "hello" ;
String s4 = "hello" ;
System . out . println ( s3 == s4 ); // true: 문자열 풀 덕분에 같은 참조
System . out . println ( s3 . equals ( s4 )); // true
문자열 비교는 항상 equals()
를 사용해야 안전
메서드로 전달된 문자열이 리터럴인지 new
인지 알 수 없기 때문에 ==
사용 금지
자바 9 이후 String
내부 구조가 char[]
→ byte[]
로 변경
영어/숫자 등은 1byte로, 나머지는 UTF-16 방식으로 인코딩 → 메모리 절약 및 성능 향상
String 클래스 - 불변 객체
불변 객체란?
생성 이후 내부 값이 변경되지 않는 객체
String
은 대표적인 불변 객체
1
2
3
String str = "hello" ;
str . concat ( " java" );
System . out . println ( "str = " + str ); // 출력: hello
concat()
은 새로운 문자열을 반환하지만 str
변수에 할당하지 않았기 때문에 원본은 변하지 않는다.
String이 불변하게 설계된 이유
문자열 풀에서 같은 문자열을 여러 객체가 공유하기 때문에
만약 하나의 값을 변경하면 다른 모든 참조도 영향을 받게되기 때문 → 사이드 이펙트
1
2
3
String a = "hello" ;
String b = "hello" ;
// 만약 b가 내부 값을 바꾸면? → a 도 영향을 받게 됨 (위험!)
이런 문제를 방지하기 위해 String은 불변으로 설계되었다.
String 주요 메서드
문자열 정보 확인
1
2
3
4
5
String str = "Hello, Java!" ;
str . length (); // 12
str . isEmpty (); // false
str . isBlank (); // false (Java 11 이상)
str . charAt ( 7 ); // 'J'
문자열 비교
1
2
3
4
5
str1 . equals ( str2 ); // 값 비교
str1 . equalsIgnoreCase ( str2 ); // 대소문자 무시
str1 . compareTo ( str3 ); // 사전순 비교
str1 . startsWith ( "Hello" ); // true
str1 . endsWith ( "Java!" ); // true
문자열 검색
1
2
3
str . contains ( "Java" ); // true
str . indexOf ( "Java" ); // 첫 등장 위치
str . lastIndexOf ( "Java" ); // 마지막 등장 위치
이 외에도 substring, replace, split, join, format, matches 등
문자열 조작 및 변환
부분 문자열 추출: substring()
1
2
3
String str = "Hello, Java!" ;
str . substring ( 7 ); // "Java!"
str . substring ( 7 , 12 ); // "Java!"
문자열 연결: concat()
1
str . concat ( "!!!" ); // "Hello, Java!!!!"
문자열 치환: replace()
, replaceFirst()
, replaceAll()
1
2
str . replace ( "Java" , "World" ); // 모든 Java → World
str . replaceFirst ( "Java" , "World" ); // 첫 번째 Java → World
대소문자 변환: toLowerCase()
, toUpperCase()
1
2
str . toLowerCase (); // 소문자
str . toUpperCase (); // 대문자
공백 제거: trim()
, strip()
, stripLeading()
, stripTrailing()
1
2
3
4
str . trim (); // 양쪽 공백 제거
str . strip (); // 양쪽 유니코드 공백 제거 (Java 11+)
str . stripLeading (); // 앞 공백 제거
str . stripTrailing (); // 뒤 공백 제거
문자열 분할 및 결합
분할: split()
1
2
String str = "Apple,Banana,Orange" ;
String [] arr = str . split ( "," );
결합: String.join()
1
2
String . join ( "-" , "A" , "B" , "C" ); // "A-B-C"
String . join ( "-" , arr ); // "Apple-Banana-Orange"
기타 유틸리티
문자열 변환: valueOf()
1
2
3
String . valueOf ( 100 ); // "100"
String . valueOf ( true ); // "true"
String . valueOf ( obj ); // obj.toString() 값
참고: “” + x 로도 문자열 변환 가능
문자 배열 변환: toCharArray()
1
char [] chars = str . toCharArray ();
1
2
String . format ( "숫자: %d, 문자열: %s" , 10 , "hello" );
// 출력: 숫자: 10, 문자열: hello
포맷 코드
설명
%d
정수 (int)
%s
문자열 (String)
%b
boolean 값
%f
실수 (float)
정규식 매칭: matches()
1
str . matches ( "Hello, (Java!|World!)" ); // true
StringBuilder
불변인 String의 단점
String은 불변객체이기 때문에 문자열을 변경할 때마다 새로운 객체가 생성된다.
예: "A" + "B" + "C" + "D"
→ new String("A") → "AB" → "ABC" → "ABCD"
중간에 생성된 문자열들은 사용되지도 않고 버려짐 → 메모리 낭비, GC 부하
1
String str = "A" + "B" + "C" + "D" ; // 실제론 여러 객체 생성됨
StringBuilder(가변 문자열) 등장
StringBuilder
는 가변(mutable) 객체
내부 문자열을 직접 수정하므로 새로운 객체를 만들지 않음
문자열 변경이 빈번한 상황에서 훨씬 빠르고 효율적
1
2
3
StringBuilder sb = new StringBuilder ();
sb . append ( "A" ). append ( "B" ). append ( "C" );
System . out . println ( sb . toString ()); // ABC
StringBuilder는 쓰레드 안전하지 않음. 멀티쓰레드 환경에서는 StringBuffer 사용
StringBuilder 주요 메서드
메서드
설명
append(str)
문자열 추가
insert(idx, str)
특정 위치에 삽입
delete(start, end)
특정 범위 삭제
reverse()
문자열 뒤집기
toString()
최종 문자열 반환 (String
)
1
2
3
4
5
StringBuilder sb = new StringBuilder ( "ABCD" );
sb . insert ( 4 , "Java" ); // ABCDJava
sb . delete ( 4 , 8 ); // ABCD
sb . reverse (); // DCBA
String result = sb . toString (); // DCBA
성능 비교: 반복문에서 문자열 더하기
String
사용 시 (비효율적)
1
2
3
4
String result = "" ;
for ( int i = 0 ; i < 100000 ; i ++) {
result += "Hello " ;
}
문자열을 더할 때마다 새로운 객체 생성
실행 시간: 2.5초 (Mac 기준)
StringBuilder
사용 시
1
2
3
4
5
StringBuilder sb = new StringBuilder ();
for ( int i = 0 ; i < 100000 ; i ++) {
sb . append ( "Hello " );
}
String result = sb . toString ();
문자열을 한 객체에서 수정
실행 시간: 3ms
자바의 문자열 최적화
언제 StringBuilder
를 직접 써야 할까?
상황
설명
반복문에서 문자열 결합
루프마다 문자열 생성 방지
조건문 등 동적 조합
if
, switch
등에서 조합이 자주 발생할 때
긴 문자열
예: HTML 생성, 로그 조립 등
문자열 일부 변경
중간 삽입, 삭제, 교체
StringBuilder
vs StringBuffer
클래스
쓰레드 안전성
속도
StringBuilder
비동기 → 빠름
빠름
StringBuffer
동기화 → 안전
느림
멀티쓰레드 환경에선 StringBuffer 사용. 단일 스레드에선 StringBuilder가 성능 우위
String
은 불변이라 작은 작업에도 객체를 많이 생성
자주 변경되는 문자열 작업엔 StringBuilder
를 사용해야 성능이 훨씬 좋다
자바는 자동으로 최적화하는 경우도 있지만, 반복문 안에선 직접 StringBuilder
사용 권장
StringBuilder와 메서드 체인(Chain)
StringBuilder
는 메서드 체이닝 기법을 제공한다
메서드 체이닝이란?
메서드 호출 결과로 자기 자신(this
)을 반환 하면, 그 반환값을 이어서 다시 메서드 호출 가능
즉, .method1().method2().method3()
처럼 연속 호출되는 구조
기본 구조
1
2
3
4
5
6
7
8
9
10
11
12
public class ValueAdder {
private int value ;
public ValueAdder add ( int addValue ) {
value += addValue ;
return this ; // 자신의 참조값 반환
}
public int getValue () {
return value ;
}
}
일반 호출 vs 체이닝 호출
일반 방식
1
2
3
4
5
ValueAdder adder = new ValueAdder ();
adder . add ( 1 );
adder . add ( 2 );
adder . add ( 3 );
System . out . println ( adder . getValue ()); // 6
체이닝 방식
1
2
3
ValueAdder adder = new ValueAdder ();
int result = adder . add ( 1 ). add ( 2 ). add ( 3 ). getValue ();
System . out . println ( result ); // 6
변수 선언 없이 바로 연결 가능 → 코드가 간결해짐
StringBuilder와 메서드 체이닝
자바의 StringBuilder
도 체이닝을 지원하는 대표적인 클래스
1
2
3
4
5
6
7
8
9
10
11
String result = new StringBuilder ()
. append ( "A" )
. append ( "B" )
. append ( "C" )
. append ( "D" )
. insert ( 4 , "Java" )
. delete ( 4 , 8 )
. reverse ()
. toString ();
System . out . println ( result ); // DCBA
append()
, insert()
, delete()
등 거의 모든 메서드가 this
를 반환함
→ 한 줄로 복잡한 문자열 처리 가능
체이닝이 가능한 이유
메서드가 return this; 를 하고 있기 때문
즉, 반환값이 자기 자신이기 때문에 .
으로 계속 이어질 수 있음
코드가 간결해지고 가독성이 좋아지지만, 너무 길거나 조건문이 섞이면 오히려 가독성이 저하 된다.
메서드 체이닝은 클래스 작성자는 조금 번거롭지만, 사용자는 훨씬 편리 해진다.
출처: 김영한의 실전 자바 - 중급 1편