總是被java字符串問題給困擾,今天總結一下:
example1:
public class Main{
public static void main(String[] args) {
String i = "abc";
String j = "abc";
String k = new String("abc");
if (i == j){
System.out.println("true");
}
else {
System.out.println("false");
}
}
}
1、i == j : 打印true
2、i == k : 打印false
用一張圖來解釋上面的結果:
- 結果分析:
1、i,j,k都是存在棧中的,i和k中存放的是指向運行時常量池中的“abc”的內存地址(所以i==j);j中存的是指向堆中“abc”的地址所以(i != k)。
2、“==”比較的是棧中變量值的大小(引用數據類型比較的是內存地址,基本數據類型比較的是值),而equals(String對equals進行了重寫)比較的是存在堆中或者常量池中的值。
example2:
public class TestStr {
public static void main(String[] args) {
String str = new String("abc");
changeStr1(str);
System.out.println(str);
// ================================================
StringBuffer sb = new StringBuffer("123");
changeStr2(sb);
System.out.println(sb);
}
public static void changeStr1(String str){
str = "def";
}
public static void changeStr2(StringBuffer str){
str.append("456");
}
}
結果:
abc
123456
解釋:
String定義的是字符串常亮,無法被修改,所以changeStr1()方法中str="def"實際上在常量池中又分配了一塊內存。
StringBuffer定義的字符串是可以修改的,所以changeStr2()是在原字符串上進行修改,還是同一塊內存!
說明:
1.方法區:
是各個線程共享的內存區域,它用於存儲class二進制文件,包含了虛擬機加載的類信息、常量、靜態變量、即時編譯後的代 碼等數據。它有個名字叫做Non-Heap(非堆),目的是與Java堆區分開。
- 常量池:
存放字符串常量和基本類型常量(public static final)。
- 靜態域:
位於方法區的一塊內存。存放類中以static聲明的靜態成員變量
2、JVM堆(Java Heap):
Java 堆也是屬於線程共享的內存區域,它在虛擬機啓動時創建,是Java 虛擬機所管理的內存中最大的一塊,主要用於存放對象實例,幾乎所有的對象實例都在這裏分配內存,注意Java 堆是垃圾收集器管理的主要區域,因此很多時候也被稱做GC 堆,如果在堆中沒有內存完成實例分配,並且堆也無法再擴展時,將會拋出OutOfMemoryError 異常。
3、程序計數器(Program Counter Register):
屬於線程私有的數據區域,是一小塊內存空間,主要代表當前線程所執行的字節碼行號指示器。字節碼解釋器工作時,通過改變這個計數器的值來選取下一條需要執行的字節碼指令,分支、循環、跳轉、異常處理、線程恢復等基礎功能都需要依賴這個計數器來完成。
4、虛擬機棧(Java Virtual Machine Stacks):
屬於線程私有的數據區域,與線程同時創建,總數與線程關聯,代表Java方法執行的內存模型。每個方法執行時都會創建一個棧楨來存儲方法的的變量表、操作數棧、動態鏈接方法、返回值、返回地址等信息。每個方法從調用直結束就對於一個棧楨在虛擬機棧中的入棧和出棧過程,如下(圖有誤,應該爲棧楨):
5、本地方法棧(Native Method Stacks):
本地方法棧屬於線程私有的數據區域,這部分主要與虛擬機用到的 Native 方法相關,一般情況下,我們無需關心此區域。
參考博客: