JVM記憶體分配及String常用方法
一,JVM記憶體分配和常量池
在介紹String類之前,先來簡單分析一下在JVM中,對記憶體的使用是如何進行分配的。如下圖所示(注意:在jdk1.8之後便沒有方法區了):
如上JVM將記憶體分為多個不同的區域,這些區域都有各自的用途、建立和銷燬的時間,有些區域隨虛擬機器程序的啟動而存在,有些區域則是依賴使用者執行緒的啟動和結束來建立和銷燬。
區域名稱的說明:
1.1,方法區:
屬於資料共享記憶體區域,儲存已被虛擬機器載入的類資訊、常量、靜態變數、即時編譯器編譯後的程式碼等資料。
1.2,虛擬機器棧
虛擬機器棧就是我們通常說的棧,是Java執行方法的記憶體模型,每當執行一次方法時,都會建立一個棧幀。把棧幀壓入棧,當Java方法呼叫時返回正常的結果或者捕獲異常時,棧幀出棧。
棧幀:棧幀儲存方法的相關資訊,包含區域性變數數表、返回值、運算元棧、動態連結。
1.3,本地方法棧
從功能上來說與虛擬機器棧類似,但是虛擬機器棧執行的是位元組碼,而本地方法棧呼叫的是Native方法,並且它是執行緒獨享的。
1.4,程式計數器
程式計數器是執行緒獨享的,它是記錄當前執行緒執行的位元組碼行號。在多執行緒執行時,CPU會來回線上程之間進行切換,那麼當再次回到一條執行緒時,是如何得知執行緒的儲存單元及執行指令。而程式計數器便會進行儲存下一條儲存單元的地址,執行完畢後程序計數器自動加 1 ,以此迴圈直到程式結束為止。
1.5,堆
說到堆這個概念想必都不陌生,它是記憶體中的重要角色。它主要是用來儲存被創建出來的物件,通過關鍵字new例項出來的,是所有執行緒共享的一塊最大的區域。
==特別注意:在JDK1.7及以後,常量池移動到堆記憶體中。==
堆還包括一個==常量池==,用來儲存編譯期間生成的==字面量和符號==引用。這部分內容在類被載入後,都會儲存到方法區中。同時,執行時產生的新常量也可以被放入常量池中,比如 String 類中的 intern() 方法產生的常量。
常量池就是這個型別用到的常量的一個有序集合。包括直接常量(基本型別,String)和對其他型別、方法、欄位的符號引用。
二,常量池
2.1,什麼是常量:
常量是指被final修飾的變數,值一旦確定就無法改變。
final可以修飾靜態變數、方法、例項變數和區域性變數。
常量池分為兩種形式:靜態常量池和執行時常量池
2.2,靜態常量池
即*.class檔案中的常量池,class檔案中的常量池不僅僅包含字串(數字)字面量,還包含類、方法的資訊,佔用class檔案絕大部分空間。這種常量池用於存放字面量和符號引用量。
2.3,執行時常量池
指JVM虛擬機器在完成類裝載操作後,將class檔案中的常量池載入到記憶體中,並儲存在方法區中,我們常說的常量池,就是指方法區中的執行時常量池。同樣執行時常量池一個重要的特徵就是具有動態性,指並不需要常量只有在編譯期才會產生,在執行期也會將新的常量儲存到常量池中,如String類中的intern()方法。
三,== 和equals
3.1,兩者之間區邊
==:
對於基本型別來說:==表示數值的比較
對於引用型別來說:==表示地址值的比較
equals:
比較的是兩者之間值是否相等,但是Java中的類都是直接或者間接繼承Object類,而equals不也例外。其實在equals原始碼中也是使用==
進行比較的,如下原始碼:
![](https://img2018.cnblogs.com/blog/1655301/201909/1655301-20190902223856542-1095893842.png)
那麼問題來了,這和==又有什麼區別呢?
上面說到equals也是繼承自java.lang.Object,因此可以對equals進行重寫來定義我們自己的比較方式。
請參看以下程式碼:
String str1 = "abc";
String str2 = "abc";
char[] strArray = {'a','b','c'};
String str3 = new String(strArray);
String str4 = "abc";
System.out.println(str1 == str2);
System.out.println(str1 == str3);
System.out.println(str2 == str3);
System.out.println(str4.equals(str1));
以上執行結果為:
true
false
false
true
接下來我們依次分析上面的結果:
1,str1與str2比較的是字串物件地址,因為它們的值是相同的,所以地址值也是相同的。
2,str3是new出來的示例物件,在堆記憶體中會開闢一塊新的記憶體地址,它並不在常量池中。所以返回結果為false。
3,同理str2與str3比較也是一樣的結果。
4,equals比較的是值是否相同,所以返回的結果為true。
如圖所示:
四,String常用方法
首先宣告字串:
String str1 = "abc";
4.1,int length()
int length = str1.length();
System.out.println(length);
4.2,char charAt(值)
String str= "abc";
char c = str.charAt(1);
System.out.println(c);
4.3,char toCharArray()
String str= "abc";
char c[] = str.toCharArray();
for (int i = 0; i < c.length; i++) {
System.out.println("轉為陣列輸出:" + c[i]);
}
4.4,int indexOf("字元"); int lastIndexOf("字元")
String str="axcdefgabc";
int a1 = str.indexOf("a");
int a2 = str.indexOf("x", 2);
int a3 = str.lastIndexOf("c");
System.out.println("你的位置為:" + a1);
System.out.println("為的位置為:" + a2);
System.out.println("點最後出現的位置為:" + a3);
4.5,字串大小寫轉換
toUpperCase(); 轉換成大寫
toLowerCase();轉換成小寫
String str = "hello world";
String str1 = "HELLO WORD";
System.out.println("將字串轉大寫為:" + str.toUpperCase());
System.out.println("將字串轉換成小寫為:" + str1.toLowerCase());
4.6,String[] split("字元")
String str = "abc,def,123";
String[] arr1 = str.split(",");
4.7,boolean equals(Object anObject)
String str = "abc";
String str1= "123";
if(str.equals(str1)) {
System.out.println("相等");
}
else{
System.out.println("不相等");
}
4.8,String trim()
String str = " abc ";
System.out.println("去掉左右空格後:" + str.trim());
4.9,字串替換
String replace(char oldChar,char newChar)
String replaceAll(String,String)
將某個內容全部替換成指定內容
String repalceFirst(String,String)
將第一次出現的某個內容替換成指定的內容
String str = "abcdefgabdc";
System.out.println("替換:" + str.replace("abc", "123"));
System.out.println("替換全部:" + str.replaceAll("ab", "12"));
System.out.println("替換第一次出現:" + str.repalceFirst("a", "a"));
4.10,String substring(int beginIndex,int endIndex)
String str = "abcdefg";
// 擷取0-3個位置的內容, 不含3
System.out.println("擷取後的字元為:" + str.substring(0, 3));
// 從第3個位置開始擷取, 含2
System.out.println("擷取後字元為:" + str.substring(2));
4.11,boolean equalsIgnoreCase(String)
String str = "ABC";
String str1 = "abc";
if(str.equalsIgnoreCase(str1)){
System.out.println("相等");
}
else{
System.out.println("不相等");
}
4.12,boolean contains(String)
String str = "ABCDEF";
String str1 = "ABC";
if(str.contains(str1)){
System.out.println("str內容中包含ABC");
}
else{
System.out.println("str內容中不包含ABC");
}
五,總結
1,對於JVM記憶體的分配,在jdk6中存在方法區,jdk8中便沒有方法區,改成元區域。
2,jdk6中常量池存在方法區中,jdk7以後常量池移動到堆中。
本篇部落格只是簡單介紹了JVM記憶體分配和常量池的概念,仍有很多更深入的原理沒有記錄。因此在後期會繼續更新關於JVM相關的原理知識,以上部落格中如有不適之處還請留言(郵箱)指教。
感謝閱讀!