java 成员函数相互调用_java 深入理解内部类以及之间的调用关系
什么是內(nèi)部類
內(nèi)部類是指在一個(gè)外部類的內(nèi)部再定義一個(gè)類。內(nèi)部類作為外部類的一個(gè)成員,并且依附于外部類而存在的。內(nèi)部類可為靜態(tài),可用protected和private修飾(而外部類只能使用public和缺省的包訪問權(quán)限)。內(nèi)部類主要有以下幾類:成員內(nèi)部類、局部內(nèi)部類、靜態(tài)內(nèi)部類、匿名內(nèi)部類
內(nèi)部類的共性
(1)、內(nèi)部類仍然是一個(gè)獨(dú)立的類,在編譯之后內(nèi)部類會被編譯成獨(dú)立的.class文件,但是前面冠以外部類的類名和$符號 。
(2)、內(nèi)部類不能用普通的方式訪問。
(3)、內(nèi)部類聲明成靜態(tài)的,就不能隨便的訪問外部類的成員變量了,此時(shí)內(nèi)部類只能訪問外部類的靜態(tài)成員變量?。
(4)、外部類不能直接訪問內(nèi)部類的的成員,但可以通過內(nèi)部類對象來訪問
內(nèi)部類是外部類的一個(gè)成員,因此內(nèi)部類可以自由地訪問外部類的成員變量,無論是否是private的 。
因?yàn)楫?dāng)某個(gè)外圍類的對象創(chuàng)建內(nèi)部類的對象時(shí),此內(nèi)部類會捕獲一個(gè)隱式引用,它引用了實(shí)例化該內(nèi)部對象的外圍類對象。通過這個(gè)指針,可以訪問外圍類對象的全部狀態(tài)。
通過反編譯內(nèi)部類的字節(jié)碼, 分析之后主要是通過以下幾步做到的:
1 編譯器自動為內(nèi)部類添加一個(gè)成員變量, 這個(gè)成員變量的類型和外部類的類型相同, 這個(gè)成員變量就是指向外部類對象的引用;
2 編譯器自動為內(nèi)部類的構(gòu)造方法添加一個(gè)參數(shù), 參數(shù)的類型是外部類的類型, 在構(gòu)造方法內(nèi)部使用這個(gè)參數(shù)為1中添加的成員變量賦值;
3 在調(diào)用內(nèi)部類的構(gòu)造函數(shù)初始化內(nèi)部類對象時(shí), 會默認(rèn)傳入外部類的引用。
為什么需要內(nèi)部類?
其主要原因有以下幾點(diǎn):
內(nèi)部類方法可以訪問該類定義所在的作用域的數(shù)據(jù),包括私有的數(shù)據(jù)
內(nèi)部類可以對同一個(gè)包中的其他類隱藏起來,一般的非內(nèi)部類,是不允許有 private 與protected權(quán)限的,但內(nèi)部類可以
可是實(shí)現(xiàn)多重繼承
當(dāng)想要定義一個(gè)回調(diào)函數(shù)且不想編寫大量代碼時(shí),使用匿名內(nèi)部類比較便捷
使用內(nèi)部類最吸引人的原因是:
每個(gè)內(nèi)部類都能獨(dú)立地繼承自一個(gè)(接口的)實(shí)現(xiàn),所以無論外圍類是否已經(jīng)繼承了某個(gè)(接口的)實(shí)現(xiàn),對于內(nèi)部類都沒有影響。大家都知道Java只能繼承一個(gè)類,它的多重繼承在我們沒有學(xué)習(xí)內(nèi)部類之前是用接口來實(shí)現(xiàn)的。但使用接口有時(shí)候有很多不方便的地方。比如我們實(shí)現(xiàn)一個(gè)接口就必須實(shí)現(xiàn)它里面的所有方法。而有了內(nèi)部類就不一樣了。它可以使我們的類繼承多個(gè)具體類或抽象類。
大家看下面的例子:
public classExample1 {publicString name(){return "liutao";
}
}public classExample2 {public intage(){return 25;
}
}public classMainExample {private class test1 extendsExample1{publicString name(){return super.name();
}
}private class test2 extendsExample2 {public intage(){return super.age();
}
}publicString name(){return newtest1().name();
}public intage(){return newtest2().age();
}public static voidmain(String args[]){
MainExample mi=newMainExample();
System.out.println("姓名:"+mi.name());
System.out.println("年齡:"+mi.age());
}
}
成員內(nèi)部類:
即在一個(gè)類中直接定義的內(nèi)部類,?成員內(nèi)部類與普通的成員沒什么區(qū)別,可以與普通成員一樣進(jìn)行修飾和限制。成員內(nèi)部類不能含有static的變量和方法。代碼如下:
public classOuter {private static int i = 1;private int j = 10;private int k = 20;public static voidouter_f1() {}public voidouter_f2() {}//成員內(nèi)部類中,不能定義靜態(tài)成員//成員內(nèi)部類中,可以訪問外部類的所有成員
classInner {//static int inner_i = 100;//內(nèi)部類中不允許定義靜態(tài)變量
int j = 100; //內(nèi)部類和外部類的實(shí)例變量可以共存
int inner_i = 1;voidinner_f1() {
System.out.println(i);//在內(nèi)部類中訪問內(nèi)部類自己的變量直接用變量名
System.out.println(j);//在內(nèi)部類中訪問內(nèi)部類自己的變量也可以用this.變量名
System.out.println(this.j);//在內(nèi)部類中訪問外部類中與內(nèi)部類同名的實(shí)例變量用外部類名.this.變量名
System.out.println(Outer.this.j);//如果內(nèi)部類中沒有與外部類同名的變量,則可以直接用變量名訪問外部類變量
System.out.println(k);
outer_f1();
outer_f2();
}
}//外部類的非靜態(tài)方法訪問成員內(nèi)部類
public voidouter_f3() {
Inner inner= newInner();
inner.inner_f1();
}//外部類的靜態(tài)方法訪問成員內(nèi)部類,與在外部類外部訪問成員內(nèi)部類一樣
public static voidouter_f4() {//step1 建立外部類對象
Outer out = newOuter();//step2 根據(jù)外部類對象建立內(nèi)部類對象
Inner inner = out.newInner();//step3 訪問內(nèi)部類的方法
inner.inner_f1();
}public static voidmain(String[] args) {//outer_f4();//該語句的輸出結(jié)果和下面三條語句的輸出結(jié)果一樣//如果要直接創(chuàng)建內(nèi)部類的對象,不能想當(dāng)然地認(rèn)為只需加上外圍類Outer的名字,//就可以按照通常的樣子生成內(nèi)部類的對象,而是必須使用此外圍類的一個(gè)對象來//創(chuàng)建其內(nèi)部類的一個(gè)對象://Outer.Inner outin = out.new Inner()//因此,除非你已經(jīng)有了外圍類的一個(gè)對象,否則不可能生成內(nèi)部類的對象。因?yàn)榇?/內(nèi)部類的對象會悄悄地鏈接到創(chuàng)建它的外圍類的對象。如果你用的是靜態(tài)的內(nèi)部類,//那就不需要對其外圍類對象的引用。
Outer out = newOuter();
Outer.Inner outin= out.newInner();
outin.inner_f1();
}
}
局部內(nèi)部類:
在方法中定義的內(nèi)部類稱為局部內(nèi)部類。與局部變量類似,局部內(nèi)部類不能有訪問說明符,因?yàn)樗皇峭鈬惖囊徊糠?#xff0c;但是它可以訪問當(dāng)前代碼塊內(nèi)的常量,和此外圍類所有的成員。
需要注意的是:
(1)、方法內(nèi)部類只能在定義該內(nèi)部類的方法內(nèi)實(shí)例化,不可以在此方法外對其實(shí)例化。
(2)、方法內(nèi)部類對象不能使用該內(nèi)部類所在方法的非final局部變量。
public classOuter {private int s = 100;private int out_i = 1;public void f(final intk) {final int s = 200;int i = 1;final int j = 10;//定義在方法內(nèi)部
classInner {int s = 300;//可以定義與外部類同名的變量//static int m = 20;//不可以定義靜態(tài)變量
Inner(intk) {
inner_f(k);
}int inner_i = 100;void inner_f(intk) {//如果內(nèi)部類沒有與外部類同名的變量,在內(nèi)部類中可以直接訪問外部類的實(shí)例變量
System.out.println(out_i);//可以訪問外部類的局部變量(即方法內(nèi)的變量),但是變量必須是final的
System.out.println(j);//System.out.println(i);//如果內(nèi)部類中有與外部類同名的變量,直接用變量名訪問的是內(nèi)部類的變量
System.out.println(s);//用this.變量名訪問的也是內(nèi)部類變量
System.out.println(this.s);//用外部類名.this.內(nèi)部類變量名訪問的是外部類變量
System.out.println(Outer.this.s);
}
}newInner(k);
}public static voidmain(String[] args) {//訪問局部內(nèi)部類必須先有外部類對象
Outer out = newOuter();
out.f(3);
}
}
靜態(tài)內(nèi)部類(嵌套類):
如果你不需要內(nèi)部類對象與其外圍類對象之間有聯(lián)系,那你可以將內(nèi)部類聲明為static。這通常稱為嵌套類(nested class)。想要理解static應(yīng)用于內(nèi)部類時(shí)的含義,你就必須記住,普通的內(nèi)部類對象隱含地保存了一個(gè)引用,指向創(chuàng)建它的外圍類對象。然而,當(dāng)內(nèi)部類是static的時(shí),就不是這樣了。嵌套類意味著:
1. 要?jiǎng)?chuàng)建嵌套類的對象,并不需要其外圍類的對象。
2. 不能從嵌套類的對象中訪問非靜態(tài)的外圍類對象。
public classOuter {private static int i = 1;private int j = 10;public static voidouter_f1() {}public voidouter_f2() {}//靜態(tài)內(nèi)部類可以用public,protected,private修飾//靜態(tài)內(nèi)部類中可以定義靜態(tài)或者非靜態(tài)的成員
private static classInner {static int inner_i = 100;int inner_j = 200;static voidinner_f1() {//靜態(tài)內(nèi)部類只能訪問外部類的靜態(tài)成員(包括靜態(tài)變量和靜態(tài)方法)
System.out.println("Outer.i" +i);
outer_f1();
}voidinner_f2() {//靜態(tài)內(nèi)部類不能訪問外部類的非靜態(tài)成員(包括非靜態(tài)變量和非靜態(tài)方法)//System.out.println("Outer.i"+j);//outer_f2();
}
}public voidouter_f3() {//外部類訪問內(nèi)部類的靜態(tài)成員:內(nèi)部類.靜態(tài)成員
System.out.println(Inner.inner_i);
Inner.inner_f1();//外部類訪問內(nèi)部類的非靜態(tài)成員:實(shí)例化內(nèi)部類即可
Inner inner = newInner();
inner.inner_f2();
}public static voidmain(String[] args) {newOuter().outer_f3();
}
}
生成一個(gè)靜態(tài)內(nèi)部類不需要外部類成員:這是靜態(tài)內(nèi)部類和成員內(nèi)部類的區(qū)別。靜態(tài)內(nèi)部類的對象可以直接生成:Outer.Inner in = new Outer.Inner();而不需要通過生成外部類對象來生成。這樣實(shí)際上使靜態(tài)內(nèi)部類成為了一個(gè)頂級類(正常情況下,你不能在接口內(nèi)部放置任何代碼,但嵌套類可以作為接口的一部分,因?yàn)樗莝tatic 的。只是將嵌套類置于接口的命名空間內(nèi),這并不違反接口的規(guī)則)
匿名內(nèi)部類:
簡單地說:匿名內(nèi)部類就是沒有名字的內(nèi)部類。什么情況下需要使用匿名內(nèi)部類?如果滿足下面的一些條件,使用匿名內(nèi)部類是比較合適的:
只用到類的一個(gè)實(shí)例。
類在定義后馬上用到。
類非常小(SUN推薦是在4行代碼以下)
給類命名并不會導(dǎo)致你的代碼更容易被理解。
在使用匿名內(nèi)部類時(shí),要記住以下幾個(gè)原則:
匿名內(nèi)部類不能有構(gòu)造方法。
匿名內(nèi)部類不能定義任何靜態(tài)成員、方法和類。
匿名內(nèi)部類不能是public,protected,private,static。
只能創(chuàng)建匿名內(nèi)部類的一個(gè)實(shí)例。
一個(gè)匿名內(nèi)部類一定是在new的后面,用其隱含實(shí)現(xiàn)一個(gè)接口或?qū)崿F(xiàn)一個(gè)類。
因匿名內(nèi)部類為局部內(nèi)部類,所以局部內(nèi)部類的所有限制都對其生效。
下面的代碼展示的是,如果你的基類需要一個(gè)有參數(shù)的構(gòu)造器,應(yīng)該怎么辦:
public classParcel7 {public Wrapping wrap(intx) {//Base constructor call:
return new Wrapping(x) { //Pass constructor argument.
public intvalue() {return super.value() * 47;
}
};//Semicolon required
}public static voidmain(String[] args) {
Parcel7 p= newParcel7();
Wrapping w= p.wrap(10);
}
}
只需簡單地傳遞合適的參數(shù)給基類的構(gòu)造器即可,這里是將x 傳進(jìn)new Wrapping(x)。在匿名內(nèi)部類末尾的分號,并不是用來標(biāo)記此內(nèi)部類結(jié)束(C++中是那樣)。實(shí)際上,它標(biāo)記的是表達(dá)式的結(jié)束,只不過這個(gè)表達(dá)式正巧包含了內(nèi)部類罷了。因此,這與別的地方使用的分號是一致的。
如果在匿名類中定義成員變量或者使用帶參數(shù)的構(gòu)造函數(shù),你同樣能夠?qū)ζ鋱?zhí)行初始化操作:
public classParcel8 {//Argument must be final to use inside//anonymous inner class:
public Destination dest(finalString name, String city) {return newDestination(name, city) {private String label =name;publicString getName() {returnlabel;
}
};
}public static voidmain(String[] args) {
Parcel8 p= newParcel8();
Destination d= p.dest("Tanzania", "gz");
}abstract classDestination {
Destination(String name, String city) {
System.out.println(city);
}abstractString getName();
}
}
注意這里的形參city,由于它沒有被匿名內(nèi)部類直接使用,而是被抽象類Inner的構(gòu)造函數(shù)所使用,所以不必定義為final。
內(nèi)部類的重載問題
如果你創(chuàng)建了一個(gè)內(nèi)部類,然后繼承其外圍類并重新定義此內(nèi)部類時(shí),會發(fā)生什么呢?也就是說,內(nèi)部類可以被重載嗎?這看起來似乎是個(gè)很有用的點(diǎn)子,但是“重載”內(nèi)部類就好像它是外圍類的一個(gè)方法,其實(shí)并不起什么作用:
classEgg {privateYolk y;protected classYolk {publicYolk() {
System.out.println("Egg.Yolk()");
}
}publicEgg() {
System.out.println("New Egg()");
y= newYolk();
}
}public class BigEgg extendsEgg {public classYolk {publicYolk() {
System.out.println("BigEgg.Yolk()");
}
}public static voidmain(String[] args) {newBigEgg();
}
}
輸出結(jié)果為:
New Egg()
Egg.Yolk()
缺省的構(gòu)造器是編譯器自動生成的,這里是調(diào)用基類的缺省構(gòu)造器。你可能認(rèn)為既然創(chuàng)建了BigEgg 的對象,那么所使用的應(yīng)該是被“重載”過的Yolk,但你可以從輸出中看到實(shí)際情況并不是這樣的。
這個(gè)例子說明,當(dāng)你繼承了某個(gè)外圍類的時(shí)候,內(nèi)部類并沒有發(fā)生什么特別神奇的變化。這兩個(gè)內(nèi)部類是完全獨(dú)立的兩個(gè)實(shí)體,各自在自己的命名空間內(nèi)。當(dāng)然,明確地繼承某個(gè)內(nèi)部類也是可以的:
classEgg2 {protected classYolk {publicYolk() {
System.out.println("Egg2.Yolk()");
}public voidf() {
System.out.println("Egg2.Yolk.f()");
}
}private Yolk y = newYolk();publicEgg2() {
System.out.println("New Egg2()");
}public voidinsertYolk(Yolk yy) {
y=yy;
}public voidg() {
y.f();
}
}public class BigEgg2 extendsEgg2 {public class Yolk extendsEgg2.Yolk {publicYolk() {
System.out.println("BigEgg2.Yolk()");
}public voidf() {
System.out.println("BigEgg2.Yolk.f()");
}
}publicBigEgg2() {
insertYolk(newYolk());
}public static voidmain(String[] args) {
Egg2 e2= newBigEgg2();
e2.g();
}
}
輸出結(jié)果為:
Egg2.Yolk()
New Egg2()
Egg2.Yolk()
BigEgg2.Yolk()
BigEgg2.Yolk.f()
現(xiàn)在BigEgg2.Yolk 通過extends Egg2.Yolk 明確地繼承了此內(nèi)部類,并且重載了其中的方法。Egg2 的insertYolk()方法使得BigEgg2 將它自己的Yolk 對象向上轉(zhuǎn)型,然后傳遞給引用y。所以當(dāng)g()調(diào)用y.f()時(shí),重載后的新版的f()被執(zhí)行。第二次調(diào)用Egg2.Yolk()是BigEgg2.Yolk 的構(gòu)造器調(diào)用了其基類的構(gòu)造器。可以看到在調(diào)用g()的時(shí)候,新版的f()被調(diào)用了。
內(nèi)部類的繼承問題
因?yàn)閮?nèi)部類的構(gòu)造器要用到其外圍類對象的引用,所以在你繼承一個(gè)內(nèi)部類的時(shí)候,事情變得有點(diǎn)復(fù)雜。問題在于,那個(gè)“秘密的”外圍類對象的引用必須被初始化,而在被繼承的類中并不存在要聯(lián)接的缺省對象。要解決這個(gè)問題,需使用專門的語法來明確說清它們之間的關(guān)聯(lián):
classWithInner {classInner {
Inner(){
System.out.println("this is a constructor in WithInner.Inner");
};
}
}public class InheritInner extendsWithInner.Inner {//! InheritInner() {}//Won't compile
InheritInner(WithInner wi) {
wi.super();
System.out.println("this is a constructor in InheritInner");
}public static voidmain(String[] args) {
WithInner wi= newWithInner();
InheritInner ii= newInheritInner(wi);
}
}
輸出結(jié)果為:
this is a constructor in WithInner.Inner
this is a constructor in InheritInner
可以看到,InheritInner 只繼承自內(nèi)部類,而不是外圍類。但是當(dāng)要生成一個(gè)構(gòu)造器時(shí),缺省的構(gòu)造器并不算好,而且你不能只是傳遞一個(gè)指向外圍類對象的引用。此外,你必須在構(gòu)造器內(nèi)使用如下語法:
enclosingClassReference.super();
這樣才提供了必要的引用,然后程序才能編譯通過。
為什么非靜態(tài)內(nèi)部類中不能有static修飾的屬性,但卻可以有常量?
如:
public classInnerClassDemo{intx;classA{static int a = 0;//這樣寫是不合法的.
static final int b=0;//這樣寫是合法的
}
}
定義一個(gè)靜態(tài)的域或者方法,要求在靜態(tài)環(huán)境或者頂層環(huán)境,即如果加上 static class A變成靜態(tài)內(nèi)部類就ok非靜態(tài)內(nèi)部類 依賴于一個(gè)外部類對象,而靜態(tài)域/方法是不依賴與對象——僅與類相關(guān)(細(xì)說了,就是加載靜態(tài)域時(shí),根本沒有外部類對象)因此,非靜態(tài)內(nèi)部類中不能定義靜態(tài)域/方法,編譯過不了。
而常量之所以可以(不論有無static),因?yàn)閖ava在編譯期就確定所有常量,放到所謂的常量池當(dāng)中。常量的機(jī)制和普通變量不一樣
總結(jié)
以上是生活随笔為你收集整理的java 成员函数相互调用_java 深入理解内部类以及之间的调用关系的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: spring 数组中随机取几个_准备几个
- 下一篇: mooc作业怎么上传附件_交作业的一二三