本文為joshua317原創文章,轉載請注明:轉載自joshua317博客 https://www.joshua317.com/article/212
在 Java 中,可以將一個類定義在另一個類里面或者一個方法里面,這樣的類稱為內部類。廣泛意義上的內部類一般來說包括這四種:成員內部類、局部內部類、匿名內部類和靜態內部類。我們知道類的五大成員:屬性、方法、構造器、代碼塊、內部類。內部類最大的特點就是可以直接訪問私有屬性,並且可以體現類與類之間的包含關系。
從定義的位置上看:
(1)定義在外部類的方法體內/代碼塊
- 局部內部類(有類名)
- 匿名內部類(沒有類名)
(2)定義在外部類的成員位置上
- 靜態內部類(有static修飾符)
- 成員內部類(沒有static修飾符)
1.成員內部類
成員內部類是最普通的內部類,它的定義為位於另一個類的內部
成員內部類語法格式
/**
* @author joshua317
*/
public class Outer {//外部類
class Inner {//內部類
}
}
類Inner像是類Outer的一個成員,Outer稱為外部類。成員內部類可以無條件訪問外部類的所有成員屬性和成員方法(包括private成員和靜態成員)
package com.joshua317;
/**
* @author joshua317
*/
public class Outer {//外部類
private String name;
public static void main(String[] args) {
Outer outerObj = new Outer("joshua317");
Outer.Inner inner = outerObj.new Inner();
inner.InnerFun1();
}
public Outer(String name) {
this.name = name;
}
public void OuterFun1() {
System.out.println("外部類成員方法");
}
class Inner {//內部類
public void InnerFun1() {
System.out.println(name);
}
}
}
雖然成員內部類可以無條件地訪問外部類的成員,而外部類想訪問成員內部類的成員卻不是這么隨心所欲了。在外部類中如果要訪問成員內部類的成員,必須先創建一個成員內部類的對象,再通過指向這個對象的引用來訪問。
當成員內部類擁有和外部類同名的成員變量或者方法時,會根據就近原則,即默認情況下訪問的是成員內部類的成員。如果要訪問外部類的同名成員,需要以下面的形式進行訪問:
外部類.this.成員變量
外部類.this.成員方法
package com.joshua317;
/**
* @author joshua317
*/
public class Outer {//外部類
private String name;
public static void main(String[] args) {
Outer outerObj = new Outer("joshua317");
Outer.Inner inner = outerObj.new Inner();
inner.InnerFun1();
}
public Outer(String name) {
this.name = name;
}
public void OuterFun1() {
System.out.println("外部類成員方法");
}
class Inner {//內部類
String name;
public void InnerFun1() {
//訪問外部類的同名成員屬性
System.out.println("我是內部類的成員屬性name:"+ name + " 我是外部類的成員屬性name:"+Outer.this.name);
}
}
}
成員內部類是依附外部類而存在的,也就是說,如果要創建成員內部類的對象,前提是必須存在一個外部類的對象。創建成員內部類對象的一般方式如下:
package com.joshua317;
public class TestInner {
public static void main(String[] args) {
//第一種方式:
Outter outter = new Outter();
Outter.Inner inner = outter.new Inner(); //必須通過Outter對象來創建
//第二種方式:
Outter.Inner inner1 = outter.getInnerInstance();
}
}
class Outter {
private Inner inner = null;
public Outter() {
}
public Inner getInnerInstance() {
if(inner == null)
inner = new Inner();
return inner;
}
class Inner {
public Inner() {
}
}
}
成員內部類可以擁有 private 訪問權限、protected 訪問權限、public 訪問權限及包訪問權限。比如上面的例子,如果成員內部類 Inner 用 private 修飾,則只能在外部類的內部訪問,如果用 public 修飾,則任何地方都能訪問;如果用 protected 修飾,則只能在同一個包下或者繼承外部類的情況下訪問;如果是默認訪問權限,則只能在同一個包下訪問。這一點和外部類有一點不一樣,外部類只能被 public 和包訪問兩種權限修飾。由於成員內部類看起來像是外部類的一個成員,所以可以像類的成員一樣擁有多種權限修飾。
2.局部內部類
局部內部類是定義在一個方法或者一個作用域里面的類,它和成員內部類的區別在於局部內部類的訪問僅限於方法內或者該作用域內。
局部內部類語法格式:
/**
* @author joshua317
*/
public class Outer {//外部類
public void OuterFun1() {
System.out.println("外部類成員方法");
class Inner {//局部內部類
}
}
}
**局部內部類就像是方法里面的一個局部變量一樣,是不能有 public、protected、private 以及 static 修飾符的。**總結下來有這幾點:
(1)局部內部類可以直接訪問外部類的所有成員,包含私有的;
(2)不能添加修飾符,因為它是一個局部變量,局部變量是不能使用修飾符的,但是可以用final修飾,因為局部變量是可以使用final修飾的。
(3)作用域僅僅在定義它的方法或者代碼塊中。
(4)局部內部類訪問外部類的成員屬性或者方法時,直接訪問;
(5)外部類訪問局部內部類的成員屬性或者方法時,通過先創建對象,再訪問,且必須要在作用域內。
package com.joshua317;
/**
* @author joshua317
*/
public class Outer {//外部類
private String name;
public static void main(String[] args) {
Outer outerObj = new Outer("joshua317");
outerObj.OuterFun1();
}
public Outer(String name) {
this.name = name;
}
public void OuterFun1() {
System.out.println("外部類成員方法");
class Inner {//內部類
String name;
public void InnerFun1() {
//訪問外部類的同名成員屬性
System.out.println("局部內部類的成員屬性name:"+ name + " 我是外部類的成員屬性name:"+Outer.this.name);
}
public void setName(String name) {
this.name = name;
}
}
Inner inner = new Inner();
inner.setName("innerName");
inner.InnerFun1();
System.out.println("局部內部類的成員屬性name:" + inner.name);
}
}
3.匿名內部類
Java 中可以實現一個類中包含另外一個類,且不需要提供任何的類名直接實例化。主要是用於在我們需要的時候創建一個對象來執行特定的任務,可以使代碼更加簡潔。匿名類是不能有名字的類,它們不能被引用,只能在創建時用 new 語句來聲明它們。
匿名內部類語法格式:
package com.joshua317;
/**
* @author joshua317
*/
public class Outer {//外部類
Object obj = new Inner(){
@Override
public void innerFun1() {
}
};
}
interface Inner {
public void innerFun1();
}
匿名內部類也是不能有訪問修飾符和 static 修飾符的。
匿名內部類是唯一一種沒有構造器的類。正因為其沒有構造器,所以匿名內部類的使用范圍非常有限,大部分匿名內部類用於接口回調。
匿名內部類在編譯的時候由系統自動起名為 Outer$1.class。
匿名內部類主要用於繼承其他類或是實現接口,並不需要增加額外的方法,只是對繼承方法的實現或是重寫。
匿名內部類使用一次,就不再使用。
package com.joshua317;
/**
* @author joshua317
*/
public class Outer {//外部類
public static void main(String[] args) {
/**
* 底層會分配一個類名Outer$1,去實現Inner接口
* class Outer$1 implements Inner {
* @Override
* public void innerFun1() {
* System.out.println("實現接口Inner,匿名內部類方法innerFun1()");
* }
* }
*/
Inner inner = new Inner(){
@Override
public void innerFun1() {
System.out.println("實現接口Inner,匿名內部類方法innerFun1()");
}
};
System.out.println(inner);
inner.innerFun1();
/**
* 底層會分配一個類名Outer$2,去繼承InnerClass
* class Outer$1 extends InnerClass {
* @Override
* public void innerFun1() {
* System.out.println("匿名類繼承了 InnerClass 類,匿名內部類方法innerFun1()");
* }
* }
*/
Inner inner2 = new InnerClass(){
@Override
public void innerFun1() {
System.out.println("匿名類繼承了 InnerClass 類,匿名內部類方法innerFun1()");
}
};
System.out.println(inner2);
inner2.innerFun1();
}
}
class InnerClass implements Inner {
@Override
public void innerFun1() {
}
}
interface Inner {
public void innerFun1();
}
4.靜態內部類
靜態內部類也是定義在另一個類里面的類,只不過在類的前面多了一個關鍵字static。靜態內部類是不需要依賴於外部類的,這點和類的靜態成員屬性有點類似,並且它不能使用外部類的非static成員變量或者方法,這點很好理解,因為在沒有外部類的對象的情況下,可以創建靜態內部類的對象,如果允許訪問外部類的非static成員就會產生矛盾,因為外部類的非static成員必須依附於具體的對象。可以添加任意的訪問修飾符public、protected、private 以及默認,因為它就是類的成員。作用域和其他類成員一樣,為整個類體。
靜態內部類語法格式:
/**
* @author joshua317
*/
public class Outer {//外部類
static class Inner {//內部類
}
}
package com.joshua317;
/**
* @author joshua317
*/
public class Outer {//外部類
static String name = "joshua317";
public static void main(String[] args) {
Outer outer = new Outer();
//方式一:內部直接訪問
Inner inner1 = new Inner();
inner1.innerFun();
//方式二:因為是靜態內部類,可以通過類名直接訪問(前提是滿足訪問權限)
Outer.Inner inner2 = new Outer.Inner();
inner2.innerFun();
//方式三:通過普通成員方法,返回靜態內部類的實例
Inner inner3 = outer.getInnerInstance();
inner3.innerFun();
//方式三:通過靜態方法,返回靜態內部類的實例
Outer.Inner inner4 = Outer.getInnerInstance2();
inner4.innerFun();
}
static class Inner {//內部類
static String name = "joshua317-inner";
public void innerFun () {
System.out.println("內部類靜態成員" + name + " 外部類靜態成員" + Outer.name);
}
}
/**
* 通過方法,返回靜態內部類的實例
* @return Inner
*/
public Inner getInnerInstance() {
return new Inner();
}
public static Inner getInnerInstance2() {
return new Inner();
}
}
5.內部類的使用好處
- 1.每個內部類都能獨立的繼承一個接口的實現,所以無論外部類是否已經繼承了某個(接口的)實現,對於內部類都沒有影響。內部類使得多繼承的解決方案變得完整;
- 2.方便將存在一定邏輯關系的類組織在一起,又可以對外界隱藏;
- 3.方便編寫事件驅動程序;
- 4.方便編寫線程代碼。
本文為joshua317原創文章,轉載請注明:轉載自joshua317博客 https://www.joshua317.com/article/212


