一、什麼樣的類屬於內部類?
毫無疑問,居然是內部類,定義在類裡面的類就是內部類。
public class Room {
class Student{//內部類
}
}
複製程式碼
內部類可以分為靜態內部類(static修飾的類)和普通內部類。下面先討論普通內部類:
(注意,以下所說的內部類都是指普通內部類)
1.內部類與外部類的關係:
對於普通內部類,實際上該類就類似於外部類的一個特殊成員,該內部類的建立必須依附於外部類,也就是說,必須先有外部類物件,才能建立內部類物件。這是因為內部類物件會隱式地連線到建立它的外部類物件上。並且內部類可以訪問外部類的所有元素。建立時通過 .new來建立
public class Room {
private String book = "資料結構";
class Student{//內部類
public String getBook(){
return book;
}
}
public static void main(String[] args) {
Room r = new Room();
Student s = r.new Student();//不能有 Student s = new Student()來建立
System.out.println(s.getBook());
}
}
複製程式碼
注:如果要在內部類通過this關鍵字來引用外部類物件,則需要在這樣引用 Room.this 。如果只是用this的話,引用的是內部類物件。
2.在方法和作用域內的內部類
(1).定義在方法內的內部類,我們稱之為區域性內部類:
public void get(){//只貼方法內的程式碼,其他程式碼省略
class Book{ //定義在方法內的內部類
String name = book;//book變數是外部類的成員屬性
}
}
複製程式碼
顯然,Book內只是方法get()的一部分,而不是外部類的一部分,所以,get()方法之外都不能訪問Book。
(2).下面展示任何在任意作用域內嵌入一個內部類
public void get2(boolean b){
if(b){//在if語句作用域內定義內部類
class Book2{
String name = book;
}
}
}
複製程式碼
(3).內部類不能定義靜態變數和靜態方法
原因:類在初始化的過程中,是先初始化靜態變數等的。也就是說,在編譯是隻要是定義為靜態變數了,系統就會自動分配記憶體給它,但是內部類是依附與外部類的存在而存在的,也就是說只有外部類存在了,內部類才能存在,這就和編譯時為靜態變數分配記憶體產生了衝突。
3.匿名內部類
看下面一個程式
public class Animal {
public void run(){
System.out.println("我是一隻不知名的動物");
}
public Animal getAnimal(){
return new Animal(){//返回匿名內部類
public void run(){
System.out.println("我是一隻小貓");
}
};
}
public static void main(String[] args) {
Animal a = new Animal();
Animal a1 = a.getAnimal();
a1.run();
}
}
複製程式碼
列印結果:我是一隻小貓
在getAnimal()方法裡,將返回值的生成與表示這個返回值的類定義結合在一起。另外,這個類是匿名的,沒有名字。對於這種用法,看起來似乎你正要建立一個Animal的物件,實際上,建立的卻是一個繼承了Animal類的匿名物件,run()方法 是對父類方法的重寫。
對於這種匿名內部類,使用的是預設構造器來生成Animal的,如果你的基類(也就是父類)需要一個有引數的構造器,該怎麼辦:
只需要把引數傳遞給基類的構造器即可。
public Animal getAnimal2(String name){
return new Animal(name){//直接把引數給它就行
public void run(){
System.out.println("我是一隻" + name);
}
};
}
複製程式碼
以下來討論靜態內部類:
對於靜態內部類,它與普通內部類不同。普通的內部類物件會隱式地儲存了一個引用,指向建立它的外部類物件。然而,對於靜態類來說,就不是這樣了:
(1).要建立靜態內部類,它並不需要其外部類的物件
public class Book {
static class Math{ }
public static void main(String[] args) {
Math m = new Math();//靜態內部類的建立
}
}
複製程式碼
(2).靜態內部類不能訪問非靜態的外部類物件
public class Book {
int B_price = 10;
static String B_name = "好書";
static class Math{
//int M_price = B_price;//不能訪問
String M_name = B_name;
}
}
複製程式碼
對於為何不能訪問非靜態變數我是這樣理解的:非靜態量是屬於類物件成員,而不是類成員,它的存在是依附於物件而不是類。靜態內部類訪問這個外部類成員的時候,這個成員還不存在,因為外部物件還沒有被建立。故不能訪問非靜態成員。(當然,也可以從類載入的順序等分析)。
(3).靜態內部類還可以作為介面的一部分。
正常情況下,是不能在介面內部放置任何程式碼的,但是靜態內部類可以作為介面的一部分。你放到介面中的任何類都自動地是public和static的。因為類是static的,只是將靜態內部類置於介面的名稱空間內,這並不會違反介面的規則:
public interface AA {
void get();
class BB implements AA{
@Override
public void get() {
System.out.println("哈哈");
}
}
}
複製程式碼
二、為啥需要內部類?
1.對於抽象類,我們都知道只能被單繼承,如果一個類想要繼承多個抽象類,那隻能使用內部類才能實現多重繼承(即定義幾個內部類,每個內部類分擔繼承一個)。
2.內部類的使用,對一個類來說,提供了更好的封裝。例如要生產一輛車,我們可以假設老闆是外部類,負責組裝車部件的團隊是內部類。那麼你想要修改車裡面的部件時,不僅僅是通過老闆就可以直接做出修改了,還得通過負責裝車部件的團隊。也就是說,外部類的有些屬性成員,只要通過內部類才能修改,這樣子設計,會更具合理性。
3.內部類可以有多個例項,每個例項都有自己的狀態資訊,並且與其外部類物件的資訊相互獨立。
4.在單個外部類中,可以讓多個內部類以不同的方式實現同一個介面,或繼承同一個類。
三、內部類可以被覆蓋嗎?
看下面一個例子
列印結果:
Eat
Eat.Apple
複製程式碼
從輸出的情況可以看出,內部類並沒有被覆蓋。也就是說,兩個內部類並沒有啥關係,是相互獨立的。
參考書籍:程式設計思想
來源與公眾號:苦逼的碼農
原文連線