
內(nèi)部類
今天接觸了一天的 Java 內(nèi)部類,這個(gè)東西給我感覺(jué)一點(diǎn)就是:變化多端。之所以說(shuō)這個(gè)東西變化多端,后面我們會(huì)用一些例子來(lái)證明,通過(guò)不同的形式來(lái)實(shí)現(xiàn)同一個(gè)方法。
內(nèi)部類,可以分為以下四種
- 成員內(nèi)部類:創(chuàng)建在類內(nèi)方法外,和成員變量及成員方法類似。
- 局部?jī)?nèi)部類:創(chuàng)建在方法內(nèi)部,類似局部代碼塊。
-
靜態(tài)內(nèi)部類:用
static修飾的內(nèi)部類。 - 匿名內(nèi)部類:也叫匿名子類對(duì)象,最特殊也是最常用的一種類型;一般用在方法參數(shù)的位置,匿名子類對(duì)象的類內(nèi)方法也叫做 閉包。
1.創(chuàng)建內(nèi)部類類對(duì)象格式
- 如果我們想訪問(wèn)一個(gè)內(nèi)部類時(shí),我們需要通過(guò)
外部類名.內(nèi)部類名來(lái)找到這個(gè)內(nèi)部類。 - 在我們創(chuàng)建下面??這個(gè)內(nèi)部類對(duì)象的格式是這樣的:
外部類.內(nèi)部類 對(duì)象名 = 外部類對(duì)象.內(nèi)部類對(duì)象
class Outer {
class Inner {
public void method() {
System.out.println("Hello Inner");
}
}
}
class Sample_InnerClass01 {
public static void main(String[] args) {
// 外部類.內(nèi)部類 對(duì)象名 = 外部類對(duì)象.內(nèi)部類對(duì)象;
Outer.Inner oi = new Outer().new Inner();
oi.method();
}
}
Tip??:直接通過(guò)這種方式在創(chuàng)建內(nèi)部類對(duì)象是不常用、也不推薦的方式,下面我們會(huì)介紹其他調(diào)用內(nèi)部類的方式。
2.成員內(nèi)部類私有的使用
- 我們知道面向?qū)ο笠粋€(gè)重要原則是封裝,就像我們會(huì)私有化成員變量一樣,在一般情況下內(nèi)部類也是需要進(jìn)行私有化,并提供外部訪問(wèn)接口。
- Sample 中我們私有化了
Inner內(nèi)部類,提供了print方法供外部調(diào)用。
class Outer {
private int num = 10;
private class Inner {
public void method() {
System.out.println("Print: " + num);
}
}
public void print() {
Inner in = new Inner();
in.method();
}
}
class Sample_InnerClass02 {
public static void main(String[] args) {
Outer o = new Outer();
o.print();
}
}
Thinking???♂?:本例中為什么內(nèi)部類能訪問(wèn)到外部類的成員變量?
3.靜態(tài)成員內(nèi)部類
- 與創(chuàng)建非靜態(tài)內(nèi)部類對(duì)象不同的是,創(chuàng)建內(nèi)部類對(duì)象格式有些不同。
- 靜態(tài)內(nèi)部類對(duì)象創(chuàng)建格式:
外部類.內(nèi)部類 對(duì)象名 = 外部類名.內(nèi)部類對(duì)象
class Outer {
private static int num = 10;
static class Inner {
public void method() {
System.out.println("Print: " + num);
}
}
}
class Sample_InnerClass03 {
public static void main(String[] args) {
// 外部類.內(nèi)部類 對(duì)象名 = 外部類名.內(nèi)部類對(duì)象;
Outer.Inner oi = new Outer.Inner();
oi.method();
}
}
Reminder?????:靜態(tài)內(nèi)部類中只能訪問(wèn)本類的靜態(tài)成員。
4.成員內(nèi)部類的小練習(xí)??
- 內(nèi)部類之所以能獲取到外部類的成員,是因?yàn)樗塬@取到外部類的引用
外部類名.this。
class Outer {
public int num = 10;
class Inner {
public int num = 20;
public void show() {
int num = 30;
System.out.println(num); // 30
System.out.println(this.num); // 20
// 內(nèi)部類之所以能獲取到外部類的成員,是因?yàn)樗塬@取到外部類的引用 外部類名.this
System.out.println(Outer.this.num); // 10
}
}
}
class Sample_InnerClass04 {
public static void main(String[] args) {
new Outer().new Inner().show();
}
}
5.局部?jī)?nèi)部類訪問(wèn)局部變量
- 在內(nèi)存中
num會(huì)隨著 method 方法的出棧而釋放,而內(nèi)部類在堆區(qū)還沒(méi)有消失。JVM 默認(rèn)為局部變量添加final,其實(shí)是將num復(fù)制 了一份提供給局部?jī)?nèi)部類來(lái)訪問(wèn),而之前真正的局部變量num已經(jīng)被釋放,所以此時(shí)將局部變量用final修飾為一個(gè)常量。 - 如果在內(nèi)部類中改變
num的值就會(huì)出現(xiàn)這樣的錯(cuò)誤提示:錯(cuò)誤: 從內(nèi)部類引用的本地變量必須是最終變量或?qū)嶋H上的最終變量。
class Outer {
public void method() {
int num = 10; // 在 JDK1.8之后 JVM 會(huì)自動(dòng)加上 final 修飾符
class Inner {
public void print() {
System.out.println(num);
}
}
Inner in = new Inner();
in.print();
}
}
class Sample_InnerClass05 {
public static void main(String[] args) {
new Outer().method();
}
}
6.匿名子類對(duì)象概述
- 概述:實(shí)際就是內(nèi)部類的簡(jiǎn)化寫(xiě)法。
- 前提:存在一個(gè) 類 或 接口 ,這里的類可以是具體類也可以是抽象類。
- 格式:
new 類名或接口() {
重寫(xiě)方法;
}
- 本質(zhì):是一個(gè)繼承了該類 或 者實(shí)現(xiàn)了該接口的 匿名子類對(duì)象。
interface Inter {
public abstract void print1();
}
class Outer {
public void method() {
Inter in = new Inter() {
public void print1() {
System.out.println("Print1");
}
};
in.print1();
}
}
class Sample_InnerClass06 {
public static void main(String[] args) {
new Outer().method();
}
}
7.匿名子類對(duì)象重寫(xiě)多個(gè)方法的調(diào)用
- 使用原則:匿名子類對(duì)象只針對(duì)重寫(xiě)一個(gè)方法的時(shí)候使用。
- 弊端:不能定義、調(diào)用子類特有的方法,且沒(méi)有子類類名。
interface Inter {
public void show1();
public void show2();
}
class Outer {
public void method() {
Inter in = new Inter() { // 父類引用指向子類對(duì)象(多態(tài))
public void show1() {
System.out.println("show1");
}
public void show2() {
System.out.println("show2");
}
};
in.show1();
in.show2();
System.out.println(in);
}
}
class Sample_InnerClass07 {
public static void main(String[] args) {
new Outer().method();
}
}
Discussion??:
Inter父類指向匿名子類對(duì)象,其實(shí)是一個(gè)多態(tài),如果在子類對(duì)象中定義了父類中不存在的方法,則編譯會(huì)報(bào)錯(cuò)。而如果創(chuàng)建一個(gè)子類對(duì)象來(lái)實(shí)現(xiàn)接口方法的話,就可以擴(kuò)展子類特有的功能了。
8.接口對(duì)象作為參數(shù)傳遞的多種實(shí)現(xiàn)形式
interface Inter {
void print();
}
class Demo {
public static void method(Inter i) {
i.print();
}
}
class InterClass implements Inter {
public void print() {
System.out.println("Hello World1!");
}
}
class Test {
private static final Inter i3;
static {
i3 = new Inter() {
public void print() {
System.out.println("Hello World3!");
}
};
}
public static void main(String[] args) {
// 1.創(chuàng)建一個(gè)實(shí)現(xiàn)接口的子類對(duì)象。
InterClass i1 = new InterClass();
Demo.method(i1);
// 2.在方法中創(chuàng)建一個(gè)匿名子類并返回。
Inter i2 = method();
Demo.method(i2);
// 3.定義一個(gè) Inter類型的 static 成員變量,并在構(gòu)造代碼塊中用賦值它的子類對(duì)象。
Demo.method(i3);
// 4.在成員方法的參數(shù)位置創(chuàng)匿名子類對(duì)象,并重寫(xiě)父類方法。(推薦方式)
Demo.method(new Inter() {
public void print() {
System.out.println("Hello World4!");
}
});
}
public static Inter method() {
return new Inter() {
public void print() {
System.out.println("Hello World2!");
}
};
}
}
Discussion??:可以看到,通過(guò)子類實(shí)現(xiàn) 抽象類 與 接口 的方式千變?nèi)f化,語(yǔ)法的掌握是基礎(chǔ),但最終目的是為了提高代碼的內(nèi)聚性、減少耦合性并簡(jiǎn)化代碼,只有從這個(gè)目的出發(fā)去使用我們的內(nèi)部類才能讓我們的編程能力得到提高。
9.悄悄話
- 這是我個(gè)人發(fā)表的第 2 篇簡(jiǎn)書(shū) Blog,第一篇文章 JavaSE 基礎(chǔ)(六)構(gòu)造函數(shù) 試著投了首頁(yè)和幾個(gè)關(guān)于 Java 的專題,都順利通過(guò)了,甚至還被專題推薦了文章,真的是沒(méi)想到的。今天寫(xiě)這篇技術(shù) Blog 完全是受著大家的鼓勵(lì)而寫(xiě)出來(lái)的。
- 因?yàn)檫@段時(shí)間本人正在 JavaSE 的閉關(guān)修行中,沒(méi)有太多時(shí)間去查閱相關(guān)資料和做文章優(yōu)化,所以有些內(nèi)容可能會(huì)有差錯(cuò),也請(qǐng)讀者們?cè)谠u(píng)論中批評(píng)并指出問(wèn)題。
-
今天開(kāi)通了簡(jiǎn)書(shū)專題 JavaSE 成長(zhǎng)之路,主要為一樣正在 JavaSE 修行中的簡(jiǎn)友們提供了技術(shù)交流的平臺(tái),希望大家多多投稿交流互動(dòng)。