Java內部類與final關鍵字詳解

來源:互聯網
上載者:User

詳解Java內部類與final關鍵字

內部類的幾種建立方法:

1、成員內部類

class Outer{private int i = 1;class Inner{public void fun() {System.out.println("Outer I=" + i)}}}

2、方法內部類

class Outer{public void fun() {final int i = 1; // 被方法內部類訪問的局部變數必須被final修飾class Inner{ // 方法內部類 不能有存取修飾詞,比如public public void print() {System.out.println("Method I=" + i)}}}}

3、匿名內部類

interface USB {public abstract void start()}class Outer{public void fun() {final int i = 1; // 被匿名內部類訪問的局部變數必須被final修飾new USB(){@Overridepublic void start(){System.out.println("local_var_i=" + i);}}.start();}}

4、靜態內部類

class Outer{private int i = 1;static class Inner{ // 不能訪問外部類的非靜態成員public void fun() {}}}

5、介面內部類

interface USB {class Inner { // 預設是public static,即可以直接new USB.Inner();}}

神馬是內部類?

乍一看,好些建立方式,挺複雜的吧?首先內部類是個啥東西?

定義:建立在一個類內部的類型。根據建立位置的不同,分為成員的、方法的、匿名的。介面中的內部類叫做介面內部類。

理解:在類的內部建立,那就是類的一部分,跟屬性、方法同級。這也就解釋了為何能訪問外部私人成員,我是你的一部分,我當然可以訪問了。

問題的引出<理解內部類>:

那問題來了,我是你的一部分,別人繼承了外部類,會不會也把內部類也繼承過去呢?這得從內部類設計的初衷與物件導向來探討了。

比如我們用Body類來描述人體。如果是描述人類的話,會去描述人類具有的屬性跟功能。

那現在我們描述人體,人體內部有心肝脾胃腎,這些再用屬性來描述就不合適了吧?那怎麼辦呢?我們可以用內部類去描述。

所以,無論內部類是公開的還是私人的,都不會被繼承,因為他不是屬性,也不是方法。而是一個內部事務的描述,我們稱之為內部類。

內部類可以更好地對內部事務進行封裝,看例子:

身體類中,有屬於身體的各個器官,各個器官有自己的功能與屬性,於是我們把它封裝成一個內部類去單獨描述。

用來描述器官的內部類是身體的一部分,所以可以去自由的訪問身體的資源(屬性與功能),各個器官與身體相互協調完成運作。

public class Body {private class Heart { // 心臟public void bloodSupply() {// 供血...}}private class Hepar { // 肝臟public void Metabolism() {// 代謝...}}private class Spleen { // 脾臟public void storageBlood() {// 儲血...}}private class Stomach { //胃部public void digest() {// 消化...}}private class Kidney { // 腎臟public void dischargePoison() {// 排毒...}}}

問題的引出<內部類訪問外部類成員方式>:

因為可以訪問外部私人成員,那問題也就誕生了,他是怎麼訪問的呢?

1:對於成員內部類來說,他會持有一份外部類當前對象的引用,Outer.this。

然後內部類在使用外部類的欄位時不是直接取值,而是通過編譯器在外部類中產生的靜態access$0()方法來取值。

調用方法是通過持有的外部類當前對象的引用去調用的。Outer.this.fun();


2:對於方法內部類(匿名內部類)來說,因為內部類要訪問所在方法中的局部變數,這時候用持有的外部類當前對象引用還能調用的到嗎?

調不到了,那Java語言的設計者是這麼來解決這個問題的:將局部變數複製一份給內部類使用,怎麼複製的?在內部類初始化的時候通過構造方法傳值的方式。

這樣,內部類中會有一份複製的private修飾的成員變數。這樣我就能訪問了。但問題又來了,比如:

public void fun() {int i = 1;class Inner{// int i = 1; 由編譯器產生public void print() { i++ }}System.out.println("i=" + i); // 還是會輸出i=1,我們明明在內部類方法中對此變數進行++了啊。抱歉,您++的是被複製的另一份。}

問題的引出<保持兩個不同變數的一致性>:

好,問題就這麼無情的出現了,怎麼解決?Java又說了,要不然給局部變數加上final吧,這樣就會保持值得一致性了。

ok,問題解決,這就是為什麼方法內部類訪問的局部變數必須被final修飾的終極原因(為了約束兩個不同變數的一致性)。設計問題,挺無奈的解決方案。

來看看另一種說法:

在Java中,方法的局部變數位於棧上,對象位於堆上。

因為局部變數的範圍被限制在該方法內,當一個方法結束時,棧結構被刪除,該變數消失。

但是,定義在這個類中的內部類對象仍然存活在堆上,所以內部類對象不能使用局部變數。除非這些局部變數被標識為最終的。 

這種說法是片面的,因為根本原因是內部類對象無法訪問局部變數,才會去複製一份。

為了保證兩個變數的一致性,才去使用final關鍵字修飾局部變數。而不是因為棧生命週期與堆生命週期不一致的問題。

最後來說說final關鍵字:

用final關鍵字修飾物件變數,只是不允許這個對象引用再指向其他的對象,但是這個引用所指向的對象的內容是可以改變的

比如一個經典的例子:

final StringBuffer sb = new StringBuffer("HelloWorld");sb = new StringBuffer("Hello"); // 編譯失敗,不能修改sb引用的指向sb.append("China"); // sb指向的對象可以被修改。

其實從final的位置就看出來了,他是在修飾參考型別變數sb,而不是在修飾堆中的執行個體對象(new StringBuffer("HelloWorld");)

原創文章,轉載請註明出處:http://blog.csdn.net/thinging_in_android

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.