자바에서 문자열 
문자 하나: 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[] 로 변경메모리 절약 및 성능 향상 
 
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) 
     
    
      %bboolean 값 
     
    
      %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편