定義在類體部,方法體部,甚至比方法體更小的代碼塊內部的類。比如:在類A中定義了一個類B,類B就是內部類。
1、內部類的訪問特點:
a、內部類可以直接訪問外部類的所有成員,包括私有b、外部類要訪問內部類的成員,必須創建對象
2、內部類的位置:
a、成員位置:在類體部,方法體外。被稱爲成員內部類
b、局部位置:定義在方法體,甚至比方法體更小的代碼塊。被稱爲局部內部類二、成員內部類
1、成員內部類無修飾符修飾,如何直接訪問內部類的成員?
格式:外部類名.內部類名 對象名=new 外部類名().new 內部類名();
package test.innerclass;
/**
* @author Nocol
*
* @TODO
*
*/
class Outer {
private int num = 100;
class Inner { //此時該內部類無任何修飾符修飾
public void show() {
System.out.println(num); //內部類直接訪問外部類私有成員
}
}
}
class InnerClassDemo {
public static void main(String[] args) {
// 需求:訪問Inner類的show()方法
// Inner i = new Inner(); 報錯
// i.show();報錯
// 格式:外部類名.內部類名 對象名 = 外部類對象.內部類對象;
Outer.Inner oi = new Outer().new Inner();
oi.show();
}
}
2、成員內部類被private修飾,如何訪問該私有內部類的方法?
方法:成員內部類可以看成是外部類的一個成員變量,所以外部類的方法裏面可以直接拿內部類創建其對象並使用。因此,我們可以在外部類寫一個方法,在這個方法內創建內部類的對象(相當於一個類裏面的方法使用成員變量),並用該對象調用私有內部類的方法package test.innerclass;
/**
* @author Nocol
*
* @TODO
*
*/
class Outer1 {
private int num = 200;
private class Inner { //內部類被Private修飾
public void show() {
System.out.println(num);//內部類直接訪問外部類私有成員
}
}
public void method() {
// 找不到符號
// show();
Inner i = new Inner();//創建內部類對象,測試時可通過創建外部類Outer對象調用method()方法從而實現對內部類中定義的私有方法的調用
i.show();
}
}
class InnerClassDemo2 {
public static void main(String[] args) {
//測試
//需求:訪問被privaet修飾的Inner類的show()方法
Outer1 o=new Outer1();
o.method(); //打印10
}
}
3、成員內部類被static修飾,如何訪問內部類的成員?格式:外部類名.內部類名 對象名 =new 外部類名.內部類名();
需要注意的一點:
靜態內部類訪問的 外部類數據必須用靜態修飾。
比如:靜態內部類的靜態方法和非靜態方法在訪問成員變量時,該成員變量必須由static修飾
package test.innerclass;
/**
* @author Nocol
*
* @TODO
*
*/
class Outer3{
//private int num=300; //非靜態
private static int num2=400; //靜態
//內部類用靜態修飾是因爲內部類可以看出是外部類的成員
public static class Inner{
/**
* 靜態內部類的普通方法
*/
public void show(){
// System.out.println(num); 報錯, 靜態內部類的方法(非靜態)訪問的 外部類成員變量必須是static靜態修飾的。
System.out.println(num2);
}
/**
* 靜態內部類的靜態方法
*/
public static void show2(){
//System.out.println(num); 報錯
System.out.println(num2);//靜態方法訪問靜態成員變量
}
}
}
public class InnerClassDemo3 {
public static void main(String[] args){
//使用內部類
//Outer3.Inner oi = new Outer().new Inner(); 報錯,限定的新靜態類
//oi.show();
//oi.show2();
//成員內部類被靜態修飾後的訪問方式是:
//格式:外部類名.內部類名 對象名 = new 外部類名.內部類名();
Outer3.Inner oi = new Outer3.Inner();
oi.show(); //400
oi.show2(); //400 //Inner.show2();
//show2()的另一種調用方式
//由於內部類被static靜態修飾,可直接【類名.方法名調用】
Outer3.Inner.show2(); //外部類Outer3下的Inner內部類
}
}
三、局部內部類1、定義在方法體內。可以訪問外部類的成員,包括私有。
2、外部怎麼調用方法內內部類裏面的方法?
可以在該方法內創建局部內部類的對象,通過對象調用內部類的方法。
3、需要注意的幾點:
a、若局部內部類定義在非靜態的方法體內:
局部內部類可以訪問外部類的所有成員。包括私有、包括(非)靜態成員變量、(非)靜態方法
b、若局部內部類定義在靜態的方法體內:
局部內部類只能訪問外部類的靜態成員。包括私有、靜態成員變量、靜態方法
c、若局部內部類訪問方法體內的局部變量:
該局部變量必須由final修飾。
原因:局部變量是隨着方法的調用而調用,隨着調用完畢而消失。 而堆內存的內容並不會立即消失。所以,我們加final修飾。加入final修飾後,這個變量就成了常量。既然是常量。你消失了。 我在內存中存儲的是真實的數據,所以,我還是有數據在使用。
package test.innerclass;
/**
* @author Nocol
*
* @TODO
*
*/
class Outer4 {
private int num = 10;
/**
* 局部內部類定義在非靜態方法體內
*/
public void method() {
// int num = 20;
final int num2 = 20; //若要被局部內部類訪問,該局部內部類的局部變量必須用final修飾
class Inner { //局部內部類
public void show() {
System.out.println(num); //訪問的 成員變量 不需要被final修飾
System.out.println(num2); //20 //從內部類中訪問本地變量(局部變量)num2; 需要被聲明爲最終類型
test(); //非靜態方法中的內部類訪問外部類的靜態方法(同樣可以訪問外部類的非靜態方法)
}
}
Inner i = new Inner(); //在局部 內部類外 創建其對象,方便實現對內部類內方法的調用
i.show();
}
public static void test(){
System.out.println("我是靜態方法");
}
}
class InnerClassDemo4 {
public static void main(String[] args) {
Outer4 o = new Outer4();
o.method();
}
}
四、匿名內部類1、匿名內部類即局部內部類的一種,其實就是內部類的簡化寫法。
2、前提:存在一個類或者是接口。(這裏的類可以是具體類也可以是抽象類)
3、格式:new 類名或接口名(){ 重寫方法;},該整體就是一個對象,本質就是繼承了該類或實現了該接口的子類匿名對象
package test.innerclass;
/**
* @author Nocol
*
* @TODO
*
*/
interface Person {
public abstract void study();
}
class PersonDemo {
public void method(Person p) {// 接口名作爲形式參數,其實這裏需要的不是接口,而是該接口的實現類的對象
p.study();
}
}
// 定義接口實現類
class Student implements Person {
public void study() {
System.out.println("study");
}
}
class InnerClassDemo6 {
public static void main(String[] args) {
// 測試
PersonDemo pd = new PersonDemo();
Person p = new Student();
pd.method(p);
System.out.println("-------------------------");
// 匿名內部類在開發中的使用
// 匿名內部類的本質是 繼承類或者實現了接口的子類匿名對象,所以講該整體對象當做抽象類Person的子類對象傳進去
//相當於:new Student();
pd.method(new Person() {
public void study() {
System.out.println("study");
}
});
}
}
五、關於內部類的兩道面試題
1、內部類訪問問題。
package test.innerclass;
/**
* @author Nocol
*
* @TODO
/*
* 面試題: 要求請填空分別輸出30,20,10。
*
* 注意:
* 1:內部類和外部類沒有繼承關係。
* 2:通過外部類名限定this對象 Outer.this
*/
class Outer5 {
public int num = 10;
class Inner { //成員內部類
public int num = 20;
public void show() {
int num = 30;
System.out.println(num);
System.out.println(this.num); //this表示本類的num
// System.out.println(new Outer5().num); //也可以
System.out.println(Outer5.this.num); //Outer5.this 表示是外部類的東西
}
}
}
class InnerClassTest {
public static void main(String[] args) {
Outer5.Inner oi = new Outer5().new Inner();
oi.show();
}
}
2、分析題
package test.innerclass;
/**
* @author Nocol
*
* @TODO
*
*/
/*
* 匿名內部類面試題: 按照要求,補齊代碼
*
* interface Inter1 {
* void show(); //默認 public abstract void show();
* }
* class Outer7 { //補齊代碼 }
*
* class OuterTest2 {
* public static void main(String[] args) {
* Outer7.method().show();
* }
* }
*
* 要求在控制檯輸出”HelloWorld”
*/
interface Inter1 {
void show();
// public abstract
}
class Outer7 {
// 補齊代碼
public static Inter1 method() { //接口作爲返回值,實際返回該接口實現類的對象,剛好就是匿名內部類
// 子類對象 -- 子類匿名對象
return new Inter1() {
public void show() {
System.out.println("HelloWorld");
}
};
}
}
class OuterTest2 {
public static void main(String[] args) {
Outer7.method().show();
/*
* 1:Outer7.method()可以看出method()應該是Outer中的一個 靜態方法。
* 2:Outer7.method().show()可以看出method()方法的返回值是一個對象。
* 又由於接口Inter1中有一個show()方法,所以我認爲method()方法的返回值類型是一個接口。
*/
}
}