자바 String 소스 코드 를 분석 하고 Sting 이 왜 변 하지 않 는 지 소개 합 니 다.
무엇이 불가 변 대상 입 니까?
자바 에 서 는 String 류 가 변 하지 않 는 다 는 것 은 잘 알려 져 있다.그렇다면 가 변 적 이지 않 은 대상 은 무엇 일 까?대상 이 생 성 된 후에 상 태 를 바 꿀 수 없다 면 이 대상 은 변 할 수 없다 고 생각 할 수 있다.상 태 를 바 꿀 수 없다 는 뜻 은 대상 안의 구성원 변 수 를 바 꿀 수 없다 는 것 이다.기본 데이터 형식의 값 을 포함 하여 바 꿀 수 없고 인용 유형의 변 수 는 다른 대상 을 가리 킬 수 없고 인용 유형 이 가리 키 는 대상 의 상태 도 바 꿀 수 없다 는 것 이다.
대상 과 대상 을 구분 하 는 인용
자바 초보 자 에 대해 서 는 String 이 가 변 적 이지 않 은 대상 이라는 것 에 대해 항상 의문 이 있다.다음 코드 보기:
String s = "ABCabc";
System.out.println("s = " + s);
s = "123456";
System.out.println("s = " + s);
인쇄 결과:
s = ABCabc
s = 123456
먼저 String 대상 s 를 만 든 다음 에 s 의 값 을'ABCabc'로 만 든 다음 에 s 의 값 을'123456'으로 만 듭 니 다.인쇄 결 과 를 통 해 알 수 있 듯 이 s 의 값 이 확실히 바 뀌 었 다.그럼 왜 String 대상 이 변 하지 않 는 다 고 해요?사실 여기에 잘못된 부분 이 존재 합 니 다.s 는 String 대상 의 인용 일 뿐 대상 자체 가 아 닙 니 다.대상 은 메모리 에 메모리 구역 입 니 다.구성원 변수 가 많 을 수록 이 메모리 구역 이 차지 하 는 공간 이 큽 니 다.4 바이트 에 불과 한 데 이 터 를 인용 하여 가리 키 는 대상 의 주 소 를 저장 하고 이 주 소 를 통 해 대상 에 접근 할 수 있 습 니 다.즉,s 는 하나의 인용 일 뿐 구체 적 인 대상 을 가리 키 는데 s="123456"이다.이 코드 가 실 행 된 후에 새로운 대상 인'123456'을 만 들 었 고 s 를 참조 하여 이 마음의 대상 을 다시 가 리 켰 습 니 다.원래 의 대상 인'ABCabc'는 메모리 에 존재 하 며 변 하지 않 았 습 니 다.메모리 구 조 는 다음 그림 과 같 습 니 다.
자바 와 C++의 차이 점 은 자바 에서 대상 자 체 를 직접 조작 할 수 없다 는 것 이다.모든 대상 은 하나의 인용 으로 가리 키 며 이 인용 을 통 해 대상 자 체 를 방문 해 야 한다.이 는 구성원 변수의 값 을 가 져 오고 대상 의 구성원 변 수 를 바 꾸 며 대상 을 호출 하 는 방법 등 을 포함한다.C++에는 인용,대상,포인터 세 가지 가 존재 하 는데 이 세 가 지 는 모두 대상 에 접근 할 수 있다.사실 자바 의 인용 과 C++의 지침 은 개념 적 으로 비슷 하 다.그들 은 모두 저장 대상 이 메모리 에 있 는 주소 값 이다.다만 자바 에서 인용 은 일부 유연성 을 잃 었 다.예 를 들 어 자바 의 인용 은 C+의 지침 처럼 가감 연산 을 할 수 없다.
왜 String 대상 은 변 하지 않 습 니까?
String 의 불가 변성 을 이해 하려 면 먼저 String 클래스 에 어떤 멤버 변수 가 있 는 지 살 펴 보 세 요.JDK 1.6 에서 String 의 멤버 변 수 는 다음 과 같은 몇 가지 가 있 습 니 다.
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence
{
/** The value is used for character storage. */
private final char value[];
/** The offset is the first index of the storage that is used. */
private final int offset;
/** The count is the number of characters in the String. */
private final int count;
/** Cache the hash code for the string */
private int hash; // Default to 0
JDK 1.7 에서 String 류 는 일부 변경 을 했 는데 주로 substring 방법 이 실 행 될 때의 행 위 를 바 꾸 었 는데 이것 은 본 고의 주제 와 관련 이 없다.JDK 1.7 에서 String 클래스 의 주요 멤버 변 수 는 두 개 만 남 았 습 니 다.
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
/** The value is used for character storage. */
private final char value[];
/** Cache the hash code for the string */
private int hash; // Default to 0
이상 의 코드 를 통 해 알 수 있 듯 이 자바 에서 String 류 는 문자 배열 에 대한 패키지 입 니 다.JDK 6 에서 value 는 String 이 봉 인 된 배열 이 고,offset 은 String 이 이 value 배열 에서 시작 하 는 위치 이 며,count 는 String 이 차지 하 는 문자 의 개수 입 니 다.JDK 7 에 서 는 하나의 value 변수 만 있 습 니 다.즉,value 의 모든 문 자 는 String 이라는 대상 에 속 합 니 다.이 변 화 는 본문의 토론 에 영향 을 주지 않 는 다.그 밖 에 hash 구성원 변 수 는 이 String 대상 의 해시 값 캐 시 입 니 다.이 구성원 변 수 는 본 논문 의 토론 과 무관 합 니 다.자바 에 서 는 배열 도 대상 입 니 다.그래서 value 도 하나의 인용 일 뿐 진정한 배열 대상 을 가리킨다.사실 String s="ABCabc"를 실 행 했 습 니 다.이 코드 이후 에 진정한 메모리 레이아웃 은 다음 과 같 아야 합 니 다.value,offset,count 세 변 수 는 모두 private 이 며,setValue,setOffset,setCount 등 공공 방법 으로 이 값 을 수정 하지 않 았 기 때문에 String 류 의 외부 에 서 는 String 을 수정 할 수 없습니다.초기 화 되면 수정 할 수 없고 String 류 외부 에 서 는 이 세 멤버 를 방문 할 수 없다 는 것 이다.그 밖 에 value,offset,count 세 변 수 는 모두 final 입 니 다. String 클래스 내부 에 서 는 이 세 값 이 초기 화 되 더 라 도 바 꿀 수 없다 는 것 이다.그래서 String 대상 은 가 변 적 이지 않다 고 볼 수 있 습 니 다.
그러면 String 에 서 는 변 경 된 값 을 얻 을 수 있 는 방법 이 분명히 존재 합 니 다.이 방법 들 은 substring,replace,replace All,toLowerCase 등 을 포함한다.예 를 들 어 다음 코드:
String a = "ABCabc";
System.out.println("a = " + a);
a = a.replace('A', 'a');
System.out.println("a = " + a);
인쇄 결과:
a = ABCabc
a = aBCabc
그러면 a 의 값 이 바 뀐 것 처럼 보이 지만 사실은 똑 같은 잘못된 부분 이다.다시 한 번 설명 하 자 면 a 는 인용 일 뿐 실제 문자열 대상 이 아니다.a.replace(A','a')를 호출 할 때 방법 내부 에 새로운 String 대상 을 만 들 고 이 마음의 대상 을 인용 a 에 다시 부여 했다.String 에서 replace 방법의 원본 코드 는 문 제 를 설명 할 수 있 습 니 다.독자 가 스스로 다른 방법 을 볼 수 있 는 것 은 모두 방법 내부 에서 새로운 String 대상 을 다시 만 들 고 이 새로운 대상 으로 돌아 가 는 것 이다.원래 의 대상 은 변 하지 않 을 것 이다.replace,substring,toLowerCase 등 방법 에 반환 값 이 존재 하 는 이유 다.하 긴 왜 아래 와 같이 호출 하면 대상 의 값 을 바 꾸 지 않 습 니까?
String ss = "123456";
System.out.println("ss = " + ss);
ss.replace('1', '0');
System.out.println("ss = " + ss);
인쇄 결과:
ss = 123456
ss = 123456
String 대상 은 정말 변 하지 않 나 요?앞에서 알 수 있 듯 이 String 의 구성원 변 수 는 private final 입 니 다.즉,초기 화 된 후에 변경 할 수 없습니다.그러면 이 몇 명의 구성원 중에서 value 는 비교적 특수 하 다.왜냐하면 그 는 진정한 대상 이 아니 라 인용 변수 이기 때문이다.value 는 final 수식 입 니 다.즉,final 은 다른 배열 대상 을 가리 키 지 않 습 니 다.그러면 value 가 가리 키 는 배열 을 바 꿀 수 있 습 니까?예 를 들 어 배열 의 특정한 위치 에 있 는 문 자 를 밑줄''로 바 꾸 는 것 이다.적어도 우리 가 쓴 일반 코드 에 서 는 할 수 없습니다.왜냐하면 우 리 는 이 value 인용 에 접근 할 수 없고 이 인용 을 통 해 배열 을 수정 할 수 없 기 때 문 입 니 다.
그렇다면 어떤 방식 으로 개인 멤버 를 방문 할 수 있 을 까?맞습니다.반사 로 String 대상 의 value 속성 을 반사 하여 얻 은 value 인용 을 통 해 배열 의 구 조 를 바 꿀 수 있 습 니 다.다음은 인 스 턴 스 코드 입 니 다.
public static void testReflection() throws Exception {
// "Hello World", s
String s = "Hello World";
System.out.println("s = " + s); //Hello World
// String value
Field valueFieldOfString = String.class.getDeclaredField("value");
// value
valueFieldOfString.setAccessible(true);
// s value
char[] value = (char[]) valueFieldOfString.get(s);
// value 5
value[5] = '_';
System.out.println("s = " + s); //Hello_World
}
인쇄 결과:
s = Hello World
s = Hello_World
이 과정 에서 s 는 항상 같은 String 대상 을 인용 하지만 재 반사 전후 에 이 String 대상 에 변화 가 생 겼 다.즉,반 사 를 통 해 이른바'불가 변'대상 을 수정 할 수 있다 는 것 이다.하지만 보통 우 리 는 이렇게 하지 않 는 다.이 반 사 된 실례 는 또 하나의 문 제 를 설명 할 수 있다.만약 에 한 대상 이 다른 대상 의 상 태 를 바 꿀 수 있다 면 이 대상 은 바 꿀 수 없 는 대상 이 아 닐 가능성 이 높다.예 를 들 어 하나의 Car 대상 은 Wheel 대상 을 조합 했다.비록 이 Wheel 대상 은 private final 이 라 고 밝 혔 지만 이 Wheel 대상 내부 의 상 태 는 바 뀔 수 있 기 때문에 Car 대상 이 변 하지 않 는 다 는 것 을 잘 보장 할 수 없다.읽 어 주 셔 서 감사합니다. 여러분 에 게 도움 이 되 기 를 바 랍 니 다.본 사이트 에 대한 여러분 의 지지 에 감 사 드 립 니 다!
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
JPA + QueryDSL 계층형 댓글, 대댓글 구현(2)이번엔 전편에 이어서 계층형 댓글, 대댓글을 다시 리팩토링해볼 예정이다. 이전 게시글에서는 계층형 댓글, 대댓글을 구현은 되었지만 N+1 문제가 있었다. 이번에는 그 N+1 문제를 해결해 볼 것이다. 위의 로직은 이...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.