java 靜態內部類的使用
Java基礎第十二講:面向物件基礎(六)[內部類]
http://android.yaohuiji.com/archives/3247本講內容:內部類
Java語言允許在類中再定義類,這種在其它類內部定義的類就叫內部類。內部類又分為:常規內部類、區域性內部類、匿名內部類和靜態巢狀類四種。我們內部類的知識在Android手機開發中經常用到。
一、常規內部類
所謂常規內部類,或者說內部類,指的就是除去後面三種之外的內部類(這算什麼解釋。。。)
先寫一個最簡單的內部類的例子,大家感覺一下:
1 |
public class
Outer { |
2 |
public
class Inner{ |
3 |
} |
4 |
} |
編譯一下,我們看到目錄中出現了兩個class檔案,其中有一個檔名叫做Outer$Inner.class,帶了一個$符號,這個特點讓我們很容易的認出來這是內部類編譯後的class檔案。
再寫一個稍微複雜一點的內部類:
01 |
public class
Outer { |
02 |
03 |
private
int x= 1 ; |
04 |
05 |
public
Outer(){ |
06 |
System.out.println( "Outer initial" ); |
07 |
} |
08 |
09 |
public
class Inner{ |
10 |
11 |
public
Inner(){ |
12 |
System.out.println( "Inner initial" ); |
13 |
} |
14 |
15 |
private
int x= 2 ; |
16 |
17 |
public
void add(){ |
18 |
int
x= 3 ; |
19 |
System.out.println(x); |
20 |
System.out.println( this .x); |
21 |
System.out.println(Outer. this .x); |
22 |
} |
23 |
24 |
} |
25 |
26 |
public
static void
main(String[] args){ |
27 |
Inner inner =
new Outer(). new
Inner(); |
28 |
inner.add(); |
29 |
} |
30 |
} |
我們編譯以後,執行一下看看:
在上面的例子裡我們可以清晰的看到:
- 內部類就像一個例項成員一樣存在於外部類中。
- 內部類可以訪問外部類的所有成員就想訪問自己的成員一樣沒有限制。
- 內部類中的this指的是內部類的例項物件本身,如果要用外部類的例項物件就可以用類名.this的方式獲得。
- 內部類物件中不能有靜態成員,原因很簡單,內部類的例項物件是外部類例項物件的一個成員。
下面我們再小結一下內部類的建立方法:
- 在外部類的內部,可以用 Inner inner = new Inner(); 方法直接建立
- 在外部類外部,必須先建立外部類例項,然後再建立內部類例項,除了上面 Inner inner = new Outer().new Inner()的寫法以外,還有 Outer outer = new Outer(); Inner inner = outer.new Inner();的寫法
二、區域性內部類
我們也可以把類定義在方法內部,這時候我們稱這個類叫區域性內部類。
我們再看一個例子:
01 |
public class
Outer { |
02 |
03 |
int
x = 1 ; |
04 |
public
void doSomething(){ |
05 |
final
int y= 2 ; |
06 |
class
Inner{ |
07 |
int
x = 3 ; |
08 |
void
print(){ |
09 |
int
x= 4 ; |
10 |
System.out.println(x); |
11 |
System.out.println( this .x); |
12 |
System.out.println(Outer. this .x); |
13 |
System.out.println(y); |
14 |
} |
15 |
} |
16 |
Inner inner =
new Inner(); |
17 |
inner.print(); |
18 |
} |
19 |
20 |
public
static void
main(String[] args){ |
21 |
Outer outer =
new Outer(); |
22 |
outer.doSomething(); |
23 |
} |
24 |
} |
執行程式,檢視結果:
我們通過上面這裡例子也可以看到下面幾點:
- 區域性內部類的地位和方法內的區域性變數的位置類似,因此不能修飾區域性變數的修飾符也不能修飾區域性內部類,譬如public、private、protected、static、transient等
- 區域性內部類只能在宣告的方法內是可見的,因此定義區域性內部類之後,想用的話就要在方法內直接例項化,記住這裡順序不能反了,一定是要先聲明後使用,否則編譯器會說找不到。
- 區域性內部類不能訪問定義它的方法內的區域性變數,除非這個變數被定義為final 。
是不是有點不好理解?關於為什麼用final修飾以後就可以用了,我打算專門在番外篇裡專門寫一篇部落格給你講清楚,先記住吧。
三、匿名內部類
當我們把內部類的定義和宣告寫到一起時,就不用給這個類起個類名而是直接使用了,這種形式的內部類根本就沒有類名,因此我們叫它匿名內部類。
我們再看一個有趣的例子:
01 |
public class
Dog { |
02 |
03 |
public
interface Pet { |
04 |
05 |
public
void beFriendly(); |
06 |
public
void play(); |
07 |
08 |
} |
09 |
10 |
public
static void
main(String[] args){ |
11 |
12 |
Pet dog =
new Pet(){ |
13 |
@Override |
14 |
public
void beFriendly() { |
15 |
System.out.println( "蹭蹭你^_^" ); |
16 |
} |
17 |
@Override |
18 |
public
void play() { |
19 |
System.out.println( "把飛盤叼給你,逼你把飛盤丟出去,然後它再撿回來讓你繼續扔,連續500次^_^" ); |
20 |
} |
21 |
}; |
22 |
23 |
dog.beFriendly(); |
24 |
dog.play(); |
25 |
26 |
} |
27 |
} |
編譯和執行程式,檢視結果:
竟然編譯和執行都很正常,我們知道抽象類和介面肯定無法例項化的,因此剛才的例子肯定有點意思:
- 第一匿名內部類可以是個介面,這個沒什麼好奇怪的哈。
- 第12行到第21行是一個語句,就是定義了一個物件,因此21行大括號後面有個分號。
- 匿名內部類用 new Pet(){ … } 的方式把宣告類的過程和建立類的例項的過程合二為一。
- 匿名內部類可以是某個類的繼承子類也可以是某個介面的實現類。
好吧我們再看一個例子,方法引數內的匿名內部類:
01 |
public class
Dog { |
02 |
03 |
static
abstract class
Ball { |
04 |
abstract
String getName(); |
05 |
} |
06 |
07 |
void
play(Ball b){ |
08 |
System.out.println(b.getName()); |
09 |
} |
10 |
11 |
public
static void
main(String[] args){ |
12 |
Dog dog =
new Dog(); |
13 |
14 |
dog.play( new
Ball(){ |
15 |
@Override |
16 |
String getName() { |
17 |
return
"qiu qiu" ; |
18 |
}}); |
19 |
} |
20 |
} |
編譯和執行以後的截圖我就不給你了,返回值就是“qiu qiu”。
從第14行到第18行是一句話,就是執行一個play方法,而這個方法的引數就由一個匿名內部類的例項來提供。
四、靜態巢狀類
為了讓你感覺舒服一些,我們也把最簡單的內部類放在最後講。
當一個內部類前面用static修飾時,我們稱之為靜態巢狀類或者說靜態內部類。
上面的例子裡其實我們已經看到過靜態巢狀類了,下面我們再舉一個例子:
01 |
public class
Outer { |
02 |
03 |
static
int x = 1 ; |
04 |
05 |
static
class Nest { |
06 |
07 |
void
print(){ |
08 |
System.out.println( "Nest " +x); |
09 |
} |
10 |
} |
11 |
12 |
public
static void
main(String[] args){ |
13 |
Outer.Nest nest =
new Outer.Nest(); |
14 |
nest.print(); |
15 |
} |
16 |
} |
因為靜態巢狀類和其他靜態方法一樣只能訪問其它靜態的成員,而不能訪問例項成員。因此靜態巢狀類和外部類(封裝類)之間的聯絡就很少了,他們之間可能也就是名稱空間上的一些關聯。上面例子中你需要注意的就是靜態巢狀類的宣告方法 new Outer.Nest() 連續寫了兩個類名,以至於我們都懷疑前面的Outer是個包名了,好在包名一般都小寫的,要不還真分不清……
再強調一遍,內部類在Android中應用的非常多,理解和使用好顯得蠻重要。好了,本講就到這裡。
如何應用Java的靜態內部類
http://java.chinaitlab.com/oop/787330.html
在上一小節我們討論了內部類,即在一個類中包含有另外一個或者多個類(見本書12.3.3小節)。與內部類相似,靜態內部類指在一個類的內部包含有另外一個或者多個靜態類。例如:
public class OuterClass {
...
static class StaticInnerClass1 { //內部靜態類
//只可以訪問OuterClass的靜態成員
...
} //StaticInnerClass結束
...
static class StaticInnerClassN { //更多靜態內部類
//只可以訪問OuterClass的靜態成員
...
} //StaticInnerClassN結束
} //OuterClass結束
與一般內部類不同,在靜態程式碼中不能夠使用this操作,所以在靜態內部類中只可以訪問外部類的靜態變數和靜態方法。使用靜態內部類的目的和使用內部類相同。如果一個內部類不依賴於其外部類的例項變數,或與例項變數無關,則選擇應用靜態內部類。
如下例子演示怎樣使用靜態內部類:
///完整程式存在本書配套資源目錄Ch12名為StaticInnerClassTest.java
public class StaticInnerClassTest {
public static void main( String args[] ) {
OuterClass2 outer = new OuterClass2();
OuterClass2.StaticInnerClass.innerMethod(); //呼叫靜態內部類的靜態方法 OuterClass2.outerMethod(); //建立靜態內部類物件 OuterClass2.StaticInnerClass staticInner = new OuterClass2.StaticInnerClass(); int num = staticInner.innerMethod2(); //呼叫靜態內部類例項方法 } } class OuterClass2 { //外部類 private double x = 0.0; //內部靜態類不可以訪問外部類實 例變數 static private int n = 10; //外部類靜態變數 static void outerMethod() { //外部類靜態方法 System.out.println("from OuterClass..."); } void outerMethod2() { System.out.println("from OuterClass’ instance method2()..."); } static class StaticInnerClass { //靜態內部類 static private int m = 5; //靜態內部類靜態變數 static void innerMethod() { //靜態內部類靜態方法 int sum; n = 20; //只可以訪問外部類靜態變數 sum = n + m; System.out.println("from InnerClass sum = " + sum); outerMethod(); //只可以呼叫外部類靜態方法 } int innerMethod2() { n = 100; outerMethod(); System.out.println("from InnerMethod2() n = " + n); return n; } } //靜態內部類結束 } //外部類結束 如同不用建立物件就可呼叫靜態方法一樣,上例靜態內部類中的靜態方法利用: OuterClass2.StaticInnerClass.innerMethod(); //靜態內部類呼叫其靜態方法 來呼叫。注意,可以在靜態內部類的方法中,直接訪問外部類的靜態變數n和呼叫靜態方法outerMethod()。但不允許訪問外部類的例項變數x以及例項方法outerMethod2()。 靜態內部類中也可以提供例項方法,如: static class StaticInnerClass { int innerMethod2() { n = 100; //只可訪問外部類靜態變數 outerMethod(); //只可呼叫外部類靜態方法 System.out.println("from InnerMethod2() n = " + n); return n; } } //靜態內部類結束 靜態內部類的例項方法中亦只允許訪問外部類的靜態成員。 可以使用下列語法格式建立一個靜態內部類物件並且呼叫其例項方法,以及靜態方法: OuterClass2.StaticInnerClass staticInner = new OuterClass2.StaticInner Class(); //建立靜態內部類物件 int num = staticInner.innerMethod2(); //呼叫例項方法 staticInner.innerMethod(); //呼叫其靜態方法
package Chapter10;
public class StaticInternal {
private static String name = "\"聶慶亮\"";
public static void setStatic(String n) { // 外部類的非靜態方法
System.out.println("[現在訪問的是外部類的靜態方法!]");
name = n;
}
static class InnerClass_2 { // 靜態內部類開始
String address, mail; // 宣告String型別變數
long phoneNum; // 宣告long型別變數
int qq; // 宣告int型別變數
static void getStatic() { // 靜態內部類的靜態方法
System.out.println("[訪問外部類的靜態變數] name = " + name);
setStatic("尹繼平"); // 訪問外部類的靜態方法
}
// 靜態內部類的非靜態方法
public void setString(String address, String mail) {
System.out.println("1.靜態內部類的帶String型引數的非靜態主法");
this.address = address;
this.mail = mail;
}
public void setInt(long phoneNum, int qq) {
System.out.println("2.靜態內部類的帶int型引數的非靜態主法!");
this.phoneNum = phoneNum;
this.qq = qq;
}
} // 靜態內部類結束
public void setValue() { // 外部類訪問靜態內部類的靜態成員:內部類.靜態成員
InnerClass_2.getStatic(); // 訪問靜態內部類的靜態方法
InnerClass_2 inner = new InnerClass_2(); // 例項化物件
inner.setString("北京昌平區沙河鎮", "[email protected]"); // 訪問靜態內部類的非靜態方法
inner.setInt(89653310, 313557706);
System.out.println("\n外部類訪問靜態內部類的結果如下:");
System.out.println("姓名:" + this.name);
System.out.println("住址:" + inner.address);
System.out.println("聯絡電話" + inner.phoneNum);
System.out.println("E-mail:" + inner.mail);
System.out.println("QQ號碼:" + inner.qq);
}
public static void main(String[] args) { // java程式主入口處
StaticInternal sin = new StaticInternal();
sin.setValue(); // 呼叫方法
}
}
Java內部類與靜態內部類
定義在一個類內部的類叫內部類,包含內部類的類稱為外部類。內部類可以宣告public、protected、private等訪問限制,可以宣告為abstract的供其他內部類或外部類繼承與擴充套件,或者宣告為static、final的,也可以實現特定的介面。外部類按常規的類訪問方式使用內部類,唯一的差別是外部類可以訪問內部類的所有方法與屬性,包括私有方法與屬性。
(1)建立例項
OutClass.InnerClass obj = outClassInstance.new InnerClass(); //注意是外部類例項.new,內部類
AAA.StaticInner in = new AAA.StaticInner();//注意是外部類本身,靜態內部類
(2)內部類中的this
內部類中的this與其他類一樣是指的本身。建立內部類物件時,它會與創造它的外圍物件有了某種聯絡,於是能訪問外圍類的所有成員,不需任何特殊條件,可理解為內部類連結到外部類。 用外部類建立內部類物件時,此內部類物件會祕密的捕獲一個指向外部類的引用,於是,可以通過這個引用來訪問外圍類的成員。
(3)外部類訪問內部類
內部類類似外部類的屬性,因此訪問內部類物件時總是需要一個建立好的外部類物件。內部類物件通過‘外部類名.this.xxx’的形式訪問外部類的屬性與方法。如:
System.out.println("Print in inner Outer.index=" + pouter.this.index);
System.out.println("Print in inner Inner.index=" + this.index);
(4)內部類向上轉型
內部類也可以和普通類一樣擁有向上轉型的特性。將內部類向上轉型為基型別,尤其是介面時,內部類就有了用武之地。如果內部類是private的,只可以被它的外部類問,從而完全隱藏實現的細節。
(5)方法內的類
方法內建立的類(注意方法中也能定義類),不能加訪問修飾符。另外,方法內部的類也不是在呼叫方法時才會建立的,它們一樣也被事先編譯了。
(6)靜態內部類
定義靜態內部類:在定義內部類的時候,可以在其前面加上一個許可權修飾符static。此時這個內部類就變為了靜態內部類。
通常稱為巢狀類,當內部類是static時,意味著:
[1]要建立巢狀類的物件,並不需要其外圍類的物件;
[2]不能從巢狀類的物件中訪問非靜態的外圍類物件(不能夠從靜態內部類的物件中訪問外部類的非靜態成員);
巢狀類與普通的內部類還有一個區別:普通內部類的欄位的欄位與方法,只能放在類的外部層次上,所以普通的內部類不能有static資料和static欄位,也不能包含巢狀類。但是在巢狀類裡可以包含所有這些東西。也就是說,在非靜態內部類中不可以宣告靜態成員,只有將某個內部類修飾為靜態類,然後才能夠在這個類中定義靜態的成員變數與成員方法。
另外,在建立靜態內部類時不需要將靜態內部類的例項繫結在外部類的例項上。普通非靜態內部類的物件是依附在外部類物件之中的,要在一個外部類中定義一個靜態的內部類,不需要利用關鍵字new來建立內部類的例項。靜態類和方法只屬於類本身,並不屬於該類的物件,更不屬於其他外部類的物件。
(7)內部類識別符號
每個類會產生一個.class檔案,檔名即為類名。同樣,內部類也會產生這麼一個.class檔案,但是它的名稱卻不是內部類的類名,而是有著嚴格的限制:外圍類的名字,加上$,再加上內部類名字。
(8)為何要用內部類?
1. 內部類一般只為其外部類使用;
2. 內部類提供了某種進入外部類的窗戶;
3. 也是最吸引人的原因,每個內部類都能獨立地繼承一個介面,而無論外部類是否已經繼承了某個介面。因此,內部類使多重繼承的解決方案變得更加完整。