一、 String 和 StringBuffer、StringBuilder 的區(qū)別是什么?String 為什么是不可變的?
1. String

- String 類中使用 final 關(guān)鍵字字符數(shù)組保存字符串, private final char value[],所以 String對象是不可變的。
- 每次對 String 類型進行改變的時候,都會生成一個新的 String 對象,然后將指針指向新的 String 對象。原先的對象依舊在內(nèi)存中,但是指針不再指向它,那么這個對象就會成為垃圾內(nèi)存,在某一個特定的時刻有Java虛擬機回收。
- String 中的對象是不可變的,也就可以理解為常量,線程安全。
2. StringBuffer




- StringBuilder 與 StringBuffer 都繼承自 AbstractStringBuilder 類,在AbstractStringBuilder中也是使用字符數(shù)組保存字符串 char[]value 但是沒有用 final 關(guān)鍵字修飾,所以這兩種對象都是可變的。
- AbstractStringBuilder 是 StringBuilder 與StringBuffer 的公共父類,定義了一些字符串的基本操作,如 expandCapacity、append、insert、indexOf 等公共方法。StringBuffer 對方法加了同步鎖或者對調(diào)用的方法加了同步鎖,所以是線程安全的。
- StringBuffer 每次都會對 StringBuffer 對象本身進行操作,而不是生成新的對象并改變對象引用。相同情況下使用
- StringBuffer()的初始容量可以容納16個字符,當該對象的實體存放的字符的長度大于16時,實體容量就自動增加。(嘗試將新容量擴為大小變成2倍+2 if 判斷一下 容量如果不夠,直接擴充到需要的容量大小。)
3. StringBuilder


- 可變
- StringBuilder 并沒有對方法進行加同步鎖,所以是非線程安全的。
- StirngBuilder 相比使用 StringBuffer 僅能獲得 10%~15% 左右的性能提升,但卻要冒多線程不安全的風險。
對于三者使用的總結(jié):
- 操作少量的數(shù)據(jù) = String
- 單線程操作字符串緩沖區(qū)下操作大量數(shù)據(jù) = StringBuilder
- 多線程操作字符串緩沖區(qū)下操作大量數(shù)據(jù) = StringBuffer
二、 String s = new String("abc");一共創(chuàng)建了幾個對象?
如果字符串常量池中不存在“abc”,該語句執(zhí)行時會先在字符串常量池中創(chuàng)建一個“abc”對象,在執(zhí)行new語句時在堆區(qū)開辟新的空間,創(chuàng)建“abc”字符串,同時棧區(qū)會有一個引用s指向堆區(qū)的對象,此時如果要算上棧區(qū)的引用,共創(chuàng)建3個對象,不算,則創(chuàng)建兩個對象。
如果字符串常量池中存在“abc”,則只會在堆區(qū)創(chuàng)建一個“abc”字符串,同時棧區(qū)有一個引用指向堆中的對像。如果算上棧中的引用,共創(chuàng)建了兩個對象,不算,則創(chuàng)建了一個對象。
三、 == 與 equals
- == : 它的作用是判斷兩個對象的地址是不是相等。即,判斷兩個對象是不是同一個對象。(基本數(shù)據(jù)類型==比較的是值,引用數(shù)據(jù)類型==比較的是內(nèi)存地址)
- equals() : 它的作用也是判斷兩個對象是否相等。但它一般有兩種使用情況:
- 情況1:類沒有覆蓋 equals() 方法。則通過 equals() 比較該類的兩個對象時,等價于通過“==”比較這兩個對象。
- 情況2:類覆蓋了 equals() 方法。一般,我們都覆蓋 equals() 方法來兩個對象的內(nèi)容相等;若它們的內(nèi)容相等,則返回 true (即,認為這兩個對象相等)。
舉個例子
public class test1 {
public static void main(String[] args) {
String a = new String("ab"); // a 為一個引用
String b = new String("ab"); // b為另一個引用,對象的內(nèi)容一樣
String aa = "ab"; // 放在常量池中
String bb = "ab"; // 從常量池中查找
if (aa == bb) // true
System.out.println("aa==bb");
if (a == b) // false,非同一對象
System.out.println("a==b");
if (a.equals(b)) // true
System.out.println("aEQb");
if (42 == 42.0) { // true
System.out.println("true");
}
}
}
說明
String 中的 equals 方法是被重寫過的,因為 object 的 equals 方法是比較的對象的內(nèi)存地址,而 String 的equals 方法比較的是對象的值。
當創(chuàng)建 String 類型的對象時,虛擬機會在常量池中查找有沒有已經(jīng)存在的值和要創(chuàng)建的值相同的對象,如果有就把它賦給當前引用。如果沒有就在常量池中重新創(chuàng)建一個 String 對象。
四、關(guān)于 final 關(guān)鍵字的一些總結(jié)
final關(guān)鍵字主要用在三個地方:變量、方法、類。
對于一個final變量,如果是基本數(shù)據(jù)類型的變量,則其數(shù)值一旦在初始化之后便不能更改;如果是引用類型的變量,則在對其初始化之后便不能再讓其指向另一個對象。
當用final修飾一個類時,表明這個類不能被繼承。final類中的所有成員方法都會被隱式地指定為final方法。
使用final方法的原因有兩個。第一個原因是把方法鎖定,以防任何繼承類修改它的含義;第二個原因是效率。在早期的Java實現(xiàn)版本中,會將final方法轉(zhuǎn)為內(nèi)嵌調(diào)用。但是如果方法過于龐大,可能看不到內(nèi)嵌調(diào)用帶來的任何性能提升(現(xiàn)在的Java版本已經(jīng)不需要使用final方法進行這些優(yōu)化了)。類中所有的private方法都隱式地指定為fianl。
五、Object類的常見方法總結(jié)
Object類是一個特殊的類,是所有類的父類。它主要提供了以下11個方法
