java內部類之成員內部類、區域性內部類和匿名內部類
內部類概念
一個類中包含另外一個類。
分類
- 成員內部類。
- 區域性內部類(包含匿名內部類)。
成員內部類
定義格式:
修飾符 class 類名稱 {
修飾符 class 類名稱 {
//...
}
//...
}
注意:
內部類使用外部,可以隨意訪問,但是外部類使用內部類要藉助內部類的物件。
使用成員內部類
兩種方法:
1. 間接方式:在外部類的方法中,使用內部類,然後main只調用外部類的方法。
2. 直接方式:公式:
一般類:類名稱 物件名 = new 類名稱();
成員內部類:外部內名稱.內部類名稱 物件名 = new 外部類名稱().new 內部類名稱();
Body.java含有一個內部類Heart
public class Body {
private String name;
//外部類方法
public void methodBody() {
System.out.println("外部類的方法");
new Heart().beat();//在外部類方法中使用內部類。
}
public class Heart { //成員內部類
//內部類方法
public void beat() {
System.out.println("心臟跳動" );
System.out.println("我叫:"+name);
}
}
}
下面分別介紹兩種方式訪問內部類:
間接方式訪問內部類
Demo01InnerClass.java
public class Demo01InnerClass {
public static void main(String[] args) {
Body body = new Body();//外部類物件
//通過外部類的物件,呼叫外部類的方法,裡面間接使用內部類Heart
body.methodBody();
直接方式訪問內部類
public class Demo01InnerClass {
public static void main(String[] args) {
Body body = new Body();//外部類對物件
//外部內名稱.內部類名稱 物件名 = new 外部類名稱().new 內部類名稱();
Body.Heart heart = new Body().new Heart();
heart.beat();
}
}
外部類和內部類變數重名問題
當外部類的變數和內部類變數重名時,在內部類方法中訪問外部類變數的格式是:外部類.this.外部類成員變數名
public class Outer {
int number = 10;
public class Inner {
int number = 20;
public void methodInner() {
int number =30;//內部類方法的區域性變數
System.out.println(number);//就近原則
System.out.println(this.number);
System.out.println(Outer.this.number);//外部類的成員變數
}
}
}
30
20
10
區域性內部類
如果類是定義在一個方法內部的,那麼這就是一個區域性內部類。
“區域性”:只有當前所屬的方法才能使用它,出了這個方法外面就不能用了。
定義格式
:
修飾符 class 外部類名稱 {
修飾符 返回值型別 外部類方法名稱(引數列表) {
class 區域性內部類名稱 {
//...
}
}
}
Outer.java
含有區域性內部類
public class Outer {
public void methodOuter() {
class Inner {
int num = 10;
public void methodInner() {
System.out.println(num);//10
}
}
//這個內部列只有當前所屬方法能夠訪問這個類,出了這個方法就不能用了
Inner inner = new Inner();
inner.methodInner();
}
}
DemoMain.java
public class DemoMain {
public static void main(String[] args) {
Outer outer = new Outer();
outer.methodOuter();
}
}
結果:
10
注意:
區域性內部類,如果希望訪問所在方法的區域性變數,那麼這個區域性變數必須是【有效final的】
備註:從java 8+開始,只要區域性變數事實不變,那麼final關鍵字可以省略。
原因:
1. new 出來的物件在堆記憶體當中。
2. 區域性變數是跟著方法走的,在棧記憶體當中。
3. 方法執行結束後,立刻出棧,區域性變數就會立刻消失。
4. 但是new出來的物件在堆當中持續存在,直到垃圾回收消失。
public class MyOuter {
public void methodOuter() {
final int num = 10;//所在方法的區域性變數
/*在棧中的區域性變數可能隨著方法結束出棧消失了,但是建立的內部類物件還存在。
物件使用常量時能直接將常量copy進來,這時就要保證這個常量是不可更改的,也就是final。
*/
//num = 20;
class MyInner {
public void methodInner() {
System.out.println(num);
}
}
Inner myinner = new MyInner();
inner.methodInner();
}
}
修飾內部類的許可權
public > protected > (default) > private
定義一個類的時候,許可權修飾符規則:
- 外部類:public / (default)
- 成員內部類:都可以寫,public > protected > (default) > private
- 區域性內部類:什麼都不能寫,但不是de
匿名內部類
如果介面的實現類(或者是父類的子類)只需要使用唯一的一次。
那麼這種情況下就可以省略調該類的定義,而改為使用【匿名內部類】。
匿名內部類定義格式:
介面名稱 物件名 = new 類名或者介面名稱() {
//覆蓋重寫所有抽象方法
};
代表繼承了或者實現了該介面的子類物件。
==介面MyInterface.java
==
public interface MyInterface {
void method1();//抽象方法
void method2();
}
==介面實現類MyInterfaceImp.java
==
public class MyInterfaceImpl implements MyInterface {
@Override
public void method1() {
System.out.println("實現類覆蓋重寫了方法1");
}
@Override
public void method2() {
System.out.println("實現了覆蓋重寫方法2");
}
}
測試類
一、一般介面實現方法
DemoMain.java
public class DemoMain {
public static void main(String[] args) {
// MyInterface impl = new MyInterfaceImpl();
// impl.method();
}
}
匿名內部類實現介面
DemoMain.java
public class DemoMain {
public static void main(String[] args) {
//使用匿名內部類,但不匿名物件,物件名稱就叫obj
MyInterface objA = new MyInterface() {
@Override
public void method1() {
System.out.println("匿名內部類實現了方法1-A");
}
@Override
public void method2() {
System.out.println("匿名內部類實現了方法2-A");
}
};
objA.method1();
objA.method2();
System.out.println("----------我是分割線----------");
//使用匿名內部類,而且省略了物件名稱,也是匿名物件
new MyInterface() {
@Override
public void method1() {
System.out.println("匿名內部類實現了方法1-B");
}
@Override
public void method2() {
System.out.println("匿名內部類實現了方法2-B");
}
}.method1();
}
}
結果:
匿名內部類實現了方法1-A
匿名內部類實現了方法2-A
----------我是分割線----------
匿名內部類實現了方法1-B
匿名內部內應用
- 實現倒計時5秒。
public class Demo02_Time {
public static void main(String[] args) {
Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("倒計時5秒");
}
},5000);//倒計時5秒
}
}
注意事項:
對格式進行解析:"new 介面名稱 {...};"
進行解析
- new代表建立物件的動作
- 介面名稱就是匿名內部類需要實現那個介面
- {…}這才是匿名內部類的內容
注意:
這個類只需要使用一次。匿名內部類在建立物件時,只能使用唯一一次。
如果希望多次希望多次建立物件,而且內容一樣的話,那麼就必須使用單獨定義的實現類。匿名物件,在呼叫的時候,只能呼叫唯一一次。
如果希望同一個物件,呼叫多次方法,那麼必須給物件取一個名字。匿名內部類是省略了【實現類/子類】,但是匿名物件是省略了【物件命名】。
強調:匿名和匿名物件不是一回事。