1.String的特殊性
1.1對象的創建
public class Test {
public static void main(String[] args) {
String str1 = new String("abcd");
String str2 = "abcd";
}
}
1.1.1下面解釋對象創建過程:
Java 對String 類的特殊管理:
>Java的每一個類都有一個常量池,這個常量池定義在class文件中有描述(javap -v 類的全類名),包括值、標識符(舉個例子:String a ="astr";int b = 1;這些代碼中的 標識符a,b和值 “astr” 都是常量池的內容,而1 則會嵌入的操作指令中)、屬性名、類名、方法名等。
>每個JVM實例同時會在方法區維持一個String pool,在裝載每個類時的會解析類的常量池,先在javaheap內創建這些這些常量對象返回其引用然後=》將這些字符串常量的引用存儲到String pool中=》最後將這些引用給每個類的常量池。
> ==是比較地址,String 類重寫了 equals()方法,比較對象內容(比較的是底層的char[] 內每一個字符是否相等)
> 個人猜測:string pool中存的是引用值而不是具體的實例對象,具體的實例對象是在堆中開闢的一塊空間存放的。但是怎麼驗證還沒有教好的思路。
1.1.1.1 String str1 = "abcd"的創建過程:
實現過程:
>首先棧區創建str1引用,然後在String池中尋找指向的內容爲"abcd"的對象的引用,如果String池中沒有,則javaheap創建一個對象返回引用到String Pool,最後返回指向String池中的引用賦值給str1;如果有,則直接返回引用賦值給str1;
推論及驗證:
>如果後來又定義了字符串變量 str2 = "abcd",則直接將str2引用指向String池中已經存在的“abcd”,不再重新創建對象;這時str1==str2。
但是需要注意的一點是::
>Java 語言提供了 字符串連接符號("+")以及將其他對象轉換爲字符串的特殊支持。
>字符串連接 底層是通過 StringBuilder(或 StringBuffer)類及其 append 方法實現的(jjava heap創建buffer或者builder對象,然後append返回);
>字符串轉換是通過 toString 方法實現的,該方法由 Object 類定義,並可被 Java 中的所有類繼承。
注意點的驗證:
>如果內容爲"abc"的str2進行了字符串的"+"連接str2 = str2+"d";此時str2指向的是在堆中新建的內容爲"abcd"的對象,即此時進行str1==str2,返回值false,因爲地址不一樣。
1.1.1.2 String str = new String("abcd")的創建過程:
實現過程:
>直接在堆中創建對象返回引用。如果後來又有String str3 = new String("abcd"),str3不會指向String pool裏面的對象,而是在堆中重新創建一個對象並指向它。
驗證方式:
>如果此時進行str2==str3以及str1 == str3 均會返回false,因爲兩個對象的地址不一樣,如果是str2.equals(str3),返回true,因爲內容相同。
注意的一點:
>str.intern()這個方法就是將str指向的String對象內容,存儲一份到String pool裏並返回在String pool裏的“引用”;
1.2String對象的不可性(immutable)
1.2.1 爲什麼設計爲final的?
因爲String類的創建銷燬等涉及到JVM的機制,一般程序員不具備這個能力。
1.2.2我們無法利用String提供的API來改變原對象的內容
public class Base {
public static void main(String[] args) {
String str1 = new String("abcd");
String str2 = "abcd";
String str3 = "abcd";
String str4 = new String("abc") + "d";// 連接 請勿直接使用 "abc"+"d" 會被編譯器有化成 "abcd"
System.out.println(str2 == str1);// false
System.out.println(str2 == str3);// true
System.out.println(str2 == str4);// false
System.out.println(str2 == str1.intern() && str2 == str3.intern() && str2 == str4.intern());// true 都是String pool裏面的引用
String str5 = str1.replace("a", "z");// 替換字符
System.out.println(str1);
System.out.println(str5);
String str6 = str1.substring(2);// 截取字符
System.out.println(str1);
System.out.println(str6);
String str7 = str1.toLowerCase();// 轉小寫
System.out.println(str1);
System.out.println(str7);
String str8 = str1.toUpperCase();// 轉大寫
System.out.println(str1);
System.out.println(str8);
String str9 = str1.trim();// 去除兩端空格字符
System.out.println(str1);
System.out.println(str9);
}
}
如以上等等的方法均未改變str1的值及其指向String對象的值。因爲所有的API 相關的操作都是對底層的char[] value進行deepcopy後進行的操作
1.2.3 但是反射可以做到
public class Test {
public static void main(String[] args) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
String str1 = new String("abcd");
//here is an way to change str1
Class clazz = str1.getClass();
Field valueField = clazz.getDeclaredField("value");
valueField.setAccessible(true);
char [] str1Changed = new char[]{'z','x','c','v'};
valueField.set(str1, str1Changed);
System.out.println(str1);//zxcv
}
}
這是利用反射修改str1指向對象的內容
1.2.4不可變對象的優勢
1 天生的線程安全性(只能讀取不能修改)
2 在性能上的提升(可緩存,不必每次都要申請內存初始化等從而提升性能)
2.關於字符及編碼及亂碼
String:"字符"串,這個字符就是我們人通常理解的抽象的符號(例如"a","b","中"等),但是在計算機是無法存儲這種抽象的符號(只能存儲數值),只能依靠數值與符號的映射關係(編碼字符集)來解決數值和符號的對應關係然後顯示字符。 計算機上很常見的顯示亂碼一般有下面幾個原因:
第一個字節序列本身有問題(文件破壞掉了,這個情況較少,而且基本無解);
第二個就是我們解碼的方式不對(這種常見,本身是UTF-8格式編碼的,我們卻以GBK的形式解碼,修改解碼方式),
第三個就是我們缺少對應的顯示方法(這種也常見,文件是UTF-8的我們以UTF-8解碼,但是其中某些字碼對應國外的文字符號,我們缺少顯示方法,解決方法爲安裝缺少對應字符集),
參考如下代碼:
public class Test {
public static void main(String[] args) throws UnsupportedEncodingException {
String str = "wo是中國人";//默認編碼格式UTF-8
byte [] gbkbytes = str.getBytes("GBK");//進行GBK編碼
byte [] utf8bytes = str.getBytes("UTF-8");//進行UTF-8編碼
System.out.println(gbkbytes.length);
assert gbkbytes.length == 10;//1英2中
System.out.println(utf8bytes.length);
assert utf8bytes.length == 14;//1英三中
//對gbkbytes 進行 UTF8解碼
System.out.println(new String(gbkbytes, "UTF-8"));//wo���й�
//對utf8bytes 進行GBK解碼
System.out.println(new String(gbkbytes, "GBK"));//wo是中國人
}
}