JavaRush /Java Blog /Random-TW /Java 中的字串(類別 java.lang.String)
Viacheslav
等級 3

Java 中的字串(類別 java.lang.String)

在 Random-TW 群組發布

介紹

程式設計師的道路是一個複雜而漫長的過程。在大多數情況下,它以一個在螢幕上顯示 Hello World 的程式開始。Java 也不例外(請參閱課程:“Hello World!”應用程式)。如我們所看到的,訊息是使用以下方式輸出的:System.out.println("Hello World!"); 如果您查看 Java API,則System.out.println方法將String作為輸入參數。我們將討論此類數據。

字串作為字元序列

其實String從英文翻譯過來就是字串。沒錯,String類型代表的是文字字串。什麼是文字字串?文字字串是某種彼此跟隨的有序字元序列。符號是字元。順序——順序。所以是的,絕對正確,String 是java.lang.CharSequence. 如果你查看 String 類別本身的內部,那麼在它的內部就只有一個字元陣列:private final char value[]; 它有java.lang.CharSequence一個相當簡單的契約:
Java 中的字串(類別 java.lang.String) - 1
我們有一個獲取元素數量的方法,獲取特定元素和獲取一組元素+ toString 方法本身,該方法將返回 this)了解 Java 8 中提供給我們的方法更有趣,這是:chars()回想codePoints() 一下 Oracle 教程中的“原始資料」類型,char 是single 16-bit Unicode character。也就是說,本質上 char 只是 int(32 位元)大小一半的類型,表示從 0 到 65535 的數字(請參閱十進位值在ASCII 表中)。也就是說,如果我們願意,我們可以將 char 表示為 int。Java 8 利用了這一點。從 Java 版本 8 開始,我們有了IntStream——一個用於處理原始 int 的流。因此,在 charSequence 中可以獲得表示 chars 或 codePoints 的 IntStream。在繼續討論之前,我們將透過一個範例來展示這種方法的便利性。讓我們使用Tutorialspoint線上java編譯器並執行程式碼:
public static void main(String []args){
        String line = "aaabccdddc";
        System.out.println( line.chars().distinct().count() );
}
現在您可以透過這種簡單的方式獲得許多獨特的符號。

代碼點

所以,我們看到了字元。現在還不清楚這些是什麼類型的程式碼點。codePoint的概念出現是因為當Java出現時,16位元(半個int)就足以編碼一個字元。因此,java中的char以UTF-16格式(「Unicode 88」規格)表示。後來出現了Unicode 2.0,其概念是將一個字元表示為代理對(2個字元)。這使我們能夠將可能值的範圍擴展到 int 值。有關更多詳細信息,請參閱 stackoverflow:“將字元與代碼點進行比較? ” JavaDoc for Character中也提到了 UTF-16 。在 JavaDoc 中,據說: In this representation, supplementary characters are represented as a pair of char values, the first from the high-surrogates range, (\uD800-\uDBFF), the second from the low-surrogates range (\uDC00-\uDFFF). 用標準字母表重現這一點是相當困難的(甚至可能是不可能的)。但符號並非以字母和數字結尾。在日本,他們想出了一種難以編碼的東西,即表情符號——表意文字和表情符號的語言。維基百科上有一篇關於此的有趣文章:「Emoji」。讓我們找一個表情符號的例子,例如:「Emoji Ghost」。正如我們所看到的,甚至在那裡指示了相同的代碼點(值 = U+1F47B)。它以十六進制格式表示。如果我們轉換為十進制數,我們會得到 128123。這超過了允許的 16 位(即超過 65535)。我們來複製一下:
Java 中的字串(類別 java.lang.String) - 2
不幸的是,JavaRush 平台不支援文本中的此類字元。因此,在下面的範例中,您需要將一個值插入 String 中。因此,現在我們將了解一個簡單的測試:
public static void main(String []args){
	    String emojiString = "Вставте сюда эмоджи через ctrl+v";
	    //На один emojiString приходится 2 чара (т.к. не влезает в 16 бит)
	    System.out.println(emojiString.codePoints().count()); //1
	    System.out.println(emojiString.chars().count()); //2
}
正如您所看到的,在這種情況下,1 個代碼點對應 2 個字元。這就是魔法。

特點

正如我們上面看到的,Java 中的字串由 char 組成。原始類型允許您儲存值,但是java.lang.Character原始類型的包裝器允許您使用此符號執行許多有用的操作。例如,我們可以將字串轉換為大寫:
public static void main(String[] args) {
    String line = "организация объединённых наций";
    char[] chars = line.toCharArray();
    for (int i = 0; i < chars.length; i++) {
        if (i == 0 || chars[i - 1] == ' ') {
            chars[i] = Character.toUpperCase(chars[i]);
        }
    }
    System.out.println(new String(chars));
}
嗯,各種有趣的東西:,,,,,,isAlphabetic()isLetter()例如,括號。 ' ('有鏡像')') 。isSpaceChar()isDigit()isUpperCase()isMirrored()

字串池

Java中的字串是不可變的,即常數。java.lang.String類別本身的 JavaDoc 中也指出了這一點。其次,也是非常重要的,字串可以指定為文字:
String literalString = "Hello, World!";
String literalString = "Hello, World!";
也就是說,如上所述,任何帶有引號的字串實際上都是一個物件。這就引出了一個問題 - 如果我們如此頻繁地使用字串並且它們通常是相同的(例如,文字“Error”或“Successively”),有什麼方法可以確保不會每次都創建字串?順便說一下,我們還有 Map,其中鍵可以是字串。那我們絕對不能讓相同的字串成為不同的對象,否則我們將無法從Map中取得對象。Java 開發人員想了又想,最後想出了字串池。這是儲存字串的地方,你可以稱之為字串快取。並不是所有的行本身都結束在那裡,而是只有程式碼中透過文字指定的行才結束。您可以自行在池中新增一條線,稍後會詳細介紹。所以,在記憶體中我們在某個地方有這個快取。一個公平的問題:這個游泳池位於哪裡?這個問題的答案可以在 stackoverflow 上找到:「Java 的字串常數池位於哪裡,堆疊還是堆疊?」它位於Heap記憶體中,一個特殊的運行時常數池區域。當虛擬機器從方法區(Java 虛擬機器內的所有執行緒都可以存取的堆中的一個特殊區域)建立類別或介面時,就會指派執行時間常數池。字串池為我們帶來了什麼?這有幾個優點:
  • 不會建立相同類型的對象
  • 透過引用進行比較比透過 equals 進行逐個字元比較更快
但是如果我們想將創建的物件放入這個快取中怎麼辦?然後,我們有一個特殊的方法:String.intern 這個方法將一個字串加入到字串池中。值得注意的是,這不僅僅是某種數組形式的快取(對於整數)。實習生方法被指定為「native」。這意味著該方法本身是用另一種語言(主要是 C++)實現的。對於基本 Java 方法,可以在 JVM 層級對其應用各種其他最佳化。一般來說,魔法會在這裡發生。閱讀以下有關實習生的文章很有趣:https://habr.com/post/79913/#comment_2345814 這似乎是個好主意。但這又將如何影響我們呢?但確實會有影響)
public static void main(String[] args) {
    String test = "literal";
    String test2 = new String("literal");
    System.out.println(test == test2);
}
正如您所看到的,行是相同的,但結果將是錯誤的。這都是因為 == 不是按值比較,而是按引用比較。這就是它的工作原理:
public static void main(String[] args) {
    String test = "literal";
    String test2 = new String("literal").intern();
    System.out.println(test == test2);
}
請注意,我們仍然會創建新的字串。也就是說,intern會從快取中傳回給我們一個String,但是我們在快取中搜尋到的原始String會被丟掉進行清理,因為 沒有人知道他的事。這顯然是不必要的資源消耗 =( 因此,您應該始終使用 equals 來比較字串,以盡可能避免突然且難以檢測的錯誤。
public static void main(String[] args) {
    String test = "literal";
    String test2 = new String("literal").intern();
    System.out.println(test.equals(test2));
}
Equals 執行逐個字元的字串比較。

級聯

我們記得,可以添加線條。正如我們所記得的,我們的字串是不可變的。那麼它是如何運作的呢?沒錯,建立了一個新行,其中包含要新增的物件的符號。加級串聯的工作原理有上百萬個版本。有些人認為每次都會有一個新的物體,有些人則認為會有別的東西。但可能只有一個人是對的。而那個人就是javac編譯器。讓我們使用線上編譯服務並運行:
public class HelloWorld {

    public static void main(String[] args) {
        String helloMessage = "Hello, ";
        String target = "World";
        System.out.println(helloMessage + target);
    }

}
現在讓我們將其儲存為 zip 存檔,將其解壓縮到目錄並執行:javap –c HelloWorld 在這裡我們可以找到所有內容:
Java 中的字串(類別 java.lang.String) - 3
當然,在循環中,最好自己透過 StringBuilder 進行連接。並不是因為某種魔法,而是使得 StringBuilder 在循環之前創建,並且在循環本身中僅發生追加。順便說一句,這裡還有一件有趣的事。有一篇很棒的文章:「Java 中的字串處理」。第一部分:String、StringBuffer、StringBuilder。” 評論裡有很多有用的信息。例如,指定在連線視圖時,new StringBuilder().append()...toString()內部最佳化生效,由 -XX:+OptimizeStringConcat 選項調節,預設啟用該選項。內在的-翻譯為「內在的」。JVM 以一種特殊的方式處理這些事情,將它們作為 Native 處理,只是沒有 JNI 的額外成本。閱讀更多內容:「HotSpot VM 中的內在方法」。

StringBuilder 和 StringBuffer

正如我們在上面看到的,StringBuilder 是一個非常有用的工具。字串是不可變的,即 不可變的。我想折疊它。因此,我們提供了 2 個類別來幫助我們:StringBuilder 和 StringBuffer。兩者的主要區別在於StringBuffer是在JDK1.0中引入的,而StringBuilder是在java 1.5中作為StringBuffer的非同步版本引入的,以消除不必要的方法同步所增加的開銷。這兩個類別都是抽象類別 AbstractStringBuilder 的實作 - 可變的字元序列。裡面儲存了一個 Charm 數組,按照以下規則進行擴充:value.length * 2 + 2。預設情況下,StringBuilder 的大小(容量)為 16。

可比

字串是可比較的,即 實作compareTo方法。這是透過逐個字元比較來完成的。有趣的是,最小長度是從兩個字串中選擇的,並對其執行循環。因此,compareTo要么傳回第一個不匹配字元的int值之間的差值,直到最小字串長度,要么返回字串長度之間的差值,如果所有字元都在最小字串長度內匹配。這種比較稱為「字典編排」。

使用 Java 字串

String 有很多有用的方法:
Java 中的字串(類別 java.lang.String) - 4
使用字串有很多任務。例如,在Coding Bat上。coursera上還有一門課:《字串演算法》。

結論

即使是對該類別的簡短概述也會佔用大量空間。這還不是全部。我強烈建議觀看 JPoint 2015 的報告:Alexey Shipilev - Catechism java.lang.String
#維亞切斯拉夫
留言
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION