第二章 字符串String、數組、數據類型轉換

(一)字符串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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章