java創建一個對象時,內存中發生了什麽
JAVA是一種面向對象的語言,它本身具有面向對象的三大特性--封裝,繼承,多態。開發時,我們要記住,屬性是用於存儲數據的。直接被訪問,容易出現安全隱患。所以,類中的屬性通常被私有化,並對外提供公共的訪問方法。這個方法一般有兩個,規範寫法:對於屬性xx,可以使用setXX(),getXX()對其進行操作。
一、類的成員變量存在於堆內存中,隨著對象的產生而存在,消失而消失。局部變量存在於棧內存中,隨著所屬區域的運行而存在,結束而釋放。(著重理解)
二、 創建一個對象都在內存中做了什麽事情?Person p = new Person();
1.先將硬盤上指定位置的Person.class文件加載進內存。
2.執行main方法時,在棧內存中開辟了main方法的空間(壓棧-進棧),然後在main方法的棧區分配了一個變量p。
3.在堆內存中開辟一個實體空間,分配了一個內存首地址值。new
4.在該實體空間中進行屬性的空間分配,並進行了默認初始化。
5.對空間中的屬性進行顯示初始化。
6.進行實體的構造代碼塊初始化。
7.調用該實體對應的構造函數,進行構造 函數初始化。
8.將首地址賦值給p,p變量就引用了該實體。(指向了該對象)
三、this和static詳解
this:代表對象。就是所在函數所屬對象的引用。哪個對象調用了this所在的函數,this就代表哪個對象。就是哪個對象的引用。在定義功能時,如果該功能內部使用到了調用該功能的對象,這時就用this來表示這個對象。this還可以用於構造函數間的調用。調用格式:this(實際參數);
this對象後面跟上.調用的是成員屬性和成員方法(一般方法);
this對象後面跟上()調用的是本類中的對應參數的構造函數。
註意:用this調用構造函數,必須定義在構造函數的第一行。因為構造函數是用於初始化的,所以初始化動作一定要執行。否則編譯失敗。
static:關鍵字是一個修飾符。用於修飾成員(成員變量 和成員函數)。
特點:
1.想要實現對象中的共性數據的對象共享。可以講這個數據進行靜態修飾。
2.被靜態修飾的成員,可以直接被類名所調用。也就是說,靜態的成員多了一種調用方式。類名.靜態方式。
3.靜態隨著類的加載而加載。而且優先於對象存在。
弊端:
1.有些數據是對象特有的數據,是不可以被靜態修飾的。因為那樣的話,特有數據會變成對象的共享數據。這樣對事物的描述就出了問題。所以,在定義靜態時,必須要明確,這個數據是否是被對象所共享的。
2.靜態方法只能訪問靜態成員,不可以訪問非靜態成員。
因為靜態方法加載時,優先於對象存在,所以沒有辦法訪問對象中的成員。
3.靜態方法中不能使用this,super關鍵字。
因為this代表對象,而靜態在時,有可能沒有對象,所以this無法使用。
4.主函數是靜態的。
什麽時候定義靜態成員呢?
成員分兩種:
1.成員變量。(數據共享時靜態化)
該成員變量的數據是否是所有對象都一樣;
如果是,那麽該變量需要被靜態修飾,因為是共享數據。如果不是,那麽就說這是對象的特有數據,要存儲到對象中。
2.成員函數。(方法中沒有調用特有數據時就定義成靜態)
如何判斷成員函數是否需要被靜態修飾呢?
只要參考,該函數內是否訪問了對象中的特有數據。如果有訪問特有數據,那方法不能被靜態修飾。如果沒有訪問過特有數據,那麽這個方法需要被靜態修飾。
靜態的生命周期很長。靜態代碼塊就是一個有靜態關鍵字標示的一個代碼區域。定義在類中。完成類的初始化。靜態代碼塊隨著類的加載而執行,而且只執行一次(new多個對象就只執行一次)。如果和主函數在同一個類中,優先於主函數執行。
主函數的解釋:保證所在類的獨立運行。是程序的入口。被JVM調用。
靜態代碼塊、構造代碼塊、構造函數同時存在時的執行順序:
靜態代碼塊--構造代碼塊--構造函數。
四、再談繼承
繼承是一種連結類的層次模型,並且允許和鼓勵類的重用,它提供了一種明確表述共性的方法。當生成子類對象時,JAVA默認首先調用父類的不帶參構造方法,然後執行該構造方法,生成父類的對象。接下來,再去調用子類的構造方法,生成子類的對象。(要想生成子類的對象,首先需要生成父類的對象,沒有父類對象就沒有子類對象。)
如果子類使用super()顯式調用父類的某個構造方法,那麽在執行的時候就會尋找與super()所對應的構造方法而不會再去尋找父類的不帶參數的構造方法。與this一樣,super也必須要作為構造方法的第一條執行語句,前面不能有其他可執行語句。
當兩個方法 形成重寫關系時,可以在子類方法中通過super.run()形式調用父類的run()方法,其中super.run()不必放在第一行語句,因此此時父類對象已經構造完畢,先調用父類的run()方法還是先調用子類的run()方法是根據程序的邏輯決定的。
方法的覆蓋(重寫)
重寫的要求:子類覆蓋方法和父類被覆蓋方法的方法返回類型,方法名稱,參數列表必須相同;子類覆蓋方法的訪問權限必須大於等於父類的方法的訪問權限;方法覆蓋只能存在於子類和父類之間;子類覆蓋方法不能比父類被覆蓋方法拋出更多異常;
方法重寫與方法重載之間的關系:重載發生在同一個類內部的兩個或多個方法。重寫發生在父類與子類之間。
final關鍵字在繼承中的使用,final可以用於以下四個地方:
1.定義變量,包括靜態的和非靜態的。
如果final修飾的是一個基本類型,就表示這個變量被賦予的值是不可變的,即它是個常量;如果final修飾的是一個對象,就表示這個變量被賦予的引用是不可變的,不可改變的只是這個變量所保存的引用,並不是這個引用所指向的對象,其實更貼切的表述final的含義的描述,那就是,如果一個變量或方法參數被final修飾,就表示它只能被賦值一次,但是Java虛擬機為變量設定的默認值不記作一次賦值。
被final修飾的變量必須被初始化。初始化的方式 有以下幾種:
1.在定義的時候初始化
2.在初始化塊中初始化
3.在類的構造器中初始化
4.靜態變量也可以再靜態初始化塊中初始化。
1>定義方法。
當final用來定義一個方法時,它表示這個方法不可以被子類重寫,但是它這不影響它被子類繼承。
2>定義類
由於final類不允許被繼承,編譯器在處理時把它所有的方法當作final的,因此final類比普通類擁有更高的效率。
關於繼承的幾點註意:
a.父類有的,子類也有
b.父類沒有的,子類可以增加
c.父類有的,子類可以改變
d.構造方法不能被繼承
e.方法和屬性可以被繼承
f.子類的構造方法隱式的調用父類的不帶參數的構造方法
g.當父類沒有不帶參數的構造方法時,子類需要使用super來顯式地調用父類的構造方法,super指的是對父類的引用。
h.super關鍵字必須是構造方法中的第一行語句。
五、多態
多態的意思就是父類的引用可以指向子類的對象。在一個類中,可以定義多個同名的方法,只要確定它們的參數個數和類型不同,這種現象稱為類的多態。類的多態體現在兩個方面:一是方法的重載上,包括成員方法和構造方法的重載;二是在繼承過程中,方法的重寫。
六、抽象
使用abstract關鍵字所修飾的類叫做抽象類。抽象方法需要定義在抽象類中。
七、接口
JAVA語言不支持一個類有多個直接的父類,但現實例子中,又有很多類似於多繼承的例子。JAVA中不可以多繼承,但可以實現(implements)多個接口,間接地實現了多繼承。
八、比較抽象類與接口
相同點:
1.代表系統的抽象層,當一個系統使用一顆繼承樹上的類時,應該盡量把引用變量聲明為 繼承樹的上層抽象類型,這樣可以提高兩個系統之間的送耦合。
2.都不能被實例化
3.都包含抽象方法,這些抽象方法用於描述系統能提供哪些服務,但不提供具體的實現。
不同點:
1. 在抽象類中可以為部分方法提供默認的實現,從而避免在子類中重復實現它們,這是抽象類的優勢,但這一優勢限制了多繼承,而接口中只能包含抽象方法.由於在抽象類中允許加入具體方法,因此擴展抽象類的功能,即向抽象類中添加具體方法,不會對它的子類造成影響,而對於接口,一旦接口被公布,就必須非常穩定,因為隨意在接口中添加抽象方法,會影響到所有的實現,這些實現類要麽實現新增的抽象方法,要麽聲明為抽象類。
2.一個類只能繼承一個直接的父類,這個父類可能是抽象類,但一個類可以實現多個接口,這是接口的優勢,但這一優勢是以不允許為任何方法提供實現為代價的。當子類覆蓋父類的實例方法或隱藏父類的成員變量及靜態方法時,JAVA虛擬機采用不同的綁定規則, 假如還允許一個類有多個直接的父類,那麽會使綁定規則更加復雜。因此,為了簡化系統結構設計和動態綁定機制,JAVA語言禁止多重繼承。一個類即時有多個接口,也不會增加JAVA虛擬機進行動態綁定的復雜度,因為JAVA虛擬機永遠不會把方法與接口綁定,而只會把方法與它的實現類綁定,使用接口和抽象類的總體原則。
java創建一個對象時,內存中發生了什麽