1. 程式人生 > >java 字串,字串緩衝區

java 字串,字串緩衝區

本文學習並總結java中的字串。內容包括String字串常用方法、StringBuffered和StringBuilder功能介紹,學習中練習了論壇中的一些提問問題,同時也對所學知識進行了驗證鞏固。

String類

String是java的字串類,被final修飾,不能被繼承,java 程式中的所有字串字面值(如"abc" )都作為此類的例項實現。
java中字串是常量,一旦被初始化就不可以被改變,對String物件的任何改變,都是返回一個新的String物件。

字串常量池

先說下常量池,每個類都有相應的常量池,常量池(constant pool)指的是在編譯期被確定,並被儲存在已編譯的.class檔案中的一些資料。它包括了關於類、方法、介面等中的常量,字串常量池是常量池的一部分。


jvm啟動前,一個類的字串常量池資訊就已經被確定,寫在.class檔案中,jvm啟動後,字串常量池被載入到記憶體中的方法區
字串常量池中,每個字串常量都是唯一的,沒有相同字面值的多個副本。
字串常量池由String類私有地維護。
程式執行時,可以通過String類的intern()方法動態地擴充字串常量池。當呼叫 intern 方法時,如果池已經包含一個等於此String物件的字串(用 equals(object)方法確定),則返回池中的字串。否則,將此String 物件新增到池中,並返回此String物件的引用。所有字面值字串和字串賦值常量表達式都使用 intern 方法進行操作。

public class StringDemo1 {
	public static void main(String[] args) {
		String s1="abc";//建立String型引用變數s1,指向字串常量池中的"abc"字串。
		String s2=new String("abc");//棧中建立s2引用變數,堆記憶體中建立"abc"字串物件,並被s2指向。
		String s3="a"+"bc";//"a"和"bc"合成為字串常量"abc",先在字串常量池中查詢是否已有"abc"字串物件,已有,不再重新建立物件,s3和s1指向的是字串常量池中的同一個物件,s1==s3.
		String s4="a"+new String("bc");//在堆中建立了"bc"物件,與"a"相加,返回新的字串物件賦給s4,s4還指向堆中的字串物件。
		System.out.println(s1==s2);//false
		System.out.println(s1==s3);//true
		System.out.println(s1==s4);//false
		System.out.println(s2==s4);//false
		System.out.println(s1.equals(s2));//true
		System.out.println("=============================");
		String s5=new String("def");//實際建立了2個物件,編譯完成後字串常量池中就有了"def",執行時在堆記憶體中建立String類物件"def".
		String s6="def";
		System.out.println(s5==s6);//false, s5指向堆記憶體中的"def"物件,s6是指向字串常量池中的"def"字串物件。
		System.out.println(s6==s5.intern());//true, s5.intern()指向字串常量池中的"def"字串物件。		
	}
}

字串常見操作
1. 獲取
1) 獲取字串的長度: int length()
2) 根據位置獲取該位置上的字元:
 char charAt(int index),index越界時,會發生StringIndexOutOfBoundsException異常。
3) 根據字元(子串)獲取該字元(子串)在串中的位置: 
 int index(int ch), 引數為字元的ASCII碼,返回ch在字串中第一次出現的位置,字串中沒有ch時返回-1;
 int index(int ch,int fromIndex), 從fromIndex位置開始搜尋,沒有時返回-1;
 int index(String str), 獲取字串str在另一個字串中第一次出現的位置,沒有時返回-1;


 int lastIndex(int ch), 反向搜尋,返回ch最後出現的位置。
2. 判斷
1) 字串是否包含某一子串: 
boolean contains(String str), 其實通過index(String str)方法完全可以實現此功能;
2) 字串是否以指定內容開頭:boolean startWith(String str) ;
3) 字串是否以指定內容結尾:boolean endWith(String str) ;
以上3個方法合起來可以實現常見的檔名搜尋
4) 字串是否有內容:boolean isEmpty(), jdk1.6版本開始才有,此方法的原理就是判斷字串長度是否為0;
5) 判斷字串內容是否相同:boolean equals(String str);
6)  判斷字串內容是否相財,忽略大小寫: boolean equalsIgnoreCase(String str);
3. 轉換
1) 將字元陣列轉成字串
可以使用String類建構函式:String(char[]), String(char[],offset,count);
還可以使用String類的靜態方法:static String copyValueOf(char[])
                                                      static String copyValueOf(char[],offset,count)
                                                     static String valueOf(char[])
2) 將字串轉成字元陣列:char[] toCharArray()
3) 將位元組陣列轉成字串:與字元陣列轉字串基本完全一樣,將char[]換成byte[]即可
4) 將字串轉成位元組陣列:byte[] getBytes[]
字串和位元組陣列轉換時,可以指定編碼表,這裡穿插說一會編碼、解碼的知識。
編碼:字串變成位元組陣列。解碼:位元組陣列變成字串。
常用的幾種字符集
Ascii碼字符集,用1個位元組來表示,第1位為0,剩餘7位表示總共127個字元,這顯然不能滿足英語之外的很多語言;
ISO8859-1編碼,歐洲編碼表,單位元組編碼,ISO 8859-1相容ASCII碼,在ASCII碼空置的0xA0-0xFF的範圍內,加入了96個字母及符號,藉以供使用變音符號的拉丁字母語言使用,ISO 8859-1又稱Latin-1;
GB2312/GBK/GBK18030, 漢字的國標碼,用2個位元組表示一個漢字。GB2312只用表示簡體字,包含6千多個漢字,GBK完全反向相容GB2312編碼,GBK編碼能夠用來同時表示繁體字和簡體字,包含2萬多個漢字,GBK還可編碼英文字母,英文字母編碼相容iso8859-1編碼(英文字母用一個位元組表示)。GBK後來又擴充套件成GBK18030.
Unicode國際標準碼值,所有字元用定長雙位元組表示(定長編碼計算機處理方便),可以用來表示所有語言的字元,包括英文字母;unicode不相容任何編碼,不過,相對於iso8859-1編碼來說,Uniocode編碼只是在前面增加了一個0位元組,比如字母'a'為"00 61"。
UTF-8國際標準碼值,可以用來表示所有語言中的字元;變長位元組編碼,每一個字元的長度從1-3個位元組不等,能用一個位元組裝下的就用一個位元組表示,依次類推。一般來說,英文字母都是用一個位元組表示,相容iso8859-1,而漢字使用三個位元組,同一個漢字在UTF-8和GBK中的編碼可能不一樣。

import java.io.UnsupportedEncodingException;
import java.util.Arrays;
public class EncodeDemo {
	public static void main(String[] args) throws UnsupportedEncodingException {
		String a="你好";
		byte[] b1=a.getBytes("GBK"); //編碼預設字符集就是GBK,所以可不寫。
		System.out.println(Arrays.toString(b1));
		String s1=new String(b1,"GBK");//解碼預設字符集也是"GBK",此處"GBK"也可以不寫.
		System.out.println(s1);
		
		byte[] b2=a.getBytes("UTF-8"); //使用UTF-8字符集編碼
		System.out.println(Arrays.toString(b2));//b1長度是6,UTF-8中一漢字佔3個位元組。
		String s2=new String(b2,"GBK");//UTF-8編碼的byte陣列,用GBK解碼,每2個位元組去匹配GBK解碼錶,出現亂碼
		System.out.println(s2);		
		
		//byte[] b3=a.getBytes("ISO8895-1"); //執行後報異常java.io.UnsupportedEncodingException;因為iso8859-1字符集中沒法識別中文字,不能對漢字編碼;字串一旦編碼失敗,就再也不能解碼成功了。
		
		byte[] b4=a.getBytes("GBK");
		System.out.println(Arrays.toString(b4));//b1長度是6,UTF-8中一漢字佔3個位元組。
		String s4=new String(b4,"ISO8859-1");//編碼成功,但解碼錯誤 ,可以補救,將b4先用iso8895-1解碼,然後再用GBK解碼。
		System.out.println(s4);
		byte[] b5=s4.getBytes("ISO8859-1");
		System.out.println(Arrays.toString(b5));
		String s5=new String(b5,"GBK");
		System.out.println(s5);;
	}
}
執行結果:
[-60, -29, -70, -61]
你好
[-28, -67, -96, -27, -91, -67]
浣犲ソ
[-60, -29, -70, -61]
????
[-60, -29, -70, -61]
你好
5) 將基本資料型別轉成字串: static String valueOf(int/byte/double等)。

4. 替換
String replace(oldChar,newChar), String replace(oldStr,newStr), 如果要替換的字元或字串不存在,則返回原串。
5. 切割
String[] split(String regex);
注意,若用“.”和“|”切割字串,因這2者都是轉義字元,必須得加"\\",即str.split("\\.")和str.split("\\|")。
6. 獲取子串
1) String subString(begin,end),返回的子串角標含begin,但不含end,含頭不含尾;
2) String subString(begin), 等價於subString(begin, str.length())。
7. 去除空格,比較
1) 將字串轉成大寫或小寫: String toUpperCase(), String toLowerCase()
2) 將字串兩端的多個空格去掉:String trim()
3) 對2個 字串進行自然順序的比較:
int CompareTo(str), 相等時返回0,否則返回2個串中第1個不相等的字元的ASCII碼值的差。
下面是String類相關的2個練習題,題目本身解決的問題也比較有意義,放在這裡作個記錄:

/*
 問題:輸入無重複字元的字串,輸出這些字元的全排列。
*/
/*
 思路:基本就是按照數學中排列組合的原理,對多個字元排列時,可以遞迴分成第一個字元與剩餘字元組成字元子串的全排列的組合。
*/
import java.lang.*;
import java.util.*;
class QuanPL{
    public static void main(String[] args){
        String str="abc";
       // System.out.println((allMaxLen(str)).size());
       // System.out.println(allMaxLen(str));
       System.out.println(all(str));
    }
    //字串全排列,長度從1到str.length()
    public static ArrayList<String> all(String str){
    	ArrayList<String> al=new ArrayList<String>();
    	if(str.length()==1){
    		al.add(str);
    		return al;
    	}else{
    		    String sa=str.charAt(0)+"";
    			al.add(sa);
    			String sub=str.replace(sa,"");
    			//對子串進行遞迴
    			ArrayList<String> tk=all(sub);
    			al.addAll(tk);
    			Iterator<String> it=tk.iterator();
    			String s=null;
    			while(it.hasNext()){
    				s=it.next();
    				for(int j=0;j<=s.length();j++){   					
    					//遍歷sa新增到s上時的位置
    					al.add(s.substring(0,j)+sa+s.substring(j,s.length()));
    				}
    			}
    		}
    	return al;
    }
    //長度為str.length()的字串全排列。
    public static ArrayList<String> allMaxLen(String str){
        ArrayList<String> al=new ArrayList<String>();
        if(str.length()==1){
            al.add(str);
            return al;            
        }else{
            char[] ch=str.toCharArray();
            for(int i=0;i<ch.length;i++){//對排列第1個位置上的可取字元進行遍歷
                String sub=str.replace(ch[i]+"","");
                ArrayList arr=allMaxLen(sub); //遞迴對除去第1個位置上字元的剩餘子串進行排列
                //System.out.println(arr);
                Iterator<String> it=arr.iterator();
                while(it.hasNext()){
                    String s=new String();
                    //String ss=it.next();
                    String re=ch[i]+it.next();//合起來就是整個字串了
                    al.add(re);
                }
            }
            return al;            
        }        
    }
}
/*
尋找2個字串中相同的最大子串,如果有2個或多個不同的最大相同子串,放在一個ArrayList物件中全部返回。
*/
import java.util.*;
class Test5{
public static void main(String[] args){
         String s1="kiokk"; 
         String s2="kib3oktr";
         System.out.println(maxSubstring(s1,s2));
}
public static ArrayList maxSubstring(String s1,String s2){
                String max,min;
                int len=0; //用於儲存最大子串的長度
                ArrayList<String> al=new ArrayList<String>(); //用於儲存最大子串              
                max=(s1.length()>s2.length())? s1:s2;
                min=(max==s1)? s2:s1;
                System.out.println(max+"....."+min);
                for(int x=0;x<min.length();x++){
                        for(int y=0,z=min.length()-x;z!=min.length()+1;y++,z++){
                                String temp=min.substring(y,z);
                                temp.toCharArray();
                                //String[] q=null;
                                 //System.out.println("temp="+temp);
                                if(max.contains(temp)){                                 
                                //System.out.println("結果是"+temp);
                                if(temp.length()>=len){ //程式碼首先產生的相同子串的長度肯定是最大的,所以這樣判斷 沒問題,後續還有相同長度的子串時也加入到al中
                                len=temp.length();                                
                                al.add(temp);
                                }
                                }                                                      
                }
                }
                return al;
}
}//執行結果會將輸出ok,kk.
StringBuffer類

字串緩衝區,是一個容器, final類,不能被繼承。
特點:長度可變化;可以直接操作多個數據型別;最終會通過toString()方法轉成字串。

StringBuffer作為一個容器,可以進行增刪改查等操作。
1. 儲存
StringBuffer append(), 引數可以為String物件、StringBuffer物件、6種基本資料型別(除byte和short)、字元陣列(可以指定offset和count),將指定引數新增到已有資料結尾處,返回原物件。
StringBuffer insert(index, 資料), 將資料插入到指定index位置,index超過字串當前最大位置時,會有角標越界異常。
2. 刪除
StringBuffer delete(start,end),刪除緩衝區中的資料,含頭不含尾
StringBuffer delete(charAt(index)), 刪除指定位置上的字元
StrubgBuffer delete(0,sb.length()),清空緩衝區
3.獲取
char charAt(int index)
int indexOf(String str)
int lastIndexOf(String str)
int length()
String subString(int start,int end)
4. 修改
void setCharAt(int index,char c)
StringBuffer replace(start,end,String str)
5. 反轉
StringBuffer reverse()
6. getChars()方法
void getChars(int srcBegin,int srcEnd,char[] det,int detBegin), 將緩衝區中指定資料儲存到目標字元陣列中。
StringBuffer的一個巧妙應用示例:

/*
 問題:把abcd...s共19個字母組成的序列重複拼接106次,得到長度為2014的串。
    接下來刪除第1個字母(即開頭的字母a),以及第3個,第5個等所有奇數位置的字母。
    得到的新串再進行刪除奇數位置字母的動作。如此下去,最後只剩下一個字母,請寫出該字母。
 */
public class StringBufferDemo
 {
         public static void main(String[] args)
         {
                 String str = "abcdefghijklmnopqrs";
                 String ss=new String();
                 //將字串拼接106次
                for (int i = 0; i < 106; i++)
                 {
                	ss=ss+str;
                 }
                System.out.println(remove(ss));
         }
         public static String remove(String str){
        	 StringBuffer strbu=new StringBuffer(str);
        	 while (1!= strbu.length())  //最後剩下一個字母,故迴圈結束條件為最後的長度為1
             {
                     for (int i = 0; i < strbu.length(); i++)
                     {
                             /*如果將字串看成一個字元陣列,那麼按照題目的意思,就是刪除陣列中所有下標為偶數(包括0)的字母,
                              * 但當刪除當前i處的字母時,後邊的字母會向前移動一個字元,這樣,當前已被刪的字母的下一個字母其實已經移到i處了,而此時i已經自增了1,
                              * 所以i剛好就是下下個字母的索引,也就是下一個偶數位位置。
                              */
                            strbu.deleteCharAt(i);  //刪除索引i處的字母
                     }
             }
             return strbu.toString();   //最後結果是q        	 
         }
 }

StringBuild類

是StringBuffer的一個簡易替換,jdk1.5版本起才有的。

與StringBuffer的不同之處:
StringBuffer是同步的,執行緒安全,但每次操作時都要判斷鎖,效率低
StringBuilder是執行緒不同步的,不用判斷鎖,效率高,執行緒安全性要求高時可以手動同步。
開發建議使用StringBuilder.