java不變字串

來源:互聯網
上載者:User

請觀察下述代碼:
 

//: Stringer.javapublic class Stringer {  static String upcase(String s) {    return s.toUpperCase();  }  public static void main(String[] args) {    String q = new String("howdy");    System.out.println(q); // howdy    String qq = upcase(q);    System.out.println(qq); // HOWDY    System.out.println(q); // howdy  }} ///:~

q傳遞進入upcase()時,它實際是q的控制代碼的一個副本。該控制代碼串連的對象實際只在一個統一的物理位置處。控制代碼四處傳遞的時候,它的控制代碼會得到複製。
若觀察對upcase()的定義,會發現傳遞進入的控制代碼有一個名字s,而且該名字只有在upcase()執行期間才會存在。upcase()完成後,本地控制代碼s便會消失,而upcase()返回結果——還是原來那個字串,只是所有字元都變成了大寫。當然,它返回的實際是結果的一個控制代碼。但它返回的控制代碼最終是為一個新對象的,同時原來的q並未發生變化。所有這些是如何發生的呢?

1. 隱式常數
若使用下述語句:
String s = "asdf";
String x = Stringer.upcase(s);
那麼真的希望upcase()方法改變自變數或者參數嗎?我們通常是不願意的,因為作為提供給方法的一種資訊,自變數一般是拿給代碼的讀者看的,而不是讓他們修改。這是一個相當重要的保證,因為它使代碼更易編寫和理解。
為了在C++中實現這一保證,需要一個特殊關鍵字的協助:const。利用這個關鍵字,程式員可以保證一個控制代碼(C++叫“指標”或者“引用”)不會被用來修改原始的對象。但這樣一來,C++程式員需要用心記住在所有地方都使用const。這顯然易使人混淆,也不容易記住。

2. 覆蓋"+"和StringBuffer
利用前面提到的技術,String類的對象被設計成“不可變”。若查閱聯機文檔中關於String類的內容(本章稍後還要總結它),就會發現類中能夠修改String的每個方法實際都建立和返回了一個嶄新的String對象,新對象裡包含了修改過的資訊——原來的String是原封未動的。因此,Java裡沒有與C++的const對應的特性可用來讓編譯器支援對象的不可變能力。若想獲得這一能力,可以自行設定,就象String那樣。
由於String對象是不可變的,所以能夠根據情況對一個特定的String進行多次別名處理。因為它是唯讀,所以一個控制代碼不可能會改變一些會影響其他控制代碼的東西。因此,唯讀對象可以很好地解決別名問題。
通過修改產生對象的一個嶄新版本,似乎可以解決修改對象時的所有問題,就象String那樣。但對某些操作來講,這種方法的效率並不高。一個典型的例子便是為String對象覆蓋的運算子“+”。“覆蓋”意味著在與一個特定的類使用時,它的含義已發生了變化(用於String的“+”和“+=”是Java中能被覆蓋的唯一運算子,Java不允許程式員覆蓋其他任何運算子——注釋④)。

④:C++允許程式員隨意覆蓋運算子。由於這通常是一個複雜的過程(參見《Thinking in C++》,Prentice-Hall於1995年出版),所以Java的設計者認定它是一種“糟糕”的特性,決定不在Java中採用。但具有諷剌意味的是,運算子的覆蓋在Java中要比在C++中容易得多。

針對String對象使用時,“+”允許我們將不同的字串聯接起來:
 

String s = "abc" + foo + "def" + Integer.toString(47);

可以想象出它“可能”是如何工作的:字串"abc"可以有一個方法append(),它建立了一個字串,其中包含"abc"以及foo的內容;這個新字串然後再建立另一個新字串,在其中添加"def";以此類推。
這一設想是行得通的,但它要求建立大量字串對象。儘管最終的目的只是獲得包含了所有內容的一個新字串,但中間卻要用到大量字串對象,而且要不斷地進行垃圾收集。我懷疑Java的設計者是否先試過種方法(這是軟體開發的一個教訓——除非自己試試代碼,並讓某些東西運行起來,否則不可能真正瞭解系統)。我還懷疑他們是否早就發現這樣做獲得的效能是不能接受的。
解決的方法是象前面介紹的那樣製作一個可變的同志類。對字串來說,這個同志類叫作StringBuffer,編譯器可以自動建立一個StringBuffer,以便計算特定的運算式,特別是面向String對象應用覆蓋過的運算子+和+=時。下面這個例子可以解決這個問題:
 

//: ImmutableStrings.java// Demonstrating StringBufferpublic class ImmutableStrings {  public static void main(String[] args) {    String foo = "foo";    String s = "abc" + foo +      "def" + Integer.toString(47);    System.out.println(s);    // The "equivalent" using StringBuffer:    StringBuffer sb =       new StringBuffer("abc"); // Creates String!    sb.append(foo);    sb.append("def"); // Creates String!    sb.append(Integer.toString(47));    System.out.println(sb);  }} ///:~

建立字串s時,編譯器做的工作大致等價於後面使用sb的代碼——建立一個StringBuffer,並用append()將新字元直接加入StringBuffer對象(而不是每次都產生新對象)。儘管這樣做更有效,但不值得每次都建立象"abc"和"def"這樣的引號字串,編譯器會把它們都轉換成String對象。所以儘管StringBuffer提供了更高的效率,但會產生比我們希望的多得多的對象。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.