공공부하자개발 · 영어 학습 노트
자바
명령어자주 쓰는 API 참조0/8 완료
  • 01String · StringBuilder · 정규식
  • 02Collections · Arrays · Map
  • 03Stream · Collectors · 함수형 인터페이스
  • 04Files · Path · I/O
  • 05java.time · BigDecimal · 기타 유틸
  • 06Oracle SQL
  • 07예외 · 디버깅
  • 08Spring Boot 설정 키
사이트 소개개인정보처리방침연락처
© 2026 공부하자
홈 › 명령어 › 01 / 8

String · StringBuilder · 정규식

섹션 15진행 0 / 8

String · StringBuilder · 정규식 명령어

문자열을 만들고, 비교하고, 찾고, 자르고, 바꾸고, 포맷하는 데 쓰는 API를 한 곳에 모았다. 원리 설명은 최소화하고 메서드 → 예제 → 실제 실행 결과만 적었다. 찾는 메서드가 있으면 Ctrl+F로 메서드명(예: strip, formatted, replaceAll)을 검색하면 된다. 모든 예제는 JDK 21에서 실행해 결과를 확인했다.

01한눈에 보기

가장 자주 쓰는 20개. 이것만 알아도 실무 문자열 처리의 80%는 해결된다.

메서드 설명 예제 결과
length() 문자 수(char 단위) "Hello, Java".length() 11
charAt(i) i번째 문자 "Hello".charAt(1) 'e'
equals(o) 내용 비교 (== 금지) "abc".equals("abc") true
equalsIgnoreCase(s) 대소문자 무시 비교 "ABC".equalsIgnoreCase("abc") true
isBlank() 공백만 있거나 빈 문자열 " ".isBlank() true
contains(s) 부분 문자열 포함 여부 "Hello, Java".contains("Java") true
indexOf(s) 첫 위치(없으면 -1) "Hello, Java".indexOf("Java") 7
substring(a, b) [a, b) 구간 추출 "Hello, Java".substring(0, 5) "Hello"
strip() 앞뒤 공백 제거(유니코드 인식) " hi ".strip() "hi"
toUpperCase() 대문자 변환 "Hello".toUpperCase() "HELLO"
replace(a, b) 문자/문자열 전부 치환(리터럴) "a-b-c".replace('-', '+') "a+b+c"
replaceAll(regex, r) 정규식 치환 "a1b22c333".replaceAll("\\d+", "#") "a#b#c#"
split(regex) 정규식으로 분할 "a,b,c".split(",") [a, b, c]
String.join(sep, ...) 구분자로 결합 String.join("-", "a", "b", "c") "a-b-c"
repeat(n) n번 반복 "ab".repeat(3) "ababab"
formatted(args) 포맷(인스턴스 메서드) "%s님 %d세".formatted("홍길동", 30) "홍길동님 30세"
String.valueOf(x) 무엇이든 문자열로 String.valueOf(42) "42"
Integer.parseInt(s) 문자열 → int Integer.parseInt("42") + 1 43
new StringBuilder().append() 가변 문자열 누적 new StringBuilder("abc").append("d").append(1) "abcd1"
matches(regex) 전체 일치 검사 "2024-01-15".matches("\\d{4}-\\d{2}-\\d{2}") true
02생성과 기본 정보
메서드/시그니처 설명 한 줄 예제 결과
"리터럴" String Pool에 저장, 같은 리터럴은 같은 객체 ("ja" + "va") == "java" true (컴파일 타임 상수 접기)
new String("x") 항상 새 객체 (거의 쓸 일 없음) new String("a") == "a" false
String.valueOf(Object) null-safe 변환 String.valueOf((Object) null) "null"
String.valueOf(char[]) char 배열 → 문자열 String.valueOf(new char[]{'a','b'}) "ab"
String.copyValueOf(char[]) valueOf(char[])와 동일 String.copyValueOf(new char[]{'x','y'}) "xy"
new String(byte[], Charset) 바이트 → 문자열 (5절 참고) new String(bytes, StandardCharsets.UTF_8) "한글"
length() UTF-16 코드 유닛 개수 "😀a".length() 3 (이모지는 2)
codePointCount(0, len) 실제 유니코드 문자 수 "😀a".codePointCount(0, 3) 2
isEmpty() 길이 0 "".isEmpty() true
isBlank() 공백뿐(탭/개행 포함) " ".isBlank() true
intern() Pool의 동일 문자열 반환 ("ja" + "va").intern() == "java" true
hashCode() 내용 기반 해시(캐시됨) "abc".hashCode() 96354
toCharArray() char 배열로 "abc".toCharArray() [a, b, c]
chars() IntStream으로 "abc".chars().boxed().toList() [97, 98, 99]
describeConstable() Optional로 감싸기(거의 안 씀) "x".describeConstable() Optional[x]
  • String은 불변이다. s.toUpperCase()를 호출해도 s는 바뀌지 않고 반환값을 받아야 한다.
  • 리터럴끼리 ==는 우연히 true가 나올 수 있어 더 위험하다. 항상 equals.
03비교
메서드/시그니처 설명 한 줄 예제 결과
equals(Object) 내용 동일 "abc".equals("abc") true
equalsIgnoreCase(String) 대소문자 무시 "ABC".equalsIgnoreCase("abc") true
compareTo(String) 사전순 (음수/0/양수) "apple".compareTo("banana") -1
compareTo 결과값 첫 다른 문자의 코드 차이 "a".compareTo("B") 31 (97-66)
compareToIgnoreCase(String) 대소문자 무시 사전순 "A".compareToIgnoreCase("a") 0
contentEquals(CharSequence) StringBuilder 등과 내용 비교 "abc".contentEquals(new StringBuilder("abc")) true
Objects.equals(a, b) null-safe equals Objects.equals(null, null) true
regionMatches(ic, off, other, ooff, len) 부분 영역 비교 "Hello World".regionMatches(true, 6, "world", 0, 5) true
String.CASE_INSENSITIVE_ORDER Comparator 상수 Stream.of("b","A","c").sorted(String.CASE_INSENSITIVE_ORDER) [A, b, c]
Comparator.nullsFirst(...) null 포함 정렬 Comparator.nullsFirst(String::compareTo).compare(null, "a") -1
  • 기본 sorted()는 대문자가 소문자보다 앞에 온다("Z" < "a"). 사용자 표시용 정렬은 CASE_INSENSITIVE_ORDER나 Collator를 쓴다.
  • "상수".equals(변수) 순서로 쓰면 변수가 null이어도 NPE가 나지 않는다.
04검색
메서드/시그니처 설명 한 줄 예제 결과
contains(CharSequence) 포함 여부 "Hello, Java".contains("Java") true
startsWith(String) 접두사 "Hello, Java".startsWith("Hel") true
startsWith(String, offset) offset부터 접두사 "Hello, Java".startsWith("Java", 7) true
endsWith(String) 접미사 "Hello, Java".endsWith("va") true
indexOf(char) 첫 위치 "Hello, Java".indexOf('l') 2
indexOf(String) 첫 위치 "Hello, Java".indexOf("Java") 7
indexOf(char, from) from 이후 첫 위치 "Hello, Java".indexOf('l', 4) -1
lastIndexOf(char) 마지막 위치 "Hello, Java".lastIndexOf('l') 3
없을 때 -1 반환 (예외 없음) "Hello".indexOf("x") -1
matches(regex) 전체가 정규식과 일치 "2024-01-15".matches("\\d{4}-\\d{2}-\\d{2}") true
chars().filter().count() 특정 문자 개수 "hello".chars().filter(c -> c == 'l').count() 2
  • 출현 횟수를 세려면 indexOf를 반복하거나(스니펫 참고) chars()/정규식을 쓴다.
  • matches는 부분 일치가 아니다. "abc1".matches("[a-z]+")는 false. 부분 검색은 Pattern.matcher().find().
05추출과 변환
메서드/시그니처 설명 한 줄 예제 결과
substring(begin) begin부터 끝까지 "Hello, Java".substring(7) "Java"
substring(begin, end) [begin, end) "Hello, Java".substring(0, 5) "Hello"
substring(len) begin == length면 빈 문자열 "abc".substring(3) ""
subSequence(a, b) CharSequence 반환 "Hello".subSequence(0, 5) "Hello"
toUpperCase() / toLowerCase() 대소문자 "Hello, Java".toLowerCase() "hello, java"
toUpperCase(Locale.ROOT) 로케일 독립 변환(터키어 i 문제 회피) "title".toUpperCase(Locale.ROOT) "TITLE"
trim() U+0020 이하 문자 제거 " hi ".trim() "hi"
strip() 유니코드 공백 제거 (권장) " hi ".strip() "hi"
stripLeading() 앞만 " hi ".stripLeading() "hi "
stripTrailing() 뒤만 " hi ".stripTrailing() " hi"
replace(char, char) 문자 전부 치환 "a-b-c".replace('-', '+') "a+b+c"
replace(CharSequence, CharSequence) 리터럴 문자열 전부 치환 "aaa".replace("a", "bb") "bbbbbb"
replaceAll(regex, repl) 정규식 전부 치환 "a1b22c333".replaceAll("\\d+", "#") "a#b#c#"
replaceFirst(regex, repl) 정규식 첫 개만 "a1b22c333".replaceFirst("\\d+", "#") "a#b22c333"
repeat(n) 반복 "ab".repeat(3) "ababab"
concat(s) 이어 붙이기 (+와 동일) "a".concat("b") "ab"
lines() 줄 단위 Stream (\n, \r\n 모두) "a\nb\r\nc".lines().toList() [a, b, c]
indent(n) 각 줄 n칸 들여쓰기(+개행 정규화) "a\nb".indent(2) " a\n b\n"
indent(-n) n칸 내어쓰기 " a\n b".indent(-1) " a\n b\n"
stripIndent() 공통 들여쓰기 제거 " a\n b".stripIndent() "a\n b"
translateEscapes() \t 같은 이스케이프 문자열 해석 "a\\tb".translateEscapes() "a<TAB>b"
transform(Function) 파이프라인처럼 함수 적용 "42".transform(Integer::parseInt) + 1 43
  • replace는 정규식이 아니다. "a.b.c".replace(".", "-")는 "a-b-c", replaceAll(".", "-")는 "-----"(모든 문자 매칭).
  • trim()은 \u2003(EM SPACE) 같은 유니코드 공백을 못 지운다. JDK 11+에서는 strip()을 쓴다.
06분할과 결합
메서드/시그니처 설명 한 줄 예제 결과
split(regex) 정규식 분할, 끝의 빈 요소 제거 "a,b,,c,,".split(",") [a, b, , c]
split(regex, -1) 끝의 빈 요소 유지 "a,b,,c,,".split(",", -1) [a, b, , c, , ]
split(regex, n) 최대 n개 "a,b,c".split(",", 2) [a, b,c]
split("\\.") 메타문자는 이스케이프 "a.b.c".split("\\.") [a, b, c]
split("\\s+") 연속 공백으로 분할 "a b\tc".split("\\s+") [a, b, c]
split("\\d") 숫자로 분할 "a1b2c3".split("\\d") [a, b, c]
String.join(sep, elems...) 가변 인자 결합 String.join("-", "a", "b", "c") "a-b-c"
String.join(sep, Iterable) 컬렉션 결합 String.join(", ", List.of("x", "y")) "x, y"
Collectors.joining(sep, pre, suf) 스트림 결합 Stream.of("a","b").collect(joining(", ", "[", "]")) "[a, b]"
new StringJoiner(sep, pre, suf) 수동 결합 new StringJoiner(", ", "[", "]").add("a").add("b") "[a, b]"
StringJoiner.setEmptyValue(s) 비었을 때 값 new StringJoiner(",", "[", "]").setEmptyValue("EMPTY") "EMPTY"
Pattern.splitAsStream(s) 분할 결과를 Stream으로 Pattern.compile(",").splitAsStream("a,b,c").toList() [a, b, c]
  • CSV 파싱에 split(",")를 쓰면 따옴표 안의 콤마가 깨진다. 스니펫 2 참고.
  • split("|")은 |가 정규식 OR라서 문자 단위로 쪼개진다. split("\\|")로 써야 한다.
07포맷과 텍스트 블록
지정자/메서드 설명 한 줄 예제 결과
String.format(fmt, args) 정적 포맷 String.format("%s님 %d세", "홍길동", 30) "홍길동님 30세"
"fmt".formatted(args) 인스턴스 포맷 (JDK 15+) "%s님 %d세".formatted("홍길동", 30) "홍길동님 30세"
%d 정수 "%d".formatted(42) "42"
%5d 폭 5 우측 정렬 "[%5d]".formatted(42) "[ 42]"
%-5d 폭 5 좌측 정렬 "[%-5d]".formatted(42) "[42 ]"
%05d 0 채움 "[%05d]".formatted(42) "[00042]"
%,d 천 단위 콤마 "%,d".formatted(1234567) "1,234,567"
%+d / %(d 부호 / 음수 괄호 "%+d %(d".formatted(5, -5) "+5 (5)"
%.2f 소수 2자리(반올림 HALF_UP) "%.2f".formatted(3.14159) "3.14"
%8.2f 폭 8, 소수 2자리 "%8.2f|".formatted(3.14159) " 3.14|"
%,.2f 콤마 + 소수 "%,.2f".formatted(1234567.891) "1,234,567.89"
%e 지수 표기 "%e".formatted(12345.678) "1.234568e+04"
%s 문자열(toString) "[%10s]".formatted("hi") "[ hi]"
%-10s 좌측 정렬 "[%-10s]".formatted("hi") "[hi ]"
%.3s 앞 3글자만 "%.3s".formatted("abcdef") "abc"
%c 문자 "%c".formatted(65) "A"
%b boolean (null → false, 그 외 → true) "%b".formatted("x") "true"
%x / %X / %o 16진/대문자/8진 "%x %X %o".formatted(255, 255, 8) "ff FF 10"
%% 퍼센트 리터럴 "%d%%".formatted(50) "50%"
%n 플랫폼 개행 "a%nb".formatted() "a\r\nb" (Windows)
%2$s %1$s 인자 순서 지정 "%2$s %1$s".formatted("world", "hello") "hello world"
%tY-%<tm-%<td 날짜(<는 앞 인자 재사용) "%tY-%<tm-%<td".formatted(LocalDate.of(2024,3,5)) "2024-03-05"
System.out.printf(fmt, args) 출력과 포맷 동시 System.out.printf("%5.1f%n", 3.14159) 3.1
""" 텍스트 블록 여러 줄 문자열(JDK 15+) 아래 코드 아래
\ (줄 끝) 텍스트 블록 줄 이어붙이기 """\n hello \\\n world""" "hello world"
\s 텍스트 블록 끝 공백 유지 """\n trailing \\s\n end""" "trailing \nend"
java
String json = """
    {
      "name": "Java",
      "ver": 21
    }
    """;
// 출력: {\n  "name": "Java",\n  "ver": 21\n}\n
// 닫는 """의 위치가 공통 들여쓰기 기준. 마지막 줄바꿈이 포함된다(닫는 """가 다음 줄에 있으면).
  • %d에 double을 넣으면 IllegalFormatConversionException. 타입을 맞춘다.
  • %.2f는 HALF_UP 반올림이다. 금액은 BigDecimal.setScale 후 toPlainString()을 쓴다(05 참고).
08정규식 (Pattern / Matcher)
메서드/시그니처 설명 한 줄 예제 결과
Pattern.compile(regex) 컴파일(재사용하려면 static final) Pattern p = Pattern.compile("(\\d{3})-(\\d{4})") Pattern
Pattern.compile(regex, flags) 플래그 아래 코드 true
Pattern.matches(regex, input) 1회성 전체 일치 Pattern.matches("[a-z]+", "abc") true
p.matcher(input) Matcher 생성 Matcher m = p.matcher("tel 010-1234, 031-9999") Matcher
m.matches() 전체 일치 Pattern.compile("[a-z]+").matcher("abc1").matches() false
m.lookingAt() 시작 부분 일치 Pattern.compile("[a-z]+").matcher("abc1").lookingAt() true
m.find() 다음 부분 일치 찾기(반복 호출) while (m.find()) list.add(m.group()) [010-1234, 031-9999]
m.group() / m.group(n) 전체 / n번 그룹 m.group(1) + "," + m.group(2) "010,1234"
m.group("name") 이름 있는 그룹 Pattern.compile("(?<y>\\d{4})-(?<m>\\d{2})") → m.group("y") "2024"
m.groupCount() 그룹 수 m.groupCount() 2
m.start() / m.end() 일치 위치 m.start() 4
m.results() 모든 일치를 Stream으로 (JDK 9+) 아래 코드 [1, 22, 333]
m.replaceAll(repl) 치환 "2024-05-17".replaceAll("(\\d{4})-(\\d{2})-(\\d{2})", "$3/$2/$1") "17/05/2024"
${name} 치환 이름 그룹 참조 아래 코드 "17.05.2024"
m.replaceAll(Function) 함수로 치환 (JDK 9+) 아래 코드 "a2b44"
m.appendReplacement/appendTail 수동 치환 루프 아래 코드 "a<1>b<2>"
Pattern.quote(s) 리터럴로 이스케이프 Pattern.quote("a.b") "\Qa.b\E"
Matcher.quoteReplacement(s) 치환문의 $,\ 이스케이프 Matcher.quoteReplacement("$1") "\$1"
p.asPredicate() 부분 일치 Predicate 아래 코드 [a1, c2]
p.asMatchPredicate() 전체 일치 Predicate .filter(Pattern.compile("[a-z]\\d").asMatchPredicate()) [a1, c2]
p.splitAsStream(s) 분할 스트림 Pattern.compile(",").splitAsStream("a,b,c").toList() [a, b, c]
p.pattern() 원본 정규식 p.pattern() "(\d{3})-(\d{4})"
java
Matcher m = Pattern.compile("\\d").matcher("a1b2");
StringBuilder sb = new StringBuilder();
while (m.find()) m.appendReplacement(sb, "<" + m.group() + ">");
m.appendTail(sb);
// 출력: a<1>b<2>

// 플래그(대소문자 무시)
boolean ci = Pattern.compile("java", Pattern.CASE_INSENSITIVE)
    .matcher("JAVA").matches(); // true

// 모든 일치를 Stream으로 수집(JDK 9+)
List<String> nums = Pattern.compile("\\d+")
    .matcher("a1b22c333")
    .results().map(MatchResult::group).toList();
// [1, 22, 333]

// 이름 있는 그룹을 ${name}으로 치환
String d = "2024-05-17".replaceAll(
    "(?<y>\\d{4})-(?<m>\\d{2})-(?<d>\\d{2})",
    "${d}.${m}.${y}"); // "17.05.2024"

// 함수로 치환: 숫자를 2배로(JDK 9+)
String r = Pattern.compile("\\d+").matcher("a1b22")
    .replaceAll(mr ->
        String.valueOf(Integer.parseInt(mr.group()) * 2));
// "a2b44"

// 부분 일치 Predicate로 필터링
List<String> has = Stream.of("a1", "bb", "c2")
    .filter(Pattern.compile("\\d").asPredicate())
    .toList(); // [a1, c2]

주요 정규식 문법 요약 (자세한 치트표는 05 시간·기타 유틸 참고):

문법 의미 예제 결과
\d \w \s 숫자 / 단어문자 / 공백 "a 1".replaceAll("\\s", "_") "a_1"
[가-힣] 한글 음절 "가".matches("[가-힣]") true
.* vs .*? 탐욕 / 비탐욕 "<a><b>".replaceAll("<.*?>", "X") "XX" (탐욕은 "X")
(?i) (?s) (?m) 대소문자 무시 / DOTALL / 여러 줄 "HELLO".matches("(?i)hello") true
\b 단어 경계 "cat category".replaceAll("\\bcat\\b", "dog") "dog category"
(?<=x) (?=x) 후방/전방 탐색 "price: $100".replaceAll("(?<=\\$)\\d+", "N") "price: $N"
$0 $1 치환문에서 전체/그룹 참조 "aa bb cd".replaceAll("(\\w)\\1", "<$0>") "<aa> <bb> cd"
a|b OR "cat".matches("cat|dog") true
  • Java 문자열 안에서 \는 \\로 써야 한다. "\\d+"가 정규식 \d+.
  • String.matches/replaceAll/split은 매번 Pattern.compile을 한다. 루프 안에서는 static final Pattern으로 미리 컴파일한다.
  • 치환문에 $가 리터럴로 들어가면 Matcher.quoteReplacement로 감싼다.
09문자 ↔ 숫자 변환
메서드/시그니처 설명 한 줄 예제 결과
Integer.parseInt(s) 문자열 → int Integer.parseInt("42") + 1 43
Integer.parseInt(s, radix) 진법 지정 Integer.parseInt("ff", 16) 255
Integer.valueOf(s) 문자열 → Integer(박싱) Integer.valueOf("42") 42
Long.parseLong(s) 문자열 → long Long.parseLong("9000000000") 9000000000
Double.parseDouble(s) 문자열 → double Double.parseDouble("3.5") 3.5
Boolean.parseBoolean(s) "true"(대소문자 무시)만 true Boolean.parseBoolean("yes") false
Integer.decode(s) 접두사로 진법 판단 Integer.decode("0x1F") 31
new BigDecimal(s) 정확한 소수 new BigDecimal("1.10").toPlainString() "1.10"
Integer.toString(n, radix) int → 진법 문자열 Integer.toString(255, 16) "ff"
Integer.toBinaryString(n) 2진 Integer.toBinaryString(10) "1010"
Integer.toHexString(n) 16진 Integer.toHexString(255) "ff"
Integer.toOctalString(n) 8진 Integer.toOctalString(8) "10"
String.valueOf(n) / n + "" int → 문자열 42 + "" "42"
(int) ch char → 코드 (int) 'A' 65
(char) n 코드 → char (char) 66 'B'
ch - '0' 숫자 문자 → 값 '7' - '0' 7
Character.getNumericValue(ch) 숫자 문자 → 값 (안전) Character.getNumericValue('7') 7
Character.digit(ch, radix) 진법 자릿값 Character.digit('f', 16) 15
Character.forDigit(n, radix) 자릿값 → 문자 Character.forDigit(11, 16) 'b'
NumberFormatException 잘못된 입력 Integer.parseInt("12a") For input string: "12a"
공백 포함 공백도 실패 Integer.parseInt(" 12") For input string: " 12"
빈 문자열 실패 Integer.parseInt("") For input string: ""
  • 외부 입력은 strip() 후 파싱하고, NumberFormatException을 반드시 처리한다.
  • 'a' + 'b'는 195(int 덧셈)다. 문자열로 잇고 싶으면 "" + 'a' + 'b'.
10인코딩 (getBytes / new String / Base64)
메서드/시그니처 설명 한 줄 예제 결과
getBytes(StandardCharsets.UTF_8) UTF-8 바이트 (한글 3바이트) "한글".getBytes(UTF_8).length 6
getBytes("EUC-KR") EUC-KR (한글 2바이트) "한글".getBytes("EUC-KR").length 4
getBytes(UTF_16) UTF-16 (BOM 2 + 2×2) "한글".getBytes(UTF_16).length 6
getBytes() (인자 없음) 기본 charset (JDK 18+ UTF-8) Charset.defaultCharset() UTF-8
new String(bytes, charset) 바이트 → 문자열 new String(bytes, UTF_8) "한글"
인코딩 불일치 깨진 문자 new String("한글".getBytes("EUC-KR"), UTF_8) "�ѱ�"
HexFormat.of().formatHex(bytes) 바이트 → 16진 문자열 (JDK 17+) HexFormat.of().formatHex("한글".getBytes(UTF_8)) "ed959ceab880"
HexFormat.of().parseHex(s) 16진 → 바이트 HexFormat.of().parseHex("01ff") [1, -1]
Base64.getEncoder().encodeToString(b) Base64 인코딩 Base64.getEncoder().encodeToString("hello".getBytes()) "aGVsbG8="
Base64.getDecoder().decode(s) Base64 디코딩 new String(Base64.getDecoder().decode("aGVsbG8=")) "hello"
Base64.getUrlEncoder().withoutPadding() URL-safe, = 없이 토큰 생성에 사용 -_ 사용
URLEncoder.encode(s, UTF_8) URL 인코딩 (공백→+) URLEncoder.encode("한 글&a=b", UTF_8) "%ED%95%9C+%EA%B8%80%26a%3Db"
URLDecoder.decode(s, UTF_8) URL 디코딩 URLDecoder.decode("%ED%95%9C+%EA%B8%80", UTF_8) "한 글"
  • 파일/네트워크 경계에서는 항상 charset을 명시한다. getBytes()/new String(bytes)처럼 인자 없는 버전은 환경에 따라 결과가 달라진다.
  • 한글 바이트 길이 제한(DB VARCHAR(byte))을 검사할 땐 getBytes(charset).length로 잰다. "abc한글".getBytes(UTF_8).length는 9.
11StringBuilder 전 메서드
메서드/시그니처 설명 한 줄 예제 결과
new StringBuilder() 기본 용량 16 new StringBuilder().capacity() 16
new StringBuilder(int) 용량 지정 new StringBuilder(100).capacity() 100
new StringBuilder(String) 초기값 (용량 = 길이+16) new StringBuilder("abc").capacity() 19
append(x) 끝에 추가(모든 타입 오버로드, 체이닝) new StringBuilder("abc").append("d").append(1).append(true) "abcd1true"
appendCodePoint(cp) 코드포인트 추가 new StringBuilder().appendCodePoint(0x1F600) "😀"
insert(idx, x) 위치에 삽입 new StringBuilder("abc").insert(1, "X") "aXbc"
delete(start, end) [start, end) 삭제 new StringBuilder("abcdef").delete(1, 3) "adef"
deleteCharAt(idx) 한 문자 삭제 new StringBuilder("abc").deleteCharAt(0) "bc"
replace(start, end, s) 구간 치환 (정규식 아님) new StringBuilder("abcdef").replace(1, 3, "XY") "aXYdef"
reverse() 뒤집기 new StringBuilder("abc").reverse() "cba"
setCharAt(idx, ch) 문자 교체 (void) sb.setCharAt(0, 'Z') "Zbc"
setLength(n) 길이 자르기/늘리기 sb.setLength(3) (from "abcdef") "abc"
setLength(0) 비우기(재사용 관용구) sb.setLength(0) ""
length() 현재 길이 new StringBuilder("abc").length() 3
capacity() 내부 버퍼 크기 new StringBuilder("abc").capacity() 19
ensureCapacity(n) 미리 확보 sb.ensureCapacity(1000) void
trimToSize() 버퍼를 길이에 맞춤 sb.trimToSize() void
charAt(idx) 문자 조회 new StringBuilder("abc").charAt(1) 'b'
indexOf(s) / lastIndexOf(s) 검색 new StringBuilder("abcabc").lastIndexOf("c") 5
substring(a, b) String 추출 new StringBuilder("abcdef").substring(2, 4) "cd"
isEmpty() 비었는지 (JDK 15+) new StringBuilder().isEmpty() true
chars() IntStream new StringBuilder("ab").chars().sum() 195
compareTo(sb) 사전순 비교 (JDK 11+) new StringBuilder("a").compareTo(new StringBuilder("b")) -1
toString() String으로 확정 sb.toString() String
equals(o) 참조 비교 (내용 비교 아님!) new StringBuilder("a").equals(new StringBuilder("a")) false
  • StringBuilder는 스레드 안전하지 않다. 여러 스레드가 공유하면 StringBuffer(동기화) 또는 스레드별 인스턴스.
  • 루프에서 +=로 문자열을 누적하면 O(n²). 반복 결합은 무조건 StringBuilder.
  • 내용 비교는 sb.toString().equals(...) 또는 "a".contentEquals(sb).
12Character 유틸
메서드/시그니처 설명 한 줄 예제 결과
Character.isDigit(ch) 숫자 Character.isDigit('7') true
Character.isLetter(ch) 문자(한글 포함) Character.isLetter('가') true
Character.isLetterOrDigit(ch) 문자 또는 숫자 Character.isLetterOrDigit('_') false
Character.isAlphabetic(cp) 알파벳 속성 Character.isAlphabetic('a') true
Character.isUpperCase(ch) 대문자 Character.isUpperCase('A') true
Character.isLowerCase(ch) 소문자 Character.isLowerCase('a') true
Character.isWhitespace(ch) 공백(탭/개행 포함) Character.isWhitespace('\t') true
Character.isSpaceChar(ch) 유니코드 공백 문자 Character.isSpaceChar(' ') true
Character.toUpperCase(ch) 대문자로 Character.toUpperCase('a') 'A'
Character.toLowerCase(ch) 소문자로 Character.toLowerCase('A') 'a'
Character.toString(cp) 코드포인트 → 문자열 Character.toString(65) "A"
Character.toChars(cp) 코드포인트 → char[] new String(Character.toChars(0x1F600)) "😀"
Character.isSurrogate(ch) 서로게이트 쌍 여부 Character.isSurrogate("😀".charAt(0)) true
Character.getType(ch) 유니코드 카테고리 Character.getType('가') == Character.OTHER_LETTER true
Character.UnicodeBlock.of(ch) 블록 Character.UnicodeBlock.of('가') HANGUL_SYLLABLES
Character.compare(a, b) 비교 Character.compare('a', 'b') -1
Character.isJavaIdentifierStart(ch) 식별자 시작 가능 Character.isJavaIdentifierStart('
#39;)
true
Character.MAX_VALUE 최대 char 값 (int) Character.MAX_VALUE 65535
13실무 스니펫

스니펫 1: 개인정보 마스킹 (이름/전화/카드/이메일)

이름은 가운데를 *로, 전화번호는 중간 자리를, 카드는 가운데 8자리를 가린다. 전화번호는 하이픈 유무에 관계없이 처리되도록 -?를 쓴다. 이메일은 앞 2글자만 남기고 @ 앞까지 마스킹한다(후방·전방 탐색 활용).

java
static String maskName(String name) {
    if (name == null || name.length() < 2) return name;
    if (name.length() == 2) return name.charAt(0) + "*";
    return name.charAt(0) + "*".repeat(name.length() - 2) + name.charAt(name.length() - 1);
}
static String maskPhone(String phone) {
    return phone.replaceAll("(\\d{3})-?(\\d{3,4})-?(\\d{4})", "$1-****-$3");
}
static String maskCard(String card) {
    return card.replaceAll("(\\d{4})-(\\d{4})-(\\d{4})-(\\d{4})", "$1-****-****-$4");
}
static String maskEmail(String email) {
    return email.replaceAll("(?<=.{2}).(?=[^@]*@)", "*");
}

maskName("홍길동");  maskName("김철");  maskName("남궁민수");
// 출력: 홍*동  김*  남**수
maskPhone("010-1234-5678");  maskPhone("01012345678");
// 출력: 010-****-5678  010-****-5678
maskCard("1234-5678-9012-3456");
// 출력: 1234-****-****-3456
maskEmail("hong.gildong@example.com");
// 출력: ho**********@example.com

스니펫 2: CSV 한 줄 파싱 (따옴표·이스케이프 처리)

split(",")는 "Kim, Chul"처럼 따옴표 안의 콤마를 처리하지 못한다. 상태(따옴표 안/밖)를 추적하는 한 번의 순회로 처리하고, ""는 따옴표 하나로 해석한다. 실제 서비스에서는 OpenCSV 같은 라이브러리를 쓰되, 원리는 이렇다.

java
static List<String> parseCsvLine(String line) {
    List<String> out = new ArrayList<>();
    StringBuilder cur = new StringBuilder();
    boolean inQuotes = false;
    for (int i = 0; i < line.length(); i++) {
        char c = line.charAt(i);
        if (inQuotes) {
            if (c == '"') {
                if (i + 1 < line.length() && line.charAt(i + 1) == '"') { cur.append('"'); i++; }
                else inQuotes = false;
            } else cur.append(c);
        } else {
            if (c == '"') inQuotes = true;
            else if (c == ',') { out.add(cur.toString()); cur.setLength(0); }
            else cur.append(c);
        }
    }
    out.add(cur.toString());
    return out;
}

parseCsvLine("1,\"Kim, Chul\",\"say \"\"hi\"\"\",,30");
// 출력: [1, Kim, Chul, say "hi", , 30]

스니펫 3: 카멜 ↔ 스네이크 변환

DB 컬럼(user_first_name)과 자바 필드(userFirstName) 사이 변환. 카멜→스네이크는 소문자/숫자 뒤의 대문자 앞에 _를 넣고, HTTPServer처럼 대문자 연속인 경우도 처리하기 위해 두 번째 치환을 추가했다.

java
static String camelToSnake(String s) {
    return s.replaceAll("([a-z0-9])([A-Z])", "$1_$2")
            .replaceAll("([A-Z])([A-Z][a-z])", "$1_$2")
            .toLowerCase();
}
static String snakeToCamel(String s) {
    StringBuilder sb = new StringBuilder();
    boolean upper = false;
    for (char c : s.toLowerCase().toCharArray()) {
        if (c == '_') { upper = true; continue; }
        sb.append(upper ? Character.toUpperCase(c) : c);
        upper = false;
    }
    return sb.toString();
}

camelToSnake("userFirstName");  camelToSnake("HTTPServerURL");  camelToSnake("id");
// 출력: user_first_name  http_server_url  id
snakeToCamel("user_first_name");  snakeToCamel("ID");
// 출력: userFirstName  id

스니펫 4: 널 안전 비교와 기본값

a.equals(b)는 a가 null이면 NPE. 세 가지 관용구: 상수를 앞에 두기, Objects.equals, Objects.toString(x, default). Optional.ofNullable(...).map(...).orElse(...)는 체인이 길 때 유용하다.

java
String status = null;

"ACTIVE".equals(status);                 // 출력: false (NPE 없음)
Objects.equals(status, "ACTIVE");        // 출력: false
Objects.equals(null, null);              // 출력: true
Objects.toString(status, "-");           // 출력: -
Objects.requireNonNullElse(status, "N/A"); // 출력: N/A
Optional.ofNullable(status).map(String::strip).filter(s -> !s.isEmpty()).orElse("(empty)");
// 출력: (empty)

// 널 안전 isBlank
static boolean isBlank(String s) { return s == null || s.isBlank(); }

스니펫 5: 고정 길이 패딩 (숫자 0 채움, 좌/우 정렬, 잘라내기)

주문번호 000042, 고정폭 리포트, 전문(전문 통신) 필드 채우기에 쓴다. String.format은 자르지 않으므로 최대 길이가 있으면 substring으로 먼저 자른다. 한글은 화면 폭이 2칸이지만 length()는 1이라 콘솔 정렬이 어긋난다(바이트 기반 패딩이 필요하면 getBytes 길이로 계산).

java
static String padLeft(String s, int width, char pad) {
    return s.length() >= width ? s : String.valueOf(pad).repeat(width - s.length()) + s;
}
static String padRight(String s, int width, char pad) {
    return s.length() >= width ? s : s + String.valueOf(pad).repeat(width - s.length());
}
static String fixed(String s, int width) {           // 넘치면 자르고, 모자라면 공백 채움
    if (s.length() > width) return s.substring(0, width);
    return String.format("%-" + width + "s", s);
}

padLeft("42", 6, '0');      // 출력: 000042
padRight("ab", 5, '.');     // 출력: ab...
padLeft("toolong", 3, ' '); // 출력: toolong (자르지 않음)
fixed("한글", 6);           // 출력: [한글    ]
fixed("abcdefgh", 6);       // 출력: [abcdef]
String.format("%05d", 42);  // 출력: 00042

스니펫 6: 자주 쓰는 한 줄 유틸 모음

isNumeric, capitalize, 단어 뒤집기, 출현 횟수, 숫자만 남기기. 각각 정규식/indexOf 루프/Collections.reverse를 쓴다.

java
static boolean isNumeric(String s) { return s != null && s.matches("-?\\d+(\\.\\d+)?"); }
static String capitalize(String s) { return s.isEmpty() ? s : Character.toUpperCase(s.charAt(0)) + s.substring(1); }
static String reverseWords(String s) {
    List<String> w = Arrays.asList(s.split(" "));
    Collections.reverse(w);
    return String.join(" ", w);
}
static int countOccur(String s, String sub) {
    int count = 0, idx = 0;
    while ((idx = s.indexOf(sub, idx)) != -1) { count++; idx += sub.length(); }
    return count;
}

isNumeric("123"); isNumeric("12.3"); isNumeric("-5"); isNumeric(""); isNumeric("1e3");
// 출력: true true true false false
capitalize("hello");                        // 출력: Hello
reverseWords("hello big world");            // 출력: world big hello
countOccur("banana", "an");                 // 출력: 2
"010-1234-5678".replaceAll("[^0-9]", "");   // 출력: 01012345678
new StringBuilder("abc").reverse();         // 출력: cba

스니펫 7: 정규식 재사용과 검증 유틸

검증 정규식은 static final Pattern으로 한 번만 컴파일한다. matcher(...).matches()가 String.matches보다 루프에서 훨씬 빠르다.

java
private static final Pattern PHONE = Pattern.compile("^01[016789]-?\\d{3,4}-?\\d{4}$");
private static final Pattern EMAIL = Pattern.compile("^[\\w.+-]+@[\\w-]+(\\.[\\w-]+)+$");
private static final Pattern KOREAN_NAME = Pattern.compile("^[가-힣]{2,5}$");

PHONE.matcher("010-1234-5678").matches();     // 출력: true
EMAIL.matcher("a.b+c@ex-ample.co.kr").matches(); // 출력: true
KOREAN_NAME.matcher("홍gil").matches();        // 출력: false

// 텍스트에서 모든 전화번호 추출
Pattern.compile("01[016789]-\\d{3,4}-\\d{4}").matcher("A 010-1111-2222, B 011-333-4444")
       .results().map(MatchResult::group).toList();
// 출력: [010-1111-2222, 011-333-4444]
14자주 하는 실수

❌ 실수 1: ==로 문자열 비교

java
String h = "hel";
("hel" + "lo") == "hello";   // true  — 컴파일 타임 상수라 Pool 공유
(h + "lo") == "hello";       // false — 런타임 결합은 새 객체
(h + "lo").equals("hello");  // true  ← 항상 이렇게

❌ 실수 2: 불변인 String의 반환값을 버림

잘못 올바름
s.toUpperCase(); (s는 그대로) s = s.toUpperCase();
s.replace("a", "b"); String r = s.replace("a", "b");
s.strip(); s = s.strip();

❌ 실수 3: replace와 replaceAll 혼동, split 메타문자

java
"a.b.c".replaceAll(".", "-");   // "-----"  (.은 모든 문자)
"a.b.c".replace(".", "-");      // "a-b-c"  (리터럴)
"a.b.c".split(".");             // []       (빈 배열!)
"a.b.c".split("\\.");           // [a, b, c]
"a|b".split("|");               // [a, |, b]
"a|b".split("\\|");             // [a, b]

❌ 실수 4: split이 끝의 빈 문자열을 버림

java
"a,b,,".split(",").length;       // 2  — 뒤의 빈 칸 2개가 사라짐
"a,b,,".split(",", -1).length;   // 4  — CSV 컬럼 수를 유지하려면 limit -1

❌ 실수 5: 루프에서 +로 문자열 누적

java
String s = "";
for (int i = 0; i < 100_000; i++) s += i;       // 매번 새 String 생성, O(n²)

StringBuilder sb = new StringBuilder();
for (int i = 0; i < 100_000; i++) sb.append(i);  // O(n)
String s = sb.toString();

❌ 실수 6: char 덧셈과 문자열 결합 순서

java
'a' + 'b'            // 195      (int 덧셈)
"" + 'a' + 'b'       // "ab"
1 + 2 + "3" + 4 + 5  // "3345"   (왼쪽부터: 3 → "33" → "334" → "3345")
"a" + null           // "anull"  (NPE 아님)

❌ 실수 7: StringBuilder.equals로 내용 비교

java
new StringBuilder("a").equals(new StringBuilder("a"));   // false (참조 비교)
new StringBuilder("a").toString().equals("a");           // true
"a".contentEquals(new StringBuilder("a"));               // true

❌ 실수 8: 인코딩 미지정

java
byte[] b = "한글".getBytes();            // 환경 의존 (JDK 18+ 기본 UTF-8이지만 명시 권장)
byte[] b = "한글".getBytes(StandardCharsets.UTF_8);   // 항상 6바이트
new String(b, StandardCharsets.UTF_8);                // "한글"
new String("한글".getBytes("EUC-KR"), StandardCharsets.UTF_8);  // "�ѱ�" 깨짐

❌ 실수 9: matches를 부분 일치로 착각

java
"abc1".matches("[a-z]+");                              // false (전체 일치)
Pattern.compile("[a-z]+").matcher("abc1").find();       // true  (부분 일치)
Pattern.compile("[a-z]+").matcher("abc1").lookingAt();  // true  (앞부분 일치)
15관련 레슨
  • 중급 04 java.lang 심화: String 불변성, String Pool, == vs equals, StringBuilder 내부, Wrapper 캐시
  • 초급 01 변수·데이터 타입·형변환: char와 int 변환, 리터럴
  • 고급 03 람다: transform, Pattern.asPredicate, 메서드 참조
  • 고급 04 스트림: chars(), lines(), Collectors.joining
  • 배치 02 파일 I/O: CSV 파싱, 인코딩 지정 읽기/쓰기
  • 명령어 05 java.time · BigDecimal · 기타: 정규식 치트표, String.format과 DecimalFormat 비교
목차
  • 한눈에 보기
  • 생성과 기본 정보
  • 비교
  • 검색
  • 추출과 변환
  • 분할과 결합
  • 포맷과 텍스트 블록
  • 정규식 (Pattern / Matcher)
  • 문자 ↔ 숫자 변환
  • 인코딩 (getBytes / new String / Base64)
  • StringBuilder 전 메서드
  • Character 유틸
  • 실무 스니펫
  • 자주 하는 실수
  • 관련 레슨
참고 영상 · 인터넷 연결 시 유튜브 검색이 열립니다자바 String · StringBuilder · 정규식명령어 String StringBuilder format
이전03 vi · tmux 명령어다음02 Collections · Arrays · Map