字符内存转成字符串_字符串内存内部
字符內存轉成字符串
本文基于我對StackOverflow的回答 。 我正在嘗試解釋String類如何存儲文本,內部存儲和常量池如何工作。這里要理解的要點是String Java對象與其內容– private value字段下的char[]之間的區別。 String基本上是char[]數組的包裝器,將其封裝并使其無法修改,因此String可以保持不變。 另外, String類還記住該數組的實際部分(請參閱下文)。 這一切都意味著您可以有兩個不同的String對象(相當輕量)指向相同的char[] 。
我會告訴你一些例子,連同hashCode()的每個String和hashCode()內部的char[] value字段(我將其稱之為文本字符串從區分)。 最后,我將顯示javap -c -verbose輸出以及測試類的常量池。 請不要將類常量池與字符串文字池混淆。 它們并不完全相同。 另請參見了解javap的常量池輸出 。
先決條件
為了進行測試,我創建了一個實用程序方法來破壞String封裝:
private int showInternalCharArrayHashCode(String s) {final Field value = String.class.getDeclaredField("value");value.setAccessible(true);return value.get(s).hashCode(); }它將打印char[] value hashCode() ,有效地幫助我們了解此特定String是否指向相同的char[]文本。
一個類中的兩個字符串文字
讓我們從最簡單的示例開始。
Java代碼
String one = "abc"; String two = "abc";順便說一句,如果您只寫"ab" + "c" ,則Java編譯器將在編譯時執行級聯,并且生成的代碼將完全相同。 僅當在編譯時知道所有字符串時,此方法才有效。
類常量池
每個類都有自己的常量池 -常量值列表,如果它們在源代碼中多次出現,則可以重用。 它包括常見的字符串,數字,方法名稱等。
這是上面示例中常量池的內容:
要注意的重要事項是String常量對象( #2 )和字符串指向的Unicode編碼文本"abc" ( #38 )之間的區別。
字節碼
這是生成的字節碼。 請注意, one引用和two引用都分配有指向"abc"字符串的相同#2常量:
輸出量
對于每個示例,我將打印以下值:
這兩對相等并不奇怪:
one.value: 23583040 two.value: 23583040 one: 8918249 two: 8918249這意味著不僅兩個對象都指向相同的char[] (下面的相同文本),所以equals()測試將通過。 但更重要的是, one和two是完全相同的引用! 因此, one == two也是正確的。 顯然,如果one和two指向同一個對象,則one.value和two.value必須相等。
文字和new String() ?
Java代碼
現在,我們都在等待該示例–一個字符串文字和一個使用相同文字的新String 。 這將如何運作?
在源代碼中兩次使用了"abc"常量這一事實應該給您一些提示……
類常量池與上面相同。
字節碼
ldc #2; //String abc astore_1 //onenew #3; //class java/lang/String dup ldc #2; //String abc invokespecial #4; //Method java/lang/String."<init>":(Ljava/lang/String;)V astore_2 //two仔細地看! 第一個對象的創建方法與上面相同,不足為奇。 它只需要從常量池中常量引用已經創建的String ( #2 )。 但是,第二個對象是通過常規構造函數調用創建的。 但! 第一個String作為參數傳遞。 可以將其反編譯為:
String two = new String(one); 輸出量
輸出有點令人驚訝。 第二對表示對String對象的引用是可以理解的-我們創建了兩個String對象-一個在常量池中為我們創建,第二個是為two手動創建的。 但是,為什么第一對建議兩個String對象都指向同一個char[] value數組呢?
當您查看String(String)構造函數的工作原理時,這一點變得很清楚(此處已大大簡化了):
public String(String original) {this.offset = original.offset;this.count = original.count;this.value = original.value; } 看到? 當基于現有對象創建新的String對象時,它會重用 char[] value 。 String是不可變的,不需要復制已知永遠不會修改的數據結構。 而且,由于new String(someString)創建了現有字符串的精確副本,并且字符串是不可變的,因此顯然沒有理由同時存在兩者。
我認為這是一些誤解的線索:即使您有兩個String對象,它們仍可能指向相同的內容。 如您所見, String對象本身很小。
運行時修改和intern() ?
Java代碼
假設您最初使用了兩個不同的字符串,但是在進行一些修改之后,它們都是相同的:
Java編譯器(至少是我的)不夠聰明,無法在編譯時執行此類操作,請看一下:
類常量池
突然我們以指向兩個不同常量文本的兩個常量字符串結尾:
字節碼
ldc #2; //String abc astore_1 //oneldc #3; //String ?abc iconst_1 invokevirtual #4; //Method String.substring:(I)Ljava/lang/String; astore_2 //two拳頭弦照常構造。 通過首先加載常量"?abc"字符串,然后在其上調用substring(1)來創建第二個。
輸出量
這里不足為奇–我們有兩個不同的字符串,指向內存中兩個不同的char[]文本:
one.value: 27379847 two.value: 7615385 one: 8388097 two: 16585653 好吧,文本并沒有真正的不同 , equals()方法仍然會產生true 。 我們有兩個不必要的相同文本副本。
現在我們應該進行兩次練習。 首先,嘗試運行:
在打印哈希碼之前。 one和two不僅指向同一文本,而且它們是相同的參考!
one.value: 11108810 two.value: 11108810 one: 15184449 two: 15184449 這意味著one.equals(two)和one == two測試都將通過。 我們還節省了一些內存,因為"abc"文本在內存中僅出現一次(第二個副本將被垃圾回收)。
第二個練習略有不同,請查看以下內容:
顯然one和two是兩個不同的對象,指向兩個不同的文本。 但是輸出如何表明它們都指向同一個char[]數組?!
one.value: 11108810 two.value: 8918249 one: 23583040 two: 23583040我將答案留給你。 它會教您substring()工作原理,這種方法的優點是什么以及何時會導致大麻煩 。
得到教訓
- String對象本身相當便宜。 它指向的文本占用了大部分內存
- String只是char[]的薄包裝,以保持不變性
- new String("abc")作為內部文本表示被重用是不是真的那么貴。 但是還是要避免這樣的構造。
- 從編譯時已知的常量值連接String時,連接由編譯器而不是由JVM完成
- substring()有點棘手,但最重要的是,就使用的內存和運行時間而言,它都很便宜(在兩種情況下均保持不變)
參考: Java和社區博客中來自JCG合作伙伴 Tomasz Nurkiewicz的字符串內存內部 。
翻譯自: https://www.javacodegeeks.com/2012/07/string-memory-internals.html
字符內存轉成字符串
總結
以上是生活随笔為你收集整理的字符内存转成字符串_字符串内存内部的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 安卓逆向工具手机版(安卓逆向工具)
- 下一篇: Apache PDFBox命令行工具:无