String

1.String的特殊性

1.1對(duì)象的初始化

public class Test {
    public static void main(String[] args) {
        String str1 = new String("abcd");
        String str2 = "abcd";
    }
}

1.1.1記住幾點(diǎn):

1.棧區(qū)(線程私有)存引用(對(duì)象的標(biāo)識(shí)==》值)和基本類型(值),不能存對(duì)象而堆區(qū)存對(duì)象。
2.==是比較地址,String 類重寫了 equals()方法,比較對(duì)象內(nèi)容(比較的是底層的char[] 內(nèi)每一個(gè)字符是否相等)。
3.Java的每一個(gè)類都有一個(gè)常量池,這個(gè)常量池定義在.class文件中有描述(javap -v 類的全類名 可以看到內(nèi)容),包括值、標(biāo)識(shí)符(舉個(gè)例子,String a ="astr";int b = 1;這些代碼中的 標(biāo)識(shí)符a,b和值 “astr”,1 都是常量池的內(nèi)容)、屬性名、類名、方法名等。在jvm裝載.class文件時(shí),會(huì)裝載這些常量到類的常量池中。
每個(gè)JVM實(shí)例同時(shí)會(huì)在方法區(qū)維持一個(gè)String pool,在裝載每個(gè)類常量池的字符串型常量時(shí),會(huì)先將這些字符串常量的引用存儲(chǔ)到String pool中,然后將這些引用給每個(gè)類的常量池。
記?。簊tring pool中存的是引用值而不是具體的實(shí)例對(duì)象,具體的實(shí)例對(duì)象是在堆中開(kāi)辟的一塊空間存放的。

1.1.2下面解釋對(duì)象創(chuàng)建過(guò)程:

1.1.2.1 String str = "abcd":

實(shí)現(xiàn)過(guò)程:

首先棧區(qū)創(chuàng)建str1引用,然后在String池中尋找其指向的內(nèi)容為"abcd"的對(duì)象,如果String池中沒(méi)有,則創(chuàng)建一個(gè),然后str1指向String池中的對(duì)象,如果有,則直接將str1指向"abcd";

推論及驗(yàn)證:

如果后來(lái)又定義了字符串變量 str2 = "abcd",則直接將str2引用指向String池中已經(jīng)存在的“abcd”,不再重新創(chuàng)建對(duì)象;這時(shí)str1==str2。

但是需要注意的一點(diǎn)是:

Java 語(yǔ)言提供對(duì)字符串串聯(lián)符號(hào)("+")以及將其他對(duì)象轉(zhuǎn)換為字符串的特殊支持,字符串串聯(lián)是通過(guò) StringBuilder(或 StringBuffer)類及其 append 方法實(shí)現(xiàn)的.字符串轉(zhuǎn)換是通過(guò) toString 方法實(shí)現(xiàn)的,該方法由 Object 類定義,并可被 Java 中的所有類繼承。

注意點(diǎn)的驗(yàn)證:

如果內(nèi)容為"abc"的str1進(jìn)行了字符串的"+"連接str1 = str1+"d";此時(shí)str1指向的是在堆中新建的內(nèi)容為"abcd"的對(duì)象,即此時(shí)進(jìn)行str1==str2,返回值false,因?yàn)榈刂凡灰粯印?/p>

1.1.2.2 String str = new String("abcd"):

實(shí)現(xiàn)過(guò)程:

直接在堆中創(chuàng)建對(duì)象。如果后來(lái)又有String str3 = new String("abcd"),str3不會(huì)指向String pool里面的對(duì)象,而是在堆中重新創(chuàng)建一個(gè)對(duì)象并指向它。

驗(yàn)證方式:

如果此時(shí)進(jìn)行str2==str3以及str1 == str3 均會(huì)返回false,因?yàn)閮蓚€(gè)對(duì)象的地址不一樣,如果是str2.equals(str3),返回true,因?yàn)閮?nèi)容相同。

注意的一點(diǎn):

str.intern()這個(gè)方法就是將str指向的String對(duì)象內(nèi)容,存儲(chǔ)一份到String pool里并返回在String pool里“引用”;

1.2String對(duì)象的不可性(immutable)

1.2.1我們無(wú)法利用String提供的API來(lái)改變對(duì)象的內(nèi)容

public class Test {
    public static void main(String[] args){
        String str1 = new String("abcd");
        String str2 = str1;
        
        String str3 = str1+"e";//增加字符
        System.out.println(str1);
        System.out.println(str2 ==str1);
        
        String str4 = str1.replace("a", "z");//替換字符
        System.out.println(str1);
        System.out.println(str2 ==str1);
        
        String str5  = str1.substring(2);//截取字符
        System.out.println(str1);
        System.out.println(str2 ==str1);
        
        String str6 = str1.toLowerCase();//轉(zhuǎn)小寫
        System.out.println(str1);
        System.out.println(str2 ==str1);
        
        String str7 = str1.toUpperCase();//轉(zhuǎn)大寫
        System.out.println(str1);
        System.out.println(str2 ==str1);
        
        String str8 = str1.trim();//去除兩端空格字符
        System.out.println(str1);
        System.out.println(str2 ==str1);
    }
}

如以上等等的方法均未改變str1的值及其指向String對(duì)象的值。因?yàn)樗械腁PI 相關(guān)的操作都是對(duì)底層的char[] value進(jìn)行deepcopy后進(jìn)行的操作

1.2.2但是反射可以做到

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);
    }
}

這是利用反射修改str1指向?qū)ο蟮膬?nèi)容

1.2.3不可變對(duì)象的優(yōu)勢(shì)

1.2.3.1 天生的線程安全性(只能讀取不能修改)

1.2.3.2 在性能上的提升(可緩存,不必每次都要申請(qǐng)內(nèi)存初始化等從而提升性能)

2.關(guān)于字符及編碼及亂碼

String:"字符"串,這個(gè)字符就是我們?nèi)送ǔ@斫獾某橄蟮姆?hào)(例如"a","b","中"等),但是在計(jì)算機(jī)是無(wú)法存儲(chǔ)這種抽象的符號(hào)(只能存儲(chǔ)數(shù)值),只能依靠數(shù)值與符號(hào)的映射關(guān)系(編碼字符集)來(lái)解決數(shù)值和符號(hào)的對(duì)應(yīng)關(guān)系然后顯示字符。計(jì)算機(jī)上很常見(jiàn)的顯示亂碼一般有下面幾個(gè)原因:一個(gè)字節(jié)序列本身有問(wèn)題(文件破壞掉了,這個(gè)情況較少,而且基本無(wú)解),第二個(gè)就是我們解碼的方式不對(duì)(這種常見(jiàn),本身是UTF-8格式編碼的,我們卻以GBK的形式解碼,修改解碼方式),第三個(gè)就是我們?nèi)鄙賹?duì)應(yīng)的顯示方法(這種也常見(jiàn),文件是UTF-8的我們以UTF-8解碼,但是其中某些字碼對(duì)應(yīng)國(guó)外的文字符號(hào),我們?nèi)鄙亠@示方法,解決方法為安裝缺少對(duì)應(yīng)字符集),參考如下代碼

public class Test {
    public static void main(String[] args) throws UnsupportedEncodingException {
        String str = "wo是中國(guó)人";//默認(rèn)編碼格式UTF-16
        byte [] gbkbytes = str.getBytes("GBK");//進(jìn)行GBK編碼
        byte [] utf8bytes = str.getBytes("UTF-8");//進(jìn)行UTF-8編碼
        System.out.println(gbkbytes.length);
        assert gbkbytes.length == 10;
        System.out.println(utf8bytes.length);
        assert utf8bytes.length == 14;
        //對(duì)gbkbytes 進(jìn)行 UTF8解碼
        System.out.println(new String(gbkbytes, "UTF-8"));//wo???й???
        //對(duì)utf8bytes 進(jìn)行GBK解碼
        System.out.println(new String(gbkbytes, "GBK"));//wo鏄鍥戒漢
    }
    
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容