1. 程式人生 > >Java程式設計思想 第十三章:字串

Java程式設計思想 第十三章:字串

1.不可變String

String物件是不可變的,每一個看似修改了String值的方法,實際上都是建立了一個全新的String物件。

public class Immutable {
	public static String upCase(String s){
		return s.toUpperCase();
	}
	public static void main(String[] args) {
		String q = "howdy";
		System.out.println(q);
		String qq = upCase(q);
		System.out.println
(qq); System.out.println(q); } } 執行結果: howdy HOWDY howdy

當把q傳給upCase方法時,實際上傳遞的是引用的一個拷貝。其實每當String物件作為引數傳遞時,傳遞的都是一個拷貝。再看upCase方法,只有當該方法執行時,區域性引用s才存在,一旦該方法結束,引用s就消失了。該方法的返回值,實際上是最終值的引用,也就是upCase返回的引用已經指向了一個新的物件,而原本的q並沒有發生任何變化。比如如下的方法,我們並不希望在經過一個操作之後,改變原有的物件。

因為方法的引數是用來傳遞資訊的,而不是用來改變原有物件本身的。

2.過載“+”與StringBuilder

String物件是不可變的,所以指向它的任何引用都不會改變該物件的值。不可變性會對效率帶來一個問題,為String物件過載“+”操作符就是一個例子,過載的意思是一個操作符應用與特定的類時被賦有特殊的意義。(用於String的“+”和"+="是Java中僅有的兩個過載過的操作符,Java不允許程式設計師自己過載操作符)

操作符“+”可以用來連線兩個String

public class Concatetion {
	public static void main(String[] args) {
		String mango = "mango"
; String s = "abc" + mango + "def" + 42; System.out.println(s); } }

上述程式碼的執行過程可能是這樣的:String可能有一個append方法,然後它會生成一個新的String物件用來連線abc和mango,然後該物件再與def相連生成新的物件,依次類推。這樣做的話會產生很多中間垃圾需要清理因此它效率極低。

我們程式碼中並沒有使用StringBuilder類,然而編譯器卻自動的引入了StringBuilder類,從編譯後的程式碼可以看出,字串連線工作主要的操作是編譯器建立一個StringBuilder物件,然後呼叫該物件的append()方法將所有的要連線的字串連線到後邊,最後呼叫toString()方法轉換成String物件存給s。

因此在編寫一個類似toString()的方法時,如果字串較短時,我們可以使用普通的拼接方式,當字串操作較為複雜的時候,我們在程式碼中直接建立一個StringBuilder物件進行操作效率會更加優異。

3. 無意識的遞迴

我們希望使用toString()方法打印出物件的記憶體地址,那麼我們可能會考慮使用this關鍵字:

import java.util.ArrayList;
import java.util.List;
public class InfiniteRecursion {
	public String toString(){
		return "InfiniteRecursion address" + this;
	}
	public static void main(String[] args) {
		List<InfiniteRecursion> v = new ArrayList<InfiniteRecursion>();
		for(int i = 0;i<10;i++){
			v.add(new InfiniteRecursion());
		}
		System.out.println(v);
	}
}

此時發生了型別轉換,編譯器發現“+”後邊不是String型別,會試圖轉換成String型別,轉換的方式就是呼叫toString方法,因此會遞迴呼叫,此處如果想正確列印地址,那麼需要使用其基類Object的toString()方法。

4. String上的操作

public int java.lang.String.hashCode()
//比較兩個字串 過載了Object的方法 當兩個字串值相同、型別相同則認為相同 忽略了引用
public boolean java.lang.String.equals(java.lang.Object)
public java.lang.String java.lang.String.toString()
//獲取指定索引下標位置上的字元
public char java.lang.String.charAt(int)
public int java.lang.String.codePointAt(int)
public int java.lang.String.codePointBefore(int)
public int java.lang.String.codePointCount(int,int)
public int java.lang.String.compareTo(java.lang.Object)
//按詞典順序比較兩個字串 大小寫並不等價
public int java.lang.String.compareTo(java.lang.String)
public int java.lang.String.compareToIgnoreCase(java.lang.String)
//字串連線 返回一個新的String為指定String連線引數
public java.lang.String java.lang.String.concat(java.lang.String)
//查詢字串中是否包含指定字元 存在返回true
public boolean java.lang.String.contains(java.lang.CharSequence)
//比較兩個字串
public boolean java.lang.String.contentEquals(java.lang.StringBuffer)
public boolean java.lang.String.contentEquals(java.lang.CharSequence)
public static java.lang.String java.lang.String.copyValueOf(char[])
public static java.lang.String java.lang.String.copyValueOf(char[],int,int)
public boolean java.lang.String.endsWith(java.lang.String)
//比較字串是否相同 忽略大小寫
public boolean java.lang.String.equalsIgnoreCase(java.lang.String)
public static java.lang.String java.lang.String.format(java.lang.String,java.lang.Object[])
public static java.lang.String java.lang.String.format(java.util.Locale,java.lang.String,java.lang.Object[])
public void java.lang.String.getBytes(int,int,byte[],int)
public byte[] java.lang.String.getBytes(java.lang.String) throws java.io.UnsupportedEncodingException
public byte[] java.lang.String.getBytes(java.nio.charset.Charset)
public byte[] java.lang.String.getBytes()
// s.getChars(1,2,char,3) 賦值s串中下標為1-2的到char中 char中起始位置為3
public void java.lang.String.getChars(int,int,char[],int)
public int java.lang.String.indexOf(int,int)
public int java.lang.String.indexOf(java.lang.String,int)
//是否包含該字元 包含返回下標 否則返回-1
public int java.lang.String.indexOf(java.lang.String)
public int java.lang.String.indexOf(int)
public native java.lang.String java.lang.String.intern()
public boolean java.lang.String.isEmpty()
public int java.lang.String.lastIndexOf(int,int)
public int java.lang.String.lastIndexOf(java.lang.String)
public int java.lang.String.lastIndexOf(java.lang.String,int)
public int java.lang.String.lastIndexOf(int)
//String 中字元的個數
public int java.lang.String.length()
public boolean java.lang.String.matches(java.lang.String)
public int java.lang.String.offsetByCodePoints(int,int)
public boolean java.lang.String.regionMatches(boolean,int,java.lang.String,int,int)
public boolean java.lang.String.regionMatches(int,java.lang.String,int,int)
//把字串中的第一個引數字元替換成第二個
public java.lang.String java.lang.String.replace(char,char)
public java.lang.String java.lang.String.replace(java.lang.CharSequence,java.lang.CharSequence)
public java.lang.String java.lang.String.replaceAll(java.lang.String,java.lang.String)
public java.lang.String java.lang.String.replaceFirst(java.lang.String,java.lang.String)
public java.lang.String[] java.lang.String.split(java.lang.String,int)
public java.lang.String[] java.lang.String.split(java.lang.String)
public boolean java.lang.String.startsWith(java.lang.String,int)
//字串起始串
public boolean java.lang.String.startsWith(java.lang.String)
public java.lang.CharSequence java.lang.String.subSequence(int,int)
//字串截斷
public java.lang.String java.lang.String.substring(int)
public java.lang.String java.lang.String.substring(int,int)
//返回一個字元陣列,該字元陣列包含字串的所有字元
public char[] java.lang.String.toCharArray()
//字串字元轉換成小寫
public java.lang.String java.lang.String.toLowerCase()
public java.lang.String java.lang.String.toLowerCase(java.util.Locale)
//字串字元轉換成大寫
public java.lang.String java.lang.String.toUpperCase()
public java.lang.String java.lang.String.toUpperCase(java.util.Locale)
//刪除String兩端的空白字元
public java.lang.String java.lang.String.trim()
public static java.lang.String java.lang.String.valueOf(char)
public static java.lang.String java.lang.String.valueOf(float)
public static java.lang.String java.lang.String.valueOf(int)
public static java.lang.String java.lang.String.valueOf(long)
public static java.lang.String java.lang.String.valueOf(double)
public static java.lang.String java.lang.String.valueOf(java.lang.Object)
public static java.lang.String java.lang.String.valueOf(char[])
public static java.lang.String java.lang.String.valueOf(char[],int,int)
public static java.lang.String java.lang.String.valueOf(boolean)
public final native java.lang.Class java.lang.Object.getClass()
public final native void java.lang.Object.notify()
public final native void java.lang.Object.notifyAll()
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
public final void java.lang.Object.wait() throws java.lang.InterruptedException

5. 格式化輸出

5.1 printf

JavaSE5提供了格式化輸出功能,這一功能使得控制輸出的功能變得更加簡單,同時也給開發者帶來了更加強大的程式碼輸出控制能力。JavaSE5引入的format方法可以用於PrintStream或PrintWriter物件,其中也包括System.out物件。format()方法模仿在C語言的printf()如下簡單示例:


public class SimpleFormat {
	public static void main(String[] args) {
		int x = 5;
		double y = 5.333221;
		System.out.println("Row1: [" + x + " " + y + "]");
		System.out.format("Row1: [%d %f]\n",x,y);
		System.out.printf("Row1: [%d %f]\n",x,y);
	}
}
執行結果:
Row 1: [5 5.332542]
Row 1: [5 5.332542]
Row 1: [5 5.332542]

5.2 System.out.format

System.out.format("Row1: [%d %f]\n",x,y);

5.3 Formatter

Java中所有新的格式化功能都由java.util.Formatter類處理,可以將其看做是一個翻譯器,它將你的格式化字串和資料翻譯成想要的結果。當你建立了一個Formatter物件的時候,需要向編譯器傳遞一些資訊,告訴他最終的結果將向哪裡輸出。

5.4 格式化說明符

%[argument_index$][flags][width][precision] conversion

有的時候我們希望做一些更精緻的格式化資訊,比如控制空格與對齊,最常見的是控制域的最小尺寸,這可以通過指定width實現,Formatter物件通過在必要時新增空格,來確保一個域至少達到某個長度。預設情況下域是右對齊的,不過也可以通過“-”指定資料的對齊方式。

7.掃描輸入

目前來說,讀取文字或者從標準輸入讀取資料一般的解決方法是讀入一行文字,然後對其進行分詞,然後使用Integer、Double的各種解析方法來解析資料。

import java.io.BufferedReader;
import java.io.IOException;
import java.io.StringReader;
 
public class SimpleRead {
	public static BufferedReader input = new BufferedReader(new StringReader("Sir Robin of Camelot\n22 1.523222"));
	public static void main(String[] args) {
		try{
			System.out.println("What's your name?");
			String name = input.readLine();
			System.out.println(name);
			System.out.println("How old are you?What's your favorite double");
			String numbers = input.readLine();
			System.out.println(numbers);
			String numArray[] = numbers.split(" ");
			int age = Integer.parseInt(numArray[0]);
			double favorite = Double.parseDouble(numArray[1]);
			System.out.format("Hi %s.\n",name);
			System.out.format("In 5 years you will be %d. \n",age+5);
			System.out.format("My Favorite double is %f.",favorite/2);
			}catch(IOException e){
				System.err.println("I/O Exception");
			}
	}
}

執行結果:
What is your name?
Sir Robin of Camelot
How old are you? What is your favorite double?
(input: <age> <double>)
22 1.61803
Hi Sir Robin of Camelot.
In 5 years you will be 27.
My favorite double is 0.809015.

以上程式碼中,使用了IO中的readLine()方法讀取輸入流中的一行,然後進行解析。終於在JavaSE5新增了Scanner類,它可以大大的減輕掃描輸入的工作。

import java.util.Scanner;
public class BetterRead {
	public static void main(String[] args) {
		Scanner stdin = new Scanner(SimpleRead.input);
		System.out.println("What's your name?");
		String name = stdin.nextLine();
		System.out.println(name);
		System.out.println("How old are you?What's your favorite double");
		System.out.println("(input:<age><double>)");
		int age = stdin.nextInt();
		double favorite = stdin.nextDouble();
		System.out.println(age);
		System.out.println(favorite);
		System.out.format("Hi %s.\n",name);
		System.out.format("In 5 years you will be %d. \n",age+5);
		System.out.format("My Favorite double is %f.",favorite/2