第一篇:Java內部類總結
內部類是指在一個外部類的內部再定義一個類。內部類作為外部類的一個成員,并且依附于外部類而存在的。內部類可為靜態,可用protected和 private修飾(而外部類只能使用public和缺省的包訪問權限)。內部類主要有以下幾類:成員內部類、局部內部類、靜態內部類、匿名內部類
為什么需要內部類?
典型的情況是,內部類繼承自某個類或實現某個接口,內部類的代碼操作創建其的外圍類的對象。所以你可以認為內部類提供了某種進入其外圍類的窗口。使用內部類最吸引人的原因是:
每個內部類都能獨立地繼承自一個(接口的)實現,所以無論外圍類是否已經繼承了某個(接口的)實現,對于內部類都沒有影響。如果沒有內部類提供的可以繼 承多個具體的或抽象的類的能力,一些設計與編程問題就很難解決。從這個角度看,內部類使得多重繼承的解決方案變得完整。接口解決了部分問題,而內部類有效 地實現了“多重繼承”。
A:成員內部類
作為外部類的一個成員存在,與外部類的屬性、方法并列。
public class Outer { private static int i = 1;private int j = 10;private int k = 20;
public static void outer_f1(){ }
public void outer_f2(){ }
// 成員內部類中,不能定義靜態成員
// 成員內部類中,可以訪問外部類的所有成員 class Inner { // static int inner_i = 100;//內部類中不允許定義靜態變量 int j = 100;// 內部類和外部類的實例變量可以共存 intinner_i = 1;
void inner_f1(){ System.out.println(i);//在內部類中訪問內部類自己的變量直接用變量名 System.out.println(j);//在內部類中訪問內部類自己的變量也可以用this.變量名 System.out.println(this.j);//在內部類中訪問外部類中與內部類同名的實例變量用外部類名.this.變量名 System.out.println(Outer.this.j);//如果內部類中沒有與外部類同名的變量,則可以直接用變量名訪問外部類變量 System.out.println(k);outer_f1();outer_f2();} }
//外部類的非靜態方法訪問成員內部類 public void outer_f3(){ Inner inner = new Inner();inner.inner_f1();}
// 外部類的靜態方法訪問成員內部類,與在外部類外部訪問成員內部類一樣 public static void outer_f4(){ //step1 建立外部類對象 Outer out = new Outer();//step2 根據外部類對象建立內部類對象 Inner inner = out.new Inner();//step3 訪問內部類的方法 inner.inner_f1();}
publicstaticvoid main(String[] args){ //outer_f4();//該語句的輸出結果和下面三條語句的輸出結果一樣
//如果要直接創建內部類的對象,不能想當然地認為只需加上外圍類Outer的名字,//就可以按照通常的樣子生成內部類的對象,而是必須使用此外圍類的一個對象來 //創建其內部類的一個對象:
//Outer.Inner outin = out.new Inner()//因此,除非你已經有了外圍類的一個對象,否則不可能生成內部類的對象。因為此 //內部類的對象會悄悄地鏈接到創建它的外圍類的對象。如果你用的是靜態的內部類,//那就不需要對其外圍類對象的引用。Outer out = new Outer();Outer.Inner outin = out.new Inner();outin.inner_f1();} }
注意:內部類是一個編譯時的概念,一旦編譯成功,就會成為完全不同的兩類。對于一個名為outer的外部類和其內部定義的名為inner的內部類。編譯完成后出現outer.class和outer$inner.class兩類。
B:局部內部類
在方法中定義的內部類稱為局部內部類。與局部變量類似,局部內部類不能有訪問說明符,因為它不是外圍類的一部分,但是它可以訪問當前代碼塊內的常量,和此外圍類所有的成員。
public class Outer { private ints = 100;private int out_i = 1;
public void f(final int k){ final int s = 200;int i = 1;final int j = 10;
//定義在方法內部 class Inner { ints = 300;// 可以定義與外部類同名的變量
// static int m = 20;//不可以定義靜態變量 Inner(int k){ inner_f(k);}
intinner_i = 100;
void inner_f(int k){ //如果內部類沒有與外部類同名的變量,在內部類中可以直接訪問外部類的實例變量 System.out.println(out_i);//可以訪問外部類的局部變量(即方法內的變量),但是變量必須是final的 System.out.println(j);//System.out.println(i);//如果內部類中有與外部類同名的變量,直接用變量名訪問的是內部類的變量 System.out.println(s);//用this.變量名訪問的也是內部類變量 System.out.println(this.s);//用外部類名.this.內部類變量名訪問的是外部類變量 System.out.println(Outer.this.s);} } new Inner(k);} publicstaticvoid main(String[] args){ // 訪問局部內部類必須先有外部類對象 Outer out = new Outer();out.f(3);} }
C:靜態內部類(嵌套類):(注意:前兩種內部類與變量類似,所以可以對照參考變量)
如果你不需要內部類對象與其外圍類對象之間有聯系,那你可以將內部類聲明為static。這通常稱為嵌套類(nested class)。想要理解static應用于內部類時的含義,你就必須記住,普通的內部類對象隱含地保存了一個引用,指向創建它的外圍類對象。然而,當內部 類是static的時,就不是這樣了。嵌套類意味著:
1.要創建嵌套類的對象,并不需要其外圍類的對象。
2.不能從嵌套類的對象中訪問非靜態的外圍類對象。
publicclass Outer { privatestaticinti = 1;privateintj = 10;publicstaticvoidouter_f1(){ }
publicvoidouter_f2(){ }
// 靜態內部類可以用public,protected,private修飾
// 靜態內部類中可以定義靜態或者非靜態的成員 staticclass Inner { staticintinner_i = 100;intinner_j = 200;staticvoidinner_f1(){ //靜態內部類只能訪問外部類的靜態成員(包括靜態變量和靜態方法)System.out.println(“Outer.i” + i);outer_f1();}
voidinner_f2(){ // 靜態內部類不能訪問外部類的非靜態成員(包括非靜態變量和非靜態方法)// System.out.println(“Outer.i”+j);// outer_f2();} }
publicvoidouter_f3(){ // 外部類訪問內部類的靜態成員:內部類.靜態成員 System.out.println(Inner.inner_i);Inner.inner_f1();// 外部類訪問內部類的非靜態成員:實例化內部類即可 Inner inner = new Inner();inner.inner_f2();}
publicstaticvoid main(String[] args){ newOuter().outer_f3();} }
生成一個靜態內部類不需要外部類成員:這是靜態內部類和成員內部類的區別。靜態內部類的對象可以直接生成:Outer.Inner in = new Outer.Inner();而不需要通過生成外部類對象來生成。這樣實際上使靜態內部類成為了一個頂級類(正常情況下,你不能在接口內部放置任何代碼,但嵌套類可以作為接口的一部 分,因為它是static 的。只是將嵌套類置于接口的命名空間內,這并不違反接口的規則)
D:匿名內部類(from thinking in java 3th)
簡單地說:匿名內部類就是沒有名字的內部類。什么情況下需要使用匿名內部類?如果滿足下面的一些條件,使用匿名內部類是比較合適的:
·只用到類的一個實例。
·類在定義后馬上用到。
·類非常小(SUN推薦是在4行代碼以下)
·給類命名并不會導致你的代碼更容易被理解。
在使用匿名內部類時,要記住以下幾個原則:
·匿名內部類不能有構造方法。
·匿名內部類不能定義任何靜態成員、方法和類。
·匿名內部類不能是public,protected,private,static。
·只能創建匿名內部類的一個實例。
·一個匿名內部類一定是在new的后面,用其隱含實現一個接口或實現一個類。
·因匿名內部類為局部內部類,所以局部內部類的所有限制都對其生效。
下面的例子看起來有點奇怪:
//在方法中返回一個匿名內部類 public class Parcel6 { public Contents cont(){ return new Contents(){ private int i = 11;
public int value(){ return i;} };// 在這里需要一個分號 }
public static void main(String[] args){ Parcel6 p = new Parcel6();Contents c = p.cont();} }
cont()方法將下面兩個動作合并在一起:返回值的生成,與表示這個返回值的類的定義!進一步說,這個類是匿名的,它沒有名字。更糟的是,看起來是你正要創建一個Contents對象:
return new Contents()
但是,在到達語句結束的分號之前,你卻說:“等一等,我想在這里插入一個類的定義”:
return new Contents(){ private int i = 11;public int value(){ return i;} };
這種奇怪的語法指的是:“創建一個繼承自Contents的匿名類的對象。”通過new 表達式返回的引用被自動向上轉型為對Contents的引用。匿名內部類的語法是下面例子的簡略形式:
class MyContents implements Contents { private int i = 11;public int value(){ return i;} } return new MyContents();
在這個匿名內部類中,使用了缺省的構造器來生成Contents。下面的代碼展示的是,如果你的基類需要一個有參數的構造器,應該怎么辦:
public class Parcel7 { public Wrapping wrap(int x){ // Base constructor call: return new Wrapping(x){ // Pass constructor argument.public int value(){ return super.value()* 47;} };// Semicolon required } public static void main(String[] args){ Parcel7 p = new Parcel7();Wrapping w = p.wrap(10);} }
只需簡單地傳遞合適的參數給基類的構造器即可,這里是將x 傳進new Wrapping(x)。在匿名內部類末尾的分號,并不是用來標記此內部類結束(C++中是那樣)。實際上,它標記的是表達式的結束,只不過這個表達式正 巧包含了內部類罷了。因此,這與別的地方使用的分號是一致的。
如果在匿名類中定義成員變量,你同樣能夠對其執行初始化操作:
public class Parcel8 { // Argument must be final to use inside // anonymous inner class: public Destination dest(final String dest){ return new Destination(){ private String label = dest;public String readLabel(){ return label;} };} public static void main(String[] args){ Parcel8 p = new Parcel8();Destination d = p.dest(“Tanzania”);} }
如果你有一個匿名內部類,它要使用一個在它的外部定義的對象,編譯器會要求其參數引用是final 型的,就像dest()中的參數。如果你忘記了,會得到一個編譯期錯誤信息。如果只是簡單地給一個成員變量賦值,那么此例中的方法就可以了。但是,如果你 想做一些類似構造器的行為,該怎么辦呢?在匿名類中不可能有已命名的構造器(因為它根本沒名字!),但通過實例初始化,你就能夠達到為匿名內部類“制作” 一個構造器的效果。像這樣做:00000000000000
abstract class Base { public Base(int i){ System.out.println(“Base constructor, i = ” + i);} public abstract void f();}
public class AnonymousConstructor { public static Base getBase(int i){ return new Base(i){ { System.out.println(“Inside instance initializer”);} public void f(){ System.out.println(“In anonymous f()”);} };} public static void main(String[] args){ Base base = getBase(47);base.f();} }
在此例中,不要求變量i 一定是final 的。因為i 被傳遞給匿名類的基類的構造器,它并不會在匿名類內部被直接使用。下例是帶實例初始化的“parcel”形式。注意dest()的參數必須是final,因為它們是在匿名類內被使用的。
public class Parcel9 { public Destinationdest(final String dest, final float price){ return new Destination(){ private int cost;// Instance initialization for each object: { cost = Math.round(price);if(cost > 100)System.out.println(“Over budget!”);}
private String label = dest;public String readLabel(){ return label;} };} public static void main(String[] args){ Parcel9 p = new Parcel9();Destination d = p.dest(“Tanzania”, 101.395F);} }
在實例初始化的部分,你可以看到有一段代碼,那原本是不能作為成員變量初始化的一部分而執行的(就是if 語句)。所以對于匿名類而言,實例初始化的實際效果就是構造器。當然它受到了限制:你不能重載實例初始化,所以你只能有一個構造器。
從多層嵌套類中訪問外部
一個內部類被嵌套多少層并不重要,它能透明地訪問所有它所嵌入的外圍類的所有成員,如下所示:
class MNA { private void f(){} class A { private void g(){} public class B { void h(){ g();f();} } } } public class MultiNestingAccess { public static void main(String[] args){ MNA mna = new MNA();MNA.A mnaa = mna.new A();MNA.A.B mnaab = mnaa.new B();mnaab.h();} }
可以看到在MNA.A.B中,調用方法g()和f()不需要任何條件(即使它們被定義為private)。這個例子同時展示了如何從不同的類里面創建多層嵌套的內部類對象的基本語法。“.new”語法能產生正確的作用域,所以你不必在調用構造器時限定類名。
內部類的重載問題
如果你創建了一個內部類,然后繼承其外圍類并重新定義此內部類時,會發生什么呢?也就是說,內部類可以被重載嗎?這看起來似乎是個很有用的點子,但是“重載”內部類就好像它是外圍類的一個方法,其實并不起什么作用:
class Egg { private Yolk y;
protectedclass Yolk { public Yolk(){ System.out.println(“Egg.Yolk()”);} }
public Egg(){ System.out.println(“New Egg()”);y = new Yolk();} }
publicclass BigEgg extends Egg { publicclass Yolk { public Yolk(){ System.out.println(“BigEgg.Yolk()”);} }
publicstaticvoid main(String[] args){ new BigEgg();} }
輸出結果為:
New Egg()Egg.Yolk()
缺省的構造器是編譯器自動生成的,這里是調用基類的缺省構造器。你可能認為既然創建了BigEgg 的對象,那么所使用的應該是被“重載”過的Yolk,但你可以從輸出中看到實際情況并不是這樣的。
這個例子說明,當你繼承了某個外圍類的時候,內部類并沒有發生什么特別神奇的變化。這兩個內部類是完全獨立的兩個實體,各自在自己的命名空間內。當然,明確地繼承某個內部類也是可以的:
class Egg2 { protected class Yolk { public Yolk(){ System.out.println(“Egg2.Yolk()”);}
public void f(){ System.out.println(“Egg2.Yolk.f()”);} }
private Yolk y = new Yolk();
public Egg2(){ System.out.println(“New Egg2()”);}
public void insertYolk(Yolk yy){ y = yy;}
public void g(){ y.f();} }
public class BigEgg2 extends Egg2 { public class Yolk extends Egg2.Yolk { public Yolk(){ System.out.println(“BigEgg2.Yolk()”);}
public void f(){ System.out.println(“BigEgg2.Yolk.f()”);} }
public BigEgg2(){ insertYolk(new Yolk());}
public static void main(String[] args){ Egg2 e2 = new BigEgg2();e2.g();} }
輸出結果為:
Egg2.Yolk()New Egg2()Egg2.Yolk()BigEgg2.Yolk()BigEgg2.Yolk.f()
現在BigEgg2.Yolk 通過extends Egg2.Yolk 明確地繼承了此內部類,并且重載了其中的方法。Egg2 的insertYolk()方法使得BigEgg2 將它自己的Yolk 對象向上轉型,然后傳遞給引用y。所以當g()調用y.f()時,重載后的新版的f()被執行。第二次調用Egg2.Yolk()是 BigEgg2.Yolk 的構造器調用了其基類的構造器。可以看到在調用g()的時候,新版的f()被調用了。
內部類的繼承問題(thinking in java 3th p294)
因為內部類的構造器要用到其外圍類對象的引用,所以在你繼承一個內部類的時候,事情變得有點復雜。問題在于,那個“秘密的”外圍類對象的引用必須被初始化,而在被繼承的類中并不存在要聯接的缺省對象。要解決這個問題,需使用專門的語法來明確說清它們之間的關聯:
class WithInner { class Inner { Inner(){ System.out.println(“this is a constructor in WithInner.Inner”);};} }
public class InheritInner extends WithInner.Inner { //!InheritInner(){} // Won't compile InheritInner(WithInner wi){ wi.super();System.out.println(“this is a constructor in InheritInner”);}
public static void main(String[] args){ WithInner wi = new WithInner();InheritInner ii = new InheritInner(wi);} }
輸出結果為:
this is a constructor in WithInner.Inner this is a constructor in InheritInner
可以看到,InheritInner 只繼承自內部類,而不是外圍類。但是當要生成一個構造器時,缺省的構造器并不算好,而且你不能只是傳遞一個指向外圍類對象的引用。此外,你必須在構造器內使用如下語法:
enclosingClassReference.super();
這樣才提供了必要的引用,然后程序才能編譯通過。
1.實例化非靜態內部類時是否需要先實例化外部類,為什么?
2.非靜態內部類可以訪問其外部類的哪些成員,為什么?
3.內部類可以定義在外部類的哪些位置,要實例化各位置的內部類有哪些限制? 4.匿名內部類和普通內部類有什么區別,為什么創建匿名內部類時可以new抽象類或接口?
5.如何在匿名內部類實現構造器的行為?
6.嵌套類(靜態內部類)和普通非靜態類有什么區別?
7.當繼承一個類時,如何覆蓋它的內部類?
回答:
1.實例化非靜態內部類需要先實例化外部類,因為非靜態內部類就像外部類的實例域一樣,和外部類是有關聯的。
2.非靜態內部類可以訪問其外部類的所有成員,因為在實例化內部類時,會隱式的保留其外部類的實例的一個引用,通過該引用來訪問外部類的成員,而內部類是在外部類的里面,所以不受訪問修飾符的限制。
3.內部類可以定義在外部類的內部、方法體、局部域,在內部的類可以通過外部類的實例來實例化,方法體和局部域 只能在定義的域實例化。4.匿名內部類沒有類名,沒有構造函數,當在匿名內部類里使用外部類定義的對象時,需要用final修改該對象,匿名
內部類只能單繼承單實現。匿名內部類new的并非對象本身,而是作為其子類實例化,所以new抽象類或接口時,實際上是實例化該抽象類的一個子類或實現該接口的一個類。
5.通過實例域初始化(在內部類加一對花括號)來實現匿名內部類的構造器行為。
6.1)嵌套類不保留其外部類的實例的引用,因為嵌套類和外部沒有任何關系。2)實例化嵌套類不需要先實例化其外部類,直接使用嵌套類的限定名進行實例化(如果嵌套類的訪問級別允許)3)內部類不能包含靜態域和嵌套類,嵌套類可以。
7.繼承一個A類時,對于A類的內部類不會有什么影響變化,如果要覆蓋A類的內部類,則可以定義一個內部類繼承A類的內部類。
第二篇:JAVA 內部類的簡單總結
核心提示:定義在一個類內部的類叫內部類,包含內部類的類稱為外部類。內部類提供更好的封裝,可以把內部類隱藏在外部類之內,不允許同一個包中其他類訪問。內部類可以聲明public、protected、private等訪問限制,可以聲明為abstract的供其他內部類或外部類繼承與擴展定義在一個類內部的類叫內部類,包含內部類的類稱為外部類。內部類提供更好的封裝,可以把內部類隱藏在外部類之內,不允許同一個包中其他類訪問。內部類可以聲明public、protected、private等訪問限制,可以聲明為abstract的供其他內部類或外部類繼承與擴展,或者聲明為static、final的,也可以實現特定的接口。static的內部類行為上象一個獨立的類,非static在行為上類似類的屬性或方法且禁止聲明static的方法。內部類可以訪問外部類的所有方法與屬性,內部類被單成其外部類的成員,同一個類的成員之間可以相互訪問。但外部類不能訪問內部類的實現細節,如內部類的屬性。static的內部類只能訪問外部類的靜態屬性與方法。匿名內部類適合用于創建那些僅需要一次使用的類。
內部類被作為成員內部類定義,而且不是局部內部類。成員內部類是一種與屬性、方法、構造器和初始化塊相似的類成員;局部內部類和匿名內部類則不是類成員。成員內部類分為兩種:靜態內部類(使用static修飾)和非靜態內部類,當在非靜態內部類的方法訪問某個變量時,系統優先在該方法內查找是否在該名字的局部變量,如果存在改名字的局部變量,就是用改變量;如果不存在則到內部類所在的外部類中查找是否存在改名字的屬性,如果存在就是用。如果不存在編譯錯誤。
如外部類屬性、內部類屬性與內部類里方法的局部變量通明,則通過使用this,外部類類名.this作為限制類區分。
1.public class DiscernVariable
2.{
3.private String prop = “外部類屬性”;
4.private class InClass
5.{
6.private String prop = “內部類屬性”;
7.public void info()
8.{
9.String prop = “局部變量”;
10.//通過 外部類類名.this.varName 訪問外部類實例屬性
11.System.out.println(“外部類的屬性值:” + DiscernVariable.this.prop);
12.//通過 this.varName 訪問外內部類實例的屬性
13.System.out.println(“內部類的屬性值:” + this.prop);
14.//直接訪問局部變量
15.System.out.println(“局部變量的屬性值:” + prop);
16.}
17.}
18.public void test()
19.{
20.InClass in = new InClass();
21.in.info();
22.}
23.public static void main(String[] args)
24.{
25.new DiscernVariable().test();
26.}
27.}
非靜態內部類的成員可以訪問外部類的private成員,但反過來不可以。非靜態內部類成員只在非靜態內部類范圍內是可知的,并不能被外部類直接使用。如果外部類需要訪問非靜態內部類成員,則必須相示創建非靜態內部類對象來調用訪問其實例成員。
1.public class Outer
2.{
3.private int outProp = 9;
4.class Inner
5.{
6.private int inProp = 5;
7.public void acessOuterProp()
8.{
9.//內部類可以直接訪問外部類的成員
10.System.out.println(“外部類的outProp屬性值:” + outProp);
11.}
12.}
13.public void accessInnerProp()
14.{
15.//外部類不能直接訪問內部類屬性,下面代碼出現編譯錯誤
16.//System.out.println(“內部類的inProp屬性值:” + inProp);
17.//如需訪問內部類成員,必須顯式創建內部類對象
18.System.out.println(“內部類的inProp屬性值:” + new Inner().inProp);19.20.}
21.public static void main(String[] args)
22.{
23.//執行下面代碼,只創建了外部類對象,還未創建內部類對象
24.Outer out = new Outer();
25.}
26.}
外部類按常規的類訪問方式使用內部類,唯一的差別是外部類可以訪問內部類的所有方法與屬性,包括私有方法與屬性。如:
1.pinner p = new pinner();
2.p.index = 20;
3.p.Print();
4.----這種方式適合外部類的非static方法;
5.6.pouter po = new pouter();
7.pinner pi = po.new pinner();
8.pi.index = 40;
9.pi.Print();
10.----這種方式適合外部類的static方法;
內部類類似外部類的屬性,因此訪問內部類對象時總是需要一個創建好的外部類對象。內部類對象通過‘外部類名.this.xxx’的形式訪問外部類的屬性與方法。如:
1.System.out.println(“Print in inner Outer.index=” + pouter.this.index);
2.System.out.println(“Print in inner Inner.index=” + this.index);
如果需要在其他類中訪問內部類,可以使用:
(1)外部類提供創建內部類的方法供其他類使用。如:
1.// 外部類
2.pinner getInner()
3.{
4.return new pinner();
5.}
6.7.// 其他類
8.pouter.pinner pi = po.getInner();
9.pi.Print();
(2)直接創建內部類的對象。如:
1.pouter po = new pouter();
2.pouter.pinner pi = po.new pinner();
3.pi.Print();
內部類可以聲明在外部類的方法中或語句塊中。如果內部類需要訪問包含它的外部類方法或語句塊的局部變量或參數,則該局部變量或參數必須是final的。外部類的其他方法、其他類無法訪問聲明在方法內部或塊內部的內部類。
如果一個類繼承內部類,則創建該類的對象時需提供一個外部類的對象作為構造方法的參數。如:
1.class Car
2.{
3.class Wheel
4.{
5.6.}
7.}
8.9.class SuperWheel extends Car.Wheel
10.{
11.SuperWheel(Car car)
12.{
13.car.super();
14.}
15.16.public static void main(String [] args)
17.{
18.Car car = new Car();
19.SuperWheel wl = new SuperWheel(car);
20.}
21.}
如果創建命名的內部類沒有多少實際意義時,可以創建匿名的內部類。比如使用內部類實現接口的功能(如事件處理器、適配器等),而功能的差異較大,需要根據實際的情況創建相應的內部類時,可以使用匿名內部類。簡單的示例如下:
1.interface WebView
2.{
3.void doGet();
4.}
5.6.class A
7.{
8.WebView ShowName()
9.{
10.return new WebView()
11.{
12.void doGet()
13.{
14.System.out.println(“Name”);
15.}
16.};
17.}
18.19.WebView ShowCode()
20.{
21.return new WebView()
22.{
23.void doGet()
24.{
25.System.out.println(“Code”);
26.}
27.};
28.}
29.}
最后,JAVA 內部類還有一個作用,那就是實現JAVA的多繼承。JAVA本身是不允許多繼承的,如果我們想一個類繼承多個基類,就可以使用內部類。通過內部類分別繼承一個基類,外部類創建內部類的對象,并使用內部類的方法,變相地實現了多繼承。
第三篇:java_內部類總結
內部類
內部類是指在一個外部類的內部再定義一個類。內部類作為外部類的一個成員,并且依附于外部類而存在的。內部類可為靜態,可用protected和private修飾(而外部類只能使用public和缺省的包訪問權限)。內部類主要有以下幾類:成員內部類、局部內部類、靜態內部類、匿名內部類
為什么需要內部類? 典型的情況是,內部類繼承自某個類或實現某個接口,內部類的代碼操作創建其的外圍類的對象。所以你可以認為內部類提供了某種進入其外圍類的窗口。使用內部類最吸引人的原因是:
每個內部類都能獨立地繼承自一個(接口的)實現,所以無論外圍類是否已經繼承了某個(接口的)實現,對于內部類都沒有影響。如果沒有內部類提供的可以繼承多個具體的或抽象的類的能力,一些設計與編程問題就很難解決。從這個角度看,內部類使得多重繼承的解決方案變得完整。接口解決了部分問題,而內部類有效地實現了“多重繼承”。
A:成員內部類
作為外部類的一個成員存在,與外部類的屬性、方法并列。
publicclass Outer { privatestaticinti = 1;privateintj = 10;privateintk = 20;
publicstaticvoidouter_f1(){ }
publicvoidouter_f2(){ }
// 成員內部類中,不能定義靜態成員
// 成員內部類中,可以訪問外部類的所有成員
class Inner { // static int inner_i = 100;//內部類中不允許定義靜態變量
intj = 100;// 內部類和外部類的實例變量可以共存
intinner_i = 1;
void inner_f1(){ System.out.println(i);
//在內部類中訪問內部類自己的變量直接用變量名
System.out.println(j);
//在內部類中訪問內部類自己的變量也可以用this.變量名
System.out.println(this.j);
//在內部類中訪問外部類中與內部類同名的實例變量用外部類名.this.變量名
System.out.println(Outer.this.j);
//如果內部類中沒有與外部類同名的變量,則可以直接用變量名訪問外部類變量
System.out.println(k);outer_f1();outer_f2();} }
//外部類的非靜態方法訪問成員內部類
publicvoidouter_f3(){ Inner inner = new Inner();inner.inner_f1();}
// 外部類的靜態方法訪問成員內部類,與在外部類外部訪問成員內部類一樣
publicstaticvoidouter_f4(){ //step1 建立外部類對象
Outer out = new Outer();//step2 根據外部類對象建立內部類對象
Inner inner = out.new Inner();//step3 訪問內部類的方法
inner.inner_f1();}
publicstaticvoid main(String[] args){ //outer_f4();//該語句的輸出結果和下面三條語句的輸出結果一樣
//如果要直接創建內部類的對象,不能想當然地認為只需加上外圍類Outer的名字,//就可以按照通常的樣子生成內部類的對象,而是必須使用此外圍類的一個對象來
//創建其內部類的一個對象:
//Outer.Inner outin = out.new Inner()
//因此,除非你已經有了外圍類的一個對象,否則不可能生成內部類的對象。因為此
//內部類的對象會悄悄地鏈接到創建它的外圍類的對象。如果你用的是靜態的內部類,//那就不需要對其外圍類對象的引用。
Outer out = new Outer();Outer.Inner outin = out.new Inner();outin.inner_f1();} }
注意:內部類是一個編譯時的概念,一旦編譯成功,就會成為完全不同的兩類。對于一個名為outer的外部類和其內部定義的名為inner的內部類。編譯完成后出現outer.class和outer$inner.class兩類。
B:局部內部類
在方法中定義的內部類稱為局部內部類。與局部變量類似,局部內部類不能有訪問說明符,因為它不是外圍類的一部分,但是它可以訪問當前代碼塊內的常量,和此外圍類所有的成員。
publicclass Outer { privateints = 100;privateintout_i = 1;
publicvoid f(finalint k){ finalint s = 200;int i = 1;finalint j = 10;
//定義在方法內部
class Inner { ints = 300;// 可以定義與外部類同名的變量
// static int m = 20;//不可以定義靜態變量
Inner(int k){ inner_f(k);}
intinner_i = 100;
voidinner_f(int k){
//如果內部類沒有與外部類同名的變量,在內部類中可以直接訪問外部類的實例變量
System.out.println(out_i);
//可以訪問外部類的局部變量(即方法內的變量),但是變量必須是final的System.out.println(j);//System.out.println(i);
//如果內部類中有與外部類同名的變量,直接用變量名訪問的是內部類的變量
System.out.println(s);
//用this.變量名訪問的也是內部類變量
System.out.println(this.s);
//用外部類名.this.內部類變量名訪問的是外部類變量
System.out.println(Outer.this.s);} } new Inner(k);}
publicstaticvoid main(String[] args){ // 訪問局部內部類必須先有外部類對象
Outer out = new Outer();out.f(3);} }
C:靜態內部類(嵌套類):(注意:前兩種內部類與變量類似,所以可以對照參考變量)如果你不需要內部類對象與其外圍類對象之間有聯系,那你可以將內部類聲明為static。這通常稱為嵌套類(nested class)。想要理解static應用于內部類時的含義,你就必須記住,普通的內部類對象隱含地保存了一個引用,指向創建它的外圍類對象。然而,當內部類是static的時,就不是這樣了。嵌套類意味著:
1.要創建嵌套類的對象,并不需要其外圍類的對象。2.不能從嵌套類的對象中訪問非靜態的外圍類對象。
publicclass Outer { privatestaticinti = 1;privateintj = 10;publicstaticvoidouter_f1(){ }
publicvoidouter_f2(){ }
// 靜態內部類可以用public,protected,private修飾
// 靜態內部類中可以定義靜態或者非靜態的成員
staticclass Inner { staticintinner_i = 100;intinner_j = 200;staticvoidinner_f1(){ //靜態內部類只能訪問外部類的靜態成員(包括靜態變量和靜態方法)
System.out.println(“Outer.i” + i);outer_f1();}
voidinner_f2(){ // 靜態內部類不能訪問外部類的非靜態成員(包括非靜態變量和非靜態方法)
// System.out.println(“Outer.i”+j);
// outer_f2();
} }
publicvoidouter_f3(){ // 外部類訪問內部類的靜態成員:內部類.靜態成員
System.out.println(Inner.inner_i);Inner.inner_f1();// 外部類訪問內部類的非靜態成員:實例化內部類即可
Inner inner = new Inner();inner.inner_f2();}
publicstaticvoid main(String[] args){ newOuter().outer_f3();} }
生成一個靜態內部類不需要外部類成員:這是靜態內部類和成員內部類的區別。靜態內部類的對象可以直接生成:Outer.Inner in = new Outer.Inner();而不需要通過生成外部類對象來生成。這樣實際上使靜態內部類成為了一個頂級類(正常情況下,你不能在接口內部放置任何代碼,但嵌套類可以作為接口的一部分,因為它是static 的。只是將嵌套類置于接口的命名空間內,這并不違反接口的規則)
D:匿名內部類(from thinking in java 3th)
簡單地說:匿名內部類就是沒有名字的內部類。什么情況下需要使用匿名內部類?如果滿足下面的一些條件,使用匿名內部類是比較合適的:
·只用到類的一個實例。
·類在定義后馬上用到。
·類非常小(SUN推薦是在4行代碼以下)
·給類命名并不會導致你的代碼更容易被理解。
在使用匿名內部類時,要記住以下幾個原則:
·匿名內部類不能有構造方法。
·匿名內部類不能定義任何靜態成員、方法和類。
·匿名內部類不能是public,protected,private,static。
·只能創建匿名內部類的一個實例。
·一個匿名內部類一定是在new的后面,用其隱含實現一個接口或實現一個類。
·因匿名內部類為局部內部類,所以局部內部類的所有限制都對其生效。
下面的例子看起來有點奇怪:
//在方法中返回一個匿名內部類 public class Parcel6 { public Contents cont(){ return new Contents(){ private int i = 11;public int value(){
return i;
}
};// 在這里需要一個分號
}
public static void main(String[] args){ Parcel6 p = new Parcel6();Contents c = p.cont();} } cont()方法將下面兩個動作合并在一起:返回值的生成,與表示這個返回值的類的定義!進一步說,這個類是匿名的,它沒有名字。更糟的是,看起來是你正要創建一個Contents對象:
return new Contents()但是,在到達語句結束的分號之前,你卻說:“等一等,我想在這里插入一個類的定義”:
return new Contents(){ private int i = 11;public int value(){ return i;} };這種奇怪的語法指的是:“創建一個繼承自Contents的匿名類的對象。”通過new 表達式返回的引用被自動向上轉型為對Contents的引用。匿名內部類的語法是下面例子的簡略形式:
class MyContents implements Contents { private int i = 11;public int value(){ return i;} } return new MyContents();在這個匿名內部類中,使用了缺省的構造器來生成Contents。下面的代碼展示的是,如果你的基類需要一個有參數的構造器,應該怎么辦:
public class Parcel7 { public Wrapping wrap(int x){ // Base constructor call: return new Wrapping(x){ // Pass constructor argument.public int value(){
return super.value()* 47;
}
};// Semicolon required } public static void main(String[] args){ Parcel7 p = new Parcel7();Wrapping w = p.wrap(10);} } 只需簡單地傳遞合適的參數給基類的構造器即可,這里是將x 傳進new Wrapping(x)。在匿名內部類末尾的分號,并不是用來標記此內部類結束(C++中是那樣)。實際上,它標記的是表達式的結束,只不過這個表達式正巧包含了內部類罷了。因此,這與別的地方使用的分號是一致的。
如果在匿名類中定義成員變量,你同樣能夠對其執行初始化操作:
public class Parcel8 { // Argument must be final to use inside // anonymous inner class: public Destination dest(final String dest){ return new Destination(){ private String label = dest;public String readLabel(){ return label;} };} public static void main(String[] args){ Parcel8 p = new Parcel8();Destination d = p.dest(“Tanzania”);} }
如果你有一個匿名內部類,它要使用一個在它的外部定義的對象,編譯器會要求其參數引用是final 型的,就像dest()中的參數。如果你忘記了,會得到一個編譯期錯誤信息。如果只是簡單地給一個成員變量賦值,那么此例中的方法就可以了。但是,如果你想做一些類似構造器的行為,該怎么辦呢?在匿名類中不可能有已命名的構造器(因為它根本沒名字!),但通過實例初始化,你就能夠達到為匿名內部類“制作”一個構造器的效果。像這樣做:
abstract class Base { public Base(int i){ System.out.println(“Base constructor, i = ” + i);} public abstract void f();}
public class AnonymousConstructor { public static Base getBase(int i){ return new Base(i){
{
System.out.println(“Inside instance initializer”);
}
public void f(){
System.out.println(“In anonymous f()”);
}
};} public static void main(String[] args){ Base base = getBase(47);base.f();} }
在此例中,不要求變量i 一定是final 的。因為i 被傳遞給匿名類的基類的構造器,它并不會在匿名類內部被直接使用。下例是帶實例初始化的“parcel”形式。注意dest()的參數必須是final,因為它們是在匿名類內被使用的。
public class Parcel9 { public Destinationdest(final String dest, final float price){ return new Destination(){
private int cost;
// Instance initialization for each object:
{
cost = Math.round(price);if(cost > 100)
System.out.println(“Over budget!”);
}
private String label = dest;
public String readLabel(){ return label;} };
} public static void main(String[] args){ Parcel9 p = new Parcel9();Destination d = p.dest(“Tanzania”, 101.395F);} } 在實例初始化的部分,你可以看到有一段代碼,那原本是不能作為成員變量初始化的一部分而執行的(就是if 語句)。所以對于匿名類而言,實例初始化的實際效果就是構造器。當然它受到了限制:你不能重載實例初始化,所以你只能有一個構造器。
從多層嵌套類中訪問外部
一個內部類被嵌套多少層并不重要,它能透明地訪問所有它所嵌入的外圍類的所有成員,如下所示:
class MNA { private void f(){} class A { private void g(){} public class B { void h(){ g();f();} } } } public class MultiNestingAccess { public static void main(String[] args){ MNA mna = new MNA();MNA.A mnaa = mna.new A();MNA.A.B mnaab = mnaa.new B();mnaab.h();} } 可以看到在MNA.A.B中,調用方法g()和f()不需要任何條件(即使它們被定義為private)。這個例子同時展示了如何從不同的類里面創建多層嵌套的內部類對象的基本語法。“.new”語法能產生正確的作用域,所以你不必在調用構造器時限定類名。
內部類的重載問題
如果你創建了一個內部類,然后繼承其外圍類并重新定義此內部類時,會發生什么呢?也就是說,內部類可以被重載嗎?這看起來似乎是個很有用的點子,但是“重載”內部類就好像它是外圍類的一個方法,其實并不起什么作用:
class Egg { private Yolk y;
protectedclass Yolk { public Yolk(){ System.out.println(“Egg.Yolk()”);} }
public Egg(){ System.out.println(“New Egg()”);y = new Yolk();} }
publicclass BigEgg extends Egg { publicclass Yolk { public Yolk(){ System.out.println(“BigEgg.Yolk()”);} }
publicstaticvoid main(String[] args){ new BigEgg();} }
輸出結果為: New Egg()Egg.Yolk()
缺省的構造器是編譯器自動生成的,這里是調用基類的缺省構造器。你可能認為既然創建了BigEgg 的對象,那么所使用的應該是被“重載”過的Yolk,但你可以從輸出中看到實際情況并不是這樣的。這個例子說明,當你繼承了某個外圍類的時候,內部類并沒有發生什么特別神奇的變化。這兩個內部類是完全獨立的兩個實體,各自在自己的命名空間內。當然,明確地繼承某個內部類也是可以的:
class Egg2 { protected class Yolk { public Yolk(){ System.out.println(“Egg2.Yolk()”);}
public void f(){ System.out.println(“Egg2.Yolk.f()”);} }
private Yolk y = new Yolk();
public Egg2(){ System.out.println(“New Egg2()”);}
public void insertYolk(Yolk yy){ y = yy;}
public void g(){ y.f();} }
public class BigEgg2 extends Egg2 { public class Yolk extends Egg2.Yolk { public Yolk(){ System.out.println(“BigEgg2.Yolk()”);}
public void f(){ System.out.println(“BigEgg2.Yolk.f()”);} }
public BigEgg2(){ insertYolk(new Yolk());}
public static void main(String[] args){ Egg2 e2 = new BigEgg2();e2.g();} }
輸出結果為: Egg2.Yolk()New Egg2()Egg2.Yolk()BigEgg2.Yolk()BigEgg2.Yolk.f()
現在BigEgg2.Yolk 通過extends Egg2.Yolk 明確地繼承了此內部類,并且重載了其中的方法。Egg2 的insertYolk()方法使得BigEgg2 將它自己的Yolk 對象向上轉型,然后傳遞給引用y。所以當g()調用y.f()時,重載后的新版的f()被執行。第二次調用Egg2.Yolk()是BigEgg2.Yolk 的構造器調用了其基類的構造器。可以看到在調用g()的時候,新版的f()被調用了。
內部類的繼承問題(thinking in java 3th p294)
因為內部類的構造器要用到其外圍類對象的引用,所以在你繼承一個內部類的時候,事情變得有點復雜。問題在于,那個“秘密的”外圍類對象的引用必須被初始化,而在被繼承的類中并不存在要聯接的缺省對象。要解決這個問題,需使用專門的語法來明確說清它們之間的關聯:
class WithInner {
class Inner { Inner(){ System.out.println(“this is a constructor in WithInner.Inner”);};} }
public class InheritInner extends WithInner.Inner {
//!InheritInner(){} // Won't compile InheritInner(WithInner wi){ wi.super();System.out.println(“this is a constructor in InheritInner”);}
public static void main(String[] args){ WithInner wi = new WithInner();InheritInner ii = new InheritInner(wi);} }
輸出結果為:
this is a constructor in WithInner.Inner this is a constructor in InheritInner
可以看到,InheritInner 只繼承自內部類,而不是外圍類。但是當要生成一個構造器時,缺省的構造器并不算好,而且你不能只是傳遞一個指向外圍類對象的引用。此外,你必須在構造器內使用如下語法:
enclosingClassReference.super();這樣才提供了必要的引用,然后程序才能編譯通過。
第四篇:內部類的問題相關(最終版)
內部類的問題相關
Java內部類的使用小結
內部類是指在一個外部類的內部再定義一個類。類名不需要和文件夾相同。
*內部類可以是靜態static的,也可用public,default,protected和private修飾。(而外部頂級類即類名和文件名相同的只能使用public和default)。
注意:內部類是一個編譯時的概念,一旦編譯成功,就會成為完全不同的兩類。對于一個名為outer的外部類和其內部定義的名為inner的內部類。編譯完成后出現outer.class和outer$inner.class兩類。所以內部類的成員變量/方法名可以和外部類的相同。
1.成員內部類
成員內部類,就是作為外部類的成員,可以直接使用外部類的所有成員和方法,即使是private的。同時外部類要訪問內部類的所有成員變量/方法,則需要通過內部類的對象來獲取。
要注意的是,成員內部類不能含有static的變量和方法。因為成員內部類需要先創建了外部類,才能創建它自己的,了解這一點,就可以明白更多事情,在此省略更多的細節了。在成員內部類要引用外部類對象時,使用outer.this來表示外部類對象;而需要創建內部類對象,可以使用outer.innerobj = outerobj.new inner();public class Outer {
public static void main(String[] args){
Outer outer = new Outer();
Outer.Inner inner = outer.new Inner();
inner.print(“Outer.new”);
inner = outer.getInner();
inner.print(“Outer.get”);
}
// 個人推薦使用getxxx()來獲取成員內部類,尤其是該內部類的構造函數無參數時public Inner getInner(){
return new Inner();
}
public class Inner {
public void print(String str){
System.out.println(str);
}
}
}
2.局部內部類
局部內部類,是指內部類定義在方法和作用域內。Thinking in Java給了這么兩個例子: 定義在方法內:
public class Parcel4 {
public Destination destination(String s){
class PDestination implements Destination {
private String label;
private PDestination(String whereTo){
label = whereTo;
}
public String readLabel(){
return label;
}
}
return new PDestination(s);
}
public static void main(String[] args){
Parcel4 p = new Parcel4();
Destination d = p.destination(“Tasmania”);
}
}
定義在作用域里:
public class Parcel5 {
private void internalTracking(boolean b){
if(b){
class TrackingSlip {
private String id;
TrackingSlip(String s){
id = s;
}
String getSlip(){
return id;
}
}
TrackingSlip ts = new TrackingSlip(“slip”);
String s = ts.getSlip();
}
}
public void track(){
internalTracking(true);
}
public static void main(String[] args){
Parcel5 p = new Parcel5();
p.track();
}
}
局部內部類也像別的類一樣進行編譯,但只是作用域不同而已,只在該方法或條件的作用域內才能使用,退出這些作用域后無法引用的。
3.嵌套內部類
嵌套內部類,就是修飾為static的內部類。聲明為static的內部類,不需要內部類對象和外部類對象之間的聯系,就是說我們可以直接引用outer.inner,即不需要創建外部類,也不需要創建內部類。
嵌套類和普通的內部類還有一個區別:普通內部類不能有static數據和static屬性,也不能包含嵌套類,但嵌套類可以。而嵌套類不能聲明為private,一般聲明為public,方便調用。
4.匿名內部類
有時候我為了免去給內部類命名,便傾向于使用匿名內部類,因為它沒有名字。例如:((Button)findViewById(R.id.start)).setOnClickListener(new Button.OnClickListener(){@Override
public void onClick(View v){
new Thread(){
@Override
public void run(){
// TODO Auto-generated method stub
}
}.start();
}
});
匿名內部類是不能加訪問修飾符的。要注意的是,new 匿名類,這個類是要先定義的,看下面例子:
public class Outer {
public static void main(String[] args){
Outer outer = new Outer();
Inner inner = outer.getInner(“Inner”, “gz”);
System.out.println(inner.getName());
}
public Inner getInner(final String name, String city){
return new Inner(){
private String nameStr = name;
public String getName(){
return nameStr;
}
};
}
}
//注釋后,編譯時提示類Inner找不到
/* interface Inner {
String getName();
} */
同時在這個例子,留意外部類的方法的形參,當所在的方法的形參需要被內部類里面使用時,該形參必須為final。這里可以看到形參name已經定義為final了,而形參city 沒有被使用則不用定義為final。為什么要定義為final呢?在網上找到本人比較如同的解釋: “這是一個編譯器設計的問題,如果你了解java的編譯原理的話很容易理解。
首先,內部類被編譯的時候會生成一個單獨的內部類的.class文件,這個文件并不與外部類在同一class文件中。
當外部類傳的參數被內部類調用時,從java程序的角度來看是直接的調用例如:public void dosome(final String a,final int b){
class Dosome{public void dosome(){System.out.println(a+b)}};
Dosome some=new Dosome();
some.dosome();
}
從代碼來看好像是那個內部類直接調用的a參數和b參數,但是實際上不是,在java編譯器編譯以后實際的操作代碼是
class Outer$Dosome{
public Dosome(final String a,final int b){
this.Dosome$a=a;
this.Dosome$b=b;
}
public void dosome(){
System.out.println(this.Dosome$a+this.Dosome$b);
}
}}
從以上代碼看來,內部類并不是直接調用方法傳進來的參數,而是內部類將傳進來的參數通過自己的構造器備份到了自己的內部,自己內部的方法調用的實際是自己的屬性而不是外部類方法的參數。
這樣理解就很容易得出為什么要用final了,因為兩者從外表看起來是同一個東西,實際上卻不是這樣,如果內部類改掉了這些參數的值也不可能影響到原參數,然而這樣卻失去了參數的一致性,因為從編程人員的角度來看他們是同一個東西,如果編程人員在程序設計的時候在內部類中改掉參數的值,但是外部調用的時候又發現值其實沒有被改掉,這就讓人非常的難以理解和接受,為了避免這種尷尬的問題存在,所以編譯器設計人員把內部類能夠使用的參數設定為必須是final來規避這種莫名其妙錯誤的存在。”
(簡單理解就是,拷貝引用,為了避免引用值發生改變,例如被外部類的方法修改等,而導致內部類得到的值不一致,于是用final來讓該引用不可改變)
因為匿名內部類,沒名字,是用默認的構造函數的,無參數的,那如果需要參數呢?則需要該類有帶參數的構造函數:
public class Outer {
public static void main(String[] args){
Outer outer = new Outer();
Inner inner = outer.getInner(“Inner”, “gz”);
System.out.println(inner.getName());
}
public Inner getInner(final String name, String city){
return new Inner(name, city){
private String nameStr = name;
public String getName(){
return nameStr;
}
};
}
}
abstract class Inner {
Inner(String name, String city){
System.out.println(city);
}
abstract String getName();
}
注意這里的形參city,由于它沒有被匿名內部類直接使用,而是被抽象類Inner的構造函數所使用,所以不必定義為final。
而匿名內部類通過實例初始化,可以達到類似構造器的效果:
public class Outer {
public static void main(String[] args){
Outer outer = new Outer();
Inner inner = outer.getInner(“Inner”, “gz”);
System.out.println(inner.getName());
System.out.println(inner.getProvince());
}
public Inner getInner(final String name, final String city){
return new Inner(){
private String nameStr = name;
private String province;
// 實例初始化
{
if(city.equals(“gz”)){
province = “gd”;
}else {
province = "";
}
}
public String getName(){
return nameStr;
}
public String getProvince(){
return province;
}
};
}
}
interface Inner {
String getName();
String getProvince();
}
5.內部類的繼承
內部類的繼承,是指內部類被繼承,普通類 extents 內部類。而這時候代碼上要有點特
別處理。
至于內部類的重載,感覺Thinking in Java的例子很復雜,在平常應用中應該很少,因為有點難懂,不清晰。而內部類和閉包之間的事情,暫時放下,以后再看。
第五篇:部類如何造句
【注音】: bu lei
【意思】:概括性較大的類。
部類造句:
1、馬克思的兩部類分析模式,對現實經濟仍然具有指導作用。
2、這里所說的是實現細節了,我們可以直接訪問外部類中的變量,就好像這些變量定義在內部類中一樣。
3、技術進步、增加工資、對外貿易都減緩兩部類生產不均衡,延緩經濟危機發生。
4、一旦定義了這些訪問限制,Eclipse這個Java訪問工具就會對內部類的訪問提供和其他編譯器警告一樣的提示。
5、史部成為獨立的部類經過了一個很長的發展過程。
6、安全科學是介于數學、自然科學、系統科學與哲學、社會科學、思維科學各科學部類之間的新興交叉學科門類。
7、要對城市休閑產業集聚進行實證性研究,就必須在對休閑產業部類劃分的前提下進行。
8、本文的目的在于改進連續桿戰斗部類導彈殺傷概率的計算模型。
9、實際上,成員完全定義在類中的要求限制了局部類成員函數的復雜性。
10、如果把不同部類的增長情況進行分類的話,大部分出口價格從2001年以來的增長都可以歸咎于燃料。
11、外圍函數對局部類的私有成員沒有特殊訪問權,當然,局部類可以將外圍函數設為友元。
12、我們擁有著自己的愛人和部類;擁有我們曾經吞咽過的美味;
13、但我們死的好富有。我們擁有著自己的愛人和部類;擁有我們曾經吞咽過的美味;
14、我們擁有著自己的愛人和部類;擁有我們曾經吞咽過的美味;擁有我們進入的身軀,我們在其中就像在河里游啊游。