1. 程式人生 > >瘋狂Java講義之內部類(二)

瘋狂Java講義之內部類(二)

        大部分時候,我們把類定義成一個獨立的程式單元。在某些情況下,我們把一個類在另一個類的內部定義,這個定義在其他類內部的類就被稱為內部類(有的地方也叫巢狀類),包含內部類的類也被稱為外部類(有的地方也叫宿主類)。Java從JDK1.1開始引入內部類,內部類主要有如下作用         a內部類提供了更好的封裝,可以把內部類隱藏在外部類之內,不允許同一個包中的其他類訪問該類。假設需要建立Cow類,Cow類需要組合一個CowLeg物件,CowLeg類只有在Cow類裡才有效,離開了Cow類之後沒有任何意義。在這種情況下,就可以把CowLeg定義成Cow的內部類,不允許其他類訪問CowLeg。
        b內部類成員可以直接訪問外部類的私有資料,因為內部類被當成其外部類成員,同一個類的成員之間可以互相訪問。但外部類不能訪問內部類的實現細節,例如內部類的成員變數。         c匿名內部類適合用於建立那些僅需要一次使用的類。對於前面介紹的命令模式,當需要傳入一個COmmand物件時,重新專門定義PrintCommand和AddCommand兩個實現類可能沒有太大的意義,因為這兩個實現類可能僅需要使用一次,在這種情況下,使用匿名內部類將更方便。 非靜態內部類:         定義內部類非常簡單,只要把一個類放在另一個類內部定義即可。此處的“類內部”包括類中的任何位置,甚至在方法中也可以定義內部類(方法裡定義的內部類被稱為區域性內部類)。內部類定義語法格式如下:         public class OuterClass         {                 //此處可以定義內部類         }         大部分時候,內部類都被作為成員內部類定義,而不是作為區域性內部類。成員內部類是一種與Field、方法、構造器和初始化塊相似的類成員;區域性內部類和匿名內部類則不是類成員。
        成員內部類分為兩種靜態內部類和非靜態內部類,使用static修飾的成員內部類是靜態內部類,反則是非靜態內部類。         因為內部類作為其外部類的成員,所以可以使用任意訪問控制符如private、protected和public等修飾符。         注意:         外部類的上一級程式單元是包,所以它只有2個作用域:同一個包內和任意位置。因此只需2種訪問許可權:包訪問許可權和公開訪問許可權,正好對應省略訪問控制符和public訪問控制符。省略訪問控制符是包訪問許可權,即同一包中的其他類可以訪問省略訪問控制符的成員。因此,如果一個外部類不使用任何訪問控制符修飾,則只能被同一個包中其他類訪問。而內部類的上一級程式單元是外部類,它就具有4個作用域:同一個類、同一個包、父子類和任何位置,因此可以使用4種訪問控制權限
。         下面程式在Cow類裡定義了一個CowLeg非靜態內部類,並在CowLeg類的例項方法中直接訪問Cow的private訪問許可權的例項Field: 程式清單:        package cow;     public class Cow     {          private double weight;          public Cow() {}          public Cow(double weight)          {               this.weight = weight;          }          //定義一個非靜態內部類          private class CowLeg          {               //非靜態內部類的兩個Field               private double length;               private String color;               public CowLeg() {}               public CowLeg(double length,String color)               {                    this.length = length;                    this.color = color;               }               public void setLength(double length)               {                    this.length = length;               }               public double getLength()               {                    return this.length;               }               public void setColor(String color)               {                    this.color = color;               }               public String getColor()               {                    return this.color;               }               //非靜態內部類的例項方法               public void info()               {                    System.out.println("當前牛腿顏色是:" + color + ",高" +                     length);                    //直接訪問外部類的private修飾的Field                    System.out.println("本牛腿所在奶牛重:" + weight);  //1               }          }          public void test()          {               CowLeg cl = new CowLeg();               cl.info();          }          public static void main(String[] args)          {               Cow cow = new Cow(399);               cow.test();          }     } 執行結果:  當前牛腿顏色是:null,高0.0                     本牛腿所在奶牛重:399.0 程式分析:         編譯上面程式,看到在檔案所在路徑生成了兩個class檔案,一個是Cow.class,另一個是Cow$CowLeg.class,前者是外部類Cow的class檔案,後者是內部類CowLeg的class檔案,即成員內部類(包括靜態內部類、非靜態內部類)的class檔案總是這種形式:QuterClass$InnerClass.class。         在非靜態內部類裡可以直接訪問外部類的private成員,上面程式中1處程式碼,就是在CowLeg類的方法內直接訪問其外部類的private例項變數。這是因為在非靜態內部類物件裡,儲存了一個它寄存的外部類物件的引用(當呼叫非靜態內部類的例項方法時,必須有一個非靜態內部類例項,而非靜態內部類例項必須寄存在外部類例項裡)。下圖顯示了程式執行時的記憶體示意圖:         當在非靜態內部類的方法內訪問某個變數時,系統優先在該方法內查詢是否存在該名字的區域性變數,如果存在就使用該變數;如果不存在,則到該方法所在的內部類中查詢是否存在該名字的成員變數,如果存在則使用該成員變數;如果不存在,則到該內部類所在的外部類中查詢是否存在該名字的成員變數,如果存在則使用該成員變數;如果依然不存在,系統將出現編譯錯誤:提示找不到該變數。         因此,如果外部類成員變數、內部類成員變數與內部類裡方法的區域性變數同名,則可通過使用this、外部類類名.this作為限定類區分。如下程式所示:        package discern;     public class DiscernVar     {          private String prop = "外部類的例項變數";          private class InClass          {               private String prop = "內部類的例項變數";               public void info()               {                    String prop = "區域性變數";                    //通過外部類類名.this.varName 訪問外部類例項Field                    System.out.println("外部類的FIeld值:" +                     DiscernVar.this.prop);                    //通過this.varName訪問內部類例項的Field                    System.out.println("內部類的Field值:" + this.prop);                    //直接訪問區域性變數                    System.out.println("區域性變數的值:" + prop);               }          }          public void test()          {               InClass inClass = new InClass();               inClass.info();          }          public static void main(String[] args)          {               new DiscernVar().test();          }     } 執行結果:外部類的FIeld值:外部類的例項變數                  內部類的Field值:內部類的例項變數                  區域性變數的值:區域性變數 非靜態內部類的成員可以訪問外部類的private成員,但反過來就不成立了。非靜態內部類的成員只在非靜態內部類範圍內是可知的,並不能被外部類直接使用。如果外部類需要訪問非靜態內部類的成員,則必須顯式建立非靜態內部類物件來呼叫訪問其例項成員 下面程式示範了這個規則: 程式清單:     package outer;     public class Outer     {          private int outProp = 9;          class Inner          {               private int inProp = 5;               public void acessOuterProp()               {                    //非靜態內部類可以直接訪問外部類的成員                    System.out.println("外部類的outProp值:" +                     outProp);               }          }          public void acessInnerProp()          {               //外部類不能直接訪問非靜態內部類的例項Field               //下面程式碼編譯出錯               //System.out.println("內部類的inProp值:" + inProp);               //如需訪問內部類的例項Field,則必須顯式建立內部類物件               System.out.println("內部類的inProp值:" + new                Inner().inProp);               //通過內部類訪問外部類的成員               Inner inner = new Inner();               inner.acessOuterProp();          }          public static void main(String[] args)          {               Outer outer = new Outer();               outer.acessInnerProp();          }     } 執行結果:內部類的inProp值:5           外部類的outProp值:9 非靜態內部類物件和外部類物件的關係     非靜態內部類物件必須寄存在外部類物件裡,而外部類物件則不必一定有非靜態內部類物件寄存其中。簡單地說,如果存在一個非靜態內部類物件,則一定存在一個被它寄存的外部類物件。但外部類物件存在時,外部類物件裡不一定寄存了非靜態內部類物件。因此外部類物件訪問非靜態內部類成員時,可能非靜態普通內部類物件根本不存在!而非靜態內部類物件訪問外部類成員時,外部類物件一定存在     根據靜態成員不能訪問非靜態成員的規則,外部類的靜態方法、靜態程式碼塊不能訪問非靜態內部類,包括不能使用非靜態內部類定義變數、建立例項等。總之,不允許在外部類的靜態成員中直接使用非靜態內部類。如下程式所示: 程式清單     package staticdemo;     public class StaticDemo     {          //定義一個非靜態內部類,是個空類          private class In{}          //外部類的靜態方法          public static void main(String[] args)          {               //下面程式碼引起編譯異常,因為靜態成員(main方法)               //無法訪問非靜態成員(In類)               new In();          }     } Java不允許在非靜態內部類裡定義靜態成員。下面程式示範了非靜態內部類裡包含靜態成員將引發編譯錯誤: 程式清單     public class InnerNoStatic     {         private class InnerClass         {             /*             下面三個靜態宣告都將引發編譯錯誤:             非靜態內部類不能有靜態宣告             */             static             {                 System.out.println("======");             }             private static int inProp;             private static void test(){}         }     } 程式分析     非靜態內部類不能有靜態方法、靜態FIeld、靜態初始化塊,所以上面三個靜態宣告將引發錯誤。     注意     非靜態內部類裡不可以有靜態初始化塊,但可以包含普通初始化塊。非靜態內部類普通初始化塊的作用與外部類初始化塊的作用完全相同 靜態內部類     如果使用static來修飾一個內部類,則這個內部類就屬於外部類本身,而不屬於外部類的某個物件。因此使用static修飾的內部類被稱為類內部類,有的地方也稱為靜態內部類。 注意     static關鍵字的作用是把類的成員變成類相關,而不是例項相關,即static修飾的成員屬於整個類,而不屬於單個物件。外部類的上一級程式單元是包,所以不可使用static修飾;而內部類的上一級程式單元是外部類,使用static修飾可以將內部類變成外部類相關,而不是外部類例項相關。因此static關鍵字不可修飾外部類,但可修飾內部類 靜態內部類可以包含靜態成員,也可以包含非靜態成員。根據靜態成員不能訪問非靜態成員的規則,靜態內部類不能訪問外部類的例項成員,只能訪問外部類的類成員。即使是靜態內部類的例項方法也不能訪問外部類的例項成員,只能訪問外部類的靜態成員。下面程式演示了這條規則: 程式清單     package staticdemo;     public class StaticInnerClass     {          private int prop1 = 5;          private static int prop2 = 9;          static class StaticInClass          {               //靜態內部類裡可以包含靜態成員               private static int age;               public void accessOuterProp()               {                    //下面程式碼出現錯誤                    //靜態內部類無法訪問外部類的例項成員                    System.out.println(prop1);                    //下面程式碼正常                    System.out.println(prop2);               }          }     } 為什麼靜態內部類的例項方法也不能訪問外部類的例項屬性呢?     因為靜態內部類是外部類的類相關,而不是外部類的物件相關的。也就是說,靜態內部類物件不是寄存在外部類物件裡的,而是寄存在外部類的類本身中。當靜態內部類物件存在時,並不存在一個被它寄存的外部類物件,靜態內部類物件裡只有外部類的類引用,沒有持有外部類物件的引用。如果允許靜態內部類的例項方法訪問外部類的例項成員,但找不到被寄存的外部類物件,這將引起錯誤

相關推薦

瘋狂Java講義之內部類

        大部分時候,我們把類定義成一個獨立的程式單元。在某些情況下,我們把一個類在另一個類的內部定義,這個定義在其他類內部的類就被稱為內部類(有的地方也叫巢狀類),包含內部類的類也被稱為外部類(有的地方也叫宿主類)。Java從JDK1.1開始引入內部類,內部類主要有

Java基礎之內部類2---內部類的訪問規則

ps:案例來源於畢向東老師Java基礎教程 知識點一:內部類的訪問規則 1,內部類可以直接訪問外部類中的成員,包括私有。 之所以可以直接訪問外部類中的成員,是因為內部類中持有了一個外部類的

JAVA學習之內部類

/* 內部類的訪問規則: 1.內部類可以直接訪問外部類中的成員,包括私有成員; 是因為內部類中持有了一個外部類的引用,格式:外部類名.this 2.外部類要訪問內部類,必須建立內部類物件。 */ class Outer { private int x = 3; cl

JAVA部類

匿名內部類 獨立 button strong 知識 desc ets .get xxx 一、為什麽要使用內部類 為什麽要使用內部類?在《Think in java》中有這樣一句話:使用內部類最吸引人的原因是:每個內部類都能獨立地繼承一個(接口的)實現,所以無論外

thinking in java (七) ----- 內部類,Inner classes

深入理解內部類 為什麼成員內部類可以無條件地訪問外部類成員? 成員內部類甚至可以訪問private的外部類成員,那麼這究竟是怎麼實現的呢?下面通過位元組碼檔案來一探究竟。下面是Outer.java的程式碼: public class Outter { private I

Java—內部類—實現閉包與回撥

前言: Java的閉包與回撥我也是第二次接觸這個概念,之前在自學Android的時候繪製View很多地方都用到了監聽器回撥,一直不是很明白,現在回頭鞏固Java的基礎總算的弄明白,儘量用我自己理解的語言來和大家分享,希望對不懂的朋友可以有一定的幫助,大神也可以

Java內存模型——重排序

序列 依賴性 種類 如果 禁止 加載 runtime 屬於 style 一、重排序   重排序是指為了提高程序的執行效率,編譯器和處理器常常會對語句的執行順序或者指令的執行順序進行重排。 編譯器優化的重排序:編譯器在不改變單線程程序語義的前提下,可以重新安排語句的執行順序

java高級工程師

代理 man 順序 方法 one java web 功能 acm dispatch 一、Java底層基礎題 1、SpringMVC的原理以及返回數據如何渲染到jsp/html上? 答:Spring MVC的核心就是 DispatcherServlet , 一個請求經過 Di

JAVA基礎實例

for bin 存在 void hset search demo 個數字 .so 1.做一個飼養員給動物餵食物的樣例體現JAVA中的面向對象思想,接口(抽象類)的用處 package com.softeem.demo; /** [email p

Java多線程 —— 線程安全、線程同步、線程間通信含面試題集

err 線程等待 共同點 -c java多線 能夠 空間 而不是 不一致 一、線程安全 多個線程在執行同一段代碼的時候,每次的執行結果和單線程執行的結果都是一樣的,不存在執行結果的二義性,就可以稱作是線程安全的。 講到線程安全問題,其實是指多線程環境下對共享資源的訪問可能會

Java之集合初探Iterator叠代器,collections,打包/解包裝箱拆箱,泛型(Generic),comparable接口

基本 generate 等於 框架 ring bin list() each 是否 Iterator(叠代器) 所有實現了Collection接口的容器都有一個iterator方法, 用來返回一個實現了Iterator接口的對象 Iterator對象稱作叠代器, 用來

Java總結篇系列:Java多線程

文章 睡眠 blog setdeamon java多線程 cep public pan level Java總結篇系列:Java多線程(二) 本文承接上一篇文章《Java總結篇系列:Java多線程(一)》。 四.Java多線程的阻塞狀態與線程控制 上文已經提到Jav

Java學習之路流程控制語句

循環 cas 學習之路 將不 乘法表 length 跳出循環 spa int if、if…else…語句 if (true) { System.out.println("為真時執行");

Java基礎—IO小結大綱待更新

16px 文件復制 buffere tro 順序 -a [] 啟用 -c 一、緩沖流的使用   每個字節流都有對應的緩沖流:      BufferedInputStream / BufferedOutputStream   構造器:         方法摘要

JAVA基礎-IO流

直寫 eno 接口 寫入 print lis ted his ride 一、字節流 字節流是通過字節來進行讀寫操作的,他的使用對象相比於字符流來說更加的廣泛。這主要是因為他們讀寫文件的方式而決定的。字符流讀寫文件時是將讀取到的字節通過默認編碼表轉換成字符,在通

Java多線程

set static 生命 斷線 true 參考 clas 方法 test 1.多線程的阻塞狀態   join():一個線程調用了join()方法,必須等待另一個線程執行完畢後才能執行 package jsontest; public class RunableDem

java版雲筆記

blank app mar emc mt4 雲筆記 shu cmm ndt qC50f曬06渙hx厙咆2http://www.docin.com/nwghs11486 評z謁64a癡嘶06棵自暮0http://jz.docin.com/zdng396 5crl71也

Java date相關 格式化

天數 所在 fda 最後一天 ria int () ktr private import java.util.Calendar; import java.util.Date;import java.util.GregorianCalendar; public class Z

Java框架之Struts2

war cit post extend bst edi 關系 執行 erp 一、Action 配置說明 //請求的直接轉發 <package name="packageUser" namespace="" extends="struts-default">

Java線程總結

@override end system interrupt clas this trace trac inf 自定義線程的數據可以共享,也可以不共享,這要看具體的實現方式。 1.不共享數據多線程實現方式: public class MyThread extends T