자바 문자열은 정말 불변일까요?
.String
Java java java java java음음음음음 java java java java java java java java java java java java java java java java java.
String s1 = "Hello World";
String s2 = "Hello World";
String s3 = s1.substring(6);
System.out.println(s1); // Hello World
System.out.println(s2); // Hello World
System.out.println(s3); // World
Field field = String.class.getDeclaredField("value");
field.setAccessible(true);
char[] value = (char[])field.get(s1);
value[6] = 'J';
value[7] = 'a';
value[8] = 'v';
value[9] = 'a';
value[10] = '!';
System.out.println(s1); // Hello Java!
System.out.println(s2); // Hello Java!
System.out.println(s3); // World
이로그 은렇 ?렇 렇? ??? 왜 of의 은 and and의 입니까?s1
★★★★★★★★★★★★★★★★★」s2
변하지는 않았다s3
String
는 불변*입니다.단, 퍼블릭 API를 사용하여 변경할 수 없습니다.
여기서 하고 있는 것은 리플렉션을 사용하여 일반 API를 우회하는 것입니다.마찬가지로 Enum 값을 변경하거나 Integer autoboxing에 사용되는 조회 테이블을 변경할 수 있습니다.
그 는요.s1
★★★★★★★★★★★★★★★★★」s2
value는 둘 다 stringchange value를 참조하는 입니다.컴파일러는 이것을 실시합니다(다른 답변에 기재되어 있습니다).
★★s3
사실 전혀 놀랍지 않았다. 왜냐하면 나는 그것이 함께 할 것이라고 생각했기 때문이다.value
array(Java 7u6 이전 버전의 Java에서 사용).단, 소스코드를 보면String
, 이렇게 볼 수 요.value
됩니다(「복사」를 사용).Arrays.copyOfRange(..)
이 때문에 변경되지 않습니다.
할 수 .SecurityManager
이러한 작업을 수행하기 위한 악성 코드를 방지합니다.그러나 일부 라이브러리는 이러한 종류의 반사 트릭(일반적으로 ORM 도구, AOP 라이브러리 등)을 사용하는 데 의존합니다.
썼어요. * ) 는 저 * * 、 * * * * * 。String
s는 실제로 불변하는 것이 아니라 단지 "효과적인 불변"입니다.는 현재 되어 있는 것은, 재재 this this ationationationationationationationationation this this this this this this this this this this this this this this this this this this 에서 오해를 일으킬 수 .String
서 는, 「」입니다value
private final
할 수 있는 접근수식자를 되지 않도록 주의해야 합니다
이 토픽이 압도적으로 인기가 있는 것 같기 때문에, 이하에, 한층 더 읽어 주세요.Heinz Kabutz의 Reflection Madness는 JavaZone 2009에서 다른 성찰과 함께 OP의 많은 문제를 다루고 있습니다.음... 미친 짓이지
이것은 왜 이것이 때때로 유용한지를 다룹니다.그리고 대부분의 경우 피해야 하는 이유:-)
Java에서 두 문자열 원시 변수가 동일한 리터럴로 초기화되면 두 변수에 동일한 참조가 할당됩니다.
String Test1="Hello World";
String Test2="Hello World";
System.out.println(test1==test2); // true
그것이 그 비교가 사실로 돌아오는 이유이다. 이렇게 하다를 사용해서 요.substring()
같은 것을 가리키는 대신 새로운 문자열을 만듭니다.
리플렉션을 사용하여 문자열에 액세스하면 실제 포인터를 얻을 수 있습니다.
Field field = String.class.getDeclaredField("value");
field.setAccessible(true);
있는 포인터를 있는 문자열이 바뀌게 .s3
는 새로운 됩니다.substring()
변하지 않을 거예요.
String의 불변성을 회피하기 위해 반사를 사용하고 있습니다.이것은 "공격"의 일종입니다.
이와 같이 작성할 수 있는 예는 많이 있지만(예를 들어 오브젝트 인스턴스화도 가능), String이 "불변"하지 않는 것은 아닙니다.
이 타입의 코드가, 가능한 한 빨리(GC전) 메모리에서 패스워드를 클리어 하는 등, 고객에게 유리하게 사용되어 「좋은 코딩」이 되는 경우가 있습니다.
보안 관리자에 따라 코드를 실행하지 못할 수 있습니다.
리플렉션을 사용하여 문자열 개체의 "실장 세부 정보"에 액세스하고 있습니다.불변성은 객체의 퍼블릭인터페이스의 기능입니다.
가시성 수정자 및 최종(즉, 불변성)은 Java에서 악성 코드에 대한 측정이 아닙니다. 이는 실수로부터 보호하고 코드를 보다 유지 보수하기 위한 도구일 뿐입니다(시스템의 주요 세일즈 포인트 중 하나).에, like음, access that the for for for for의 백킹 문자 등, 내부 실장의 한 것에 할 수 .String
스스스
것이 다 입니다.String
은 「변경」의으로는 「변경」s1
. 캐싱등 Java String 리터럴의 특정 속성으로, 캐시와 같이 자동으로 삽입됩니다.String Literal의 String Literal." " 가 붙은 new
자동으로 인턴이 되지 않아 이 효과를 볼 수 없습니다.
#substring
최근까지(Java 7u6)는 유사한 방식으로 작동했고, 이것이 질문의 원래 버전에서 동작을 설명했을 것입니다.새로운 backing char 배열을 작성한 것이 아니라 원래 String에서 다시 사용한 배열을 작성했습니다.이 배열의 일부만 표시하기 위해 오프셋과 길이를 사용하는 새로운 String 객체를 작성했을 뿐입니다.이것은 Strings가 불변하기 때문에 일반적으로 작동한다. - 당신이 그것을 회피하지 않는 한.의 이 #substring
또한 원본 String에서 생성된 더 짧은 하위 문자열이 아직 존재하면 원본 String 전체가 가비지 수집이 되지 않습니다.
및 에서는 "Java"의 .#substring
.
문자열 불변성은 인터페이스의 관점에서 볼 때 발생합니다.리플렉션을 사용하여 인터페이스를 바이패스하고 String 인스턴스의 내부를 직접 변경합니다.
s1
★★★★★★★★★★★★★★★★★」s2
둘 다 같은 "intern" String 인스턴스에 할당되어 있기 때문에 둘 다 변경됩니다.스트링의 등화와 인터닝에 관한 이 기사에서 그 부분에 대해 조금 더 자세히 알 수 있습니다.샘플 코드에 있는 것을 알면 놀라실지도 모릅니다.s1 == s2
true
!
어떤 버전의 Java를 사용하고 있습니까?Java 1.7.0_06부터 Oracle은 String, 특히 하위 문자열의 내부 표현을 변경했습니다.
새로운 패러다임에서는 String offset 및 count 필드가 삭제되어 하위 문자열은 기본 char [] 값을 공유하지 않게 되었습니다.
이 변경으로 인해 반영되지 않고 발생할 수 있습니다(--?).
여기에는 두 가지 질문이 있습니다.
- 끈은 정말 불변의 것인가?
- 왜 s3는 변경되지 않는 거죠?
포인트 1: 컴퓨터에는 ROM 이외에는 불변의 메모리는 없습니다.오늘날에는 ROM조차 쓸 수 있는 경우가 있습니다.메모리 주소에 쓸 수 있는 코드가 항상 있습니다(관리 환경을 회피하는 커널 코드인지 네이티브 코드인지에 관계없이).그래서 "현실"에서 그들은 완전히 불변하는 것은 아니다.
포인트 2: 이는 서브스트링이 어레이를 복사하는 새로운 문자열 인스턴스를 할당하고 있을 가능성이 높기 때문입니다.복사를 하지 않는 방법으로 서브스트링을 실장하는 것은 가능하지만, 그것이 복사를 의미하는 것은 아닙니다.여기에는 트레이드오프가 관련되어 있습니다.
를 들어, 를 a to to for 、 for 、 에 、 에 、 for 、 for 、 for 、 for 、 for 、 for 、 for 、 for 、 for 、 for 。reallyLargeString.substring(reallyLargeString.length - 2)
많은 양의 메모리가 활성화되어 있는지, 아니면 몇 바이트만 활성화되어 있는지요?
이는 서브스트링이 어떻게 구현되는지에 따라 달라집니다.딥 복사는 메모리를 적게 유지하지만 실행 속도는 약간 느려집니다.얕은 복사는 더 많은 메모리를 유지하지만 더 빠릅니다.또한 딥 복사를 사용하면 스트링 개체와 해당 버퍼를 2개의 개별 힙 할당이 아닌 1개의 블록에 할당할 수 있으므로 힙 조각화를 줄일 수 있습니다.
어느 경우든 JVM이 서브스트링 호출에 딥 복사본을 사용하도록 선택한 것 같습니다.
@haraldK의 답변에 덧붙이자면, 이것은 앱에 심각한 영향을 미칠 수 있는 보안 해킹입니다.
String Pool(스트링 풀)입니다.이 " " " 로 된 String s = "Hello World";
향후 재사용 가능성을 높이기 위해 특별한 오브젝트 풀에 배치됩니다.문제는 컴파일러가 컴파일 시에 수정된 버전에 대한 참조를 배치하고 사용자가 런타임에 이 풀에 저장된 문자열을 수정하면 코드 내의 모든 참조가 수정된 버전을 가리킵니다.이로 인해 다음과 같은 버그가 발생합니다.
System.out.println("Hello World");
인쇄:
Hello Java!
그런 위험한 문자열에 대해 무거운 계산을 실행할 때 또 다른 문제가 있었습니다.계산 중에 1000000번 중 1번 정도 버그가 발생하여 결과를 알 수 없습니다.JIT를 끄면 문제를 발견할 수 있었습니다.JIT를 끄면 항상 같은 결과가 나옵니다.JIT 최적화 계약 중 일부를 어긴 String 보안 해킹 때문인 것 같습니다.
풀링의 개념에 따라 같은 값을 포함하는 모든 String 변수는 동일한 메모리 주소를 가리킵니다.따라서 "Hello World"의 동일한 값을 포함하는 s1과 s2는 동일한 메모리 위치(예: M1)를 가리킵니다.
한편, s3에는 「월드」가 포함되어 있기 때문에, 다른 메모리 할당(M2)을 가리킵니다.
여기서 일어나고 있는 것은 (char [ ]값을 사용하여) S1의 값이 변경되고 있는 것입니다.따라서 s1과 s2가 가리키는 메모리 위치 M1의 값이 변경되었다.
그 결과 메모리 위치 M1이 변경되어 s1, s2의 값이 변화한다.
그러나 로케이션 M2의 값은 변경되지 않고 그대로 유지되므로 s3은 원래 값과 동일합니다.
s3가 실제로 변경되지 않는 이유는 Java에서 서브스트링을 실행하면 서브스트링의 값 문자 배열이 내부적으로 복사되기 때문입니다(Arrays.copyOfRange() 사용).
s1과 s2는 Java에서는 둘 다 같은 내부 문자열을 참조하기 때문에 동일합니다.자바어로 되어 있습니다.
문자열은 불변이지만 리플렉션을 통해 String 클래스를 변경할 수 있습니다.방금 String 클래스를 실시간으로 가변으로 재정의했습니다.필요에 따라 메서드를 퍼블릭, 프라이빗 또는 스태틱으로 재정의할 수 있습니다.
[집에서는 하지 말라고 대답하는 것이 당연하다고 생각하기 때문에 일부러 억지스러운 대답이라고 주장합니다]
이다.field.setAccessible(true);
개인 필드에 대한 액세스를 허용함으로써 공용 API를 위반할 수 있습니다.이는 보안 매니저를 설정함으로써 잠글 수 있는 거대한 보안 구멍입니다.
질문의 현상은 구현 세부사항입니다.이러한 코드 행을 사용하여 리플렉션을 통해 액세스 수식자를 위반하지 않을 경우 확인할 수 없습니다.2개의 (통상) 불변의 문자열은 같은 문자 배열을 공유할 수 있습니다.서브스트링이 동일한 어레이를 공유할 수 있는지 여부와 개발자가 공유하기로 생각했는지 여부에 따라 달라집니다.통상, 이러한 실장의 상세는 보이지 않습니다.이러한 상세 정보는, 액세스 수식자를 그 코드의 행으로 머리를 관통하지 않는 한, 알 필요가 없습니다.
리플렉션을 사용한 액세스 수식자를 위반하지 않고는 경험할 수 없는 세부 사항에 의존하는 것은 좋은 생각이 아닙니다.해당 클래스의 소유자는 일반 공개 API만 지원하며 향후 구현 변경을 자유롭게 수행할 수 있습니다.
총을 가지고 있을 때 코드 라인이 매우 유용하다고 말했지만, 그런 위험한 일을 강요하는 것은 당신의 머리를 쥐어주고 있다.그 뒷문을 사용하는 것은 보통 더 나은 라이브러리 코드로 업그레이드해야 하는 코드 냄새입니다.이 위험한 코드 라인의 또 다른 일반적인 용도는 "부두 프레임워크"(orm, 주입 용기 등)를 작성하는 것입니다.많은 사람들이 (찬성이나 반대 모두) 그러한 틀에 대해 종교적이기 때문에, 나는 대다수의 프로그래머들이 거기에 갈 필요가 없다고 말함으로써 불꽃 전쟁을 일으키는 것을 피할 것이다.
문자열은 JVM 힙메모리의 영속적인 영역에 작성됩니다.네, 정말 불변하고 생성 후 변경할 수 없습니다.JVM에는 3가지 유형의 힙메모리가 있습니다.1 .젊은 세대 2구세대 3영구 세대
오브젝트가 생성되면 String 풀링용으로 예약된 젊은 세대 힙 영역 및 PermGen 영역으로 이동합니다.
자세한 내용은 다음 사이트에서 확인할 수 있습니다.Java에서의 가비지 컬렉션 기능
String 객체를 수정할 메서드가 없기 때문에 String은 본질적으로 불변입니다.그래서 String Builder와 String Buffer 클래스를 도입했습니다.
이것은 모든 것에 대한 빠른 안내서입니다.
// Character array
char[] chr = {'O', 'K', '!'};
// this is String class
String str1 = new String(chr);
// this is concat
str1 = str1.concat("another string's ");
// this is format
System.out.println(String.format(str1 + " %s ", "string"));
// this is equals
System.out.println(str1.equals("another string"));
//this is split
for(String s: str1.split(" ")){
System.out.println(s);
}
// this is length
System.out.println(str1.length());
//gives an score of the total change in the length
System.out.println(str1.compareTo("OK!another string string's"));
// trim
System.out.println(str1.trim());
// intern
System.out.println(str1.intern());
// character at
System.out.println(str1.charAt(5));
// substring
System.out.println(str1.substring(5, 12));
// to uppercase
System.out.println(str1.toUpperCase());
// to lowerCase
System.out.println(str1.toLowerCase());
// replace
System.out.println(str1.replace("another", "hello"));
// output
// OK!another string's string
// false
// OK!another
// string's
// 20
// 7
// OK!another string's
// OK!another string's
// o
// other s
// OK!ANOTHER STRING'S
// ok!another string's
// OK!hello string's
언급URL : https://stackoverflow.com/questions/20945049/is-a-java-string-really-immutable
'programing' 카테고리의 다른 글
Vee-validate - [Vue warn] :지시문을 확인하지 못했습니다. 검증 (0) | 2022.08.30 |
---|---|
vue js cli에 Axios가 정의되어 있지 않습니다. (0) | 2022.08.30 |
vue 속성 "$v"의 vuelidate가 렌더링 중에 액세스되었지만 인스턴스에서 정의되지 않았습니다. (0) | 2022.08.30 |
페이로드에 전달된 Vuex 어레이 변환 (0) | 2022.08.30 |
VueJ에 컴포넌트를 재장착하는 방법 (0) | 2022.08.30 |