Java數組與堆棧相關知識總結
一、數組創建
1.1 聲明並賦值
int[] a = {1,2,3};
1.2 聲明數組名開辟空間並且賦值
int[] a; a = new int[]{1,2,3};
1.3 聲明數組時指定元素個數然後賦值
int[] a= new int[3];
這裡Java會默認數組元素值為0
1.4 在以上的基礎上創建多維數組
int[][] a = {{1,2,3},{4,5,6},{7,8,9}}; //每個子數組元素個數不要求均相同 int[][] a = new int[m][n]; //其中n可以省略,在創建的時候可以指定 int[][][] a = new int[m][n][q]; //同樣其中n、q可以省略
同樣的,在new一個數組時,如不初始化,Java會默認數組元素值為0。
二、數據類型
Java中的數據類型有兩種:
2.1 八種基本數據類型
- int
- short
- long
- byte
- float
- double
- boolean
- char
這種類型的定義是通過諸如int a = 3; long b = 255L;的形式來定義的,稱為自動變量。
自動變量存的是字面值,這些字面值固定定義在某個程序塊裡面,程序塊退出後,字段值就消失瞭,出於追求速度的原因,就存在於棧中。
2.2 包裝類數據
包裝類的數據是如 Integer, String, Double等將相應的基本數據類型包裝起來的類。這些類數據全部存在於堆中,Java用new()語句來顯式地告訴編譯器,在運行時才根據需要動態創建,因此比較靈活,但缺點是要占用更多的時間。
String是一個特殊的包裝類數據。即可以用String str = "abc";
的形式來創建,也可以用String str = new String("abc");
的形式來創建。String str = "abc";
中,並沒有通過new()來創建實例,因為String str = "abc";
是存儲在字符串常量池中。 字符串常量池則存在於方法區。
JVM為瞭提高性能和減少內存開銷,在實例化字符串常量的時候進行瞭一些優化。
1.為字符串開辟一個字符串常量池,類似於緩存區。
2.創建字符串常量時,首先堅持字符串常量池是否存在該字符串。存在該字符串,
3.返回引用實例,不存在,實例化該字符串並放入池中。
String str1 = "abc"; String str2 = "abc"; System.out.println(str1==str2); //true
可以看到結果是true,結果說明,JVM創建瞭兩個引用str1和str2,但隻創建瞭一個對象,而且兩個引用都指向瞭這個對象。
String str1 = "abc"; String str2 = "abc"; str1 = "bcd"; System.out.println(str1 + "," + str2); //bcd, abc System.out.println(str1==str2); //false
參考上面的代碼可以知道,賦值的變化導致瞭類對象引用的變化,str1指向瞭另外一個新對象!而str2仍舊指向原來的對象。上例中,當我們將str1的值改為”bcd”時,JVM發現在棧中沒有存放該值的地址,便開辟瞭這個地址,並創建瞭一個新的對象,其字符串的值指向這個地址。
事實上,String類被設計成為不可改變(immutable)的類。如果你要改變其值,可以,但JVM在運行時根據新值悄悄創建瞭一個新對象,然後將這個對象的地址返回給原來類的引用。這個創建過程雖說是完全自動進行的,但它畢竟占用瞭更多的時間。在對時間要求比較敏感的環境中,會帶有一定的不良影響。
因此,並沒有與String是不可變的相矛盾。
繼續修改代碼:
String str1 = "abc"; String str2 = "abc"; str1 = "bcd"; String str3 = str1; System.out.println(str3); //bcd String str4 = "bcd"; System.out.println(str1 == str4); //true
可以看出,str3 這個對象的引用直接指向str1所指向的對象(註意,str3並沒有創建新對象)。當str1改完其值後,再創建一個String的引用str4,並指向因str1修改值而創建的新的對象。可以發現,這回str4也沒有創建新的對象,從而再次實現棧中數據的共享。
繼續修改代碼:
String str1 = new String("abc"); String str2 = "abc"; System.out.println(str1==str2); //false
可以發現此時返回false,這是因為通過new出來的放在瞭堆中,而第二個存於棧中,所以不相等。
另外:數據類型包裝類的值不可修改。不僅僅是String類的值不可修改,所有的數據類型包裝類都不能更改其內部的值。
三、棧、棧、方法區
棧是一種連續儲存的數據結構,具有先進後出的性質。
堆是一種非連續的樹形儲存數據結構,每個節點有一個值,整棵樹是經過排序的。特點是根結點的值最小(或最大),且根結點的兩個子樹也是一個堆。常用來實現優先隊列,存取隨意。
棧(stack)與堆(heap)都是Java用來在Ram中存放數據的地方。與C++不同,Java自動管理棧和堆,程序員不能直接地設置棧或堆。
3.1 棧
1.每個線程包含一個棧區,棧中隻保存基礎數據類型的對象和自定義對象的引用(不是對象)。
2.每個棧中的數據(原始類型和對象引用)都是私有的。
3.棧分為3個部分:基本類型變量區、執行環境上下文、操作指令區(存放操作指令)
4.數據大小和生命周期是可以確定的,當沒有引用指向數據時,這個數據就會自動消失。
通常的操作有入棧(壓棧),出棧和棧頂元素。想要讀取棧中的某個元素,就是將其之間的所有元素出棧才能完成。
棧的優勢是,存取速度比堆要快,僅次於直接位於CPU中的寄存器。但缺點是,存在棧中的數據大小與生存期必須是確定的,缺乏靈活性。
另外,棧數據可以共享
類似於int a = 3;
等都是存放於棧中,當新創建值時Java會判斷棧中是否已存在,若存在則引用該地址,不存在則創建。例如:
int a = 3; int b = 3;
編譯器先處理int a = 3;首先它會在棧中創建一個變量為a的引用,然後查找有沒有字面值為3的地址,沒找到,就開辟一個存放3這個字面值的地址,然後將a指向3的地址。接著處理int b = 3;在創建完b的引用變量後,由於在棧中已經有3這個字面值,便將b直接指向3的地址。這樣,就出現瞭a與b同時均指向3的情況。
特別註意的是,這種字面值的引用與類對象的引用不同。假定兩個類對象的引用同時指向一個對象,如果一個對象引用變量修改瞭這個對象的內部狀態,那麼另一個對象引用變量也即刻反映出這個變化。相反,通過字面值的引用來修改其值,不會導致另一個指向此字面值的引用的值也跟著改變的情況。如上例,我們定義完a與 b的值後,再令a=4;那麼,b不會等於4,還是等於3。在編譯器內部,遇到a=4;時,它就會重新搜索棧中是否有4的字面值,如果沒有,重新開辟地址存放4的值;如果已經有瞭,則直接將a指向這個地址。因此a值的改變不會影響到b的值。
3.2 堆
堆的優勢是可以動態地分配內存大小,生存期也不必事先告訴編譯器,Java的垃圾收集器會自動收走這些不再使用的數據。但缺點是,由於要在運行時動態分配內存,存取速度較慢。
1.存儲的是對象,每個對象都包含一個與之對應的class。
2.JVM隻有一個堆區(heap)被所有線程共享,堆中不存放基本類型和對象引用,隻存放對象本身。
3.對象的由垃圾回收器負責回收,因此大小和生命周期不需要確定。
3.3 方法區
1.靜態區,跟堆一樣,被所有的線程共享。
2.方法區中包含的都是在整個程序中永遠唯一的元素,如class,static變量
字符串常量池則存在於方法區
符串對象的創建 面試題
面試題:String str4 = new String(“abc”) 創建多少個對象?
1、在常量池中查找是否有“abc”對象。
1)有則返回對應的引用實例; 2)沒有則創建對應的實例對象。
2、在堆中 new 一個 String(“abc”) 對象。
3、將對象地址賦值給str4,創建一個引用。
所以,常量池中沒有“abc”字面量則創建兩個對象,否則創建一個對象,以及創建一個引用。
到此這篇關於Java數組與堆棧相關知識總結的文章就介紹到這瞭,更多相關Java數組與堆棧內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- Java基本數據類型族譜與易錯點梳理解析
- java數據類型和運算符的深入講解
- Java八種基本變量作為類的成員變量的默認值操作
- 深入瞭解JAVA數據類型與運算符
- 詳談java中int和Integer的區別及自動裝箱和自動拆箱