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