Java內部類分類
1. 成員內部類
相當于成員,和成員定義位置相同
public class Test {
public static void main(String[] args) {
//第一種方式:
Outter outter = new Outter();
Outter.Inner inner = outter.new Inner(); //必須通過Outter對象來創建
inner.showInfo();
//第二種方式:
Outter.Inner inner1 = outter.getInnerInstance();
inner1.showInfo();
}
}
class Outter {
private int count = 10;
private int outterCount = 10;
private Inner inner = null;
public Outter() {
}
public Inner getInnerInstance() {
if (inner == null)
inner = new Inner();
System.out.println(inner.count);
return inner;
}
class Inner {
private int count = 20;
public Inner() {
}
public void showInfo() {
System.out.println(count);
System.out.println(outterCount);
}
}
}
- 成員內部類由于包含外部類實例,故可訪問外部類成員和方法即使是私有
- 當成員內部類擁有和外部類同名的成員變量或者方法時,會發生隱藏現象,即默認情況下訪問的是成員內部類的成員。如果要訪問外部類的同名成員,需要
Outter.this.xxx
- 外部類中如果要訪問成員內部類的成員,必須先創建一個成員內部類的對象,再通過指向這個對象的引用來訪問,可以訪問私有變量和方法
- 成員內部類是外部類成員,故要創建成員內部類的對象,前提是必須存在一個外部類的對象。有如下兩種創建方式:
//第一種方式:
Outter outter = new Outter();
Outter.Inner inner = outter.new Inner(); //必須通過Outter對象來創建
//第二種方式:
Outter.Inner inner1 = outter.getInnerInstance();
- 外部類只能被public和包訪問兩種權限修飾,但成員內部類擁有成員的修飾符特性,有4種 public,private,protected,默認
2. 局部內部類
相當于局部變量,定義位置和局部變量位置相同,定義在作用域(方法或者代碼塊內)
class Outter {
public void method() {
final int count = 20;
class LocalInner {
public void method() {
System.out.println(count);
}
}
new LocalInner().method();
}
}
- 和局部變量一樣不能使用任何的訪問修飾符。
- 會生成兩個.class文件,一個是Outter.class ,另一個是Outter$LocalInner.class。
- 局部內部類只能訪問方法中聲明的final類型的變量。
- 我們是無法在外部去創建局部內部類的實例對象的,因為局部內部類是定義在方法中的,而方法是需要所在類的對象去調用。
局部內部類在實際開發中用的并不多,不是很常見的,了解一下就好了。
3. 靜態內部類
class Outter {
private static int a = 20;
public Outter() {
}
static class Inner {
public Inner() {
System.out.println(a);
}
}
}
- 靜態內部類就是一個普通類,只是位置在另一個類的內部,并不持有外圍類的引用
- 只能訪問外部類的靜態成員變量或者靜態方法
- 會生成兩個.class文件,一個是外部的類Outter.class , 另一個是 Outter$Inner.class
4. 匿名內部類
- 本質:匿名內部類會隱式的繼承一個類或者實現一個接口,或者說,匿名內部類是一個繼承了該類或者實現了該接口的子類匿名對象。
- 格式:
new 類名/接口/抽象類(){
}
例如:
new Interface(){
}
- 實例:
public class Test {
public static void main(String[] args) {
// 匿名內部類1 如果Air是類,則根據參數使用對應構造方法
Air air = new Air("rocket") {
@Override
void fly() {
System.out.println("fly");
}
};
air.fly();
// 匿名內部類2 該匿名類使用類的默認構造方法也就是空構造方法
Fly fly = new Fly() {
@Override
public void fly() {
System.out.println("fly");
}
};
fly.fly();
}
}
abstract class Air {
public Air(String s) {
System.out.println(s);
}
abstract void fly();
}
interface Fly {
void fly();
}
輸出
rocket
fly
fly
編譯代碼后匿名內部類1會生成Test$1.class, 反編譯該字節碼得到
static class Test$1 extends Air
{
void fly()
{
System.out.println("fly");
}
Test$1(String s)
{
super(s);
}
}
自動生成了帶參數的構造方法,并調用了父類的同參數的構造方法
- 特性
- 匿名內部類由于沒有名字,故無法定義構造方法,但編譯后會生成構造方法根據傳入的參數
- 匿名內部類編譯后生成的.class文件的命名方式是”外部類名稱$編號.class”,編號為1,2,3…n,編號為x的文件對應的就是第x個匿名類
- 匿名內部類
為什么需要內部類
- 可以彌補沒有多繼承的缺陷
public class Test {
public static void main(String[] args) {
Swan swan = new Swan();
swan.fly();
swan.swim();
}
}
abstract class FlyingAnimal {
abstract void fly();
}
abstract class SwimmingAnimal {
abstract void swim();
}
class Swan extends FlyingAnimal {
@Override
void fly() {
System.out.println("Swan.fly()");
}
void swim() {
this.getSwimming().swim();
}
SwimmingAnimal getSwimming() {
return new SwimmingAnimal() {
@Override
void swim() {
System.out.println("Swan.swim()");
}
};
}
}
Swan繼承了FlyingAnimal,無法再繼承SwimmingAnimal,此時可通過如上匿名內部類方式解決問題
但是,這種設計方式,有明顯的缺陷。因為,Swan類本身只繼承了FlyingAnimal類,所以它和FlyingAnimal是is - a的關系,它和SwimmingAnimal并沒有繼承關系,所以并不是is-a的關系,其實更像一種has - a的關系。所以,這并不符合常理。
- 解決命令沖突(例如抽象類的方法和接口中的方法同名)
- 方便將存在一定邏輯關系的類組織在一起,又可以對外界隱藏
內部類問題
1. 局部內部類訪問局部變量,該變量必須聲明為final類型 ,但如何又可以改變該變量
解決辦法:
將該變量可以生命為數組類型
eg:
final int[] counter = new int[1];
這樣可以使counter中的內容發生改變