文章目錄
(一)字符串String
1、String、StringBuffer和StringBuilder的區別
1.String
字符串常量,字符串長度不可變。Java中String是immutable(不可變)的。
/** The value is used for character storage. */
private final char value[];
用於存放字符的數組被聲明爲final的,因此只能賦值一次,不可再更改。
2.StringBuffer(JDK1.0)
字符串變量(Synchronized,即線程安全)。如果要頻繁對字符串內容進行修改,出於效率考慮最好使用StringBuffer,如果想轉成String類型,可以調用StringBuffer的toString()方法。
Java.lang.StringBuffer線程安全的可變字符序列。在任意時間點上它都包含某種特定的字符序列,但通過某些方法調用可以改變該序列的長度和內容。可將字符串緩衝區安全地用於多個線程。
StringBuffer 上的主要操作是 append 和 insert 方法,可重載這些方法,以接受任意類型的數據。每個方法都能有效地將給定的數據轉換成字符串,然後將該字符串的字符追加或插入到字符串緩衝區中。append 方法始終將這些字符添加到緩衝區的末端;而 insert 方法則在指定的點添加字符。
3.StringBuilder(JDK5.0)
字符串變量(非線程安全)。在內部,StringBuilder對象被當作是一個包含字符序列的變長數組。
java.lang.StringBuilder是一個可變的字符序列,是JDK5.0新增的。此類提供一個與 StringBuffer 兼容的 API,但不保證同步。該類被設計用作 StringBuffer 的一個簡易替換,用在字符串緩衝區被單個線程使用的時候(這種情況很普遍)。
4.String、StringBuffer和StringBuilder的區別
2、String的不可變性
2.1)不可變對象
不可變類只是其實例不能被修改的類,每個實例中包含的所有信息都必須在創建該實例的時候就提供,並在對象的整個生命週期內固定不變。
2.2)如何實現不可變
1、不要提供任何會修改對象狀態的方法。
2、保證類不會被拓展(一般聲明爲final即可)。
3、使所有的域都是 private final的。
4、確保對於任何可變組件的互斥訪問(可以理解如果中存在可變對象的域,得確保客戶端無法獲得其引用,並且不要使用客戶端提供的對象來初始化這樣的域)。
2.3)String的不可變
實例
public static void main(String[] args) {
String s = "ABCDEF";
System.out.println("s = " + s);
s = "123456";
System.out.println("s = " + s);
}
打印結果
s = ABCDEF
s = 123456
從結果上看,s的值改變了,但s僅僅是一個String對象的引用,並不是對象本身。對象在內存中是一塊內存區,而s只是一個引用,它指向了一個具體的對象。
在創建String對象的時候,s指向的是內存中的"ABDCEF",當執行語句s = “123456"後,其實又創建了一個新的對象"123456”,而s重新指向了這個新的對象,同時原來的"ABCDEF"並沒有發生改變,仍保存在內存中。
2.4)改變"不可變對象"
現在我們已經知道了String的成員變量是private final 的,也就是初始化之後不可改變的。同時也提到value這個成員變量其實也是一個引用,指向真正的數組內存地址,不能改變它的引用指向,我們能不能直接改變內存數組中的數據呢,那麼就需要獲取到value,而value是私有的,可用反射獲取
public static void reflectString() throws Exception{
String s = "ABCDEF";
System.out.println("s = " + s);
Field valueField = s.getClass().getDeclaredField("value");
valueField.setAccessible(true);
char[] value = (char[]) valueField.get(s);
value[0] = 'a';
value[2] = 'c';
value[4] = 'e';
System.out.println("s = " + s);
}
打印結果
s = ABCDEF
s = aBcDeF
(二)數組
(三)數據類型轉換
1、Java中基本類型轉換
(1)由String轉換成其他類型
由於在JDK 5以後就有了自動拆箱和自動裝箱。因此我們可以用數據的類型來做數據的轉換。
String str="123456";
int i=Integer.parseInt(str);
(2)其他類型轉換爲String類型
方法1:在String中有解析數據的方法,可以把任意基礎類型轉換成String類型
char ch[]=new char[]{'5','a','B'};
String str=String.valueOf(ch);
方法2:直接用變量加" "的形式直接把整形變成字符串
String st=5+"";
2、Java 8 種數據類型 字節大小
(四)Java基礎類型與封裝類型
4.1)Java基礎類型與封裝類型的區別
一、傳遞方式不同
基本類型(原始數據類型)在傳遞參數時都是按值傳遞。
封裝類型是引用類型,按引用傳遞的(其實“引用也是按值傳遞的”,傳遞的是對象的地址)。由於包裝類型都是不可變量,因此沒有提供改變它值的方法,增加了對“按引用傳遞”的理解難度。
int是基本類型,直接存放數值;Integer是類,產生對象時用一個引用指向這個對象。
二、封裝類可以有方法和屬性
封裝類可以有方法和屬性,利用這些方法和屬性來處理數據,如Integer.parseInt(Strings)。基本數據類型都是final修飾的,不能繼承擴展新的類、新的方法。
三、默認值不同
基本類型跟封裝類型的默認值是不一樣的。如int i,i的預設爲0;Integer j,j的預設爲null,因爲封裝類產生的是對象,對象默認值爲null。
四、存儲位置
基本類型在內存中是存儲在棧中,引用類型的引用(值的地址)存儲在棧中,而實際的對象(值)是存在堆中。
雖然基本類型在棧上分配內存效率高,但是在堆棧上分配內存可能有內存泄漏的問題。
五、基礎類型與封裝類型對比
基本數據類型的好處就是速度快(不涉及到對象的構造和回收),封裝類的目的主要是更好的處理數據之間的轉換。
JDK5.0開始可以自動封包了,基本數據類型可以自動封裝成封裝類。
比如集合List,往裏添加對象Object,在JDK5.0 之前,需要將數字封裝成封裝類型對象,再存到List中。
List list=new ArreyList();
list.add(new Integer(1));
在JDK5.0 以後可以自動封包,簡寫成
List list=new ArrayList();
list.add(1);
面試題:int與Integer不同
1、Integer是int的包裝類,int則是java的一種基本數據類型
2、Integer變量必須實例化後才能使用,而int變量不需要
3、Integer實際是對象的引用,當new一個Integer時,實際上是生成一個指針指向此對象;而int則是直接存儲數據值
4、Integer的默認值是null,int的默認值是0
延伸:
關於Integer和int的比較
1、由於Integer變量實際上是對一個Integer對象的引用,所以兩個通過new生成的Integer變量永遠是不相等的(因爲new生成的是兩個對象,其內存地址不同)。
Integer i = new Integer(100);
Integer j = new Integer(100);
System.out.print(i == j); //false
2、Integer變量和int變量比較時,只要兩個變量的值是相等的,則結果爲true(因爲包裝類Integer和基本數據類型int比較時,java會自動拆包裝爲int,然後進行比較,實際上就變爲兩個int變量的比較)
Integer i = new Integer(100);
int j = 100;
System.out.print(i == j); //true
3、非new生成的Integer變量和new Integer()生成的變量比較時,結果爲false。(因爲非new生成的Integer變量指向的是java常量池中的對象,而new Integer()生成的變量指向堆中新建的對象,兩者在內存中的地址不同)
Integer i = new Integer(100);
Integer j = 100;
System.out.print(i == j); //false
4、對於兩個非new生成的Integer對象,進行比較時,如果兩個變量的值在區間-128到127之間,則比較結果爲true,如果兩個變量的值不在此區間,則比較結果爲false
Integer i = 100;
Integer j = 100;
System.out.print(i == j); //true
Integer i = 128;
Integer j = 128;
System.out.print(i == j); //false
原因:
java在編譯Integer i = 100 ;時,會翻譯成爲Integer i = Integer.valueOf(100);,而java API中對Integer類型的valueOf的定義如下:
public static Integer valueOf(int i){
assert IntegerCache.high >= 127;
if (i >= IntegerCache.low && i <= IntegerCache.high){
return IntegerCache.cache[i + (-IntegerCache.low)];
}
return new Integer(i);
}
java對於-128到127之間的數,會直接從緩存中取,故相等。否則創建一個新的對象,故不等。
4.2)Java自動裝箱與拆箱
1、什麼是自動裝箱、拆箱
裝箱就是 自動將基本數據類型轉換爲包裝器類型;拆箱就是 自動將包裝器類型轉換爲基本數據類型。
Integer i = 10; //裝箱
int n = i; //拆箱
2、自動裝箱的原理及使用場景
基本數據類型–>封裝類
Integer i = 10;
執行時實際上系統執行了
Integer i = Integer.valueOf(10);
分析Integer的valueOf源碼:
public static Integer valueOf(int i) {
if(i >= -128 && i <= IntegerCache.high) // 沒有設置的話,IngegerCache.high 默認是127
return IntegerCache.cache[i + 128];
else
return new Integer(i);
}
對於–128到127(默認是127)之間的值,Integer.valueOf(int i) 返回的是緩存的Integer對象(並不是新建對象),而其他值,執行Integer.valueOf(int i) 返回的是一個新建的 Integer對象。裝箱的過程會創建對應的對象,這個會消耗內存,所以裝箱的過程會增加內存的消耗,影響性能。
3、自動拆箱的原理及使用場景
封裝類–>基本數據類型
Integer i = 10; //裝箱
int t = i; //拆箱,實際上執行了 int t = i.intValue();
進行運算時也可以進行拆箱
Integer i = 10;
System.out.println(i++);
Integer與int運算
int i = 10;
Integer integer1 = new Integer(10);
System.out.println(i==integer1);//true,integer1自動拆箱
intValue函數很簡單,直接返回value值即可
@Override
public int intValue() {
return value;
}