Java类和对象初始化
首先對Java 較為深層技術(shù)提幾個問題(包含class 文件格式的了解):
1.類的訪問權(quán)限在class二進(jìn)制文件中怎么體現(xiàn)的? 2.類中static 區(qū)域 怎么初始化的,時間,順序,特點是什么? 3.jvm pc 程序計數(shù)器(寄存器) 會不會出現(xiàn)OOM問題? 4.類中成員方法和成員變量有沒有最大個數(shù)限制? 5.類裝載是一次性全加載的嗎,還是動態(tài)的? 6.jvm 本地方法棧是什么? 7.類對象是什么,什么時候被創(chuàng)建的?和加載器有什么關(guān)系? 8.不同多個classloader ?在管理方法區(qū)上是怎么劃分的?不同的classloader可以在同一個jvm上加載同一個類嗎? 9.jvm 為每一個線程在堆上面配置一個緩存空間嗎?這個空間可以使用jvm配置嗎? 10.jvm 會為每一個線程配一個程序計數(shù)器嗎?類的生命周期:
在Jvm內(nèi)存管理上有一個很重要的內(nèi)存區(qū)--運(yùn)行時常量池。其中保存類加載后的符號引用,常量,字面量等信息。在常見對象的時候,首先要到這個常量池中尋找對應(yīng)類的符號引用,才能判斷這個類是否已經(jīng)加載了。
創(chuàng)建對象的流程:
到運(yùn)行池尋找對應(yīng)類的符號引用,如果沒有,那么就加載類,類在加載的時候就會在方法區(qū)創(chuàng)建類對象數(shù)據(jù),其是一切類數(shù)據(jù)(包含方法)被訪問的數(shù)據(jù)入口(類對象)。創(chuàng)建的類對象和加載此類的類加載器也是相關(guān)的,因為不同類加載器在方法區(qū)有自己不同的命名空間,那么創(chuàng)建的類數(shù)據(jù)入口也是有空間區(qū)分的,那么在運(yùn)行池中的符號引用也是包含類加載信息的。所以在方法區(qū)中加載類就是聲明類數(shù)據(jù)入口,也就是傳統(tǒng)所說的類對象。
接著才是驗證字節(jié)碼的正確性,安全性,準(zhǔn)備階段主要是在方法區(qū)分配類數(shù)據(jù)空間。解析就是在空間上創(chuàng)建引用,變量等。初始化是clinit方法完成的,對類成員進(jìn)行數(shù)據(jù)初始化賦值。
Java類的初始化:
本階段負(fù)責(zé)為類變量賦正確的初始值。(類變量即靜態(tài)變量)
Java編譯器把所有的類變量初始化語句和靜態(tài)初始化器通通收集到<clinit>方法中,該方法只能被JVM調(diào)用,專門承擔(dān)初始化工作。
初始化一個類必須保證其直接超類已被初始化。
并非所有類都擁有<clinit>()方法。以下類不會擁有<clinit>方法:
Java類初始化的時機(jī):
規(guī)范定義類的初始化時機(jī)為“initialize on first active use”,即“在首次主動使用時初始化”。裝載和鏈接在初始化之前就要完成。
首次主動使用的情形:
除了以上6種情形,java中類的其他使用方式都是被動使用,不會導(dǎo)致類的初始化。
注意:類對象的初始化clinit調(diào)用不是加載器自我觸發(fā)的,而是被動調(diào)用的時候初始化的!!!!!如果沒有任何的類成員變量,或者成員初始化語句,那么也就沒有clinit調(diào)用,那么也就沒有類對象。類被加載后,會在字符常量池中創(chuàng)建類信息,那么創(chuàng)建對象的時候,首先要到字符常量池中尋找確認(rèn)有沒有這個類已經(jīng)被加載了,否在類加載器需要去加載該類。
Class對象 -- 訪問一切類數(shù)據(jù)(包含方法)的入口
類型信息在運(yùn)行時如何表示(RTTI),由Class對象的特殊對象完成,Class對象包含與類有關(guān)的信息。類作為程序中的一部分,每個類都有一個Class對象(即,每當(dāng)編寫且編譯了一個新類,就會產(chǎn)生一個Class對象,這個對象被保存在一個同名的.class文件中,Class對象也是類的唯一標(biāo)識(如果你了解class二進(jìn)制文件格式,那么在utf-8字符常量池中包含類信息)
Class 沒有公共構(gòu)造方法。Class 對象是在加載類時由 Java 虛擬機(jī)以及通過調(diào)用類加載器中的 defineClass 方法自動構(gòu)造的,因此不能顯式地聲明一個Class對象。?
虛擬機(jī)為每種類型管理一個獨一無二的Class對象。也就是說,每個類(型)都有一個Class對象。運(yùn)行程序時,Java虛擬機(jī)(JVM)首先檢查是否所要加載的類對應(yīng)的Class對象(被映射存儲在常量池中)是否已經(jīng)加載。如果沒有加載,JVM就會根據(jù)類名查找.class文件,并將其Class對象載入。一般某個類的Class對象被載入內(nèi)存,它就用來創(chuàng)建這個類的所有對象。
Java對象初始化:
編譯器為每個類生成至少一個實例初始化方法,即<init>()方法。此方法與源程序里的每個構(gòu)造方法對應(yīng)。如果類沒有聲明構(gòu)造方法,則生成一個默認(rèn)構(gòu)造方法,該方法僅調(diào)用父類的默認(rèn)構(gòu)造方法,同時生成與該默認(rèn)構(gòu)造方法對應(yīng)的<init>()方法。
<init>()方法內(nèi)容大概為:
Java對象初始化的時機(jī):
對象初始化又稱為對象實例化。Java對象在其被創(chuàng)建時初始化。有兩種方式創(chuàng)建Java對象:
一種是顯示對象創(chuàng)建,通過new關(guān)鍵字來調(diào)用一個類的構(gòu)造函數(shù),通過構(gòu)造函數(shù)創(chuàng)建一個對象。
一種是隱式對象創(chuàng)建:
?
?
對象實例初始化的例子:
public class Base {Base() {preProcess();}void preProcess() {} } public class Derived extends Base {public String whenAmISet = "set when declared";@Override void preProcess(){whenAmISet = "set in preProcess()";} } public class Main {public static void main(String[] args){Derived d = new Derived();System.out.println( d.whenAmISet );} }下面是整個的運(yùn)行流程:
可以看到在執(zhí)行完父類的構(gòu)造函數(shù)后,第6步才對Derived類的成員變量初始化。
Java類初始化例子:
import java.util.Map; import java.util.HashMap; import java.util.Collections; public class Singleton {public static Singleton singleton = new Singleton();public static Map m;static{m = new HashMap(); }private Singleton(){initM();}public static void initM(){if(null == m){System.out.println("m 為空");m = new HashMap();}m.put("1", "鄭");m.put("2", "陳");}public static Singleton getInstance(){return singleton;} } public class Main {public static void main(String [] args){Singleton singleton = Singleton.getInstance();} }運(yùn)行結(jié)果輸出m 為空
是因為在類初始化階段先對singleton賦值調(diào)用Singleton類構(gòu)造函數(shù),然后Singleton類構(gòu)造函數(shù)調(diào)用initM()方法。但是此時還沒有運(yùn)行static方法,所以m=null.
總結(jié)
以上是生活随笔為你收集整理的Java类和对象初始化的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 《DNF》尼尔狙击详解及狙击兵装备搭配分
- 下一篇: java美元兑换,(Java实现) 美元