《Java從入門到失業》第四章:類和對象(4.2):String類


4.2String類

       這一節,我們學習第一個類:String類。String翻譯成漢語就是“字符串”,是字符的序列。我們知道,在Java中,默認采用Unicode字符集,因此字符串就是Unicode字符的序列。例如字符串“Java大失叔”,就是由7個Unicode字符‘J’、‘a’、‘v’、‘a’、‘大’、‘失’、‘叔’組成。在JDK中,把字符串抽象成一個類String提供給我們使用。String類在java.lang包中。

4.2.1構造String對象

  上面我們說了,想看電視得先買一台電視,電視在出廠的時候廠家會初始化它的狀態。想使用String類,得先得到一個String的對象,然后指定屬性的初始狀態,然后才能使用它。得到對象的過程,叫做構造對象。在Java中,我們用構造器(constructor)來構造實例,構造器其實是一種特殊的方法,用來構造並初始化對象。我們采用在構造器前面加上new關鍵字來實現,例如:

new 構造器();

我們查看String類的API文檔(怎么查這里不再贅述),構造方法截圖如下:

 

發現String類的構造方法有幾個特點:

  • 足足有15個構造方法
  • 有的方法上標有Deprecated,這個標簽的含義是不推薦使用,將來在新版本中可能會移除
  • 構造方法的名字和類名相同

構造方法的名字和類名相同,這是Java構造器的特點,也是規定。我們挑選其中一個構造方法:String(char[] value)

我們看到,這其實就是用一個char數組來構造一個字符串,那么首先我們得有一個char數組才行,例如我們想要得到一個字符串“Java大失叔,你真棒”。那么代碼如下:

char[] a = { 'J', 'a', 'v', 'a', '大', '失', '叔', ',', '你', '真', '棒' };  
String s = new String(a);  
System.out.println(s);// 結果輸出:Java大失叔,你真棒 

事實上,由於String太常用了,Java給我們提供了更加簡便的構造方法,直接用雙引號將一段字符序列包起來,就得到了一個String的實例:

String s = "Java大失叔,你真棒";  

OK,我們得到了一個String對象了,下面我們來使用這個對象。我們可以看到,API中有幾十個方法,我們挑選一些常用的演示一下。

4.2.2代碼點和代碼單元

       首先,我們回憶一下關於char和Unicode的知識。Unicode定義了U+0000到U+10FFFF一共1114112個碼位(code point),英文直譯為代碼點。一個代碼點表示一個字符。char是用來存放UTF-16編碼中的一個代碼單元(code unit),即2個字節。平面0的代碼點用一個代碼單元即一個char就可以表示,其余的代碼點需要用2個代碼單元即2個char才能表示。

       我們知道Stirng是Unicode字符的序列,但是底層的實現實際上是用char構成的。String類提供了一些關於代碼點和代碼單元相關的方法,請看下面摘抄的幾個方法:

修飾和類型

方法

描述

int

length()

返回字符串的長度

int

codePointCount(int beginIndex, int endIndex)

返回beginIndex和endIndex-1之間的代碼點的數量。

char

charAt(int index)

返回index索引處的char

int

codePointAt(int index)

返回index索引處的代碼點

我們想獲得字符的數量(即代碼點的數量),需要用codePointCount方法,而length方法返回的是char的數量(即代碼單元的數量)。調用對象的方法很簡單,用如下形式:

對象.方法();

代碼示例如下:

String s = "大失叔喜歡打麻將🀀🀁🀂🀃🀄🀅";//  
System.out.println("字符串s的代碼單元數量為:" + s.length());  
System.out.println("字符串s的代碼點數量為" + s.codePointCount(0, s.length())); 

輸出結果:

字符串s的代碼單元數量為:20  
字符串s的代碼點數量為:14 

我們可以看到,對於🀀🀁🀂🀃🀄🀅,這6個字符,每個字符占用2個代碼單元,所以length方法的結果是20,而codePointCount方法的結果是14。

       我們再看看后面2個方法,這應該就相對簡單了,一個是返回index處的代碼單元,一個是返回index處的代碼點。我們直接看代碼:

String s = "大失叔喜歡打麻將🀀🀁🀂🀃🀄🀅";//  
int c = s.charAt(8);// 把char賦值給一個int,對應這個代碼單元對應的十進制,結果是55356,十六進制為0xD83C
int d = s.codePointAt(8);// 結果是126976,十六進制為0x1F000 
 

4.2.3對象與變量

       上面我們看到,創建出來一個String對象,一般我們會賦值給一個變量。那么對象和變量之間有什么關系和區別呢?我們先看幾行代碼:

String a; 
String b;
a = "大失叔喜歡打麻將";  
b = a;
 

這幾行代碼,會涉及到下面一些行為:

  • 第1、2行,我們定義了2個String類型的變量a和b。這時候Java會在內存中分別分配一塊空間給a和b,但是這時候這2塊內存空間中沒有存放任何值。
  • 第3行,我們把一個字符串賦值給變量a。Java會在內存中分配一塊空間,存放這個字符串,然后把這塊空間的地址存放到變量a的內存空間中。
  • 第4行,把變量a賦值給b,相當於把變量a內存空間中的地址存放到變量b的內存空間中,這時候a和b同時指向字符串“大失叔喜歡打麻將”對應的內存空間。

我們用一張圖示意如下:

 

我們需要牢牢記住一點:在Java中,任何對象的值都是存放在堆內存中的,而對象類型的變量對應的內存中保存的是對象的內存地址,我們稱之為對象引用。因此new操作符返回的結果其實是一個引用。

       我們可以顯式的把一個對象變量設置為null,這時候該變量的內存存放的將是空值,表明它不引用任何對象。如果我們對一個值為null的變量進行方法調用,程序在運行時則會拋出異常。

4.2.4字符串拼接

       在Java中,字符串的拼接有一種很簡單的方法,就是用加號(+)連接兩個字符串,結果會構造出一個新的字符串對象。我們看代碼:

String a = "Java大失叔";  
String b = "喜歡打麻將";  
String c = a + b;  
System.out.println(c);// 結果將輸出:"Java大失叔喜歡打麻將"

在這段代碼中,堆內存中將會分配3塊空間,分別對應字符串"Java大失叔"、"喜歡打麻將"、" Java大失叔喜歡打麻將"。我們用一張圖來演示這個過程:

 

我們還可以將一個字符串和一個非字符串用+連接起來,這時候非字符串對象會被轉換為字符串(具體如何轉換,后續會詳細探討)。例如:

String a = "Java大失叔卡里只有";  
int b = 200;  
String c = "元錢了";  
System.out.println(a + b + c);// 結果將輸出:Java大失叔卡里只有200元錢了

String類的API中還提供了一個方法concat用來拼接字符串,方法摘抄如下:

修飾和類型

方法

描述

String

concat(String str)

將str拼接在本字符串后面

使用起來也很簡單,代碼如下:

String a = "Java大失叔";  
String b = "喜歡打麻將";  
String c = a.concat(b);  
System.out.println(c);// 結果將輸出:Java大失叔喜歡打麻將

  有的時候,需要將很多個字符串拼接成一個大字符串,這時,如果用+的方式,不是很合適了。因為用+的方式,每次都會構建一個新的對象,比較耗時,還占內存,效率比較低。好在Java提供了另外一種方式,就是采用StringBuilder類和StringBuffer類。一般情況下我們都會采用StringBuilder類,因為它的效率略高。而Stringbuffer類是線程安全的,關於線程會在后面專門討論。這2個類的API幾乎完全一樣。用StringBuilder非常簡單,代碼演示如下:

StringBuilder sb = new StringBuilder();// 首先構建StringBuilder對象  
sb.append("Java");// 然后用append方法添加小字符串  
sb.append("大失叔");  
sb.append("太帥了");  
String s = sb.toString();// 最后調用toString()方法,返回一個字符串對象  
System.out.println(s);// 結果將輸出:Java大失叔太帥了

其實append方法返回的依然是StringBuilder對象,因此還可以采用一種更為簡潔的方式:

String s = new StringBuilder().append("Java").append("大失叔").append("太帥了").toString();  
System.out.println(s);// 結果將輸出:Java大失叔太帥了

關於加號、concat、StringBuilder這三者的比較,筆者給出如下結論:

  1. 對於拼接少量的字符串,用哪種方式都差不多,加號書寫起來更加方便。筆者幾乎沒用過concat方法。
  2. 加號和StringBuilder都可以拼接非字符串類型(可以查看API,有很多個append方法)。
  3. 對於需要拼接多個字符串的時候,強烈建議使用StringBuilder。(筆者在早年編寫一個網絡程序的時候,吃過虧)

4.2.5字符串截取和比較

       關於字符串還會經常使用比較和截取的方法,先列出方法如下:

修飾和類型

方法

描述

boolean

startsWith(String prefix)

檢查字符串是否以指定的前綴prefix開始

boolean

endsWith(String suffix)

檢查字符串是否以指定的后綴suffix結尾

String

trim()

刪除字符串前后的空白,並返回一個新字符串

boolean

equals(Object anObject)

檢測2個字符串是否相等

boolean

equalsIgnoreCase(String anotherString)

檢測2個字符串在忽略大小寫的情況下是否相等

String

substring(int beginIndex)

截取從beginIndex到末尾的字符串並返回

String

substring(int beginIndex, int endIndex)

截取從beginIndex到endIndex的字符串並返回,不包括endIndex

我們經常會比較一個字符串是否以某個字符串開頭或結尾,代碼如下:

String a = "Java大失叔";  
boolean b1 = a.startsWith("Java");// 結果為true  
boolean b2 = a.startsWith("java");// 結果為false  
boolean b3 = a.endsWith("叔");// 結果為true  

  有時候,經過網絡傳輸后的字符串經常前后會帶一些空白,眼睛又看不見,很不利於比較,會用trim方法去掉前后的空白:

String a = "   Java大失叔       ";  
String b = a.trim();  
System.out.println(b);// 結果將輸出:Java大失叔 

需要注意,這里的空白指的是Unicode編碼小於或等於”\u0020”的字符。

       對於字符串的截取,用subString方法將非常方便:

String a = "Java大失叔 ";  
String b = a.substring(4);// 結果是:大失叔  
String c = a.substring(2, 6);// 結果是:va大失

這里要注意的是,返回的結果字符串是包括beginIndex位置的代碼單元,但是不包括endIndex位置的代碼單元。

       比較2個字符串是否相等,用equals方法,如果相等返回ture,否則返回false。如果想不區分大小寫比較是否相等,則可以使用equalsIgnoreCase方法。表達式為:

a.equals(b)

其中,a和b即可以是變量,也可以是字符串常量。

String a = "Java大失叔";  
String b = "java大失叔";  
System.out.println(a.equals(b));// 結果為false  
System.out.println(a.equalsIgnoreCase(b));// 結果為true
System.out.println("JAVA大失叔".equalsIgnoreCase(b));// 結果為true

這里需要特別注意,千萬不能用==運算符來比較2個字符串是否相等。因為==運算符比較的是2個字符串是否存放在同一個內存位置上。但是事實上,對於2個字符內容完全一樣的字符串,是很有可能存放在不同的內存空間的,因此用==比較結果將為false。這個問題Java新手經常會犯。

  最后我們很容易發現,String的API中沒有提供修改字符串內容的方法。這其實是因為String類被定義為final的(關於final后面也會介紹),我們看一下String的源代碼(在Eclipse中,可以很輕松的查看源代碼,鼠標移動的任意一個String字符上,按住Ctrl鍵后,點擊鼠標左鍵):

public final class String  
    implements java.io.Serializable, Comparable<String>, CharSequence 

用final修飾一個類后,這個類的對象將不能被修改。

       String類提供了50個多個方法,這些方法都很有用,但是我們不可能記住所有的方法名和參數要求,這里還有一個Eclipse的小技巧,當我們敲完變量名加“點”后,Eclipse會自動彈出提示,或者還可以用Ctrl+/自動補全,如下圖:

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM