Java基礎(chǔ)(三)String類解析

字符串廣泛應(yīng)用 在 Java 編程中袁波,在 Java 中字符串屬于對(duì)象,Java 提供了 String 類來(lái)創(chuàng)建和操作字符串。

創(chuàng)建字符串

  • 三種構(gòu)造方法
    • public String():創(chuàng)建一個(gè)空白字符串,不含有任何內(nèi)容
    • public String(char[] array):根據(jù)字符數(shù)組的內(nèi)容,來(lái)創(chuàng)建對(duì)應(yīng)的字符串宴咧。
    • public String(byte[] array):根據(jù)字節(jié)數(shù)組的內(nèi)容,來(lái)創(chuàng)建對(duì)應(yīng)的字符串径缅。
  • 一種直接創(chuàng)建(字面量)
    • String str = "Hello";//右邊直接用雙引號(hào)
                //使用空參構(gòu)造
        String str1 = new String();//小括號(hào)留空掺栅,說(shuō)明字符串說(shuō)明內(nèi)容都沒(méi)有
        System.out.println("第1個(gè)字符串" +str1);

        //根據(jù)字符數(shù)組創(chuàng)建字符串
        char[] charArray = {'A','B','C'};
        String str2 = new String(charArray);
        System.out.println("第2個(gè)字符串" +str2)
        ;
        //根據(jù)字節(jié)數(shù)組的內(nèi)容,來(lái)創(chuàng)建對(duì)應(yīng)的字符串
        byte[] byteArray = {97,98,99};
        String str3 = new String(byteArray);
        System.out.println("第3個(gè)字符串" +str3);

使用直接創(chuàng)建的方式(字面量)創(chuàng)建的字符串放在常量池中纳猪,使用new String() 創(chuàng)建的字符串放在堆內(nèi)存中

String 類是不可改變的

String s = "Hello";
System.out.println("s = " + s);

s = "World";
System.out.println("s = " + s);

// 輸出結(jié)果為:
Hello
World

實(shí)例中的 s 只是一個(gè) String 對(duì)象的引用氧卧,并不是對(duì)象本身,當(dāng)執(zhí)行 s = "Hello"; 創(chuàng)建了一個(gè)新的對(duì)象 "World"氏堤,而原來(lái)的 "Hello" 還存在于內(nèi)存中沙绝。

  • String為什么不可變?
public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
    /** The value is used for character storage. */
    private final char value[];

查看string的源碼鼠锈,即可知道字符串實(shí)際上就是一個(gè) char 數(shù)組闪檬,并且內(nèi)部就是封裝了一個(gè) char 數(shù)組。并且這里 char 數(shù)組是被 final 修飾的购笆。String 中的所有的方法粗悯,都是對(duì)于 char 數(shù)組的改變,只要是對(duì)它的改變由桌,方法內(nèi)部都是返回一個(gè)新的 String 實(shí)例为黎。

  • 那放在內(nèi)存中的hello,什么時(shí)候進(jìn)行釋放呢行您?

這里需要引入一個(gè)新的概念,String 常量池

1.常量池(Constant Pool)

1.1 常量池(Class文件常量池):.java經(jīng)過(guò)編譯后生成的.class文件剪廉,是Class文件的資源倉(cāng)庫(kù)娃循。

1.2 常量池中主要存放倆大常量:字面量(文本字符串,final常量)和符號(hào)引用(類和接口的全局定名斗蒋,字段的名稱和描述捌斧,方法的名稱和描述),如下圖:


image.png
  1. 運(yùn)行時(shí)常量池(Constant Pool)

運(yùn)行時(shí)常量池是方法區(qū)的一部分泉沾。在Class常量池中捞蚂,用于存放編譯期間生成的字面量和符號(hào)量,在類加載完之后跷究,存入運(yùn)行時(shí)常量池中姓迅。而運(yùn)行時(shí)常量池期間也有可能加入新的常量(如:String.intern方法)

  1. String常量池,

String常量池,JVM為了減少字符串對(duì)象的重復(fù)創(chuàng)建丁存,在堆區(qū)開(kāi)一段內(nèi)存存放字符串肩杈。

根據(jù)JVM相關(guān)知識(shí),可以知道JVM 的GC操作主要發(fā)生在堆區(qū)解寝,但是具體什么時(shí)候釋放扩然,需要根據(jù)JVM具體來(lái)分析,詳見(jiàn)后續(xù)JVM文章

String的長(zhǎng)度

String是使用的一個(gè)char類型的數(shù)組來(lái)存儲(chǔ)字符串中的字符的聋伦,Java中定義數(shù)組是可以給數(shù)組指定長(zhǎng)度的夫偶,當(dāng)然不指定的話默認(rèn)會(huì)根據(jù)數(shù)組元素來(lái)指定。

/**
 * Returns the length of this string.
 * The length is equal to the number of <a href="Character.html#unicode">Unicode
 * code units</a> in the string.
 *
 * @return  the length of the sequence of characters represented by this
 *          object.
 */
public int length() {
    return value.length >> coder();
}

通過(guò)源碼來(lái)看看int類型對(duì)應(yīng)的包裝類Integer可以看到觉增,其長(zhǎng)度最大限制為2^31 -1兵拢,那么說(shuō)明了數(shù)組的長(zhǎng)度是0~231-1,那么計(jì)算一下就是(231-1 = 2147483647 = 4GB)

但是實(shí)際我們創(chuàng)建超過(guò)65535個(gè)字符抑片,就會(huì)編譯報(bào)錯(cuò)卵佛。

這個(gè)又涉及到JVM編譯規(guī)范,如果我們將字符串定義成了字面量的形式敞斋,編譯時(shí)JVM是會(huì)將其存放在常量池中截汪,這時(shí)候JVM對(duì)這個(gè)常量池存儲(chǔ)String類型做出了限制,接下來(lái)我們先看下手冊(cè)是如何說(shuō)的植捎。

image.png
image.png
image.png
image.png

在class文件中u2表示的是無(wú)符號(hào)數(shù)占2個(gè)字節(jié)單位衙解,我們知道1個(gè)字節(jié)占8位,2個(gè)字節(jié)就是16位 焰枢,那么2個(gè)字節(jié)能表示的范圍就是2^16- 1 = 65535蚓峦。

所以首先字符串的內(nèi)容是由一個(gè)字符數(shù)組 char[] 來(lái)存儲(chǔ)的,由于數(shù)組的長(zhǎng)度及索引是整數(shù)济锄,且String類中返回字符串長(zhǎng)度的方法length() 的返回值也是int 暑椰,所以通過(guò)查看java源碼中的類Integer我們可以看到Integer的最大范圍是2^31 -1,由于數(shù)組是從0開(kāi)始的,所以數(shù)組的最大長(zhǎng)度可以使【0~2^31】通過(guò)計(jì)算是大概4GB荐绝。

但是通過(guò)翻閱java虛擬機(jī)手冊(cè)對(duì)class文件格式的定義以及常量池中對(duì)String類型的結(jié)構(gòu)體定義我們可以知道對(duì)于索引定義了u2一汽,就是無(wú)符號(hào)占2個(gè)字節(jié),2個(gè)字節(jié)可以表示的最大范圍是2^16 -1 = 65535低滩。其實(shí)是65535召夹,但是由于JVM需要1個(gè)字節(jié)表示結(jié)束指令,所以這個(gè)范圍就為65534了恕沫。超出這個(gè)范圍在編譯時(shí)期是會(huì)報(bào)錯(cuò)的监憎,但是運(yùn)行時(shí)拼接或者賦值的話范圍是在整形的最大范圍。

來(lái)自:CSDN(作者:wellleo)

String拼接

  • String:字符串常量婶溯,字符串長(zhǎng)度不可變鲸阔。Java中String 是immutable(不可變)的偷霉。用于存放字符的數(shù)組被聲明為final的,因此只能賦值一次隶债,不可再更改腾它。

    • concat()通過(guò)復(fù)制數(shù)組在通過(guò) char 數(shù)組進(jìn)行拼接生成一個(gè)新的對(duì)象,所以地址值會(huì)有變動(dòng)
    • ”+“ 號(hào)死讹,編譯時(shí)不會(huì)進(jìn)行地址值的改變(jvm優(yōu)化)瞒滴,**運(yùn)行時(shí)拼接會(huì)改變地址,底層使用stringbuilder實(shí)現(xiàn)
  • StringBuffer:字符串變量(Synchronized赞警,即線程安全)妓忍。如果要頻繁對(duì)字符串內(nèi)容進(jìn)行修改,出于效率考慮最好使用 StringBuffer愧旦,如果想轉(zhuǎn)成 String 類型世剖,可以調(diào)用 StringBuffer 的 toString() 方法。Java.lang.StringBuffer 線程安全的可變字符序列笤虫。在任意時(shí)間點(diǎn)上它都包含某種特定的字符序列,但通過(guò)某些方法調(diào)用可以改變?cè)撔蛄械拈L(zhǎng)度和內(nèi)容酬凳≡馐可將字符串緩沖區(qū)安全地用于多個(gè)線程宁仔。

  • StringBuilder:字符串變量(非線程安全)。在內(nèi)部 StringBuilder 對(duì)象被當(dāng)作是一個(gè)包含字符序列的變長(zhǎng)數(shù)組峦睡。

基本原則:

  • 如果要操作少量的數(shù)據(jù)用 String ;
  • 單線程操作大量數(shù)據(jù)用StringBuilder 煎谍;
  • 多線程操作大量數(shù)據(jù),用StringBuffer龙屉。

常見(jiàn)面試題

  • 寫一個(gè)方法來(lái)判斷一個(gè)String是否是回文(順讀和倒讀都一樣的詞)粱快?
// 回文就是正反都一樣的詞叔扼,如果需要判斷是否是回文漫雷,只需要比較正反是否相等即可。String類并沒(méi)有提供反轉(zhuǎn)方法供我們使用与柑,但StringBuffer和StringBuilder有reverse方法。
private static boolean isReversesame(String str) {
        if (str == null)
            return false;
        StringBuilder strBuilder = new StringBuilder(str);
        strBuilder.reverse();
        return strBuilder.toString().equals(str);
    }
// 假設(shè)面試官讓你不使用任何其他類來(lái)實(shí)現(xiàn)的話价捧,我們只需要首尾一一對(duì)比就知道是不是回文了结蟋。
private static boolean isReversesame(String str) {
        if (str == null)
            return false;
        int length = str.length();
        System.out.println(length / 2);
        for (int i = 0; i < length / 2; i++) {
            if (str.charAt(i) != str.charAt(length - i - 1))
                return false;
        }
        return true;
    }
  • String是不可變的有什么好處?
    • 由于String是不可變類嵌屎,所以在多線程中使用是安全的宝惰,我們不需要做任何其他同步操作。
    • String是不可變的尼夺,它的值也不能被改變淤堵,所以用來(lái)存儲(chǔ)數(shù)據(jù)密碼很安全。
    • 因?yàn)閖ava字符串是不可變的粘勒,可以在java運(yùn)行時(shí)節(jié)省大量java堆空間。因?yàn)椴煌淖址兞靠梢砸贸刂械南嗤淖址硭H绻址强勺兊迷捠赂唬魏我粋€(gè)變量的值改變乘陪,就會(huì)反射到其他變量,那字符串池也就沒(méi)有任何意義了贱勃。
    • 允許對(duì)象HashCode谤逼,hash之后值不會(huì)變化,所以hashmap大多數(shù)使用String作為鍵值

String 支持的api

SN(序號(hào)) 方法描述
1 char charAt(int index) 返回指定索引處的 char 值戚绕。
2 int compareTo(Object o) 把這個(gè)字符串和另一個(gè)對(duì)象比較枝冀。
3 int compareTo(String anotherString) 按字典順序比較兩個(gè)字符串。
4 int compareToIgnoreCase(String str) 按字典順序比較兩個(gè)字符串球切,不考慮大小寫吨凑。
5 String concat(String str) 將指定字符串連接到此字符串的結(jié)尾。
6 boolean contentEquals(StringBuffer sb) 當(dāng)且僅當(dāng)字符串與指定的StringBuffer有相同順序的字符時(shí)候返回真费封。
7 static String copyValueOf(char[] data) 返回指定數(shù)組中表示該字符序列的 String蒋伦。
8 static String copyValueOf(char[] data, int offset, int count) 返回指定數(shù)組中表示該字符序列的 String。
9 boolean endsWith(String suffix) 測(cè)試此字符串是否以指定的后綴結(jié)束韧献。
10 boolean equals(Object anObject) 將此字符串與指定的對(duì)象比較研叫。
11 boolean equalsIgnoreCase(String anotherString) 將此 String 與另一個(gè) String 比較嚷炉,不考慮大小寫。
12 byte[] getBytes() 使用平臺(tái)的默認(rèn)字符集將此 String 編碼為 byte 序列绘证,并將結(jié)果存儲(chǔ)到一個(gè)新的 byte 數(shù)組中哗讥。
13 byte[] getBytes(String charsetName) 使用指定的字符集將此 String 編碼為 byte 序列,并將結(jié)果存儲(chǔ)到一個(gè)新的 byte 數(shù)組中魏宽。
14 void getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin) 將字符從此字符串復(fù)制到目標(biāo)字符數(shù)組决乎。
15 int hashCode() 返回此字符串的哈希碼构诚。
16 int indexOf(int ch) 返回指定字符在此字符串中第一次出現(xiàn)處的索引。
17 int indexOf(int ch, int fromIndex) 返回在此字符串中第一次出現(xiàn)指定字符處的索引凳寺,從指定的索引開(kāi)始搜索。
18 int indexOf(String str) 返回指定子字符串在此字符串中第一次出現(xiàn)處的索引嘶卧。
19 int indexOf(String str, int fromIndex) 返回指定子字符串在此字符串中第一次出現(xiàn)處的索引煮寡,從指定的索引開(kāi)始。
20 String intern() 返回字符串對(duì)象的規(guī)范化表示形式脑慧。
21 int lastIndexOf(int ch) 返回指定字符在此字符串中最后一次出現(xiàn)處的索引闷袒。
22 int lastIndexOf(int ch, int fromIndex) 返回指定字符在此字符串中最后一次出現(xiàn)處的索引岩梳,從指定的索引處開(kāi)始進(jìn)行反向搜索。
23 int lastIndexOf(String str) 返回指定子字符串在此字符串中最右邊出現(xiàn)處的索引也物。
24 int lastIndexOf(String str, int fromIndex) 返回指定子字符串在此字符串中最后一次出現(xiàn)處的索引滑蚯,從指定的索引開(kāi)始反向搜索抵栈。
25 int length() 返回此字符串的長(zhǎng)度。
26 boolean matches(String regex) 告知此字符串是否匹配給定的正則表達(dá)式创葡。
27 boolean regionMatches(boolean ignoreCase, int toffset, String other, int ooffset, int len) 測(cè)試兩個(gè)字符串區(qū)域是否相等绢慢。
28 boolean regionMatches(int toffset, String other, int ooffset, int len) 測(cè)試兩個(gè)字符串區(qū)域是否相等胰舆。
29 String replace(char oldChar, char newChar) 返回一個(gè)新的字符串,它是通過(guò)用 newChar 替換此字符串中出現(xiàn)的所有 oldChar 得到的棘幸。
30 String replaceAll(String regex, String replacement) 使用給定的 replacement 替換此字符串所有匹配給定的正則表達(dá)式的子字符串倦零。
31 String replaceFirst(String regex, String replacement) 使用給定的 replacement 替換此字符串匹配給定的正則表達(dá)式的第一個(gè)子字符串吨悍。
32 String[] split(String regex) 根據(jù)給定正則表達(dá)式的匹配拆分此字符串育瓜。
33 String[] split(String regex, int limit) 根據(jù)匹配給定的正則表達(dá)式來(lái)拆分此字符串栽烂。
34 boolean startsWith(String prefix) 測(cè)試此字符串是否以指定的前綴開(kāi)始。
35 boolean startsWith(String prefix, int toffset) 測(cè)試此字符串從指定索引開(kāi)始的子字符串是否以指定前綴開(kāi)始焰手。
36 CharSequence subSequence(int beginIndex, int endIndex) 返回一個(gè)新的字符序列怀喉,它是此序列的一個(gè)子序列磺送。
37 String substring(int beginIndex) 返回一個(gè)新的字符串,它是此字符串的一個(gè)子字符串崇呵。
38 String substring(int beginIndex, int endIndex) 返回一個(gè)新字符串馅袁,它是此字符串的一個(gè)子字符串。
39 char[] toCharArray() 將此字符串轉(zhuǎn)換為一個(gè)新的字符數(shù)組犹褒。
40 String toLowerCase() 使用默認(rèn)語(yǔ)言環(huán)境的規(guī)則將此 String 中的所有字符都轉(zhuǎn)換為小寫弛针。
41 String toLowerCase(Locale locale) 使用給定 Locale 的規(guī)則將此 String 中的所有字符都轉(zhuǎn)換為小寫削茁。
42 String toString() 返回此對(duì)象本身(它已經(jīng)是一個(gè)字符串!)慰丛。
43 String toUpperCase() 使用默認(rèn)語(yǔ)言環(huán)境的規(guī)則將此 String 中的所有字符都轉(zhuǎn)換為大寫瘾杭。
44 String toUpperCase(Locale locale) 使用給定 Locale 的規(guī)則將此 String 中的所有字符都轉(zhuǎn)換為大寫。
45 String trim() 返回字符串的副本贤笆,忽略前導(dǎo)空白和尾部空白。
46 static String valueOf(primitive data type x) 返回給定data type類型x參數(shù)的字符串表示形式银萍。
47 contains(CharSequence chars) 判斷是否包含指定的字符系列。
48 isEmpty() 判斷字符串是否為空搀绣。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末链患,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子纲仍,更是在濱河造成了極大的恐慌贸毕,老刑警劉巖明棍,帶你破解...
    沈念sama閱讀 216,372評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異沸版,居然都是意外死亡兴蒸,警方通過(guò)查閱死者的電腦和手機(jī)橙凳,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)区宇,“玉大人值戳,你說(shuō)我怎么就攤上這事∥韵” “怎么了?”我有些...
    開(kāi)封第一講書人閱讀 162,415評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)胜宇。 經(jīng)常有香客問(wèn)我,道長(zhǎng)财破,這世上最難降的妖魔是什么从诲? 我笑而不...
    開(kāi)封第一講書人閱讀 58,157評(píng)論 1 292
  • 正文 為了忘掉前任系洛,我火速辦了婚禮,結(jié)果婚禮上定页,老公的妹妹穿的比我還像新娘荆烈。我一直安慰自己憔购,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布导绷。 她就那樣靜靜地躺著屎飘,像睡著了一般钦购。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上葵萎,一...
    開(kāi)封第一講書人閱讀 51,125評(píng)論 1 297
  • 那天,我揣著相機(jī)與錄音谎痢,去河邊找鬼卷雕。 笑死,一個(gè)胖子當(dāng)著我的面吹牛滨嘱,可吹牛的內(nèi)容都是我干的浸间。 我是一名探鬼主播发框,決...
    沈念sama閱讀 40,028評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼梅惯,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼仿野!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起葫哗,我...
    開(kāi)封第一講書人閱讀 38,887評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤球涛,失蹤者是張志新(化名)和其女友劉穎亿扁,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體襟己,經(jīng)...
    沈念sama閱讀 45,310評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡牍陌,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評(píng)論 2 332
  • 正文 我和宋清朗相戀三年毒涧,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片萌狂。...
    茶點(diǎn)故事閱讀 39,690評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡茫藏,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出凉当,到底是詐尸還是另有隱情售葡,我是刑警寧澤,帶...
    沈念sama閱讀 35,411評(píng)論 5 343
  • 正文 年R本政府宣布楼雹,位于F島的核電站贮缅,受9級(jí)特大地震影響介却,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜桂肌,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評(píng)論 3 325
  • 文/蒙蒙 一崎场、第九天 我趴在偏房一處隱蔽的房頂上張望仰禀。 院中可真熱鬧,春花似錦饺蚊、人聲如沸悬嗓。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)苗缩。三九已至,卻和暖如春退盯,著一層夾襖步出監(jiān)牢的瞬間泻肯,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 32,812評(píng)論 1 268
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留稚铣,地道東北人惕医。 一個(gè)月前我還...
    沈念sama閱讀 47,693評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親沛简。 傳聞我的和親對(duì)象是個(gè)殘疾皇子斥废,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容