java字符串拼接例子_Java详解【String】+【StringBuilder vs StringBuffer】+【字符串拼接】...
String詳解
注意區分對象和對象的引用
首先來看一下我在jdk中找到的String源代碼,這里只截取開頭的小小一部分
public final class String
implements java.io.Serializable, Comparable, CharSequence {
/** The value is used for character storage. */
private final char value[];
從這里可以看出,String類是被final所修飾的,因此String類對象不可變,也不可繼承。這里要注意一個誤區,字符串對象不可變,但字符串變量所指的值是可變的,即引用地址可變。String變量存儲的是對String對象的引用,String對象里存儲的才是字符串的值【注意區分對象和對象的引用】。看下面的例子
String str = "abc"; //str是個對象引用
System.out.println(str); //輸出abc
str = "abcde"; //不會出錯
System.out.println(str); //輸出abcde
當給str第二次賦值的時候,對象"abc"并沒有被銷毀,仍存放在常量池中(String自帶),只是讓str指向了"abcde"的內存地址,而字符串對象"abcde"是新生成的,即str第二次賦值時并不是在原內存地址上修改數據,而是重新指向一個新對象,新地址。記住,對String對象的任何改變都不影響到原對象,相關的任何改變的操作都會生成新的對象。
String對象是否真的不可變?!
答案并不是的,那么該如何做呢?那就是使用反射(反射不太懂的可以點擊鏈接學習——
從上面的源碼可知String的成員變量是private final的,也就是初始化之后不可改變。那么在這幾個成員中,value比較特殊,因為它是一個引用變量,而不是真正的對象。value是final修飾的,即final不能再指向其他數組對象,那么我想改變value指向的數組時,比如將數組中的某個位置上的字符變為下劃線"_"。因為不能直接通過這個引用去修改數組,那么為了訪問到至value這個引用,我們可以使用反射來訪問私有成員,反射出String對象中的value屬性,進而改變value數組的內容。
public static void testReflection() throws Exception {
// 創建字符串"Hello World" 并賦給引用s
String s = "Hello World";
System.out.println("s = " + s); // 打印出Hello World
// 獲取String類中的value字段————Field類獲取成員變量
Field valueFieldOfString = String.class.getDeclaredField("value");
valueFieldOfString.setAccessible(true); // 改變value屬性的訪問權限
value[5] = '_'; // 獲取s對象上的value屬性的值 改變value所引用的數組中的第5個字符
System.out.println("s = " + s); // 打印出Hello_World
}
下面這個例子將引出兩個問題
String str="hello world" 和 String str=new String("hello world") 有什么不同
為什么使用 "==" 和 "equals" 會有不同的結果public static void main(String[] args) {
String str1 = "hello world";
String str2 = new String("hello world");
String str3 = "hello world";
String str4 = new String("hello world");
String str5 = "Hello" + " World";
String str6 = "Hello" + new String(" World");
String str7 = str2.intern();
String ex1 = "Hello";
String ex2 = " World";
String str8 = ex1 + ex2;
System.out.println(str1 == str3); //true
System.out.println(str1 == str2); //false
System.out.println(str2 == str4); //false
System.out.println(str1.equals(str2)); //true
System.out.println(str2.equals(str4)); //true
System.out.println(str1 == str5); //true
System.out.println(str1 == str6); //false
System.out.println(str1 == str7); //true
System.out.println(str1 == str8); //false
}
【運行結果】是 true、false、false、true、true、true、false、true、false
先把下面四大點看懂了,我會在最后寫出【解析】
String的兩種賦值方式
※ 區分【String str="HW"】和【String str=new String("HW")】
(1)字面量賦值方式? ? ?eg:String str = "Hello";
該種直接賦值的方法,JVM會去字符串常量池(String對象不可變)中尋找是否有equals("Hello")的String對象,如果有,就把該對象在字符串常量池中"Hello"的引用復制給字符串變量str,如若沒有,就在堆中新建一個對象,同時把引用駐留在字符串常量池中,再把引用賦給字符串變量str。
用該方法創建字符串時,無論創建多少次,只要字符串的值(內容)相同,那么它們所指向的都是堆中的同一個對象。
該方法直接賦值給變量的字符串存放在常量池里
(2)new關鍵字創建新對象? ? ?eg:String str = new String("Hello");
利用new來創建字符串時,無論字符串常量池中是否有與當前值相同的對象引用,都會在堆中新開辟一塊內存,創建一個新的對象。
注意:對字符串進行拼接操作,即做"+"運算的時候,分2種情況:
表達式右邊是純字符串常量,那么存放在常量池里面。eg:String str = "Hello" + "World";
表達式右邊如果存在字符串引用,也就是字符串對象的句柄,那么就存放在堆里面。eg:String str = str1 + str2;
總結:常量池是方法區的一部分,而方法區是線程共享的,所以常量池也是線程共享的,且它是線程安全的,它讓有相同值的引用指向同一個位置,如果引用值變化了,但是常量池中沒有新的值,那么就會新建一個常量結果來交給新的引用,對于同一個對象,new出來的字符串存放在堆中,而直接賦值給變量的字符串存放在常量池里。
??????
字符串比較——區分"=="和"equals"
"==":比較引用變量的地址,即兩個對象是否引用同一地址的內容,用"=="時會檢測是否指向同一個對象
"equals":比較對象的內容,即兩個對象內容上是否相同
字符串用這兩種比較方式都是可行的,具體看想要比較什么,總體來看,"=="稍微強大些,因為它既要求內容相同,也要求引用對象相同
intern() 方法
當使用?intern()方法時,會先查詢字符串常量池是否存在當前字符串,如果存在,則返回常量池中的引用,若不存在,則將字符串添加到字符串常量池中,并返回字符串常量池中的引用。
【代碼解析】
str1 == str3——str1與str3指向常量池中同一個對象,引用對象相同,因此用"=="比較時結果為true
str1 == str2——new會創建一個新的對象放在堆中,str1所指對象在常量池,即使str1與str2內容相同,但并不是同一個對象
str2 == str4——new會創建一個新的對象放在堆中,str2與str4指向不同的對象,即使內容相同
str1.equals(str2)——str1于str2各自引用對象不同,但內容相同,因此用"equals"比較時結果為true
str2.equals(str4)——str2于str4各自引用對象不同,但內容相同,因此用"equals"比較時結果為true
str1 == str5——對字符串進行拼接操作("+"運算)時,表達式右邊是純字符串常量,那么存放在常量池里面,若常量池中有該字符串,則返回該引用
str1 == str6——對字符串進行拼接操作("+"運算)時,表達式右邊如果存在字符串引用,也就是字符串對象的句柄,那么就存放在堆里面
str1 == str7——intern()方法會去常量池中找是否存在當前字符,存在則返回引用,該對象引用剛好是str1所指向的
str1 == str8——str8由兩個變量拼接,編譯期不知道它們的具體位置,所以不會做出優化,必須要等到運行時才能確定,因此新對象的地址和前面的不同。
StringBuilder? & StringBuffer
StringBuilder和StringBuffer類類似于String類,但區別在于String創建的對象是不可改變的,而StringBuilder和StringBuffer這兩個類創建對象后都是可以對對象進行修改的。即String為字符串常量,而StringBuilder和StringBuffer均為字符串變量
運行速度(快到慢):StringBuilder > StringBuffer > String
StringBuilder和StringBuffer的對象是變量,對變量進行操作就是直接對該對象進行更改,而不進行像String對象那樣子進行創建和回收的操作,所以速度要比String快很多。
在線程安全上,StringBuilder是線程不安全的,而StringBuffer是線程安全的
如果一個StringBuffer對象在字符串緩沖區被多個線程使用時,StringBuffer中很多方法可以帶有synchronized關鍵字,所以可以保證線程是安全的,但StringBuilder的方法則沒有該關鍵字,所以不能保證線程安全,有可能會出現一些錯誤的操作。所以如果要進行的操作是多線程的,那么就要使用StringBuffer,但是在單線程的情況下,還是建議使用速度比較快的StringBuilder。
總結
String:適用于少量的字符串操作的情況
StringBuilder:適用于單線程下在字符緩沖區進行大量操作的情況
StringBuffer:適用多線程下在字符緩沖區進行大量操作的情況
字符串拼接五種方法
使用 +
使用?concat
使用?StringBuilder
使用?StringBuffer
使用?StringUtils.join
注:先看完上面對String、StringBuilder和StringBuffer的詳解后再看下面的文章會好理解很多的
由于String是Java中一個不可變的類,所以他一旦被實例化就無法被修改,因此所有的所謂字符串拼接,都是重新生成了一個新的字符串。
效率(用時短到長):StringBuilder
(1)"+"是Java提供的一個語法糖,而使用+拼接的字符串,它將String轉成了StringBuilder后,再使用StringBuilder.append進行處理。如果不是在循環體中進行字符串拼接的話,直接使用+就好了。
(2)concat方法,其實是new了一個新的String
(3)StringUtils.join也是通過StringBuilder來實現的
(4)StringBuffer在StringBuilder的基礎上,做了同步處理,所以在耗時上會相對多一些。
(5)如果在并發場景中進行字符串拼接的話,要使用StringBuffer來替代StringBuilder。因為StringBuilder是線程不安全的,而StringBuffer是線程安全的
其他參考文章:
總結
以上是生活随笔為你收集整理的java字符串拼接例子_Java详解【String】+【StringBuilder vs StringBuffer】+【字符串拼接】...的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 幼儿园老师让带多肉植物男孩带猪肉:小朋友
- 下一篇: java美元兑换,(Java实现) 美元