String到底在內存中是如何存儲的


String會出現在哪些地方

  • 方法內的局部string
  • 類內的字段String
  • static string
  • 容器中存儲的string
  • String數組
    那么String的位置會影響其存儲方式嗎?
    顯然是不會的,類永遠只會儲存在堆上。
    但是實際上類的字段並不是一直在堆上的。

String的構造方法

以下來自String類 源碼,一些無關緊要的實現被我省略了:

 private final char value[];
    private int hash; // Default to 0
    private static final long serialVersionUID = -6849794470754667710L;
    private static final ObjectStreamField[] serialPersistentFields =
        new ObjectStreamField[0];
   public String() {
        this.value = "".value;
    }
  public String(String original) {
        this.value = original.value;//底層char[]指向了同一位置!
        this.hash = original.hash;
    }
    public String(char value[]) {
        this.value = Arrays.copyOf(value, value.length);//底層char[]指向不同位置!下面的截取也是如此
    }
    public String(char value[], int offset, int count) { ... }

    public String(int[] codePoints, int offset, int count) {...}

    public String(byte ascii[], int hibyte, int offset, int count) {...}

    public String(byte ascii[], int hibyte) {...}

我們可以發現String的構造器並不關注是否將char[]指向同一位置,之所以有一些沒有指向同一位置完全是為了保證char[]是immutable的。
這並不能說明調用構造器構造的String的內存位置有什么特別之處。

String另一種構造方式--為包裝類型提供的專有構造方式

運行如下代碼:

class StringPointerTest{
    String g="gh",h="gh";
}
public class StringTest {
    public static void main(String[] args) {
        String a="abc";
        String b="abc";
        System.out.println(a==b);//true使用簡寫的構造能夠復用創建的string類
        String c=new String("abc");
        System.out.println(a==c);//false是用構造器則不能
        String d=new String("def");
        String e="def";
        System.out.println(d==e);//false即使先構造器再使用簡化構造也不行。
        StringPointerTest spt=new StringPointerTest();
        System.out.println(spt.g==spt.h); //true與String的屬於方法局部變量還是類字段也無關
    }
}
class IntegerPointTest1{
    Integer a=1;

}
class IntegerPointTest2{
    Integer b=1;
}
public class IntegerTest {
    public static void main(String[] args) {
       System.out.println(new IntegerPointTest1().a==new IntegerPointTest2().b);//true!
    }
}

原理

需要存儲的代碼元素有:

  • Class類文件
  • 方法,類的所以實例應該共用一段方法
  • static字段
  • 字符串常量
  • 值常量
  • 類實例
  • 各種引用
  • 基本變量
    他們各自有各自的存儲位置,方法內的引用存在方法棧,類內的引用存在堆,類存儲在堆上,方法中的局部基本變量存於棧但是類字段的基本變量存在堆上(方法區內)。
    值得一提的是方法區(又叫靜態區),其存儲值常量、字符串常量、方法、靜態字段、.class文件,等只用一個備份的數據。
    棧和方法區都有共享數據的功能。
    因此使用簡化方法構造String類的時候,在方法內和類內會將字符串存於棧/方法區,這無關緊要,重要的是,
    使用這種構造方法,如果沒有所構造的字符串常量存在於內存中,那么會在棧/方法區中存上一份,然后再堆中新建一個String類,把String類的char[]引用指向在棧/方法區中的字符串常量;
    如果所構造的字符串常量已經存在於內存中,那么則會檢索關聯與之對應的堆中String實例,並直接使用這個String類實例。
    只有使用簡化方法構造才能被棧/方法區記錄下來,如果使用new則不行,這也是為什么上例即使先new,再使用簡化構造相同字符串也不會引用相同。
    因為new出來的String實例的字符串常量存儲在堆上,和棧/方法區無關。

拓展到所有包裝類型---可以嗎?

完全不可以。
他們都沒有像String那樣實現緩存。
但是實現了類似的緩存,它們的自動包裝機制也提供緩存功能,
但是是基於valueof方法的,該方法會對一定范圍內進行緩存。
而且實現方式非常暴力,是在對應類里存一個靜態的類數組,並靜態初始化全部填充。

可以緩存的范圍:
byte Byte -128–127
short Short -128–127
int Integer -128—127
long Long -128—127
char Character 0–127
boolean Boolean TURE,FALSE
String
不緩存的:
float Float
double Double

可見不是所有的自動包裝機制都實現了全緩存。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM