java在類的內(nèi)部創(chuàng)建本類的對象是怎么做到的?不理解啊?
問題描述
問題解答
回答1:先明確幾個(gè)概念,java代碼是跑在jvm中的,而jvm的內(nèi)存區(qū)域劃分為這么幾個(gè)模塊:
程序計(jì)數(shù)器(Program Counter Register):程序計(jì)數(shù)器是一個(gè)比較小的內(nèi)存區(qū)域,用于指示當(dāng)前線程所執(zhí)行的字節(jié)碼執(zhí)行到了第幾行,可以理解為是當(dāng)前線程的行號指示器。字節(jié)碼解釋器在工作時(shí),會通過改變這個(gè)計(jì)數(shù)器的值來取下一條語句指令。
虛擬機(jī)棧(JVM Stack):一個(gè)線程的每個(gè)方法在執(zhí)行的同時(shí),都會創(chuàng)建一個(gè)棧幀(Statck Frame),棧幀中存儲的有局部變量表、操作站、動(dòng)態(tài)鏈接、方法出口等,當(dāng)方法被調(diào)用時(shí),棧幀在JVM棧中入棧,當(dāng)方法執(zhí)行完成時(shí),棧幀出棧。
本地方法棧(Native Method Statck):本地方法棧在作用,運(yùn)行機(jī)制,異常類型等方面都與虛擬機(jī)棧相同,唯一的區(qū)別是:虛擬機(jī)棧是執(zhí)行Java方法的,而本地方法棧是用來執(zhí)行native方法的,在很多虛擬機(jī)中(如Sun的JDK默認(rèn)的HotSpot虛擬機(jī)),會將本地方法棧與虛擬機(jī)棧放在一起使用。
堆區(qū)(Heap):堆區(qū)是理解Java GC機(jī)制最重要的區(qū)域,沒有之一。在JVM所管理的內(nèi)存中,堆區(qū)是最大的一塊,堆區(qū)也是Java GC機(jī)制所管理的主要內(nèi)存區(qū)域,堆區(qū)由所有線程共享,在虛擬機(jī)啟動(dòng)時(shí)創(chuàng)建。堆區(qū)的存在是為了存儲對象實(shí)例,原則上講,所有的對象都在堆區(qū)上分配內(nèi)存(不過現(xiàn)代技術(shù)里,也不是這么絕對的,也有棧上直接分配的)。
方法區(qū)(Method Area):(也被稱為永久代),方法區(qū)是各個(gè)線程共享的區(qū)域,用于存儲已經(jīng)被虛擬機(jī)加載的類信息(即加載類時(shí)需要加載的信息,包括版本、field、方法、接口等信息)、final常量、靜態(tài)變量、編譯器即時(shí)編譯的代碼等。
直接內(nèi)存(Direct Memory):直接內(nèi)存并不是JVM管理的內(nèi)存,可以這樣理解,直接內(nèi)存,就是JVM以外的機(jī)器內(nèi)存,比如,你有4G的內(nèi)存,JVM占用了1G,則其余的3G就是直接內(nèi)存,JDK中有一種基于通道(Channel)和緩沖區(qū)(Buffer)的內(nèi)存分配方式,將由C語言實(shí)現(xiàn)的native函數(shù)庫分配在直接內(nèi)存中,用存儲在JVM堆中的DirectByteBuffer來引用。由于直接內(nèi)存收到本機(jī)器內(nèi)存的限制,所以也可能出現(xiàn)OutOfMemoryError的異常。
明白這幾個(gè)基本概念以后再來看看題主疑惑的地方。其實(shí)題主疑惑的是在java中,對象的引用是如何實(shí)現(xiàn)的。為什么可以在定義一個(gè)類的同時(shí),定義自己的引用,同時(shí)如果再實(shí)例化了這個(gè)引用以后,難道不會導(dǎo)致無線循環(huán)引用下去嗎?
別急我們先來分析下java中一個(gè)引用是怎么實(shí)現(xiàn)的:
一個(gè)Java的引用訪問涉及到3個(gè)內(nèi)存區(qū)域:JVM棧,堆,方法區(qū)。
以最簡單的本地變量引用:Object obj = new Object()為例:
Object obj表示一個(gè)本地引用,存儲在JVM棧的本地變量表中,表示一個(gè)reference類型數(shù)據(jù);
new Object()作為實(shí)例對象數(shù)據(jù)存儲在堆中;
堆中還記錄了Object類的類型信息(接口、方法、field、對象類型等)的地址,這些地址所執(zhí)行的數(shù)據(jù)存儲在方法區(qū)中;
具體的實(shí)現(xiàn)方式有很多種,句柄是其中一種,關(guān)系如圖所示。
看到這里應(yīng)該就明白了。類本身的信息,類實(shí)例數(shù)據(jù),以及指向?qū)ο蟮囊眯畔⒎謩e放在 java 的方法區(qū)和棧區(qū)以及堆區(qū)。
在題主的例子中,java加載順序是這樣的:
jvm先加載了方法區(qū)的類定義(但此時(shí)并沒有實(shí)例化這個(gè)類)
因?yàn)?public static final Direction FRONT = new Direction(); 是個(gè)靜態(tài)變量,所以這個(gè)變量也會在 jvm 第一次讀取方法區(qū)定義時(shí)被裝載進(jìn)方法區(qū)中。
同時(shí),這也意味著,在裝載這個(gè)變量的同時(shí),也在堆區(qū)實(shí)例化了這個(gè)類的實(shí)例。
注意這里面的關(guān)鍵點(diǎn),因?yàn)?FRONT 變量是靜態(tài)變量,而加載類定義只會加載一次,所以這個(gè)靜態(tài)變量也只可能加載一次。并不會像非靜態(tài)變量一樣因?yàn)檠h(huán)引用重復(fù)實(shí)例化而導(dǎo)致棧溢出。
回答2:推薦你看看R大的回答
先有Class還是先有Object?https://www.zhihu.com/questio...
回答3:說說你的理解,為什么類里面不能創(chuàng)建自己的對象?這幾個(gè)變量加上了static后就變成了類的屬性了,只會創(chuàng)建一次。
回答4:如果自己都不能創(chuàng)建自己,那其他類就更不能了。這樣的話這個(gè)類怎么實(shí)例化……
回答5:設(shè)計(jì)模式:單例模式
回答6:本質(zhì)是對java的面向?qū)ο缶幊痰牟焕斫?。看?3種設(shè)計(jì)模式你可能就會理解
回答7:構(gòu)造函數(shù)也是一個(gè)方法。
具有 private 訪問權(quán)限的方法表示私有的,只有本類可見。
所以,本類可以調(diào)用具有 private 訪問權(quán)限的構(gòu)造函數(shù)實(shí)例化一個(gè)對象。
回答8:使用內(nèi)部類的原因:每個(gè)內(nèi)部類都能獨(dú)立的繼承自一個(gè)(接口的)實(shí)現(xiàn),所以無論外部類是否已經(jīng)繼承了某個(gè)(接口的)的實(shí)現(xiàn),對內(nèi)部類都沒有影響。實(shí)際上內(nèi)部類有效的實(shí)現(xiàn)了“多重繼承”,就是說,內(nèi)部類允許繼承多個(gè)非接口類型。
我們知道內(nèi)部類自動(dòng)擁有對外部類所有成員的訪問權(quán),那么這是如何做到的嗎?當(dāng)某個(gè)外部類對象創(chuàng)建了一個(gè)內(nèi)部類對象時(shí),此內(nèi)部類對象必定會秘密的捕獲一個(gè)指向那個(gè)外部類對象的引用。然后,在你訪問外部類的成員時(shí),就是用那個(gè)引用來選擇外部類的成員。當(dāng)然這些細(xì)節(jié)是編譯器處理,并且這里的內(nèi)部類是非static的。如果一個(gè)類都不能創(chuàng)建自己的類對象,那我要你這個(gè)類何用?啊,哈哈哈哈,開玩笑咯
相關(guān)文章:
1. python - nginx 小白, nginx 啟動(dòng)后 80 端口無法訪問2. javascript - avalon使用:duplex設(shè)置select默認(rèn)option的bug3. java - 讀寫鎖中 寫鎖的降級問題4. javascript - js輸入框限定字?jǐn)?shù)問題5. 刷新頁面出現(xiàn)彈框6. python - 在sqlalchemy中獲取剛插入的數(shù)據(jù)id?7. javascript - jquery怎么給select option一個(gè)點(diǎn)擊時(shí)觸發(fā)的事件,如圖 如果選擇自定義觸發(fā)一個(gè)時(shí)間?8. javascript - vue項(xiàng)目里的package.json9. java里自定義類重載ClassLoader有什么用?10. java - C++ 編譯器選擇問題
