java堆、棧、堆棧的區別
Java把內存劃分成兩種:一種是棧內存,一種是堆內存。
棧(stack):是一個先進後出的數據結構,通常用於保存方法(函數)中的參數,局部變量.
在java中,所有基本類型和引用類型都在棧中存儲.棧中數據的生存空間一般在當前scopes內(就是由{...}括起來的區域).
堆(heap):是一個可動態申請的內存空間(其記錄空閑內存空間的鏈表由操作系統維護),C中的malloc語句所產生的內存空間就在堆中.
在java中,所有使用new xxx()構造出來的對象都在堆中存儲,當垃圾回收器檢測到某對象未被引用,則自動銷毀該對象.所以,理論上說java中對象的生存空間是沒有限制的,只要有引用類型指向它,則它就可以在任意地方被使用.
堆與堆棧的關系:
堆:堆是heap,是所謂的動態內存,其中的內存在不需要時可以回收,以分配給新的內存請求,其內存中的數據是無序的,即先分配的和隨後分配的內存並沒有什麽必然的位置關系,釋放時也可以沒有先後順序。一般由使用者自由分配,malloc分配的就是堆,需要手動釋放。
堆棧:就是stack。實際上是只有一個出入口的隊列,即後進先出(First In Last Out),先分配的內存必定後釋放。一般由,由系統自動分配,存放存放函數的參數值,局部變量等,自動清除。
棧(stack)的優缺點:
優點是:棧的存取速度比堆要快,僅次於CPU中的寄存器。棧數據可以共享
缺點是:存在棧中的數據大小與生存期必須是確定的,缺乏靈活性。
堆(heap)的優缺點:
優點是:可以動態地分配內存大小,生存期也不必事先告訴編譯器,Java的垃圾收集器會自動收走這些不再使用的數據。
缺點是:由於要在運行時動態分配內存,所以存取速度較慢。
2、Java中的數據類型有兩種。
基本類型:即int, short, long, byte, float, double, boolean, char(註意,string是引用類型)。如 int a = 3 存的是字面值。
棧的由來:這些字面值的數據,由於大小可知,生存期可知(這些字面值固定定義在某個程序塊裏面,程序塊退出後,字段值就消失了),出於追求速度的原因,就存在於棧中。
引用類型包括類、接口、數組、Integer, String, Double等。 引用類型聲明的變量是指該變量在內存中實際存儲的是一個引用地址,實體在堆中。
包裝類就屬於引用類型,也就是將相應的基本數據類型包裝起來的類(int → Integer),自動裝箱和拆箱就是基本類型和引用類型之間的轉換。
為什麽要轉換?
因為基本類型轉換為引用類型後,就可以new對象,從而調用包裝類中封裝好的方法進行基本類型 之間的轉換或者toString(當然用類名直接調用也可以,便於一眼看出該方法是靜態的),還有就是如果集合中想存放基本類型,泛型的限定類型只能是對應的包裝類型。
3、關於String str =new String("abc")和 String str = "abc"的比較
String str =new String("abc")
String str1 = "abc"
System.out.println(str == str1)
System.out.println(str.equal(str1))
結果:
false
true
原因解析:
- Java運行環境有一個字符串池,由String類維護。
1. 執行語句String str="abc";時。首先查看字符串池中是否存在字符串"abc",如果存在則直接將“abc”賦給str,如果不存在則先在 字 符串池中新建一個字符串"abc",然後再將其賦給str.
2. 執行語句String str = new String("abc");時。不管字符串池中是否存在字符串“abc”,直接新建一個字符串“abc”,(註意,新建的字符串“abc”不是在字符串池中), 然後將其賦給str。由此可見 1.的效率要高於2的效率。
3. String str1="java";//指向字符串池
String str2="blog";//指向字符串池
String s = str1+str2;
+運算符會在堆中建立起兩個String對象,這兩個對象的值分別是“java”,"blog",也就是說從字符串常量池中復制這兩個值,然後再堆中創建兩個對象。然後再建立對象s,然後將“javablog”的堆地址賦給s. 這句話共創建了3個String對象。
System.out.println(s=="javablog");//結果是false;
JVM確實對形如String str="javablog";的對象放在常量池中,但是它是在編譯時name做的。而String s=str1+str2;是在運行時候才能知道的,也就是說str1+str2是在堆裏創建的,所以結果為false了。
String s="java"+"blog";//直接將javablog對象放入字符串池中。 System.out.println(s=="javablog");//結果是true;
String s=str1+"blog";//不放在字符串池中,而是在堆中分分配。 System.out.println(s=="javablog");//結果是false;
總之,創建字符串有兩種方式:兩種內存區域(pool,heap)
1.""創建的字符串在字符串池中。
2.new 創建字符串時,首先查看池中是否有相同的字符串,如果有則拷貝一份放到堆中,然後返回堆中的地址;如果池中沒有則在堆中創建一分,然後返回堆中的地址,
3.在對字符串賦值時,如果右操作數含有一個或一個以上的字符串引用時,則在堆中再建立一個字符串對象,返回引用如:String s= str1+"blog";
之間的區別
第1種:
String a="abc";
String b="abc";
System.out.print(a==b);
結果:true
原因:編譯時,這兩個"abc"被認為是同一個對象保存到了常量池中;運行時JVM則認為這兩個變量賦的是同一個對象,所以返回true。
---------------------
第2種:
String a=new String("abc");
String b=new String("abc");
System.out.print(a==b);
結果:false
原因:用構造器創建的對象,是不會被放入常理池中的,也很明顯這完全是兩個對象,只是內容相同罷了,結果當然為false了。用equals()或者System.out.print(a.intern()==b.intern());就返回true了。
------------------------------
第3種
String a="abc";
String b=new String("abc");
System.out.print(a==b);
結果:false
原因:同上。此外,a的類加載時就完成了初始化,而b要在執行引擎執行到那一行代碼時才完成初始化。
---------------------------
第4種
String a="abcdef";
System.out.print(a=="abcdef");
結果:true
原因:運行出現的字符串常量,若是在常量池中出現過,則JVM會認為同一個對象,以節省內存開銷,所以這兩個字符串會被認為是同一個對象。
-------------------------------------------
第5種
String a="abcdef";
String b="";
String c=a+b;
System.out.print(c=="abcdef");
結果:false
原因:編譯時,先將"abcedf"放在常量池中,而c的值則是在運行時在堆裏創建的。所以為false。
6. 結論與建議:
(1)我們在使用諸如String str = "abc";的格式定義類時,總是想當然地認為,我們創建了String類的對象str。擔心陷阱!對象可能並沒有被創建!唯一可以肯定的是,指向 String類的引用被創建了。至於這個引用到底是否指向了一個新的對象,必須根據上下文來考慮,除非你通過new()方法來顯要地創建一個新的對象。因 此,更為準確的說法是,我們創建了一個指向String類的對象的引用變量str,這個對象引用變量指向了某個值為"abc"的String類。清醒地認 識到這一點對排除程序中難以發現的bug是很有幫助的。
(2)使用String str = "abc";的方式,可以在一定程度上提高程序的運行速度,因為JVM會自動根據棧中數據的實際情況來決定是否有必要創建新對象。而對於String str = new String("abc");的代碼,則一概在堆中創建新對象,而不管其字符串值是否相等,是否有必要創建新對象,從而加重了程序的負擔。這個思想應該是 享元模式的思想,但JDK的內部在這裏實現是否應用了這個模式,不得而知。
(3)當比較包裝類裏面的數值是否相等時,用equals()方法;當測試兩個包裝類的引用是否指向同一個對象時,用==。
(4)由於String類的immutable性質,當String變量需要經常變換其值時,應該考慮使用StringBuffer類,以提高程序效率。
java堆、棧、堆棧的區別