Java String 클래스 소스 코드 분석
package java.lang;
코드 블록 1
public final class String
implements Comparable<String>, CharSequence {
// 1. 핵심 기본 배열: 문자열 각 문자 저장
private final char[] value;
// 2. 해시값 캐시, 기본값 0
private int hash;
// 기본 생성자: 빈 문자열 생성
public String() {
this.value = new char[0];
}
// 문자열 전달 생성자
public String(String original) {
this.value = original.value;
this.hash = original.hash;
}
1. 클래스 선언
public final class String
implements Comparable<String>, CharSequence
- public: 공개 클래스, 프로젝트 어디서든 사용 가능
- final: 상속 불가, 문자열 기본 구조 변경 방지
- Comparable: 비교 기능 제공, a.compareTo(b) 가능
- CharSequence: 문자열 시퀀스 규약, 문자 타입에 속함
2. 핵심 멤버 변수
// 1.底层存储内容
private final char[] value;
// 2. 哈希缓存
private int hash;
① private final char[] value · 문자열 실제 데이터 저장 위치 · "abc" 입력 시 내부적으로 ['a','b','c'] 배열로 저장 · final: 배열 주소 변경 불가 → 문자열 불변성의 근원 · private: 외부 접근 차단, 보장됨 ② private int hash · 해시값 저장, 초기값 = 0 · 용도: 한번 계산해 저장해두고 다음엔 바로 사용, 성능 향상
3. 기본 생성자
public String() {
this.value = new char[0];
}
· 빈 문자열 "" 생성 · 길이가 0인 빈 문자 배열 할당
4. 복사 생성자 (중요)
public String(String original) {
this.value = original.value;
this.hash = original.hash;
}
String s = new String("abc");
· 전달된 문자열의 value 배열 직접 재사용 · 데이터 복사 없음, 메모리 절약
코드 블록 2
// 문자열 길이 가져오기
public int length() {
return value.length;
}
// 빈 문자열 여부 확인
public boolean isEmpty() {
return value.length == 0;
}
// 인덱스로 단일 문자 가져오기
public char charAt(int index) {
if ((index < 0) || (index >= value.length)) {
throw new StringIndexOutOfBoundsException(index);
}
return value[index];
}
5. length() 메서드
public int length() {
return value.length;
}
· 문자열 길이 = 내부 문자 배열 길이
· 배열 길이 직접 반환
6. isEmpty() 빈 문자열 확인
public boolean isEmpty() {
return value.length == 0;
}
· 배열 길이가 0 → 빈 문자열 ""
7. charAt() 인덱스로 문자 가져오기
public char charAt(int index) {
if ((index < 0) || (index >= value.length)) {
throw new StringIndexOutOfBoundsException(index);
}
return value[index];
}
· 인덱스 범위 확인 먼저 수행 · 범위 초과 시 오류 발생 · 정상 범위면 배열 해당 위치 문자 반환
코드 블록 3
// 중요: 문자열 내용 비교
public boolean equals(Object anObject) {
// 1. 동일 객체면 true
if (this == anObject) {
return true;
}
// 2. String 타입 확인
if (anObject instanceof String) {
String anotherString = (String) anObject;
int n = value.length;
// 3. 길이 다르면 false
if (n == anotherString.value.length) {
char[] v1 = value;
char[] v2 = anotherString.value;
int i = 0;
// 4. 문자 하나씩 비교
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
// 중요: 해시값 계산 + 캐싱
public int hashCode() {
int h = hash;
// 계산된 적 없으면(h=0) 계산 시작
if (h == 0 && value.length > 0) {
char[] val = value;
// 계산식: h = 31 * h + 문자 코드
for (int i = 0; i < value.length; i++) {
h = 31 * h + val[i];
}
hash = h; // 계산값 캐싱, 다음엔 바로 사용
}
return h;
}
8. equals() 소스 코드
public boolean equals(Object anObject) {
// 1. 동일 객체인지 확인(주소 같음)
if (this == anObject) {
return true;
}
// 2. String 타입인지 확인
if (anObject instanceof String) {
String anotherString = (String) anObject;
int n = value.length;
// 3. 길이 다르면 바로 false
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
// 4. 문자 하나씩 비교
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
실행 흐름: this == anObject: 주소 같으면 → 바로 true String 타입 아님 → 바로 false 길이 다름 → 바로 false 하나씩 문자 비교 모두 같아야 true 반환 요약: == 주소 비교 equals 내용 비교
9. hashCode() 해시 메서드
public int hashCode() {
int h = hash;
// 계산된 적 없으면(h=0)이고 문자열이 비어있지 않으면
if (h == 0 && value.length > 0) {
char val[] = value;
// 해시 계산 공식
for (int i = 0; i < value.length; i++) {
h = 31 * h + val[i];
}
hash = h; // 계산된 값 캐싱
}
return h;
}
-
h = hash로 캐시 읽기
-
처음 사용 시 hash=0이므로 계산 진입
-
공식: h = 31 * h + 현재 문자 코드
-
계산 후 hash에 캐싱 저장
-
다음 호출 시 캐시 바로 사용, 재계산 안함
코드 블록 4
// 문자열 일부 추출
public String substring(int beginIndex) {
return new String(value, beginIndex, value.length - beginIndex);
}
// 문자열 연결
public String concat(String str) {
int len = value.length;
int otherLen = str.length();
char[] buf = new char[len + otherLen];
System.arraycopy(value, 0, buf, 0, len);
System.arraycopy(str.value, 0, buf, len, otherLen);
return new String(buf);
}
// 자기 자신 문자열 반환
public String toString() {
return this;
}
}
10. 문자열 일부 추출 substring()
public String substring(int beginIndex) {
return new String(value, beginIndex, value.length - beginIndex);
}
new String( char[] 원본배열, int 시작인덱스, int 추출길이 )
· value 현재 문자열의 기본 char[] 원본 문자 배열 · beginIndex 추출 시작 위치(몇 번째 문자부터 자를지) · value.length - beginIndex 전체 길이 - 시작 위치 = 추출할 문자 개수
- 원래 문자 배열 value를 직접 수정하지 않음
- 대신 새로운 String 객체 생성
- 추출된 내용을 새 문자열에 담아 반환
11. 문자열 연결 concat()
public String concat(String str) {
int len = value.length; // 원래 문자열 길이
int otherLen = str.length(); // 연결할 문자열 길이
char[] buf = new char[len + otherLen]; // [새 배열] 생성
System.arraycopy(value, 0, buf, 0, len); // 원본 문자열 복사
System.arraycopy(str.value, 0, buf, len, otherLen); // 연결 문자열 복사
return new String(buf); // [새 문자열] 반환
}
- 총 길이 = 원래 길이 + 연결할 길이 계산
- 더 큰 새 문자 배열 생성
- 두 문자열 모두 복사
- 마지막으로 새 String 객체 생성 반환
12. toString()
String 자체가 문자열이므로 그대로 자기 자신 반환.