把一個類放到另一個類的内部定義,這個定義在其他類内部的類就被稱為内部類。
内部類的作用:
(1).内部類提供了更好的封裝,可以把内部類隐藏在外部類之内,不允許同一個包中的其他類通路該類。
(2).内部類成員可以直接通路外部類的私有資料,因為内部類被當成其外部類成員,同一個類的成員之間可以互相通路。但外部類不能通路内部類的實作細節。
(3).匿名内部類适用于建立那些僅需要一次使用的類。
(4).内部類比外部類可以多使用三個修飾符:private、protected、static——外部類不可以使用這三個修飾符。
(5).非靜态内部類不能擁有靜态成員。
1.非靜态内部類
定義内部類非常簡單,隻要把一個類放在另一個類内部定義即可。此處的“類内部”包括類中的任何位置,甚至在方法中也可以定義内部類(方法裡定義的内部類被稱為局部内部類)。
成員内部類是一種與成員變量、方法、構造器和初始化塊相似的類成員;局部内部類和匿名内部類則不是類成員。
成員内部類分為兩種:靜态内部類和非靜态内部類,使用static修飾的成員内部類是靜态内部類,沒有使用static修飾的成員内部類是非靜态内部類。
因為内部類作為其外部類的成員,是以可以使用任意通路控制符如private、protected和public等修飾。
外部類的上一級程式單元是包,是以他隻有兩個作用域:同一包内和任何位置。而内部類的上一級程式單元是外部類,它就具有4個作用域:同一個類、同一個包、父子類和任何位置,是以可以使用四中通路控制權限。
public class Cow {
private double weight;
//外部類的兩個重載的構造器
public Cow(){}
public Cow(double weight){
this.weight=weight;
}
//定義一個非靜态内部類
@SuppressWarnings("unused")
private class CowLeg{
//非靜态内部類的兩個執行個體變量
private double length;
private String color;
//非靜态内部類的兩個重載的構造器
public CowLeg(){}
public CowLeg(double length,String color){
this.length=length;
this.color=color;
}
public double getLength() {
return length;
}
public void setLength(double length) {
this.length = length;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
//非靜态内部類的執行個體方法
public void info(){
System.out.println("目前牛腿顔色是:"+color+",高:"+length);
//通路外部類的private修飾的成員變量
System.out.println("本牛腿所在奶牛重:"+weight);
}
}
public void test(){
CowLeg c1=new CowLeg(1.12,"黑白相間");
c1.info();
}
public static void main(String[] args) {
Cow cow=new Cow(378.9);
cow.test();
/*
* 輸出結果:
* 目前牛腿顔色是:黑白相間,高:1.12
* 本牛腿所在奶牛重:378.9
*/
}
}
上面程式在編譯後生成兩個class檔案,一個是Cow.class,另一個是Cow$CowLeg.class,前者是外部類Cow的class檔案,後者是内部類CowLeg的class檔案,即成員内部類(包括靜态内部類、非靜态内部類)的class檔案總是這種形式:OuterClass$InnerClass.class。
在非靜态内部類對象裡,儲存了一個它所寄生的外部類對象的引用(當調用非靜态内部類的執行個體方法時,必須有一個非靜态内部類執行個體,非靜态内部類執行個體必須寄生在外部類執行個體中)。
當在非靜态内部類的方法内通路某個變量時,系統優先在該方法内查找是夠存在該名字的局部變量,如果存在就是用該變量;如果不存在,則到該方法所在的内部類中查找是否存在該名字的成員變量,如果存在就是用該成員變量;如果不存在,則到該内部類所在的外部類中查找是否存在該名字的成員變量,如果存在則使用該成員變量,如果已然不存在,系統将出現編譯錯誤:提示找不到該變量。
如果外部類成員變量、内部類成員變量與内部類裡方法的局部變量同名,則可通過使用this、外部類類名.this作為限定來區分。
public class DiscernVariable {
private String prop="外部類的執行個體變量";
private class InClass{
private String prop="内部類的執行個體變量";
public void info(){
String prop="局部變量";
//通過外部類類名.this.varname通路外部類執行個體變量
System.out.println("外部類的執行個體變量值:"+DiscernVariable.this.prop);
//通過this.varname通路内部類執行個體的變量
System.out.println("内部類的執行個體變量值:"+this.prop);
//直接通路局部變量
System.out.println("局部變量的值:"+prop);
}
}
public void test(){
InClass in=new InClass();
in.info();
}
public static void main(String[] args) {
new DiscernVariable().test();
/*
* 輸出結果:
* 外部類的執行個體變量值:外部類的執行個體變量
内部類的執行個體變量值:内部類的執行個體變量
局部變量的值:局部變量
*/
}
}
通過OuterClass.this.propName的形式通路外部類的執行個體變量,通過this.propName的形式通路非靜态内部類的執行個體變量。
非靜态内部類的成員可以通路外部類的private成員,但反過來不成立。非靜态内部類的成員隻在非靜态内部類範圍内是可知的,并不能被外部類直接使用。如果外部類需要通路非靜态内部類的成員,則必須顯式建立非靜态内部類對象來調用通路其執行個體成員。
public class Outer {
private int outProp=9;
class Inner{
private int inProp=5;
public void acessOuterProp(){
//非靜态内部類可以直接通路外部類的private成員變量
System.out.println("外部類的outProp值:"+outProp);
}
}
public void accessInnerProp(){
//外部類不能直接通路非靜态内部類的執行個體變量
//下面代碼編譯錯誤
//System.out.println("内部類的inProp值:"+inProp);
//如需通路内部類的執行個體變量,必須顯式建立内部類對象
System.out.println("内部類的inProp值:"+new Inner().inProp);
}
public static void main(String[] args) {
Outer out=new Outer();
out.accessInnerProp();
/*
* 輸出結果:
* 内部類的inProp值:5
*/
}
}
非靜态内部類對象和外部類對象的關系:非靜态内部類對象必須寄生在外部類對象裡,而外部類對象則不必一定有非靜态内部類對象寄生其中。簡單地說,如果存在一個非靜态内部類對象,則一定存在一個被它寄生的外部類對象那個。但外部類對象存在時,外部類對象裡不一定寄生非靜态内部類對象。是以外部類對象通路非靜态内部類成員時,可能非靜态普通内部類對象根本不存在。而非靜态内部類對象通路外部類成員時,外部類對象一定存在。
根據靜态成員不能通路非靜态成員的規則,外部類的靜态方法、靜态代碼塊不能通路非靜态内部類,包括不能使用非靜态内部類定義變量、建立執行個體等。總之,不允許在外部類的靜态成員中直接使用非靜态内部類。
Java不允許在非靜态内部類中定義靜态變量。
非靜态内部類中不能有靜态方法、靜态成員變量、靜态初始化塊。
非靜态内部類中不可以有靜态初始化塊,但可以包含普通初始化塊。非靜态被不累普通初始化塊的作用于外部類的初始化塊作用完全相同。
2.靜态内部類
如果使用static來修飾一個内部類,則這個内部類就屬于外部類本身,而不屬于外部類的某個對象。是以使用static修飾的内部類被稱為類内部類,有的地方也成為靜态内部類。
static關鍵字的作用是把類的成員變成類相關,即static修飾的成員屬于整個類,而不屬于單個對象。外部類的上一級程式單元是包,是以不可使用static修飾;而内部類的上一級程式單元是外部類,使用static修飾可以将内部類變成外部類相關,而不是外部類執行個體相關,是以static關鍵字不可修飾外部類,但可修飾内部類。
靜态内部類可以包含靜态成員,也可以包含非靜态成員。根據靜态成員不能通路非靜态成員的規則,非靜态内部類不能通路外部類的執行個體成員,隻能通路外部類的類成員。即使是靜态内部類的執行個體方法也不能通路外部類的執行個體成員,隻能通路外部類的就靜态成員。
靜态内部類可以包含靜态成員,也可以包含非靜态成員。根據靜态成員不能通路非靜态成員的規則,靜态内部類不能通路外部類的執行個體成員,隻能通路外部類的類成員。即使是靜态内部類的執行個體方法也不能通路外部類的執行個體成員,隻能通路外部類的靜态成員。
靜态内部類是外部類的一個靜态成員,是以外部類的所有方法、所有初始化塊中可以使用靜态内部類來定義變量、建立對象等。
外部類依然不能直接通路靜态内部類的成員,但可以使用靜态内部類的類名來作為調用者來通路靜态内部類的類成員,也可以使用靜态内部類對象作為調用者來通路靜态内部類的執行個體成員。
public class AccessStaticInnerClass {
static class StaticInnerClass{
private static int prop1=5;
private int prop2=9;
}
public void accessInnerProp(){
//System.out.println(prop1);
//上面代碼出現錯誤,應改為如下形式
//通過類名通路靜态内部類的類成員
System.out.println(StaticInnerClass.prop1);
//System.out.println(prop2);
//上面代碼出現錯誤,應該為如下形式
//通過執行個體通路靜态内部類的執行個體成員
System.out.println(new StaticInnerClass().prop2);
}
}
Java還允許在接口裡定義内部類,接口裡定義的内部類預設使用public static修飾——接口内部類隻能是靜态内部類。
如果為接口内部類指定通路控制符,則隻能指定public通路控制符;如果定義接口内部類暫時省略通路控制符,則該内部類預設是public通路控制權限。