基礎(chǔ)篇
int與integer的區(qū)別
Integer對(duì)象會(huì)占用更多的內(nèi)存。Integer是一個(gè)對(duì)象,需要存儲(chǔ)對(duì)象的元數(shù)據(jù)。但是int是一個(gè)原始類型的數(shù)據(jù),所以占用的空間更少。
int和Integer的更多區(qū)別:
1.兩個(gè)通過(guò)new出來(lái)的Integer變量比較,結(jié)果為false。
/**
* 比較兩個(gè)new出來(lái)的Integer
*/
public class Test {
public static void main(String[] args) {
Integer i = new Integer(200);
Integer j = new Integer(200);
System.out.print(i == j);
//輸出:false
}
}
Integer變量實(shí)際上是對(duì)一個(gè)Integer對(duì)象的引用。當(dāng)new一個(gè)Integer時(shí),實(shí)際上是生成一個(gè)指針指向此對(duì)象,兩次new Integer生成的是兩個(gè)對(duì)象,其內(nèi)存地址不同,所以兩個(gè)new出來(lái)的Integer變量不等。
2.非new生成的Integer變量與new Integer()生成的變量比較,結(jié)果為false。
/**
* 比較非new生成的Integer變量與new生成的Integer變量
*/
public class Test {
public static void main(String[] args) {
Integer i= new Integer(200);
Integer j = 200;
System.out.print(i == j);
//輸出:false
}
}
因?yàn)榉莕ew生成的Integer變量指向的是java常量池中的對(duì)象,而new Integer()生成的變量指向堆中新建的對(duì)象,兩者在內(nèi)存中的地址不同。所以 輸出為false。
3.兩個(gè)非new生成的Integer對(duì)象進(jìn)行比較,如果兩個(gè)變量的值在區(qū)間[-128,127]之間,比較結(jié)果為true;否則,結(jié)果為false。
/**
* 比較兩個(gè)非new生成的Integer變量
*/
public class Test {
public static void main(String[] args) {
Integer i1 = 127;
Integer ji = 127;
System.out.println(i1 == ji);//輸出:true
Integer i2 = 128;
Integer j2 = 128;
System.out.println(i2 == j2);//輸出:false
}
}
java在編譯Integer i1 = 127時(shí),會(huì)翻譯成Integer i1 = Integer.valueOf(127)。java會(huì)將[-128,127]之間的數(shù)進(jìn)行緩存。Integer i1 = 127時(shí),會(huì)將127緩存,Integer j2 = 127時(shí),就直接從緩存中取,不會(huì)new了,所以結(jié)果為true。Integer i2 = 128時(shí),不會(huì)將128緩存,Integer j2 = 128時(shí),會(huì)return new Integer(128)。所以結(jié)果為false。
- Integer變量(無(wú)論是否是new生成的)與int變量比較,只要兩個(gè)變量的值是相等的,結(jié)果都為true。
/**
* 比較Integer變量與int變量
*/
public class Test {
public static void main(String[] args) {
Integer i1 = 200;
Integer i2 = new Integer(200);
int j = 200;
System.out.println(i1 == j);//輸出:true
System.out.println(i2 == j);//輸出:true
}
}
String、StringBuffer、StringBuilder區(qū)別
1.三者在執(zhí)行速度方面的比較:StringBuilder > StringBuffer > String
2.String:字符串常量
StringBuffer:字符串變量
StringBuilder:字符串變量
從上面的名字可以看到,String是“字符串常量”,也就是不可改變的對(duì)象。
3.StringBuilder與 StringBuffer:
StringBuilder:線程非安全的
StringBuffer:線程安全的
當(dāng)我們?cè)谧址彌_去被多個(gè)線程使用是,JVM不能保證StringBuilder的操作是安全的,雖然他的速度最快,但是可以保證StringBuffer是可以正確操作的。當(dāng)然大多數(shù)情況下就是我們是在單線程下進(jìn)行的操作,所以大多數(shù)情況下是建議用StringBuilder而不用StringBuffer的,就是速度的原因。
總結(jié):
1.如果要操作少量的數(shù)據(jù)用 = String
2.單線程操作字符串緩沖區(qū) 下操作大量數(shù)據(jù) = StringBuilder
3.多線程操作字符串緩沖區(qū) 下操作大量數(shù)據(jù) = StringBuffer
什么是內(nèi)部類??jī)?nèi)部類的作用
內(nèi)部類可直接訪問(wèn)外部類的屬性
Java中內(nèi)部類主要分為成員內(nèi)部類、局部?jī)?nèi)部類(嵌套在方法和作用域內(nèi))、匿名內(nèi)部類(沒構(gòu)造方法)、靜態(tài)內(nèi)部類(static修飾的類,不能使用任何外圍類的非static成員變量和方法, 不依賴外圍類)
進(jìn)程和線程的區(qū)別
進(jìn)程是cpu資源分配的最小單位,線程是cpu調(diào)度的最小單位。
進(jìn)程之間不能共享資源,而線程共享所在進(jìn)程的地址空間和其它資源。
一個(gè)進(jìn)程內(nèi)可擁有多個(gè)線程,進(jìn)程可開啟進(jìn)程,也可開啟線程。
一個(gè)線程只能屬于一個(gè)進(jìn)程,線程可直接使用同進(jìn)程的資源,線程依賴于進(jìn)程而存在。
final,finally,finalize的區(qū)別
final:修飾類、成員變量和成員方法,類不可被繼承,成員變量不可變,成員方法不可重寫
finally:與try...catch...共同使用,確保無(wú)論是否出現(xiàn)異常都能被調(diào)用到
finalize:類的方法,垃圾回收之前會(huì)調(diào)用此方法,子類可以重寫finalize()方法實(shí)現(xiàn)對(duì)資源的回收
Serializable 和Parcelable 的區(qū)別
Serializable Java 序列化接口 在硬盤上讀寫 讀寫過(guò)程中有大量臨時(shí)變量的生成,內(nèi)部執(zhí)行大量的i/o操作,效率很低。
Parcelable Android 序列化接口 效率高 使用麻煩 在內(nèi)存中讀寫(AS有相關(guān)插件 一鍵生成所需方法) ,對(duì)象不能保存到磁盤中
靜態(tài)屬性和靜態(tài)方法是否可以被繼承?是否可以被重寫?以及原因?
可繼承 不可重寫 而是被隱藏
如果子類里面定義了靜態(tài)方法和屬性,那么這時(shí)候父類的靜態(tài)方法或?qū)傩苑Q之為"隱藏"。如果你想要調(diào)用父類的靜態(tài)方法和屬性,直接通過(guò)父類名.方法或變量名完成。
成員內(nèi)部類、靜態(tài)內(nèi)部類、局部?jī)?nèi)部類和匿名內(nèi)部類的理解,以及項(xiàng)目中的應(yīng)用
Java中內(nèi)部類主要分為成員內(nèi)部類、局部?jī)?nèi)部類(嵌套在方法和作用域內(nèi))、匿名內(nèi)部類(沒構(gòu)造方法)、靜態(tài)內(nèi)部類(static修飾的類,不能使用任何外圍類的非static成員變量和方法, 不依賴外圍類)
使用內(nèi)部類最吸引人的原因是:每個(gè)內(nèi)部類都能獨(dú)立地繼承一個(gè)(接口的)實(shí)現(xiàn),所以無(wú)論外圍類是否已經(jīng)繼承了某個(gè)(接口的)實(shí)現(xiàn),對(duì)于內(nèi)部類都沒有影響。
因?yàn)镴ava不支持多繼承,支持實(shí)現(xiàn)多個(gè)接口。但有時(shí)候會(huì)存在一些使用接口很難解決的問(wèn)題,這個(gè)時(shí)候我們可以利用內(nèi)部類提供的、可以繼承多個(gè)具體的或者抽象的類的能力來(lái)解決這些程序設(shè)計(jì)問(wèn)題??梢赃@樣說(shuō),接口只是解決了部分問(wèn)題,而內(nèi)部類使得多重繼承的解決方案變得更加完整。
哪些情況下的對(duì)象會(huì)被垃圾回收機(jī)制處理掉?
1.所有實(shí)例都沒有活動(dòng)線程訪問(wèn)。
2.沒有被其他任何實(shí)例訪問(wèn)的循環(huán)引用實(shí)例。
3.Java 中有不同的引用類型。判斷實(shí)例是否符合垃圾收集的條件都依賴于它的引用類型。
要判斷怎樣的對(duì)象是沒用的對(duì)象。這里有2種方法:
1.采用標(biāo)記計(jì)數(shù)的方法:
給內(nèi)存中的對(duì)象給打上標(biāo)記,對(duì)象被引用一次,計(jì)數(shù)就加1,引用被釋放了,計(jì)數(shù)就減一,當(dāng)這個(gè)計(jì)數(shù)為0的時(shí)候,這個(gè)對(duì)象就可以被回收了。當(dāng)然,這也就引發(fā)了一個(gè)問(wèn)題:循環(huán)引用的對(duì)象是無(wú)法被識(shí)別出來(lái)并且被回收的。所以就有了第二種方法:
2.采用根搜索算法:
從一個(gè)根出發(fā),搜索所有的可達(dá)對(duì)象,這樣剩下的那些對(duì)象就是需要被回收的
Java中實(shí)現(xiàn)多態(tài)的機(jī)制是什么?
方法的重寫Overriding和重載Overloading是Java多態(tài)性的不同表現(xiàn)
重寫Overriding是父類與子類之間多態(tài)性的一種表現(xiàn)
重載Overloading是一個(gè)類中多態(tài)性的一種表現(xiàn).
你對(duì)Java反射的理解
JAVA反射機(jī)制是在運(yùn)行狀態(tài)中, 對(duì)于任意一個(gè)類, 都能夠知道這個(gè)類的所有屬性和方法; 對(duì)于任意一個(gè)對(duì)象, 都能夠調(diào)用它的任意一個(gè)方法和屬性。
從對(duì)象出發(fā),通過(guò)反射(Class類)可以取得取得類的完整信息(類名 Class類型,所在包、具有的所有方法 Method[]類型、某個(gè)方法的完整信息(包括修飾符、返回值類型、異常、參數(shù)類型)、所有屬性 Field[]、某個(gè)屬性的完整信息、構(gòu)造器 Constructors),調(diào)用類的屬性或方法自己的總結(jié): 在運(yùn)行過(guò)程中獲得類、對(duì)象、方法的所有信息。
String為什么要設(shè)計(jì)成不可變的?
1、字符串池的需求
字符串池是方法區(qū)(Method Area)中的一塊特殊的存儲(chǔ)區(qū)域。當(dāng)一個(gè)字符串已經(jīng)被創(chuàng)建并且該字符串在 池 中,該字符串的引用會(huì)立即返回給變量,而不是重新創(chuàng)建一個(gè)字符串再將引用返回給變量。如果字符串不是不可變的,那么改變一個(gè)引用(如: string2)的字符串將會(huì)導(dǎo)致另一個(gè)引用(如: string1)出現(xiàn)臟數(shù)據(jù)。
2、允許字符串緩存哈希碼
在java中常常會(huì)用到字符串的哈希碼,例如: HashMap 。String的不變性保證哈希碼始終一,因此,他可以不用擔(dān)心變化的出現(xiàn)。 這種方法意味著不必每次使用時(shí)都重新計(jì)算一次哈希碼——這樣,效率會(huì)高很多。
3、安全
String廣泛的用于java 類中的參數(shù),如:網(wǎng)絡(luò)連接(Network connetion),打開文件(opening files )等等。如果String不是不可變的,網(wǎng)絡(luò)連接、文件將會(huì)被改變——這將會(huì)導(dǎo)致一系列的安全威脅。操作的方法本以為連接上了一臺(tái)機(jī)器,但實(shí)際上卻不是。由于反射中的參數(shù)都是字符串,同樣,也會(huì)引起一系列的安全問(wèn)題。
Object類的equal和hashCode方法重寫,為什么?
首先equals與hashcode間的關(guān)系是這樣的:
1、如果兩個(gè)對(duì)象相同(即用equals比較返回true),那么它們的hashCode值一定要相同;
2、如果兩個(gè)對(duì)象的hashCode相同,它們并不一定相同(即用equals比較返回false)
由于為了提高程序的效率才實(shí)現(xiàn)了hashcode方法,先進(jìn)行hashcode的比較,如果不同,那沒就不必在進(jìn)行equals的比較了,這樣就大大減少了equals比較的次數(shù),這對(duì)比需要比較的數(shù)量很大的效率提高是很明顯的
List,Set,Map的區(qū)別
Set是最簡(jiǎn)單的一種集合。集合中的對(duì)象不按特定的方式排序,并且沒有重復(fù)對(duì)象。 Set接口主要實(shí)現(xiàn)了兩個(gè)實(shí)現(xiàn)類:HashSet: HashSet類按照哈希算法來(lái)存取集合中的對(duì)象,存取速度比較快
TreeSet :TreeSet類實(shí)現(xiàn)了SortedSet接口,能夠?qū)现械膶?duì)象進(jìn)行排序。
List的特征是其元素以線性方式存儲(chǔ),集合中可以存放重復(fù)對(duì)象。
ArrayList() : 代表長(zhǎng)度可以改變得數(shù)組??梢詫?duì)元素進(jìn)行隨機(jī)的訪問(wèn),向ArrayList()中插入與刪除元素的速度慢。
LinkedList(): 在實(shí)現(xiàn)中采用鏈表數(shù)據(jù)結(jié)構(gòu)。插入和刪除速度快,訪問(wèn)速度慢。
Map 是一種把鍵對(duì)象和值對(duì)象映射的集合,它的每一個(gè)元素都包含一對(duì)鍵對(duì)象和值對(duì)象。 Map沒有繼承于Collection接口 從Map集合中檢索元素時(shí),只要給出鍵對(duì)象,就會(huì)返回對(duì)應(yīng)的值對(duì)象。
HashMap:Map基于散列表的實(shí)現(xiàn)。插入和查詢“鍵值對(duì)”的開銷是固定的。可以通過(guò)構(gòu)造器設(shè)置容量capacity和負(fù)載因子load factor,以調(diào)整容器的性能。
LinkedHashMap: 類似于HashMap,但是迭代遍歷它時(shí),取得“鍵值對(duì)”的順序是其插入次序,或者是最近最少使用(LRU)的次序。只比HashMap慢一點(diǎn)。而在迭代訪問(wèn)時(shí)發(fā)而更快,因?yàn)樗褂面湵砭S護(hù)內(nèi)部次序。
TreeMap : 基于紅黑樹數(shù)據(jù)結(jié)構(gòu)的實(shí)現(xiàn)。查看“鍵”或“鍵值對(duì)”時(shí),它們會(huì)被排序(次序由Comparabel或Comparator決定)。TreeMap的特點(diǎn)在 于,你得到的結(jié)果是經(jīng)過(guò)排序的。TreeMap是唯一的帶有subMap()方法的Map,它可以返回一個(gè)子樹。
WeakHashMao :弱鍵(weak key)Map,Map中使用的對(duì)象也被允許釋放: 這是為解決特殊問(wèn)題設(shè)計(jì)的。如果沒有map之外的引用指向某個(gè)“鍵”,則此“鍵”可以被垃圾收集器回收。
ArrayMap和HashMap的對(duì)比
1、存儲(chǔ)方式不同
HashMap內(nèi)部有一個(gè)HashMapEntry<K, V>[]對(duì)象,每一個(gè)鍵值對(duì)都存儲(chǔ)在這個(gè)對(duì)象里,當(dāng)使用put方法添加鍵值對(duì)時(shí),就會(huì)new一個(gè)HashMapEntry對(duì)象,
2、添加數(shù)據(jù)時(shí)擴(kuò)容時(shí)的處理不一樣,進(jìn)行了new操作,重新創(chuàng)建對(duì)象,開銷很大。ArrayMap用的是copy數(shù)據(jù),所以效率相對(duì)要高。
3、ArrayMap提供了數(shù)組收縮的功能,在clear或remove后,會(huì)重新收縮數(shù)組,是否空間
4、ArrayMap采用二分法查找;
HashMap和HashTable的區(qū)別
HashMap不是線程安全的,效率高一點(diǎn)、方法不是Synchronize的要提供外同步,有containsvalue和containsKey方法。
hashtable是,線程安全,不允許有null的鍵和值,效率稍低,方法是是Synchronize的。有contains方法方法。Hashtable 繼承于Dictionary 類
HashMap與HashSet的區(qū)別
hashMap:HashMap實(shí)現(xiàn)了Map接口,HashMap儲(chǔ)存鍵值對(duì),使用put()方法將元素放入map中,HashMap中使用鍵對(duì)象來(lái)計(jì)算hashcode值,HashMap比較快,因?yàn)槭鞘褂梦ㄒ坏逆I來(lái)獲取對(duì)象。
HashSet實(shí)現(xiàn)了Set接口,HashSet僅僅存儲(chǔ)對(duì)象,使用add()方法將元素放入set中,HashSet使用成員對(duì)象來(lái)計(jì)算hashcode值,對(duì)于兩個(gè)對(duì)象來(lái)說(shuō)hashcode可能相同,所以equals()方法用來(lái)判斷對(duì)象的相等性,如果兩個(gè)對(duì)象不同的話,那么返回false。HashSet較HashMap來(lái)說(shuō)比較慢。
ArrayList和LinkedList的區(qū)別,以及應(yīng)用場(chǎng)景
ArrayList是基于數(shù)組實(shí)現(xiàn)的,ArrayList線程不安全。
LinkedList是基于雙鏈表實(shí)現(xiàn)的:
使用場(chǎng)景:
(1)如果應(yīng)用程序?qū)Ω鱾€(gè)索引位置的元素進(jìn)行大量的存取或刪除操作,ArrayList對(duì)象要遠(yuǎn)優(yōu)于LinkedList對(duì)象;
(2)如果應(yīng)用程序主要是對(duì)列表進(jìn)行循環(huán),并且循環(huán)時(shí)候進(jìn)行插入或者刪除操作,LinkedList對(duì)象要遠(yuǎn)優(yōu)于ArrayList對(duì)象;
數(shù)組和鏈表的區(qū)別
數(shù)組:是將元素在內(nèi)存中連續(xù)存儲(chǔ)的;它的優(yōu)點(diǎn):因?yàn)閿?shù)據(jù)是連續(xù)存儲(chǔ)的,內(nèi)存地址連續(xù),所以在查找數(shù)據(jù)的時(shí)候效率比較高;它的缺點(diǎn):在存儲(chǔ)之前,我們需要申請(qǐng)一塊連續(xù)的內(nèi)存空間,并且在編譯的時(shí)候就必須確定好它的空間的大小。在運(yùn)行的時(shí)候空間的大小是無(wú)法隨著你的需要進(jìn)行增加和減少而改變的,當(dāng)數(shù)據(jù)兩比較大的時(shí)候,有可能會(huì)出現(xiàn)越界的情況,數(shù)據(jù)比較小的時(shí)候,又有可能會(huì)浪費(fèi)掉內(nèi)存空間。在改變數(shù)據(jù)個(gè)數(shù)時(shí),增加、插入、刪除數(shù)據(jù)效率比較低
鏈表:是動(dòng)態(tài)申請(qǐng)內(nèi)存空間,不需要像數(shù)組需要提前申請(qǐng)好內(nèi)存的大小,鏈表只需在用的時(shí)候申請(qǐng)就可以,根據(jù)需要來(lái)動(dòng)態(tài)申請(qǐng)或者刪除內(nèi)存空間,對(duì)于數(shù)據(jù)增加和刪除以及插入比數(shù)組靈活。還有就是鏈表中數(shù)據(jù)在內(nèi)存中可以在任意的位置,通過(guò)應(yīng)用來(lái)關(guān)聯(lián)數(shù)據(jù)(就是通過(guò)存在元素的指針來(lái)聯(lián)系)
run()和start()方法區(qū)別
start()方法被用來(lái)啟動(dòng)新創(chuàng)建的線程,而且start()內(nèi)部調(diào)用了run()方法,這和直接調(diào)用run()方法的效果不一樣。當(dāng)你調(diào)用run()方法的時(shí)候,只會(huì)是在原來(lái)的線程中調(diào)用,沒有新的線程啟動(dòng),start()方法才會(huì)啟動(dòng)新線程。
在Java中wait和seelp方法的不同
Java程序中wait 和 sleep都會(huì)造成某種形式的暫停,它們可以滿足不同的需要。wait()方法用于線程間通信,如果等待條件為真且其它線程被喚醒時(shí)它會(huì)釋放鎖,而sleep()方法僅僅釋放CPU資源或者讓當(dāng)前線程停止執(zhí)行一段時(shí)間,但不會(huì)釋放鎖。
如何實(shí)現(xiàn)線程同步?
1、synchronized關(guān)鍵字修改的方法。
2、synchronized關(guān)鍵字修飾的語(yǔ)句塊
3、使用特殊域變量(volatile)實(shí)現(xiàn)線程同步
談?wù)剬?duì)Synchronized關(guān)鍵字,類鎖,方法鎖,重入鎖的理解
java的對(duì)象鎖和類鎖:java的對(duì)象鎖和類鎖在鎖的概念上基本上和內(nèi)置鎖是一致的,但是,兩個(gè)鎖實(shí)際是有很大的區(qū)別的,對(duì)象鎖是用于對(duì)象實(shí)例方法,或者一個(gè)對(duì)象實(shí)例上的,類鎖是用于類的靜態(tài)方法或者一個(gè)類的class對(duì)象上的。我們知道,類的對(duì)象實(shí)例可以有很多個(gè),但是每個(gè)類只有一個(gè)class對(duì)象,所以不同對(duì)象實(shí)例的對(duì)象鎖是互不干擾的,但是每個(gè)類只有一個(gè)類鎖。但是有一點(diǎn)必須注意的是,其實(shí)類鎖只是一個(gè)概念上的東西,并不是真實(shí)存在的,它只是用來(lái)幫助我們理解鎖定實(shí)例方法和靜態(tài)方法的區(qū)別的
synchronized 和volatile 關(guān)鍵字的區(qū)別
1.volatile本質(zhì)是在告訴jvm當(dāng)前變量在寄存器(工作內(nèi)存)中的值是不確定的,需要從主存中讀??;synchronized則是鎖定當(dāng)前變量,只有當(dāng)前線程可以訪問(wèn)該變量,其他線程被阻塞住。
2.volatile僅能使用在變量級(jí)別;synchronized則可以使用在變量、方法、和類級(jí)別的
3.volatile僅能實(shí)現(xiàn)變量的修改可見性,不能保證原子性;而synchronized則可以保證變量的修改可見性和原子性
4.volatile不會(huì)造成線程的阻塞;synchronized可能會(huì)造成線程的阻塞。
5.volatile標(biāo)記的變量不會(huì)被編譯器優(yōu)化;synchronized標(biāo)記的變量可以被編譯器優(yōu)化
死鎖的四個(gè)必要條件?
死鎖產(chǎn)生的原因
- 系統(tǒng)資源的競(jìng)爭(zhēng)系統(tǒng)資源的競(jìng)爭(zhēng)導(dǎo)致系統(tǒng)資源不足,以及資源分配不當(dāng),導(dǎo)致死鎖。
- 進(jìn)程運(yùn)行推進(jìn)順序不合適
互斥條件:一個(gè)資源每次只能被一個(gè)進(jìn)程使用,即在一段時(shí)間內(nèi)某 資源僅為一個(gè)進(jìn)程所占有。此時(shí)若有其他進(jìn)程請(qǐng)求該資源,則請(qǐng)求進(jìn)程只能等待。
請(qǐng)求與保持條件:進(jìn)程已經(jīng)保持了至少一個(gè)資源,但又提出了新的資源請(qǐng)求,而該資源 已被其他進(jìn)程占有,此時(shí)請(qǐng)求進(jìn)程被阻塞,但對(duì)自己已獲得的資源保持不放。
不可剝奪條件:進(jìn)程所獲得的資源在未使用完畢之前,不能被其他進(jìn)程強(qiáng)行奪走,即只能 由獲得該資源的進(jìn)程自己來(lái)釋放(只能是主動(dòng)釋放)。
循環(huán)等待條件: 若干進(jìn)程間形成首尾相接循環(huán)等待資源的關(guān)系
這四個(gè)條件是死鎖的必要條件,只要系統(tǒng)發(fā)生死鎖,這些條件必然成立,而只要上述條件之一不滿足,就不會(huì)發(fā)生死鎖。
死鎖避免的基本思想:
系統(tǒng)對(duì)進(jìn)程發(fā)出每一個(gè)系統(tǒng)能夠滿足的資源申請(qǐng)進(jìn)行動(dòng)態(tài)檢查,并根據(jù)檢查結(jié)果決定是否分配資源,如果分配后系統(tǒng)可能發(fā)生死鎖,則不予分配,否則予以分配。這是一種保證系統(tǒng)不進(jìn)入死鎖狀態(tài)的動(dòng)態(tài)策略。
理解了死鎖的原因,尤其是產(chǎn)生死鎖的四個(gè)必要條件,就可以最大可能地避免、預(yù)防和解除死鎖。所以,在系統(tǒng)設(shè)計(jì)、進(jìn)程調(diào)度等方面注意如何讓這四個(gè)必要條件不成立,如何確定資源的合理分配算法,避免進(jìn)程永久占據(jù)系統(tǒng)資源。此外,也要防止進(jìn)程在處于等待狀態(tài)的情況下占用資源。因此,對(duì)資源的分配要給予合理的規(guī)劃。
死鎖避免和死鎖預(yù)防的區(qū)別:
死鎖預(yù)防是設(shè)法至少破壞產(chǎn)生死鎖的四個(gè)必要條件之一,嚴(yán)格的防止死鎖的出現(xiàn),而死鎖避免則不那么嚴(yán)格的限制產(chǎn)生死鎖的必要條件的存在,因?yàn)榧词顾梨i的必要條件存在,也不一定發(fā)生死鎖。死鎖避免是在系統(tǒng)運(yùn)行過(guò)程中注意避免死鎖的最終發(fā)生。
有三個(gè)線程T1,T2,T3,怎么確保它們按順序執(zhí)行?
在多線程中有多種方法讓線程按特定順序執(zhí)行,你可以用線程類的join()方法在一個(gè)線程中啟動(dòng)另一個(gè)線程,另外一個(gè)線程完成該線程繼續(xù)執(zhí)行。為了確保三個(gè)線程的順序你應(yīng)該先啟動(dòng)最后一個(gè)(T3調(diào)用T2,T2調(diào)用T1),這樣T1就會(huì)先完成而T3最后完成。
Binder的工作機(jī)制
直觀來(lái)說(shuō),Binder是Android中的一個(gè)類,它實(shí)現(xiàn)了IBinder接口,從IPC的角度來(lái)說(shuō),Binder是Android中的一種跨進(jìn)程通信的一種方式,同時(shí)還可以理解為是一種虛擬的物理設(shè)備,它的設(shè)備驅(qū)動(dòng)是/dev/binder/。從Framework角度來(lái)說(shuō),Binder是ServiceManager的橋梁。從應(yīng)用層來(lái)說(shuō),Binder是客戶端和服務(wù)端進(jìn)行通信的媒介。
我們先來(lái)了解一下這個(gè)類中每個(gè)方法的含義:
DESCRIPTOR:Binder的唯一標(biāo)識(shí),一般用于當(dāng)前Binder的類名表示。
asInterface(android.os.IBinder obj):用于將服務(wù)端的Binder對(duì)象轉(zhuǎn)換成客戶端所需的AIDL接口類型的對(duì)象,這種轉(zhuǎn)化過(guò)程是區(qū)分進(jìn)程的,如果客戶端和服務(wù)端位于同一個(gè)進(jìn)程,那么這個(gè)方法返回的是服務(wù)端的stub對(duì)象本身,否則返回的是系統(tǒng)封裝后的Stub.proxy對(duì)象。
asBinder():用于返回當(dāng)前Binder對(duì)象。
onTransact:該方法運(yùn)行在服務(wù)端的Binder線程池中,當(dāng)客戶端發(fā)起跨進(jìn)程通信請(qǐng)求的時(shí)候,遠(yuǎn)程請(qǐng)求通過(guò)系統(tǒng)底層封裝后交給該方法處理。注意這個(gè)方法public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags),服務(wù)端通過(guò)code可以確定客戶端所請(qǐng)求的目標(biāo)方法是什么,接著從data中取出目標(biāo)方法所需的參數(shù),然后執(zhí)行目標(biāo)方法。當(dāng)目標(biāo)方法執(zhí)行完畢后,就像reply中寫入返回值。這個(gè)方法的執(zhí)行過(guò)程就是這樣的。如果這個(gè)方法返回false,客戶端是會(huì)請(qǐng)求失敗的,所以我們可以在這個(gè)方法中做一些安全驗(yàn)證。
Binder的工作機(jī)制但是要注意一些問(wèn)題:1、當(dāng)客戶端發(fā)起請(qǐng)求時(shí),由于當(dāng)前線程會(huì)被掛起,直到服務(wù)端返回?cái)?shù)據(jù),如果這個(gè)遠(yuǎn)程方法很耗時(shí)的話,那么是不能夠在UI線程,也就是主線程中發(fā)起這個(gè)遠(yuǎn)程請(qǐng)求的。
2、由于Service的Binder方法運(yùn)行在線程池中,所以Binder方法不管是耗時(shí)還是不耗時(shí)都應(yīng)該采用同步的方式,因?yàn)樗呀?jīng)運(yùn)行在一個(gè)線程中了。
抽象類和接口區(qū)別
抽象類(abstract class):
使用abstract修飾符修飾的類。官方點(diǎn)的定義就是:如果一個(gè)類沒有包含足夠多的信息來(lái)描述一個(gè)具體的對(duì)象,這樣的類就是抽象類。實(shí)際點(diǎn)來(lái)說(shuō),一個(gè)抽象類不能實(shí)例化,因?yàn)椤皼]有包含足夠多的信息來(lái)描述一個(gè)具體的對(duì)象”。但終歸屬于類,所以仍然擁有普通類一樣的定義。依然可以在類的實(shí)體(直白點(diǎn)就是能在{}里面)定義成員變量,成員方法,構(gòu)造方法等。那么可能初學(xué)者會(huì)問(wèn):既然不能實(shí)例化,那么在類里面定義成員方法,成員變量有什么用。抽象類在實(shí)際應(yīng)用中,更多的是因?yàn)轭愔杏谐橄蠓椒?。抽象方法:只聲明,不?shí)現(xiàn)。具體的實(shí)現(xiàn)由繼承它的子類來(lái)實(shí)現(xiàn)。實(shí)際點(diǎn)就是:被abstract修飾的方法,只有方法名沒有方法實(shí)現(xiàn),具體的實(shí)現(xiàn)要由子類實(shí)現(xiàn)。方法名后面直接跟一個(gè)分號(hào),而不是花括號(hào)。例如:public abstract int A();一個(gè)類中含有抽象方法(被abstract修飾),那么這個(gè)類必須被聲明為抽象類(被abstract修飾)。
接口(interface):
官方定義:接口在java中是一個(gè)抽象類型,是抽象方法的集合。
一個(gè)類通過(guò)繼承接口的方式,從而繼承接口的抽象方法。
從定義上看,接口是個(gè)集合,并不是類。類描述了屬性和方法,而接口只包含方法(未實(shí)現(xiàn)的方法)。
接口和抽象類一樣不能被實(shí)例化,因?yàn)椴皇穷悺5墙涌诳梢员粚?shí)現(xiàn)(使用 implements 關(guān)鍵字)。
實(shí)現(xiàn)某個(gè)接口的類必須在類中實(shí)現(xiàn)該接口的全部方法。
雖然接口內(nèi)的方法都是抽象的(和抽象方法很像,沒有實(shí)現(xiàn))但是不需要abstract關(guān)鍵字。
接口中沒有構(gòu)造方式(因?yàn)榻涌诓皇穷悾┙涌谥械姆椒ū仨毷浅橄蟮模ú荒軐?shí)現(xiàn))接口中除了static、final變量,不能有其他變量接口支持多繼承(一個(gè)類可以實(shí)現(xiàn)多個(gè)接口)
抽象類和接口的區(qū)別:
默認(rèn)的方法實(shí)現(xiàn) 抽象類可以有默認(rèn)的方法實(shí)現(xiàn)完全是抽象的。接口根本不存在方法的實(shí)現(xiàn)。
抽象類中可以有已經(jīng)實(shí)現(xiàn)了的方法,也可以有被abstract修飾的方法(抽象方法),因?yàn)榇嬖诔橄蠓椒?,所以該類必須是抽象類。但是接口要求只能包含抽象方法,抽象方法是指沒有實(shí)現(xiàn)的方法。所以就不能像抽象類那么無(wú)賴了,接口就根本不能存在方法的實(shí)現(xiàn)。實(shí)現(xiàn) 抽象類使用extends關(guān)鍵字來(lái)繼承抽象類。如果子類不是抽象類的話,它需要提供抽象類中所有聲明的方法的實(shí)現(xiàn)。子類使用關(guān)鍵字implements來(lái)實(shí)現(xiàn)接口。它需要提供接口中所有聲明的方法的實(shí)現(xiàn)。抽象類雖然不能實(shí)例化來(lái)使用,但是可以被繼承,讓子類來(lái)具體實(shí)現(xiàn)父類的所有抽象方法。有點(diǎn)老子沒完成的夢(mèng)想交給兒子來(lái)完成,但是如果子類將抽象方法沒有全部實(shí)現(xiàn),就必須把自己也修飾成抽象類,交于繼承它的子類來(lái)完成實(shí)現(xiàn)。就相當(dāng)于,兒子能力不夠也沒完成老爹的夢(mèng)想,現(xiàn)在兒子等著再生兒子(被繼承),然后讓孫子去完成。以此類推,知道沒有抽象函數(shù)。接口的實(shí)現(xiàn),通過(guò)implements關(guān)鍵字。實(shí)現(xiàn)該接口的類,必須把接口中的所有方法給實(shí)現(xiàn)。不能再推給下一代。和抽象類相比,抽象類是將夢(mèng)想傳給家族,一代一代去完成。那么接口就是掌門人找大師兄來(lái)完成幫派的鴻星偉業(yè),這時(shí)候就只有一次希望,要么有能力就實(shí)現(xiàn),沒能力就不要接。抽象類可以有構(gòu)造器,而接口不能有構(gòu)造器這個(gè)原因很簡(jiǎn)單,我們回到雙方的定義上來(lái),抽象類再怎么流氓無(wú)賴也好,終究是屬于類,就天生享有類的所有特性(但是不能實(shí)例化),當(dāng)然包括類的構(gòu)造方法,也就是構(gòu)造器。但是接口是所有抽象方法的集合,注意,是集合,不是類。當(dāng)然沒有構(gòu)造方法一說(shuō),更別提什么構(gòu)造器了。抽象方法可以有public、protected和default這些修飾符 接口方法默認(rèn)修飾符是public。你不可以使用其它修飾符。抽象類的目的就是被繼承,抽象方法就是為了被重寫,所以肯定不能用private修飾符,肯定是可以用public的。但是protected和default也是可以的。接口就有且只有一個(gè)public修飾。(是不是感覺抽象類像小兒子各種耍無(wú)賴,接口就像私生子,說(shuō)什么只能是什么)抽象類在java語(yǔ)言中所表示的是一種繼承關(guān)系,一個(gè)子類只能存在一個(gè)父類,但是可以存在多個(gè)接口。
java在類的繼承上并沒有多繼承。抽象類屬于類,所以可以被繼承。但子類只能繼承一個(gè)父類。java為了實(shí)現(xiàn)多繼承,使用了接口。一個(gè)類可以實(shí)現(xiàn)多個(gè)接口。繼承就好比生了孩子,只能有一個(gè)爹,但是這個(gè)孩子可以學(xué)語(yǔ)文,學(xué)數(shù)學(xué),學(xué)英語(yǔ)等等很多東西,而語(yǔ)文、數(shù)學(xué)、英語(yǔ)就相當(dāng)于接口??偟膩?lái)說(shuō),因?yàn)閖ava中抽象類只有單繼承,接口就可以實(shí)現(xiàn)多繼承。抽象方法比接口速度要快接口是稍微有點(diǎn)慢的,因?yàn)樗枰獣r(shí)間去尋找在類中實(shí)現(xiàn)的方法。記住抽象方法是小兒子,從小吃的好所以跑的快,接口是私生子,從小日子苦,營(yíng)養(yǎng)不良。如果你往抽象類中添加新的方法,你可以給它提供默認(rèn)的實(shí)現(xiàn)。因此你不需要改變你現(xiàn)在的代碼。 如果你往接口中添加方法,那么你必須改變實(shí)現(xiàn)該接口的類。
抽象類可以有一些非抽象方法的存在,這些方法被稱為默認(rèn)實(shí)現(xiàn)。如果添加一個(gè)默認(rèn)實(shí)現(xiàn)方法(不能是抽象方法),就不需要在子類中去實(shí)現(xiàn),所以繼承這個(gè)抽象類的子類無(wú)須改動(dòng)。但是,接口中只能添加抽象方法,當(dāng)你添加了抽象方法,實(shí)現(xiàn)該接口的類就必須實(shí)現(xiàn)這個(gè)新添加的方法。因?yàn)?,定義中說(shuō)的很清楚,接口的實(shí)現(xiàn)必須實(shí)現(xiàn)所有的方法。所有,當(dāng)然包括新添加的方法。
父類的靜態(tài)方法能不能被子類重寫?
答案是不能。
因?yàn)殪o態(tài)方法從程序開始運(yùn)行后就已經(jīng)分配了內(nèi)存,也就是說(shuō)已經(jīng)寫死了。所有引用到該方法的對(duì)象(父類的對(duì)象也好子類的對(duì)象也好)所指向的都是同一塊內(nèi)存中的數(shù)據(jù),也就是該靜態(tài)方法,并沒有重寫這一說(shuō)法。
java虛擬機(jī)的工作原理
參考文章:https://www.cnblogs.com/lishun1005/p/6019678.html
java虛擬機(jī)和Dalvik虛擬機(jī)的區(qū)別
參考文章:http://www.itdecent.cn/p/923aebd31b65
哪些情況下的對(duì)象會(huì)被垃圾回收機(jī)制處理掉?
參考文章:http://www.itdecent.cn/p/5261a62e4d29
http://www.itdecent.cn/p/778dd3848196
造成內(nèi)存泄漏的原因探索:
參考文章:http://www.itdecent.cn/p/3ea9e9dfdb28
常用排序算法(必須會(huì)手寫)
參考文章:http://www.itdecent.cn/p/b06f95178d49
現(xiàn)有線程 T1、T2 和 T3。你如何確保 T2 線程在 T1 之后執(zhí)行,并且 T3 線程在 T2 之后執(zhí)行?
這個(gè)線程面試題通常在第一輪面試或電話面試時(shí)被問(wèn)到,這道多線程問(wèn)題為了測(cè)試面試者是否熟悉 join 方法的概念。答案也非常簡(jiǎn)單——可以用 Thread 類的 join 方法實(shí)現(xiàn)這一效果。
Java 中新的 Lock 接口相對(duì)于同步代碼塊(synchronized block)有什么優(yōu)勢(shì)?如果讓你實(shí)現(xiàn)一個(gè)高性能緩存,支持并發(fā)讀取和單一寫入,你如何保證數(shù)據(jù)完整性?
多線程和并發(fā)編程中使用 lock 接口的最大優(yōu)勢(shì)是它為讀和寫提供兩個(gè)單獨(dú)的鎖,可以讓你構(gòu)建高性能數(shù)據(jù)結(jié)構(gòu),比如 ConcurrentHashMap 和條件阻塞。
這道 Java 線程面試題越來(lái)越多見,而且隨后的面試題都基于面試者對(duì)這道題的回答。
我強(qiáng)烈建議在任何 Java 多線程面試前都要多看看有關(guān)鎖的知識(shí),因?yàn)槿缃耠娮咏灰紫到y(tǒng)的客戶端和數(shù)據(jù)交互中,鎖被頻繁使用來(lái)構(gòu)建緩存。
Java 中 wait 和 sleep 方法有什么區(qū)別?
兩者主要的區(qū)別就是等待釋放鎖和監(jiān)視器。sleep方法在等待時(shí)不會(huì)釋放任何鎖或監(jiān)視器。wait 方法多用于線程間通信,而 sleep 只是在執(zhí)行時(shí)暫停??梢钥次伊硪黄嘘P(guān)Java 中 wait 和 sleep的文章。
Java 中你如何喚醒阻塞線程?
這是有關(guān)線程的一個(gè)很狡猾的問(wèn)題。有很多原因會(huì)導(dǎo)致阻塞,如果是 IO 阻塞,我認(rèn)為沒有方式可以中斷線程(如果有的話請(qǐng)告訴我)。另一方面,如果線程阻塞是由于調(diào)用了 wait(),sleep() 或 join() 方法,你可以中斷線程,通過(guò)拋出 InterruptedException 異常來(lái)喚醒該線程??梢钥催@篇文章了解有關(guān)處理阻塞線程的知識(shí)Java 中如何處理阻塞方法。
字節(jié)流與字符流的區(qū)別
字節(jié)流和字符流使用是非常相似的,字節(jié)流在操作的時(shí)候本身是不會(huì)用到緩沖區(qū)(內(nèi)存)的,是與文件本身直接操作的,而字符流在操作的時(shí)候是使用到緩沖區(qū)的。
字節(jié)流在操作文件時(shí),即使不關(guān)閉資源(close 方法),文件也能輸出,
但是如果字符流不使用 close 方法的話,則不會(huì)輸出任何內(nèi)容,說(shuō)明字符流用的是緩沖區(qū),并且可以使用 flush 方法強(qiáng)制進(jìn)行刷新緩沖區(qū),這時(shí)才能在不 close 的情況下輸出內(nèi)容。
Java集合類框架的基本接口有哪些?
Java集合類里面最基本的接口有:
Collection:代表一組對(duì)象,每一個(gè)對(duì)象都是它的子元素。
Set:不包含重復(fù)元素的Collection。
List:有順序的Collection,并且可以包含重復(fù)元素。
Map:可以把鍵(key)映射到值(value)的對(duì)象,鍵不能重復(fù)。
&和&&的區(qū)別
&是位運(yùn)算符,表示按位與運(yùn)算,&&是邏輯運(yùn)算符,表示邏輯與(and)。
判斷條件a&&b,如果a是false就不會(huì)判斷b,但是a&b,a、b都會(huì)判斷。
&&相當(dāng)于有阻斷作用。
heap(堆)和stack(棧)有什么區(qū)別
棧是后進(jìn)先出的線性表結(jié)構(gòu),存取速度比堆快。創(chuàng)建對(duì)象的時(shí)候new一個(gè)對(duì)象,引用存在棧上具體的內(nèi)容存在堆上。
棧與堆都是Java用來(lái)在RAM中存放數(shù)據(jù)的地方。與C++不同,Java自動(dòng)管理?xiàng):投?,程序員不能直接地設(shè)置?;蚨选?br>
Java的堆是一個(gè)運(yùn)行時(shí)數(shù)據(jù)區(qū),類的對(duì)象從中分配空間。這些對(duì)象通過(guò)new指令建立,它們不需要程序代碼來(lái)顯式的釋放。堆是由垃圾回收來(lái)負(fù)責(zé)的,
堆的優(yōu)勢(shì)是可以動(dòng)態(tài)地分配內(nèi)存大小,生存期也不必事先告訴編譯器,因?yàn)樗窃谶\(yùn)行時(shí)動(dòng)態(tài)分配內(nèi)存的,Java的垃圾收集器會(huì)自動(dòng)收走這些不再使用的數(shù)據(jù)。但缺點(diǎn)是,
由于要在運(yùn)行時(shí)動(dòng)態(tài)分配內(nèi)存,存取速度較慢。
棧的優(yōu)勢(shì)是,存取速度比堆要快,僅次于寄存器,棧數(shù)據(jù)可以共享。
但缺點(diǎn)是,存在棧中的數(shù)據(jù)大小與生存期必須是確定的,缺乏靈活性。棧中主要存放一些基本類型的變量(,int, short, long, byte, float, double, boolean, char)和對(duì)象句柄。
棧有一個(gè)很重要的特殊性,就是存在棧中的數(shù)據(jù)可以共享。假設(shè)我們同時(shí)定義:
int a = 3;
int b = 3;
編譯器先處理int a = 3;首先它會(huì)在棧中創(chuàng)建一個(gè)變量為a的引用,然后查找棧中是否有3這個(gè)值,如果沒找到,就將3存放進(jìn)來(lái),然后將a指向3。接著處理int b = 3;在創(chuàng)建完b的引用變量后,
因?yàn)樵跅V幸呀?jīng)有3這個(gè)值,便將b直接指向3。這樣,就出現(xiàn)了a與b同時(shí)均指向3的情況。
這時(shí),如果再令a=4;那么編譯器會(huì)重新搜索棧中是否有4值,如果沒有,則將4存放進(jìn)來(lái),并令a指向4;如果已經(jīng)有了,則直接將a指向這個(gè)地址。因此a值的改變不會(huì)影響到b的值。
要注意這種數(shù)據(jù)的共享與兩個(gè)對(duì)象的引用同時(shí)指向一個(gè)對(duì)象的這種共享是不同的,因?yàn)檫@種情況a的修改并不會(huì)影響到b, 它是由編譯器完成的,它有利于節(jié)省空間。而一個(gè)對(duì)象引用變量修改了這個(gè)對(duì)象的內(nèi)部狀態(tài),會(huì)影響到另一個(gè)對(duì)象引用變量。
String是一個(gè)特殊的包裝類數(shù)據(jù)??梢杂茫?br>
String str = new String("abc");
String str = "abc";
兩種的形式來(lái)創(chuàng)建,第一種是用new()來(lái)新建對(duì)象的,它會(huì)在存放于堆中。每調(diào)用一次就會(huì)創(chuàng)建一個(gè)新的對(duì)象。
而第二種是先在棧中創(chuàng)建一個(gè)對(duì)String類的對(duì)象引用變量str,然后查找棧中有沒有存放"abc",如果沒有,則將"abc"存放進(jìn)棧,并令str指向”abc”,如果已經(jīng)有”abc” 則直接令str指向“abc”。
比較類里面的數(shù)值是否相等時(shí),用equals()方法;當(dāng)測(cè)試兩個(gè)包裝類的引用是否指向同一個(gè)對(duì)象時(shí),用==,下面用例子說(shuō)明上面的理論。
String str1 = "abc";
String str2 = "abc";
System.out.println(str1==str2); //true
可以看出str1和str2是指向同一個(gè)對(duì)象的。
String str1 =new String ("abc");
String str2 =new String ("abc");
System.out.println(str1==str2); // false
用new的方式是生成不同的對(duì)象。每一次生成一個(gè)。
因此用第一種方式創(chuàng)建多個(gè)”abc”字符串,在內(nèi)存中其實(shí)只存在一個(gè)對(duì)象而已. 這種寫法有利與節(jié)省內(nèi)存空間. 同時(shí)它可以在一定程度上提高程序的運(yùn)行速度,因?yàn)镴VM會(huì)自動(dòng)根據(jù)棧中數(shù)據(jù)的實(shí)際情況來(lái)決定是否有必要?jiǎng)?chuàng)建新對(duì)象。而對(duì)于String str = new String("abc");的代碼,則一概在堆中創(chuàng)建新對(duì)象,而不管其字符串值是否相等,是否有必要?jiǎng)?chuàng)建新對(duì)象,從而加重了程序的負(fù)擔(dān)。
另一方面, 要注意: 我們?cè)谑褂弥T如String str = "abc";的格式定義類時(shí),總是想當(dāng)然地認(rèn)為,創(chuàng)建了String類的對(duì)象str。擔(dān)心陷阱!對(duì)象可能并沒有被創(chuàng)建!而可能只是指向一個(gè)先前已經(jīng)創(chuàng)建的對(duì)象。只有通過(guò)new()方法才能保證每次都創(chuàng)建一個(gè)新的對(duì)象。
由于String類的immutable性質(zhì),當(dāng)String變量需要經(jīng)常變換其值時(shí),應(yīng)該考慮使用StringBuffer類,以提高程序效率。
2.2申請(qǐng)后系統(tǒng)的響應(yīng)
棧:只要棧的剩余空間大于所申請(qǐng)空間,系統(tǒng)將為程序提供內(nèi)存,否則將報(bào)異常提示棧溢出。
堆: 首先應(yīng)該知道操作系統(tǒng)有一個(gè)記錄空閑內(nèi)存地址的鏈表,當(dāng)系統(tǒng)收到程序的申請(qǐng)時(shí),會(huì)遍歷該鏈表,尋找第一個(gè)空間大于所申請(qǐng)空間的堆結(jié)點(diǎn),然后將該結(jié)點(diǎn)從空閑 結(jié)點(diǎn)鏈表中刪除,
并將該結(jié)點(diǎn)的空間分配給程序,另外,對(duì)于大多數(shù)系統(tǒng),會(huì)在這塊內(nèi)存空間中的首地址處記錄本次分配的大小,這樣,代碼中的delete語(yǔ)句才能正確的釋放本內(nèi)存空間。
另外,由于找到的堆結(jié)點(diǎn)的大小不一定正好等于申請(qǐng)的大小,系統(tǒng)會(huì)自動(dòng)的將多余的那部分重新放入空閑鏈表中。
2.3申請(qǐng)大小的限制
棧:在Windows下,棧是向低地址擴(kuò)展的數(shù)據(jù)結(jié)構(gòu),是一塊連續(xù)的內(nèi)存的區(qū)域。這句話的意思是棧頂?shù)牡刂泛蜅5淖畲笕萘渴窍到y(tǒng)預(yù)先規(guī)定好的,在WINDOWS下,棧的大小是2M
(也可能是1M,它是一個(gè)編譯時(shí)就確定的常數(shù)),如果申請(qǐng)的空間超過(guò)棧的剩余空間時(shí),將提示overflow。因此,能從棧獲得的空間較小。
堆:堆是向高地址擴(kuò)展的數(shù)據(jù)結(jié)構(gòu),是不連續(xù)的內(nèi)存區(qū)域。這是由于系統(tǒng)是用鏈表來(lái)存儲(chǔ)的空閑內(nèi)存地址的,自然是不連續(xù)的,而鏈表的遍歷方向是由低地址向高地址。
堆的大小受限于計(jì)算機(jī)系統(tǒng)中有效的虛擬內(nèi)存。由此可見,堆獲得的空間比較靈活,也比較大。
2.4申請(qǐng)效率的比較:
棧由系統(tǒng)自動(dòng)分配,速度較快。但程序員是無(wú)法控制的。
堆是由new分配的內(nèi)存,一般速度比較慢,而且容易產(chǎn)生內(nèi)存碎片,不過(guò)用起來(lái)最方便.
另外,在WINDOWS下,最好的方式是用VirtualAlloc分配內(nèi)存,他不是在堆,也不是在棧是直接在進(jìn)程的地址空間中保留一快內(nèi)存,雖然用起來(lái)最不方便。但是速度快,也最靈活。
2.5堆和棧中的存儲(chǔ)內(nèi)容
棧:在函數(shù)調(diào)用時(shí),第一個(gè)進(jìn)棧的是主函數(shù)中后的下一條指令(函數(shù)調(diào)用語(yǔ)句的下一條可執(zhí)行語(yǔ)句)的地址,然后是函數(shù)的各個(gè)參數(shù),在大多數(shù)的C編譯器中,
參數(shù)是由右往左入棧的,然后是函數(shù)中的局部變量。注意靜態(tài)變量是不入棧的。當(dāng)本次函數(shù)調(diào)用結(jié)束后,局部變量先出棧,然后是參數(shù),最后棧頂指針指向最開始存的地址,
也就是主函數(shù)中的下一條指令,程序由該點(diǎn)繼續(xù)運(yùn)行。
堆:一般是在堆的頭部用一個(gè)字節(jié)存放堆的大小。堆中的具體內(nèi)容有程序員安排。
2.6存取效率的比較
char s1[] = "aaaaaaaaaaaaaaa";
char *s2 = "bbbbbbbbbbbbbbbbb";
aaaaaaaaaaa是在運(yùn)行時(shí)刻賦值的;
而bbbbbbbbbbb是在編譯時(shí)就確定的;
但是,在以后的存取中,在棧上的數(shù)組比指針?biāo)赶虻淖址?例如堆)快。
堆和棧的區(qū)別可以用如下的比喻來(lái)看出:
使用棧就象我們?nèi)ワ堭^里吃飯,只管點(diǎn)菜(發(fā)出申請(qǐng))、付錢、和吃(使用),吃飽了就走,不必理會(huì)切菜、洗菜等準(zhǔn)備工作和洗碗、刷鍋等掃尾工作,他的好處是快捷,但是自由度小。
使用堆就象是自己動(dòng)手做喜歡吃的菜肴,比較麻煩,但是比較符合自己的口味,而且自由度大。