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