Java對(duì)象與內(nèi)存管理
1.實(shí)例變量和類(lèi)變量的內(nèi)存分配
類(lèi)變量 :使用static修飾的成員變量是類(lèi)變量,屬于該類(lèi)本身
實(shí)例變量:沒(méi)有使用static修飾的成員變量是實(shí)例變量,屬于該類(lèi)的實(shí)例
由于同一個(gè)JVM內(nèi)每個(gè)累只對(duì)應(yīng)一個(gè)Class對(duì)象,因此同一個(gè)JVM內(nèi)的一個(gè)類(lèi)的類(lèi)變量只需一塊內(nèi)存空間。
對(duì)于實(shí)例變量而言,該類(lèi)沒(méi)創(chuàng)建一次實(shí)例,就需要為實(shí)例變量分配一塊內(nèi)存空間,所以,程序中有幾個(gè)實(shí)例,實(shí)例變量就需要幾塊內(nèi)存空間。
2.類(lèi)變量的初始化時(shí)機(jī)總是出于實(shí)例變量的初始化之前
我們先看下下面三段代碼:
1)因?yàn)閮蓚€(gè)實(shí)例變量都是在創(chuàng)建變量的時(shí)候才開(kāi)始分配空間,此時(shí)num2還沒(méi)有分配,所以前向引用就會(huì)出現(xiàn)編譯錯(cuò)誤。
- int num = num2 + 3; //非法前向引用,會(huì)報(bào)錯(cuò)
- int num2 = 2
2)因?yàn)閮蓚€(gè)類(lèi)變量在JVM加載類(lèi)的時(shí)候分配空間,此時(shí)num2還沒(méi)有分配,所以前向引用就出現(xiàn)變異錯(cuò)誤。
- static int num = num2 + 3; //非法前向引用,會(huì)報(bào)錯(cuò)
- tatic int num2 = 2
3)因?yàn)轭?lèi)變量num2在JVM加載類(lèi)的時(shí)候空間已經(jīng)分配好,而num在創(chuàng)建實(shí)例的時(shí)候踩分配空間,此時(shí)num2已經(jīng)分配成功了,所以num前向引用成功。
- int num = num2 + 3; //正確使用
- static int num2 = 2;
由上面三段代碼塊就可以驗(yàn)證得:類(lèi)變量的初始化時(shí)機(jī)總是出于實(shí)例變量的初始化之前
3.Java對(duì)象的初始化方式及其執(zhí)行順序
Java對(duì)象的初始化方式有三種:1)構(gòu)造器 2)初始化塊 3)定義變量時(shí)指定初始化值
如果這三種初始化方式同時(shí)出現(xiàn),也要注意,他們也有一個(gè)執(zhí)行順序的規(guī)定:
1)靜態(tài)初始化塊只在類(lèi)第一次創(chuàng)建對(duì)象的時(shí)候運(yùn)行一次,后面就不會(huì)再運(yùn)行,而類(lèi)在每次創(chuàng)建對(duì)象時(shí),非靜態(tài)初始化塊總是會(huì)運(yùn)行一次。
- public class Test{
- static {
- System.out.println("執(zhí)行---靜態(tài)初始化代碼塊.");
- }
- {
- System.out.println("執(zhí)行---非靜態(tài)初始化代碼塊.");
- }
- public static void main(String[] args) {
- for (int i = 1; i <= 2; i++) {
- System.out.println("創(chuàng)建第 " + i + " 個(gè)對(duì)象");
- new Test();
- System.out.println();
- }
- }
- }
運(yùn)行結(jié)果:
2)構(gòu)造器每次創(chuàng)建對(duì)象時(shí),構(gòu)造器必然有執(zhí)行的機(jī)會(huì),此時(shí),非靜態(tài)初始化塊必定也將獲得機(jī)會(huì)并且運(yùn)行在構(gòu)造器之前
- public class Test{
- {
- System.out.println("執(zhí)行---非靜態(tài)初始化代碼塊.");
- }
- public Test() {
- System.out.println("執(zhí)行---構(gòu)造器.");
- }
- public static void main(String[] args) {
- for (int i = 1; i <= 2; i++) {
- System.out.println("創(chuàng)建第 " + i + " 個(gè)對(duì)象");
- new Test();
- System.out.println();
- }
- }
- }
運(yùn)行結(jié)果:
3)定義變量時(shí)指定的初始化值和初始化塊中指定的初始值的執(zhí)行順序與他們?cè)谠闯绦蛑械呐帕许樞蛳嗤?/p>
驗(yàn)證代碼一:
- public class Test{
- String i = "定義變量時(shí)指定的初始化值";
- {
- i = "初始化塊中指定的初始值";
- }
- public static void main(String[] args) {
- for (int i = 1; i <= 2; i++) {
- System.out.println("創(chuàng)建第 " + i + " 個(gè)對(duì)象");
- System.out.println(new Test().i);
- System.out.println();
- }
- }
- }
運(yùn)行結(jié)果
驗(yàn)證代碼二 :
- public class Test{
- {
- i = "初始化塊中指定的初始值";
- }
- String i = "定義變量時(shí)指定的初始化值";
- public static void main(String[] args) {
- for (int i = 1; i <= 2; i++) {
- System.out.println("創(chuàng)建第 " + i + " 個(gè)對(duì)象");
- System.out.println(new Test().i);
- System.out.println();
- }
- }
- }
運(yùn)行結(jié)果:
4.關(guān)于父子實(shí)例的內(nèi)存控制
(一般情況下是不用內(nèi)部類(lèi)來(lái)驗(yàn)證的,但是都是一樣的啦,我偷懶下,所以使用了內(nèi)部類(lèi),大家原諒哈)
1)當(dāng)子類(lèi)重寫(xiě)父類(lèi)方法后,父類(lèi)表面上只是調(diào)用屬于自己的被子類(lèi)重寫(xiě)的方法。
- public class Test{
- class Base {
- Base() {
- this.info();
- }
- public void info() {
- System.out.println("Base");
- }
- public void getInfo() {
- info();
- }
- }
- public class Child extends Base{
- @Override
- public void info() {
- System.out.println("Child");
- }
- }
- public static void main(String[] args) {
- Test test = new Test();
- Base base = test.new Child();
- base.info();
- base.getInfo();
- }
- }
運(yùn)行結(jié)果:
2)上述是屬于多態(tài)中方法的體現(xiàn),但是方法有多態(tài),實(shí)例變量無(wú)多態(tài)。
解釋下“方法有多態(tài),變量無(wú)多態(tài)”這句話:意思是,不管怎樣,父類(lèi)表面上只是調(diào)用屬于自己的被子類(lèi)重寫(xiě)的方法。而變量不一樣,假設(shè)父類(lèi)和子類(lèi)都有同一個(gè)變量名的實(shí)例變量,向上轉(zhuǎn)型后,通過(guò)父類(lèi)訪問(wèn)的實(shí)例變量得到的值是自身的而非子類(lèi)的。向下轉(zhuǎn)型后,通過(guò)子類(lèi)訪問(wèn)的實(shí)例變量得到的值是自身的而非父類(lèi)的。
很多書(shū)上或教學(xué)視頻上都講,創(chuàng)建一個(gè)子類(lèi)對(duì)象的時(shí)候,Java 會(huì)順著繼承結(jié)構(gòu)往上一直找到 Object,然后從 Object 開(kāi)始往下依次執(zhí)行構(gòu)造函數(shù)。先執(zhí)行父類(lèi)的構(gòu)造函數(shù),然后在其子類(lèi)中會(huì)創(chuàng)建一個(gè)成員變量指向他的父類(lèi)。其實(shí)這個(gè)說(shuō)法是錯(cuò)誤的,系統(tǒng)并不會(huì)真正的去創(chuàng)建父類(lèi)對(duì)象,只是在子類(lèi)對(duì)象中不僅保存了本身的實(shí)例變量,還有它父類(lèi)的全部實(shí)例變量。
- public class Test{
- class Base { //父類(lèi)
- int i = 2;
- }
- public class Child extends Base{ //子類(lèi)
- int i = 20;
- }
- public static void main(String[] args) {
- Test test = new Test();
- Child child = test.new Child();
- Base base = child;
- System.out.println(" Base.i : " + base.i);
- System.out.println("Child.i : " + child.i);
- }
- }
運(yùn)行結(jié)果:
5.final修飾符
final變來(lái)那個(gè)在編譯時(shí)就被確定下來(lái)了,相當(dāng)于一個(gè)直接量。
1)final修飾的實(shí)例變量賦值時(shí)機(jī):
定義final實(shí)例變量時(shí) 指定初始值
在非靜態(tài)初始化模塊中為final實(shí)例變量指定的初始值
在構(gòu)造器中為final實(shí)例變量指定初始值
2)final修飾的類(lèi)變量賦值時(shí)機(jī):
定義final類(lèi)變量時(shí)指定初始值
在靜態(tài)初始化模塊中為final實(shí)例變量指定的初始值
原文鏈接:http://www.cnblogs.com/xiaoxuetu/archive/2013/03/27/2985616.html
【編輯推薦】