將一個類別定義在另一個類別的內部或介面內部或是方法體內部,這個類別就被稱為內部類,我們不妨將內部類所在的類稱為外圍類,除了定義在類,接口,方法中的內部類,還有一種特殊的內部類,那就是使用關鍵字new創建一個匿名類的對象,而這個匿名類其實就是一個內部類,具體說是一個匿名內部類,常用於傳入構造器實參構造對象,例如PriorityQueue對象的創建。
一個類別定義在另一個類別的內部或介面內部,且沒有static修飾時,這個內部類別就稱為實例內部類,使用static修飾時,這個內部類別稱為靜態內部類別(巢狀類別),將一個類別定義在程式碼區塊內部,特別是方法體內部,這個內部類別被稱為本地內部類別或局部內部類,還有一種就是上面所說的匿名內部類別。
實例內部類別的定義很簡單,就直接定義在外圍類別的裡面就可以了。問題是如何建立一個內部類別對象,首先,需要明白內部類別與外圍類別中的成員變數與方法是平起平坐的,都是屬於外圍類別物件的(無static修飾),所以想要內部類別物件先得有外圍類別對象,第一種建立內部類別物件的方式就是使用一個方法傳回一個內部類別對象,而這個內部類別物件的型別為OutClassName.InnerClassName,即外圍類別名稱.內部類別名稱。
public class Outer { class Inner1 { private String str; public Inner1(String s) { this.str = s; } public String readStr() { return this.str; } } class Inner2 { private int val; public Inner2(int i) { this.val = i; } public int readVal() { return this.val; } } //创建内部类 public Inner1 creatInner1(String s) { return new Inner1(s); } public Inner2 creatInner2(int i) { return new Inner2(i); } public static void main(String[] args) { Outer outer = new Outer(); Outer.Inner1 inner1 = outer.creatInner1("Inner1"); Outer.Inner2 inner2 = outer.creatInner2(2); System.out.println(inner1.readStr()); System.out.println(inner2.readVal()); } }
//output: Inner1 2 Process finished with exit code 0
當然,建立一個內部類別還有其他的方法,就是使用外圍類別的物件.new來建立一個內部類別對象,語法為:
外部類別物件.new 內部類別建構方法;
public class Outer { class Inner1 { private String str; public Inner1(String s) { this.str = s; } public String readStr() { return this.str; } } class Inner2 { private int val; public Inner2(int i) { this.val = i; } public int readVal() { return this.val; } } public static void main(String[] args) { Outer outer = new Outer(); Outer.Inner1 inner1 = outer.new Inner1("Inner1"); Outer.Inner2 inner2 = outer.new Inner2(2); System.out.println(inner1.readStr()); System.out.println(inner2.readVal()); } }
//output: Inner1 2 Process finished with exit code 0
內部類別的特性不只如此,內部類別還能與外圍類別連結,首先實例內部類別物件的創建是依賴外圍類別物件的引用,實例內部類別與外圍類別的成員變數地位一樣,如果想要透過內部類別物件存取外圍類別的成員變數與方法(特別是同名變數)。在內部類別中,可以使用外圍類別名稱.this取得外圍類別對象,然後使用獲得的這個外圍類別物件來使用外圍類別的變數與方法。
public class Outer { private String outStr; public Outer(String str) { this.outStr = str; } public void printOuter() { System.out.println(this.outStr); } class Inner { private String str; public Inner(String s) { this.str = s; } public String readStr() { return this.str; } //使用.this获取父类对象引用 public Outer outer() { return Outer.this; } } public static void main(String[] args) { Outer outer = new Outer("outerString"); Outer.Inner inner = outer.new Inner("innerString"); Outer out = inner.outer(); out.printOuter(); } }
//output: outerString Process finished with exit code 0
其實內部類別物件中有兩個this,直接使用this.成員取得的是內部類別物件自己的成員,而像上面使用外圍類別名稱.this.成員取得的是外圍類別的成員,也就是說在內部類別中this指向內部類別物件自己的引用,外部類別名稱.this指向外圍類別物件的引用。
當出現內部類別與外圍類別變數名稱相同時,這兩個this就有很大的用處了。
public class Outer { public int a = 12; public int b = 16; public int c = 20; class Inner{ public int a = 8; public int c = 48; public int d = 2; public void printVal() { System.out.println("外围类变量a=" + Outer.this.a); System.out.println("外围类变量b=" + Outer.this.b); System.out.println("外围类变量c=" + Outer.this.c); System.out.println("内部类变量a=" + this.a); System.out.println("内部类变量c=" + this.c); System.out.println("内部类变量d=" + this.d); } } public Inner creatInner() { return new Inner(); } public static void main(String[] args) { Outer outer = new Outer(); Outer.Inner inner = outer.creatInner(); inner.printVal(); } }
//output: 外围类变量a=12 外围类变量b=16 外围类变量c=20 内部类变量a=8 内部类变量c=48 内部类变量d=2 Process finished with exit code 0
如果沒有出現同名,直接取得的成員即可,可以不用使用上面所說的this,出現同名,直接存取的成員預設是內部類別物件的。
內部類別可以與週邊類別的所有元素的存取權限,所以我們可以嘗試使用內部類別來遍歷輸出外圍類別中的元素,雖然內部類與外圍類別的成員地位是平等,但內部類別畢竟也是類別,它也可以像外圍類別一樣繼承類別和實作介面。
import java.util.Arrays; interface Selector{ //判断是否迭代完全部元素 public boolean end(); //获取当前元素 public Object current(); //迭代下一个元素 public void next(); } public class SequeArray { private Object[] items; private int usedSize; public SequeArray(int capacity) { items = new Object[capacity]; } public void add(Object val) { if (usedSize == items.length) items = Arrays.copyOf(items, 2 * usedSize); items[usedSize++] = val; } private class SequeSelector implements Selector{ private int index; public boolean end() { return index == usedSize; } public Object current() { return items[index]; } public void next() { if (index < usedSize) index++; } } public SequeSelector sequeSelector() { return new SequeSelector(); } public static void main(String[] args) { SequeArray array = new SequeArray(10); for (int i = 0; i < 10; i++) { array.add(i+1); } //发生了向上转型 Selector selector = array.sequeSelector(); while (!selector.end()) { System.out.print(selector.current() + " "); selector.next(); } } }
//output: 1 2 3 4 5 6 7 8 9 10 Process finished with exit code 0
內部類別的繼承有一點麻煩,因為內部類別的建構器必須依賴外圍類別的引用,所以需要一段特殊的語法來說明內部類別與外圍類別的關聯:
外圍類別參考.super();
#具體怎麼會看如下一段程式碼:
public class Outer { class Inner{ } } class Person extends Outer.Inner { private String str; private int id; public Person(Outer out, String str, int id) { out.super(); this.str = str; this.id = id; } public void readVal() { System.out.println(this.str + this.id); } }
當內部類別被繼承的時候,需要透過外圍類別的參考傳入父類別的建構方法中並呼叫super()才能通過編譯。
如果內部類別繼承外部類,直接繼承即可,不需要這麼麻煩。
程式產生一個java檔案的字節碼檔案時,命名為公共外部類別$內部類別。
內部類別可以無限嵌套,一般沒人這麼幹。
靜態內部類別也稱為嵌套類,它就是在實例內部類別的定義前加入一個static關鍵字,像下面這樣:
public class Outer { static class Inner{ } }
與實例內部類別的區別是靜態內部類別是屬於類別的而不是屬於對象的,因此靜態內部類別的創建不依賴與外部類別對象,這是和實例內部類別最大的區別之一。
public class Outer { static class Inner{ private String str; public Inner(String s) { str = s; } } public static void main(String[] args) { Outer.Inner inner = new Outer.Inner("我是静态内部类!"); System.out.println(inner.str); } }
//output: 我是静态内部类! Process finished with exit code 0
匿名內部類別的用法我們已經看過了,就是建立PriorityQueue物件時,預設建立的是小堆,需要傳入比較器來建立大堆。
PriorityQueue<Integer> priorityQueue = new PriorityQueue<>(new Comparator<Integer>() { @Override public int compare(Integer o1, Integer o2) { return o1 - o2; } });
像上面這種在方法中使用new關鍵字創建一個匿名類別的物件作為實參,裡面這個匿名類別就是匿名內部類,創建過程中的Comparator匿名類別物件是繼承所需傳參類型的,在這裡也就是Comparator類別。
如果使用自訂類型傳入優先隊列,是一定要實作比較器的,實作比較器最常見的方式就是匿名內部類別與Lambda表達式。
假设我有一个Person类数组,需要对他们的name排序,如果不使用内部类,就需要在Person类中实现比较器。
import java.util.Arrays; import java.util.Comparator; class Preson { public String name; public Integer id; public Preson(String name, Integer id) { this.name = name; this.id = id; } @Override public String toString() { return "Preson{" + "name='" + name + '\'' + ", id=" + id + '}'; } } class PersonComparator implements Comparator<Preson> { @Override public int compare(Preson o1, Preson o2) { return o1.name.compareTo(o2.name); } } public class Main { public static void main(String[] args) { Preson preson1 = new Preson("aboluo", 24); Preson preson2 = new Preson("zhousi", 25); Preson preson3 = new Preson("syyfjy", 2); Preson[] presons = {preson1, preson2, preson3}; Arrays.parallelSort(presons, new PersonComparator()); System.out.println(Arrays.toString(presons)); } }
//output: [Preson{name='aboluo', id=24}, Preson{name='syyfjy', id=2}, Preson{name='zhousi', id=25}] Process finished with exit code 0
使用内部类上述代码就变为:
import java.util.Arrays; import java.util.Comparator; class Preson { public String name; public Integer id; public Preson(String name, Integer id) { this.name = name; this.id = id; } @Override public String toString() { return "Preson{" + "name='" + name + '\'' + ", id=" + id + '}'; } } public class Main { public static void main(String[] args) { Preson preson1 = new Preson("aboluo", 24); Preson preson2 = new Preson("zhousi", 25); Preson preson3 = new Preson("syyfjy", 2); Preson[] presons = {preson1, preson2, preson3}; Arrays.parallelSort(presons, new Comparator<Preson>() { @Override public int compare(Preson o1, Preson o2) { return o1.name.compareTo(o2.name); } }); System.out.println(Arrays.toString(presons)); } }
//output: [Preson{name='aboluo', id=24}, Preson{name='syyfjy', id=2}, Preson{name='zhousi', id=25}] Process finished with exit code 0
还有一个本地内部类,就是类定义在方法中,就不多说了,基本上不用,用法如下:
public class Outer { public void func() { class Inner{ // } } }
以上是Java內部類別範例分析的詳細內容。更多資訊請關注PHP中文網其他相關文章!