国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁(yè) > 學(xué)院 > 開發(fā)設(shè)計(jì) > 正文

Java 對(duì)象及其內(nèi)存控制

2019-11-14 22:00:49
字體:
來(lái)源:轉(zhuǎn)載
供稿:網(wǎng)友
java 對(duì)象及其內(nèi)存控制

作者:禪樓望月(http://m.survivalescaperooms.com/yaoyinglong)

更新:其實(shí)這里有好多的變戲法,只要你理解了他們?cè)贘VM的中的實(shí)現(xiàn)機(jī)制,就豁然開朗了。有時(shí)間我會(huì)把這些變戲法的東西說(shuō)明的。


Java 向程序員許下了美好的承諾:無(wú)需關(guān)心內(nèi)存的回收,Java提供了優(yōu)秀的垃圾回收機(jī)制來(lái)回收已經(jīng)分配的內(nèi)存。

所以初學(xué)者往往會(huì)肆無(wú)忌憚的揮霍Java內(nèi)存,從而導(dǎo)致Java程序的運(yùn)行效率下降,主要壞處為:

  1. 不斷分配內(nèi)存使得系統(tǒng)中可用內(nèi)存減少,從而降低程序運(yùn)行性能;
  2. (更重要的)大量已分配內(nèi)存的回收使得垃圾回收的負(fù)擔(dān)加重,降低程序的運(yùn)行性能。
1 前向應(yīng)用

這說(shuō)明Java中定義實(shí)例成員變量時(shí),必須采用合法的前向引用。同樣兩個(gè)類成員變量也必須采用合法的前向引用

但是,如果一個(gè)是實(shí)例成員變量,一個(gè)是類成員變量,則實(shí)例成員變量總是可以引用類成員變量:

這是因?yàn)椋?strong>類成員變量初始化時(shí)機(jī)總是在實(shí)例成員變量初始化時(shí)機(jī)之前,確切的說(shuō)是在類加載時(shí)進(jìn)行的。

2、靜態(tài)成員可實(shí)例成員

使用static修飾的靜態(tài)變量屬于類本身,而實(shí)例變量數(shù)據(jù)類的實(shí)例。在同一JVM中,每個(gè)類只對(duì)應(yīng)一個(gè)Class對(duì)象,因此同一JVM內(nèi)的一個(gè)類的類變量只需一塊內(nèi)存空間,但每個(gè)類可以創(chuàng)建多個(gè)Java對(duì)象,因此JVM必須為每個(gè)Java對(duì)象的實(shí)例變量分配一塊內(nèi)存空間。

Java允許通過類來(lái)訪問類成員變量,也允許類實(shí)例訪問類成員變量,(Java這樣設(shè)計(jì)是不合理的)

但是java設(shè)計(jì)者,卻在類實(shí)例訪問類成員變量時(shí),底層依然轉(zhuǎn)換為類來(lái)訪問類成員變量。怎么證明呢?

通過反編譯來(lái)看看:

反編譯查看靜態(tài)成員的調(diào)用

JVM在底層使用someTh對(duì)象所對(duì)應(yīng)的引用類型來(lái)調(diào)用靜態(tài)成員,這就給程序員造成了一定的錯(cuò)覺,以為調(diào)用的是自己對(duì)象的東西,但是改變靜態(tài)成員的值,在其他的對(duì)象的中會(huì)體現(xiàn)出來(lái),這個(gè)很危險(xiǎn):

靜態(tài)成員被修改了

在一個(gè)類實(shí)例中修改了類成員變量的值,在另一個(gè)類實(shí)例中卻體現(xiàn)出來(lái)了。

3 實(shí)例變量的初始化時(shí)機(jī)

從語(yǔ)法角度看,我們可以在如下3個(gè)地方對(duì)實(shí)例變量執(zhí)行初始化:①定義實(shí)例變量是指定初始值②非靜態(tài)初始化塊中對(duì)實(shí)例變量指定初始值③構(gòu)造器中對(duì)實(shí)例變量指定初始值。其中①和②比③更早執(zhí)行,①和②那個(gè)更早執(zhí)行,就看那個(gè)在代碼中出現(xiàn)的更早。如:

實(shí)例變量初始化時(shí)機(jī)

由此可見類實(shí)例變量只能放在構(gòu)造器中初始化,但是作為程序員編程時(shí),可以放在定義處,也可以放在非靜態(tài)塊中,但是結(jié)果都是一樣的,JVM會(huì)把它們抽取出來(lái)放在構(gòu)造函數(shù)中。

4 類變量初始化時(shí)機(jī)

從語(yǔ)法角度看,可以在如下兩個(gè)地方對(duì)類變量初始化:①定義類變量時(shí)初始化;②靜態(tài)初始化塊中對(duì)類變量指定初始值。這兩種方式的執(zhí)行順序和它們?cè)谠创a中出現(xiàn)的順序相同:

由此可見,類變量只能在靜態(tài)塊中被初始化,但是作為程序員編程來(lái)說(shuō),可以放在定義處也可以放在靜態(tài)快中,結(jié)果都是一樣的,JVM會(huì)把它們收取出來(lái)都放進(jìn)靜態(tài)快中。

5 父類構(gòu)造器

看如下代碼:

這里便引發(fā)了一個(gè)疑問:在這里Sub類還沒有被創(chuàng)建(因?yàn)檎{(diào)用display的時(shí)候父類的構(gòu)造函數(shù)還沒有走完,怎么會(huì)走子類的構(gòu)造函數(shù)),怎么能調(diào)用它的方法呢?誒!難道類實(shí)例不是由構(gòu)造器創(chuàng)建的嗎? 很多書籍中都是這樣說(shuō)的:類實(shí)例是由構(gòu)造器創(chuàng)建。 其實(shí),這句話是完全錯(cuò)誤的。實(shí)際的情況是構(gòu)造器只是負(fù)責(zé)對(duì)Java對(duì)象實(shí)例變量執(zhí)行初始化(即賦初始值),在執(zhí)行構(gòu)造器代碼之前,該對(duì)象所占的內(nèi)存已經(jīng)被分配下來(lái)了。這些內(nèi)存里的值都是各個(gè)類型的默認(rèn)值。 所以上面代碼在執(zhí)行new Sub();的時(shí)候系統(tǒng)已經(jīng)為Sub對(duì)象分配了內(nèi)存空間(兩塊內(nèi)存空間,一塊用于存放Sub的i另一塊用于存放Base的i(這一塊內(nèi)存,子類和父類共用,改變?nèi)魏我粋€(gè)另一個(gè)會(huì)跟著動(dòng)),原因是子類不能完全覆蓋父類的成員變量)注意: 對(duì)象是由new關(guān)鍵字創(chuàng)建的,在執(zhí)行new……的時(shí)候,一個(gè)Java對(duì)象已經(jīng)建成了,只是它的變量還沒有初始化,構(gòu)造函數(shù)的功能就是對(duì)這些變量進(jìn)行初始化。沒有運(yùn)行完構(gòu)造函數(shù)Java對(duì)象的方法是可以被調(diào)用的,因?yàn)樗鸵话鉐ava對(duì)象沒有任何的區(qū)別。再來(lái)看一段代碼:

是否會(huì)感覺到this指代有點(diǎn)混亂呢? 但是從打印出來(lái)的結(jié)果來(lái)看,this確實(shí)指代的是Sub,但是我們也知道,當(dāng)this在構(gòu)造器中this指的是正在被初始化Java對(duì)象。怎么理解呢?從源代碼看,此時(shí)this位于Base構(gòu)造器中,但是這些代碼實(shí)際放在Sub()構(gòu)造器內(nèi)執(zhí)行,是Sub()構(gòu)造器隱式調(diào)用了Base()構(gòu)造器的代碼。由此可見,this指的是Sub而不是Base。現(xiàn)在問題又來(lái)了,既然this指的是Sub,那么,為什么System.out.6 父子實(shí)例的內(nèi)存控制

由上圖可知:

1、變量d2b和d實(shí)際指向同一個(gè)對(duì)象,但是訪問他們的實(shí)例變量時(shí)卻輸出不同的值,這表明d2b和d變量所指向的java對(duì)象中包含了兩塊內(nèi)存,更別存放著值為2的count實(shí)例變量和值為20的count實(shí)例變量。2、不管d、db、d2b,只要它們指向一個(gè)Sub對(duì)象,不管聲明它們使用什么類型,當(dāng)通過這些變量來(lái)調(diào)用時(shí),方法的行為總是表現(xiàn)出它們實(shí)際類型的行為。但如果通過這些變量來(lái)訪問它們所指對(duì)象的實(shí)例變量,這些實(shí)例變量的值總是表現(xiàn)出聲明這些變量所用的類型的行為。由此可見Java繼承在處理成員變量和方法時(shí),是有區(qū)別的。

但是,還是可以通過super來(lái)調(diào)用父類中被覆蓋的方法。我們?cè)賮?lái)看一下這段代碼:

//父類public class Base {      private int x=10;      public int getX() {          return x;        }      public void setX(int x) {          this.x = x;      }  }
//子類public class Sub extends Base {      public Sub() {          this.setX(20);          }  }
//測(cè)試  public static void main(String[] args) {          Base b=new Base();          System.out.println("我是父類:"+b.getX());       //-->10        Base base=new Sub();          System.out.println("我是父類:"+base.getX());    //-->20        Sub s=new Sub();          System.out.println("我是子類:"+s.getX());       //-->20        System.out.println("我是父類:"+base.getX());    //-->20    }

用javap工具查看:

由此可見子類繼承了父類的實(shí)例變量,內(nèi)存中值為父類中的變量申請(qǐng)了空間,并沒有為子類中該變量開辟內(nèi)存空間。有人可能說(shuō)你這里的實(shí)例變量x是private,其實(shí)即是public也是一樣的,不信的話可以試試。 那么,我們?cè)谧宇愔姓{(diào)用setX方法其實(shí),設(shè)置的是父類中的實(shí)例變量x。因?yàn)檫@個(gè)方法是從父類繼承過來(lái)的。由此也可以得出父類中一般不要設(shè)置靜態(tài)全局變量,這樣會(huì)有線程安全的問題。

所以在子類中使用super的意思是,使用自己對(duì)象里面保存的從父類繼承下來(lái)的那個(gè)方法。 由此可見,super本身并沒有引用任何對(duì)象,它只能算作一個(gè)標(biāo)記。它的作用僅限于在子類中(不是子類的對(duì)象)調(diào)用在父類中定義的,被隱藏了的實(shí)例變量,或者在子類中定義的,被覆蓋的方法。注意:雖然說(shuō)這是父類中的方法和變量。其實(shí)和父類沒有一點(diǎn)關(guān)系了。只是在調(diào)用上有點(diǎn)區(qū)別,其他的和類自己的方法沒什么區(qū)別。

7 父子類的類變量

記住:Java允許通過實(shí)例對(duì)象來(lái)調(diào)用類的靜態(tài)變量其他的和實(shí)例變量一樣。

8 final

final修飾的變量final修飾的變量必須顯示的指定初始值(普通變量系統(tǒng)會(huì)為其設(shè)置默認(rèn)值),而且只能在以下3個(gè)地方制定初始值:

對(duì)于一個(gè)final變量而言,不管它是類變量、實(shí)例變量還是局部變量,只要該變量被final修飾,并且被賦予的初始值(必須的),那么該在類編譯的時(shí)候就被確定了,那么,這個(gè)final變量就不再是變量了,而是相當(dāng)于一個(gè)直接量。

內(nèi)部類中的局部變量如果程序需要在內(nèi)部類中使用局部變量,那么這個(gè)局部變量必須由final修飾。但是為什么內(nèi)部類中要訪問的局部變量都必須使用final修飾呢?原因是:對(duì)于普通的局部變量,它的作用于就停留在該方法內(nèi),該方法結(jié)束后該局部變量就消失了;但是內(nèi)部類則可能產(chǎn)生隱式的“閉包”,閉包將使得局部變量脫離了它所在的方法繼續(xù)存在。


發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 庆云县| 昭平县| 柳州市| 罗江县| 乌拉特后旗| 林西县| 山阴县| 杭锦后旗| 德惠市| 高碑店市| 贵阳市| 长子县| 城步| 金湖县| 临海市| 湟中县| 安阳市| 温泉县| 普格县| 监利县| 泰安市| 洪湖市| 金昌市| 乐东| 右玉县| 兴宁市| 惠水县| 阿图什市| 湘阴县| 万年县| 台湾省| 松桃| 新津县| 茂名市| 康定县| 高碑店市| 叙永县| 油尖旺区| 伊金霍洛旗| 衢州市| 公安县|