編程內部類
Ⅰ 請問,java中什麼叫內部類,什麼叫外部類謝謝!!
內部類可以實現介面,當類與介面或者介面與介面發生方法命名沖突的時候,還必須用內部類來實現介面。
實現Runnable這個介面與繼承Thread這個父類這是Java中實現線程的兩種方式。因為Java的單繼承的體系結構,也就說你編寫的類只能有一個父類,所以有的時候不能把Thread作為父類,那麼就可以實現Runnable介面,來實現多線程。而且Thread類本身也是實現了Runnable介面
Ⅱ 什麼是內部類
內部類的共性
內部類分為: 成員內部類、靜態嵌套類、方法內部類、匿名內部類。 (1)、內部類仍然是一個獨立的類,在編譯之後內部類會被編譯成獨立的.class文件,但是前面冠以外部類的類名和$符號。 (2)、內部類不能用普通的方式訪問。內部類是外部類的一個成員,因此內部類可以自由地訪問外部類的成員變數,無論是否是private的。 (3)、內部類聲明成靜態的,就不能隨便的訪問外部類的成員變數,仍然是只能訪問外部類的靜態成員變數。
編輯本段成員內部類
class Outer { class Inner{} } 編譯上述代碼會產生兩個文件:Outer.class和Outer$Inner.class。
編輯本段方法內部類
把類放在方法內 class Outer { public void doSomething(){ class Inner{ public void seeOuter(){ } } } } (1)、方法內部類只能在定義該內部類的方法內實例化,不可以在此方法外對其實例化。 (2)、方法內部類對象不能使用該內部類所在方法的非final局部變數。 因為方法的局部變數位於棧上,只存在於該方法的生命期內。當一個方法結束,其棧結構被刪除,局部變數成為歷史。但是該方法結束之後,在方法內創建的內部類對象可能仍然存在於堆中!例如,如果對它的引用被傳遞到其他某些代碼,並存儲在一個成員變數內。正因為不能保證局部變數的存活期和方法內部類對象的一樣長,所以內部類對象不能使用它們。 下面是完整的例子: class Outer { public void doSomething(){ final int a =10; class Inner{ public void seeOuter(){ System.out.println(a); } } Inner in = new Inner(); in.seeOuter(); } public static void main(String[] args) { Outer out = new Outer(); out.doSomething(); } }
編輯本段匿名內部類
顧名思義,沒有名字的內部類。表面上看起來它們似乎有名字,實際那不是它們的名字。 匿名內部類就是沒有名字的內部類。什麼情況下需要使用匿名內部類?如果滿足下面的一些條件,使用匿名內部類是比較合適的: ·只用到類的一個實例。 ·類在定義後馬上用到。 ·類非常小(SUN推薦是在4行代碼以下) ·給類命名並不會導致你的代碼更容易被理解。 在使用匿名內部類時,要記住以下幾個原則: ·匿名內部類不能有構造方法。 ·匿名內部類不能定義任何靜態成員、方法和類。 ·匿名內部類不能是public,protected,private,static。 ·只能創建匿名內部類的一個實例。 ·一個匿名內部類一定是在new的後面,用其隱含實現一個介面或實現一個類。 ·因匿名內部類為局部內部類,所以局部內部類的所有限制都對其生效。 A、繼承式的匿名內部類 public class Car { public void drive(){ System.out.println("Driving a car!"); } public static void main(String[] args) { Car car = new Car(){ public void drive() { System.out.println("Driving anther car!"); } }; car.drive(); } } 結果輸出了:Driving another car! Car引用變數不是引用Car對象,而是Car匿名子類的對象。 B、介面式的匿名內部類。 interface Vehicle { public void drive(); } class Test{ public static void main(String[] args) { Vehicle v = new Vehicle(){ public void drive(){ System.out.println("Driving a car!"); } }; v.drive(); } } 上面的代碼很怪,好像是在實例化一個介面。事實並非如此,介面式的匿名內部類是實現了一個介面的匿名類。而且只能實現一個介面。 C、參數式的匿名內部類。 class Bar{ void doStuff(Foo f){} } interface Foo{ void foo(); } class Test{ static void go(){ Bar b = new Bar(); b.doStuff(new Foo(){ public void foo(){ System.out.println("foofy"); } }); } }
編輯本段靜態嵌套類
靜態內部類中可以定義靜態或者非靜態的成員。 從技術上講,靜態嵌套類不屬於內部類。因為內部類與外部類共享一種特殊關系,更確切地說是對實例的共享關系。而靜態嵌套類則沒有上述關系。它只是位置在另一個類的內部,因此也被稱為頂級嵌套類。 靜態的含義是該內部類可以像其他靜態成員一樣,沒有外部類對象時,也能夠訪問它。靜態嵌套類不能訪問外部類的成員和方法。 class Outer{ static class Inner{} } class Test { public static void main(String[] args){ Outer.Inner n = new Outer.Inner(); } }
為什麼需要內部類?
典型的情況是,內部類繼承自某個類或實現某個介面,內部類的代碼操作創建其的外圍類的對象。所以你可以認為內部類提供了某種進入其外圍類的窗口。使用內部類最吸引人的原因是: 每個內部類都能獨立地繼承自一個(介面的)實現,所以無論外圍類是否已經繼承了某個(介面的)實現,對於內部類都沒有影響。如果沒有內部類提供的可以繼承多個具體的或抽象的類的能力,一些設計與編程問題就很難解決。從這個角度看,內部類使得多重繼承的解決方案變得完整。介面解決了部分問題,而內部類有效地實現了「多重繼承」。
Ⅲ 什麼是內部類Static Nested Class 和 Inner Class的不同。
內部類就是在一個類的內部定義的類,內部類中不能定義靜態成員(靜態成員不是對象的特性,只是為了找一個容身之處,所以需要放到一個類中而已,這么一點小事,你還要把它放到類內部的一個類中,過分了啊!提供內部類,不是為讓你干這種事情,無聊,不讓你干。我想可能是既然靜態成員類似c語言的全局變數,而內部類通常是用於創建內部對象用的,所以,把「全局變數」放在內部類中就是毫無意義的事情,既然是毫無意義的事情,就應該被禁止),內部類可以直接訪問外部類中的成員變數,內部類可以定義在外部類的方法外面,也可以定義在外部類的方法體中,如下所示:
public class Outer
{
int out_x = 0;
public void method() {
Inner1 inner1 = new Inner1();
public class Inner2 //在方法體內部定義的內部類
{
public method()
{
out_x = 3;
}
}
Inner2 inner2 = new Inner2();
}
public class Inner1 //在方法體外面定義的內部類
{
}
}
在方法體外面定義的內部類的訪問類型可以是public,protecte,默認的,private等4種類型,這就好像類中定義的成員變數有4種訪問類型一樣,它們決定這個內部類的定義對其他類是否可見;對於這種情況,我們也可以在外面創建內部類的實例對象,創建內部類的實例對象時,一定要先創建外部類的實例對象,然後用這個外部類的實例對象去創建內部類的實例對象,代碼如下:
Outer outer = new Outer();
Outer.Inner1 inner1 = outer.new Innner1();
在方法內部定義的內部類前面不能有訪問類型修飾符,就好像方法中定義的局部變數一樣,但這種內部類的前面可以使用final或abstract修飾符。這種內部類對其他類是不可見的其他類無法引用這種內部類,但是這種內部類創建的實例對象可以傳遞給其他類訪問。這種內部類必須是先定義,後使用,即內部類的定義代碼必須出現在使用該類之前,這與方法中的局部變數必須先定義後使用的道理也是一樣的。這種內部類可以訪問方法體中的局部變數,但是,該局部變數前必須加final修飾符。
對於這些細節,只要在eclipse寫代碼試試,根據開發工具提示的各類錯誤信息就可以馬上了解到。
在方法體內部還可以採用如下語法來創建一種匿名內部類,即定義某一介面或類的子類的同時,還創建了該子類的實例對象,無需為該子類定義名稱:
public class Outer
{
public void start()
{
new Thread(
new Runable(){
public void run(){};
}
).start();
}
}
最後,在方法外部定義的內部類前面可以加上static關鍵字,從而成為Static Nested Class,它不再具有內部類的特性,所有,從狹義上講,它不是內部類。Static Nested Class與普通類在運行時的行為和功能上沒有什麼區別,只是在編程引用時的語法上有一些差別,它可以定義成public、protected、默認的、private等多種類型,而普通類只能定義成public和默認的這兩種類型。在外面引用Static Nested Class類的名稱為「外部類名.內部類名」。在外面不需要創建外部類的實例對象,就可以直接創建Static Nested Class,例如,假設Inner是定義在Outer類中的Static Nested Class,那麼可以使用如下語句創建Inner類:
Outer.Inner inner = new Outer.Inner();
由於static Nested Class不依賴於外部類的實例對象,所以,static Nested Class能訪問外部類的非static成員變數。當在外部類中訪問Static Nested Class時,可以直接使用Static Nested Class的名字,而不需要加上外部類的名字了,在Static Nested Class中也可以直接引用外部類的static的成員變數,不需要加上外部類的名字。
在靜態方法中定義的內部類也是Static Nested Class,這時候不能在類前面加static關鍵字,靜態方法中的Static Nested Class與普通方法中的內部類的應用方式很相似,它除了可以直接訪問外部類中的static的成員變數,還可以訪問靜態方法中的局部變數,但是,該局部變數前必須加final修飾符。
備註:首先根據你的印象說出你對內部類的總體方面的特點:例如,在兩個地方可以定義,可以訪問外部類的成員變數,不能定義靜態成員,這是大的特點。然後再說一些細節方面的知識,例如,幾種定義方式的語法區別,靜態內部類,以及匿名內部類。
Ⅳ java內部類怎麼使用
1.內部類的方法可以訪問它所在的外部類中的所有域,包括私有型別的;
2.對於同一個包中的其它類它是隱藏的;
3. 匿名的內部類可以讓我們很方便的定義事件響應(call back),這在GUI編程中很常見
interface OutInterface{ //定義一個介面 public void f(); } public class InterfaceInner { //主類 public static void main(String args[]){ OuterClass2 out=new OuterClass2(); OutInterface outinter=out.doit(); outinter.f(); } } class OuterClass2{ //定義一個內部類,並且介面OutInterface private class InnerClass implements OutInterface{ InnerClass(String s){ System.out.println(s); } public void f(){ System.out.println("訪問內部類中的f()方法"); } } public OutInterface doit(){ //方法返回介面 return new InnerClass("訪問內部類構造方法"); } }
Ⅳ 大家是如何理解內部類的 JAVA之父為什麼要設計內部類出來
1) 當我們需要在某一情形下實現一個介面,而在另一情形下又不需要實現這個介面時,我們可以使用內部類來解決這一問題。讓內部類來實現這個介面。
2) 內部類有效的解決了多重繼承的問題。
Ⅵ java 為什麼需要內部類
給你舉兩個例子 特典型,(1)比如定義一個類Panda(熊貓),繼承抽象類Animal(動物)這個類,並且實現Protect(受保護的)這個介面,但是奇怪的是Animal和Protect中都有抽象方法:price(價格){}這個方法(動物類),請問Panda(熊貓)怎麼處理?這是就要用到內部類。。。
(2)有時候一個類只需要在一處代碼中使用一次,其他位置不會再用到這個類,這時我們就可以用匿名內部類來解決,無需為該類命名。。。例如GUI中監聽事件。。。
Ⅶ 什麼是內部類內部類有什麼作用(java)
(1)、內部類仍然是一個獨立的類,在編譯之後內部類會被編譯成獨立的.class文件,但是前面冠以外部類的類名和$符號。(2)、內部類不能用普通的方式訪問。內部類是外部類的一個成員,因此內部類可以自由地訪問外部類的成員變數,無論是否是private的。(3)、內部類聲明成靜態的,就不能隨便的訪問外部類的成員變數,仍然是只能訪問外部類的靜態成員變數。典型的情況是,內部類繼承自某個類或實現某個介面,內部類的代碼操作創建其的外圍類的對象。所以你可以認為內部類提供了某種進入其外圍類的窗口。使用內部類最吸引人的原因是:每個內部類都能獨立地繼承自一個(介面的)實現,所以無論外圍類是否已經繼承了某個(介面的)實現,對於內部類都沒有影響。如果沒有內部類提供的可以繼承多個具體的或抽象的類的能力,一些設計與編程問題就很難解決。從這個角度看,內部類使得多重繼承的解決方案變得完整。介面解決了部分問題,而內部類有效地實現了「多重繼承」。
Ⅷ 在 java 中使用 內部類 的好處是什麼有什麼優缺點
內部類就是可以個別
搞特殊化
Ⅸ 深入理解Java中為什麼內部類可以訪問外部類的成員
內部類簡介
雖然Java是一門相對比較簡單的編程語言,但是對於初學者, 還是有很多東西感覺雲里霧里,
理解的不是很清晰。內部類就是一個經常讓初學者感到迷惑的特性。 即使現在我自認為Java學的不錯了,
但是依然不是很清楚。其中一個疑惑就是為什麼內部類對象可以訪問外部類對象中的成員(包括成員變數和成員方法)?
早就想對內部類這個特性一探究竟了,今天終於抽出時間把它研究了一下。
內部類就是定義在一個類內部的類。定義在類內部的類有兩種情況:一種是被static關鍵字修飾的, 叫做靜態內部類,
另一種是不被static關鍵字修飾的, 就是普通內部類。 在下文中所提到的內部類都是指這種不被static關鍵字修飾的普通內部類。
靜態內部類雖然也定義在外部類的裡面, 但是它只是在形式上(寫法上)和外部類有關系,
其實在邏輯上和外部類並沒有直接的關系。而一般的內部類,不僅在形式上和外部類有關系(寫在外部類的裡面), 在邏輯上也和外部類有聯系。
這種邏輯上的關系可以總結為以下兩點:
1 內部類對象的創建依賴於外部類對象;
2 內部類對象持有指向外部類對象的引用。
上邊的第二條可以解釋為什麼在內部類中可以訪問外部類的成員。就是因為內部類對象持有外部類對象的引用。但是我們不禁要問, 為什麼會持有這個引用? 接著向下看, 答案在後面。
通過反編譯位元組碼獲得答案
在源代碼層面, 我們無法看到原因,因為Java為了語法的簡介, 省略了很多該寫的東西, 也就是說很多東西本來應該在源代碼中寫出, 但是為了簡介起見, 不必在源碼中寫出,編譯器在編譯時會加上一些代碼。 現在我們就看看Java的編譯器為我們加上了什麼?
首先建一個工程TestInnerClass用於測試。 在該工程中為了簡單起見, 沒有創建包, 所以源代碼直接在默認包中。在該工程中, 只有下面一個簡單的文件。
?
1
2
3
4
5
6
7
8
9
public class Outer {
int outerField = 0;
class Inner{
void InnerMethod(){
int i = outerField;
}
}
}
該文件很簡單, 就不用過多介紹了。 在外部類Outer中定義了內部類Inner, 並且在Inner的方法中訪問了Outer的成員變數outerField。
雖然這兩個類寫在同一個文件中, 但是編譯完成後, 還是生成各自的class文件:
這里我們的目的是探究內部類的行為, 所以只反編譯內部類的class文件Outer$Inner.class 。 在命令行中, 切換到工程的bin目錄, 輸入以下命令反編譯這個類文件:
?
1
javap -classpath . -v Outer$Inner
-classpath . 說明在當前目錄下尋找要反編譯的class文件
-v 加上這個參數輸出的信息比較全面。包括常量池和方法內的局部變數表, 行號, 訪問標志等等。
注意, 如果有包名的話, 要寫class文件的全限定名, 如:
?
1
javap -classpath . -v com..Outer$Inner
反編譯的輸出結果很多, 為了篇幅考慮, 在這里我們省略了常量池。 下面給出除了常量池之外的輸出信息。
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
{
final Outer this$0;
flags: ACC_FINAL, ACC_SYNTHETIC
Outer$Inner(Outer);
flags:
Code:
stack=2, locals=2, args_size=2
0: aload_0
1: aload_1
2: putfield #10 // Field this$0:LOuter;
5: aload_0
6: invokespecial #12 // Method java/lang/Object."<init>":()V
9: return
LineNumberTable:
line 5: 0
LocalVariableTable:
Start Length Slot Name Signature
0 10 0 this LOuter$Inner;
void InnerMethod();
flags:
Code:
stack=1, locals=2, args_size=1
0: aload_0
1: getfield #10 // Field this$0:LOuter;
4: getfield #20 // Field Outer.outerField:I
7: istore_1
8: return
LineNumberTable:
line 7: 0
line 8: 8
LocalVariableTable:
Start Length Slot Name Signature
0 9 0 this LOuter$Inner;
8 1 1 i I
}</init>
首先我們會看到, 第一行的信息如下:
?
1
final Outer this$0;
這句話的意思是, 在內部類Outer$Inner中, 存在一個名字為this$0 , 類型為Outer的成員變數, 並且這個變數是final的。
其實這個就是所謂的「在內部類對象中存在的指向外部類對象的引用」。但是我們在定義這個內部類的時候, 並沒有聲明它,
所以這個成員變數是編譯器加上的。
雖然編譯器在創建內部類時為它加上了一個指向外部類的引用, 但是這個引用是怎樣賦值的呢?畢竟必須先給他賦值,它才能指向外部類對象。下面我們把注意力轉移到構造函數上。 下面這段輸出是關於構造函數的信息。
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Outer$Inner(Outer);
flags:
Code:
stack=2, locals=2, args_size=2
0: aload_0
1: aload_1
2: putfield #10 // Field this$0:LOuter;
5: aload_0
6: invokespecial #12 // Method java/lang/Object."<init>":()V
9: return
LineNumberTable:
line 5: 0
LocalVariableTable:
Start Length Slot Name Signature
0 10 0 this LOuter$Inner;</init>
我們知道, 如果在一個類中, 不聲明構造方法的話, 編譯器會默認添加一個無參數的構造方法。 但是這句話在這里就行不通了, 因為我們明明看到, 這個構造函數有一個構造方法, 並且類型為Outer。 所以說,
編譯器會為內部類的構造方法添加一個參數, 參數的類型就是外部類的類型。
下面我們看看在構造參數中如何使用這個默認添加的參數。 我們來分析一下構造方法的位元組碼。 下面是每行位元組碼的意義:
aload_0 :
將局部變數表中的第一個引用變數載入到操作數棧。 這里有幾點需要說明。
局部變數表中的變數在方法執行前就已經初始化完成;局部變數表中的變數包括方法的參數;成員方法的局部變數表中的第一個變數永遠是this;操作數棧就是
執行當前代碼的棧。所以這句話的意思是: 將this引用從局部變數表載入到操作數棧。
aload_1:
將局部變數表中的第二個引用變數載入到操作數棧。 這里載入的變數就是構造方法中的Outer類型的參數。
putfield #10 // Field this$0:LOuter;
使用操作數棧頂端的引用變數為指定的成員變數賦值。 這里的意思是將外面傳入的Outer類型的參數賦給成員變數this$0 。
這一句putfield位元組碼就揭示了, 指向外部類對象的這個引用變數是如何賦值的。
下面幾句位元組碼和本文討論的話題無關, 只做簡單的介紹。 下面幾句位元組碼的含義是: 使用this引用調用父類(Object)的構造方法然後返回。
用我們比較熟悉的形式翻譯過來, 這個內部類和它的構造函數有點像這樣: (注意, 這里不符合Java的語法, 只是為了說明問題)
?
1
2
3
4
5
6
7
8
class Outer$Inner{
final Outer this$0;
public Outer$Inner(Outer outer){
this.this$0 = outer;
super();
}
}
說到這里, 可以推想到, 在調用內部類的構造器初始化內部類對象的時候, 編譯器默認也傳入外部類的引用。 調用形式有點像這樣: (注意, 這里不符合java的語法, 只是為了說明問題)
vcq9ysfP4M2stcShoyDU2sTasr//wOC1xLPJ1LGx5MG/b3V0ZXJGaWVsZKOsIM/Cw++NDQtcSjugo8YnI+Cgo8cHJlIGNsYXNzPQ=="brush:java;">
void InnerMethod();
flags:
Code:
stack=1, locals=2, args_size=1
0: aload_0
1: getfield #10 // Field this$0:LOuter;
4: getfield #20 // Field
Outer.outerField:I
7: istore_1
8: return
getfield #10 // Field this$0:LOuter;
將成員變數this$0載入到操作數棧上來
getfield #20 // Field Outer.outerField:I
使用上面載入的this$0引用, 將外部類的成員變數outerField載入到操作數棧
istore_1
將操作數棧頂端的int類型的值保存到局部變數表中的第二個變數上(注意, 第一個局部變數被this佔用,
第二個局部變數是i)。操作數棧頂端的int型變數就是上一步載入的outerField變數。 所以, 這句位元組碼的含義就是:
使用outerField為i賦值。
上面三步就是內部類中是如何通過指向外部類對象的引用, 來訪問外部類成員的。
文章寫到這里, 相信讀者對整個原理就會有一個清晰的認識了。 下面做一下總結:
本文通過反編譯內部類的位元組碼, 說明了內部類是如何訪問外部類對象的成員的,除此之外, 我們也對編譯器的行為有了一些了解, 編譯器在編譯時會自動加上一些邏輯, 這正是我們感覺困惑的原因。
關於內部類如何訪問外部類的成員, 分析之後其實也很簡單, 主要是通過以下幾步做到的:
1 編譯器自動為內部類添加一個成員變數, 這個成員變數的類型和外部類的類型相同, 這個成員變數就是指向外部類對象的引用;
2 編譯器自動為內部類的構造方法添加一個參數, 參數的類型是外部類的類型, 在構造方法內部使用這個參數為1中添加的成員變數賦值;
3 在調用內部類的構造函數初始化內部類對象時, 會默認傳入外部類的引用。