- 什么是內(nèi)部類
內(nèi)部類是指在一個外部類的內(nèi)部再定義一個類。
內(nèi)部類可以是靜態(tài)static的,也可用public,default,protected和private修飾。(而外部頂級類即類名和文件名相同的只能使用public和default)。
注意:內(nèi)部類是一個編譯時的概念,一旦編譯成功,就會成為完全不同的兩類。對于一個名為Outer的外部類和其內(nèi)部定義的名為Inner的內(nèi)部類。編譯完成后出現(xiàn)Outer.class和Outer$Inner.class兩類。所以內(nèi)部類的成員變量/方法名可以和外部類的相同。
java內(nèi)部類分為: 成員內(nèi)部類、靜態(tài)嵌套類、局部內(nèi)部類、匿名內(nèi)部類 。
- 內(nèi)部類的共性
(1)、內(nèi)部類仍然是一個獨立的類,在編譯之后內(nèi)部類會被編譯成獨立的.class文件,但是前面冠以外部類的類名和$符號 。
(2)、內(nèi)部類不能用普通的方式訪問。內(nèi)部類是外部類的一個成員,因此內(nèi)部類可以自由地訪問外部類的成員變量,無論是否是private的 。
(3)、內(nèi)部類聲明成靜態(tài)的,就不能隨便的訪問外部類的成員變量了,此時內(nèi)部類只能訪問外部類的靜態(tài)成員變量 。
- 成員內(nèi)部類
public class Outer {
private int a = 10;
private int b = 15;
public void print() {
System.out.println("Outer.a="+this.a);
}
public int add(int i,int j) {
return i+j;
}
class Inner {
private int a = 20;
private int c = b;
public void print() {
//內(nèi)部類訪問外部類中的同名成員變量
System.out.println("Outer.a="+Outer.this.a);
System.out.println("Inner.a="+this.a);
System.out.println("a="+a);
//內(nèi)部類中可以直接訪問外部類的成員變量和方法(內(nèi)部類對象持有指向外部類對象的引用。)
System.out.println("c="+c);
System.out.println("c+1="+add(c,1));
}
}
public static void main(String[] args) {
Outer outer = new Outer();
outer.print();
//內(nèi)部類對象的創(chuàng)建依賴于外部類對象;
outer.new Inner().print();
}
}
這個類編譯后出現(xiàn)Outer.class和Outer$Inner.class兩個class文件。編譯后成為兩個獨立的類,
因此,內(nèi)部類中可以定義與外部類中同名的屬性和方法。
成員內(nèi)部類,就是作為外部類的成員,與外部類的屬性、方法并列。可以直接使用外部類的所有成員和方法,即使是private的。
同時外部類要訪問內(nèi)部類的所有成員變量和方法,則需要通過內(nèi)部類的對象來獲取。
要注意的是,成員內(nèi)部類不能含有static的變量和方法。因為成員內(nèi)部類需要先創(chuàng)建了外部類,才能創(chuàng)建它自己的
- 局部內(nèi)部類
局部內(nèi)部類,是指內(nèi)部類定義在方法和作用域內(nèi)。
(1)、定義在方法內(nèi)
public class Teacher {
private String name;
private int age;
public void teach() {
final String str = "I'm a teacher!";
class EnglishTeacher {
public void teachEnglish() {
System.out.println("My name is "+name);
System.out.println("I'm "+age);
System.out.println(str);
System.out.println("I teach English");
}
}
EnglishTeacher englishTeacher = new EnglishTeacher();
englishTeacher.teachEnglish();
}
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Teacher teacher = new Teacher();
teacher.name = "張三";
teacher.age = 35;
teacher.teach();
}
}
(2)、定義在作用域內(nèi)
public class Teacher1 {
private String name;
private int age;
public void teach(String course) {
final String str = "I'm a teacher!";
System.out.println(str);
if("English".equals(course)) {
class EnglishTeacher {
public void teachEnglish() {
System.out.println("My name is "+name);
System.out.println("I'm "+age);
System.out.println("I teach English");
}
}
EnglishTeacher englishTeacher = new EnglishTeacher();
englishTeacher.teachEnglish();
}
}
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Teacher1 teacher = new Teacher1();
teacher.name = "張三";
teacher.age = 35;
teacher.teach("English");
}
}
(1)、局部內(nèi)部類只能在定義該內(nèi)部類的方法內(nèi)實例化,不可以在此方法外對其實例化。
(2)、局部內(nèi)部類對象不能使用該內(nèi)部類所在方法的非final局部變量。
(3)、局部內(nèi)部類的地位和方法內(nèi)的局部變量的位置類似,因此不能修飾局部變量的修飾符也不能修飾局部內(nèi)部類,
譬如public、private、protected、static、transient等
因為方法的局部變量位于棧上,只存在于該方法的生命期內(nèi)。當一個方法結(jié)束,其棧結(jié)構(gòu)被刪除,局部變量成為歷史。但是該方法結(jié)束之后,在方法內(nèi)創(chuàng)建的內(nèi)部類對象可能仍然存在于堆中!例如,如果對它的引用被傳遞到其他某些代碼,并存儲在一個成員變量內(nèi)。正因為不能保證局部變量的存活期和方法內(nèi)部類對象的一樣長,所以內(nèi)部類對象不能使用它們。
- 靜態(tài)嵌套類
public class StaticOuter {
private int a = 10;
private static int b = 15;
static class StaticInner {
public static void print() {
System.out.println("b="+b);
}
void print1() {
System.out.println("b="+b);
}
}
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
StaticOuter.StaticInner.print();
new StaticOuter.StaticInner().print1();
}
}
嵌套類和普通的內(nèi)部類還有一個區(qū)別:普通內(nèi)部類不能有static數(shù)據(jù)和static屬性,也不能包含嵌套類,但嵌套類可以。
而嵌套類不能聲明為private,一般聲明為public,方便調(diào)用。
在靜態(tài)方法中定義的內(nèi)部類也是StaticNested Class,這時候不能在類前面加static關(guān)鍵字,靜態(tài)方法中的StaticNested Class與
普通方法中的內(nèi)部類的應(yīng)用方式很相似,它除了可以直接訪問外部類中的static的成員變量,還可以訪問靜態(tài)方法中的局部變量,
但是,該局部變量前必須加final修飾符。
- 匿名內(nèi)部類
匿名內(nèi)部類就是沒有名字的內(nèi)部類。什么情況下需要使用匿名內(nèi)部類?如果滿足下面的一些條件,使用匿名內(nèi)部類是比較合適的:
·只用到類的一個實例 。
·類在定義后馬上用到。
·類非常小(SUN推薦是在4行代碼以下)
·給類命名并不會導(dǎo)致你的代碼更容易被理解。
在使用匿名內(nèi)部類時,要記住以下幾個原則:
·匿名內(nèi)部類不能有構(gòu)造方法。
·匿名內(nèi)部類不能定義任何靜態(tài)成員、靜態(tài)方法。
·匿名內(nèi)部類不能是public,protected,private,static。
·只能創(chuàng)建匿名內(nèi)部類的一個實例。
·一個匿名內(nèi)部類一定是在new的后面,用其隱含實現(xiàn)一個接口或?qū)崿F(xiàn)一個類。
·因匿名內(nèi)部類為局部內(nèi)部類,所以局部內(nèi)部類的所有限制都對其生效。
(1)、繼承式的匿名內(nèi)部類
public class Singer {
public void sing() {
System.out.println("Sing a song");
}
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Singer singer = new Singer() {
@Override
public void sing() {
// TODO Auto-generated method stub
System.out.println("Sing a popular song");
}
};
singer.sing();
}
}
輸出結(jié)果:Sing a popular song
(2)、接口式的匿名內(nèi)部類
public interface Programmer {
public void code();
}
public class TestProgrammer {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Programmer javaProgrammer = new Programmer() {
@Override
public void code() {
// TODO Auto-generated method stub
System.out.println("Java programmer");
}
};
javaProgrammer.code();
Programmer phpProgrammer = new Programmer() {
@Override
public void code() {
// TODO Auto-generated method stub
System.out.println("Php programmer");
}
};
phpProgrammer.code();
}
}
(3)、參數(shù)式的匿名內(nèi)部類
public interface Programmer {
public void code();
}
public class TestProgrammer1 {
public void testCode(Programmer p) {
p.code();
}
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
TestProgrammer1 testProgrammer1 = new TestProgrammer1();
testProgrammer1.testCode(new Programmer() {
@Override
public void code() {
// TODO Auto-generated method stub
System.out.println("testing code");
}
});
}
}
- 為什么需要內(nèi)部類
典型的情況是,內(nèi)部類繼承自某個類或?qū)崿F(xiàn)某個接口,內(nèi)部類的代碼操作創(chuàng)建其的外圍類的對象。所以你可以認為內(nèi)部類提供了某種進入其外圍類的窗口。
使用內(nèi)部類最吸引人的原因是:
每個內(nèi)部類都能獨立地繼承自一個(接口的)實現(xiàn),所以無論外圍類是否已經(jīng)繼承了某個(接口的)實現(xiàn),對于內(nèi)部類都沒有影響。如果沒有內(nèi)部類提供的可以繼承多個具體的或抽象的類的能力,一些設(shè)計與編程問題就很難解決。從這個角度看,內(nèi)部類使得多重繼承的解決方案變得完整。接口解決了部分問題,而內(nèi)部類有效地實現(xiàn)了“多重繼承”。