Java中的內部類
- 什么是內部類
內部類是指在一個外部類的內部再定義一個類。內部類可以是靜態static的,也可用public,default,protected和private修飾。(而外部頂級類即類名和文件名相同的只能使用public和default)。注意:內部類是一個編譯時的概念,一旦編譯成功,就會成為完全不同的兩類。對于一個名為Outer的外部類和其內部定義的名為Inner的內部類。編譯完成后出現Outer.class和Outer$Inner.class兩類。所以內部類的成員變量/方法名可以和外部類的相同。java內部類分為: 成員內部類、靜態嵌套類、局部內部類、匿名內部類 。
- 內部類的共性
(1)、內部類仍然是一個獨立的類,在編譯之后內部類會被編譯成獨立的.class文件,但是前面冠以外部類的類名和$符號 。(2)、內部類不能用普通的方式訪問。內部類是外部類的一個成員,因此內部類可以自由地訪問外部類的成員變量,無論是否是private的 。(3)、內部類聲明成靜態的,就不能隨便的訪問外部類的成員變量了,此時內部類只能訪問外部類的靜態成員變量 。 - 成員內部類
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() {
//內部類訪問外部類中的同名成員變量
System.out.println("Outer.a="+Outer.this.a);
System.out.println("Inner.a="+this.a);
System.out.println("a="+a);
//內部類中可以直接訪問外部類的成員變量和方法(內部類對象持有指向外部類對象的引用。)
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();
//內部類對象的創建依賴于外部類對象;
outer.new Inner().print();
}
}這個類編譯后出現Outer.class和Outer$Inner.class兩個class文件。編譯后成為兩個獨立的類,
因此,內部類中可以定義與外部類中同名的屬性和方法。
成員內部類,就是作為外部類的成員,與外部類的屬性、方法并列。可以直接使用外部類的所有成員和方法,即使是private的。
同時外部類要訪問內部類的所有成員變量和方法,則需要通過內部類的對象來獲取。要注意的是,成員內部類不能含有static的變量和方法。因為成員內部類需要先創建了外部類,才能創建它自己的
- 局部內部類
局部內部類,是指內部類定義在方法和作用域內。(1)、定義在方法內
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)、定義在作用域內
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)、局部內部類只能在定義該內部類的方法內實例化,不可以在此方法外對其實例化。(2)、局部內部類對象不能使用該內部類所在方法的非final局部變量。
(3)、局部內部類的地位和方法內的局部變量的位置類似,因此不能修飾局部變量的修飾符也不能修飾局部內部類,
譬如public、private、protected、static、transient等因為方法的局部變量位于棧上,只存在于該方法的生命期內。當一個方法結束,其棧結構被刪除,局部變量成為歷史。但是該方法結束之后,在方法內創建的內部類對象可能仍然存在于堆中!例如,如果對它的引用被傳遞到其他某些代碼,并存儲在一個成員變量內。正因為不能保證局部變量的存活期和方法內部類對象的一樣長,所以內部類對象不能使用它們。 - 靜態嵌套類
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();
}
}
嵌套類和普通的內部類還有一個區別:普通內部類不能有static數據和static屬性,也不能包含嵌套類,但嵌套類可以。
而嵌套類不能聲明為private,一般聲明為public,方便調用。
在靜態方法中定義的內部類也是StaticNested Class,這時候不能在類前面加static關鍵字,靜態方法中的StaticNested Class與
普通方法中的內部類的應用方式很相似,它除了可以直接訪問外部類中的static的成員變量,還可以訪問靜態方法中的局部變量,
但是,該局部變量前必須加final修飾符。
- 匿名內部類
- 為什么需要內部類
匿名內部類就是沒有名字的內部類。什么情況下需要使用匿名內部類?如果滿足下面的一些條件,使用匿名內部類是比較合適的:
·只用到類的一個實例 。
·類在定義后馬上用到。
·類非常小(SUN推薦是在4行代碼以下)
·給類命名并不會導致你的代碼更容易被理解。
在使用匿名內部類時,要記住以下幾個原則:
·匿名內部類不能有構造方法。
·匿名內部類不能定義任何靜態成員、靜態方法。
·匿名內部類不能是public,protected,private,static。
·只能創建匿名內部類的一個實例。
·一個匿名內部類一定是在new的后面,用其隱含實現一個接口或實現一個類。
·因匿名內部類為局部內部類,所以局部內部類的所有限制都對其生效。(1)、繼承式的匿名內部類
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();
}
}
輸出結果:Sing a popular song
(2)、接口式的匿名內部類
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)、參數式的匿名內部類
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");
}
});
}
}
典型的情況是,內部類繼承自某個類或實現某個接口,內部類的代碼操作創建其的外圍類的對象。所以你可以認為內部類提供了某種進入其外圍類的窗口。
使用內部類最吸引人的原因是:
使用內部類最吸引人的原因是:
每個內部類都能獨立地繼承自一個(接口的)實現,所以無論外圍類是否已經繼承了某個(接口的)實現,對于內部類都沒有影響。如果沒有內部類提供的可以繼承多個具體的或抽象的類的能力,一些設計與編程問題就很難解決。從這個角度看,內部類使得多重繼承的解決方案變得完整。接口解決了部分問題,而內部類有效地實現了“多重繼承”。