【Java 程式設計】嵌入類,內部類,區域性類,匿名類
文章目錄
在程式碼中使用嵌入類,能增強程式碼的封裝性和可讀性,讓程式碼更簡潔,有效。
1. 嵌入類,內部類 ,外部類
在 Java 中,允許在一個類中定義另一個類,稱之為嵌入類,其中 non-static 嵌入類又稱為內部類(inner class)。外部類也可以成為包裹類。
class OuterClass{
// outer class
static class StaticNestedClass{
// static nested class
}
class NestedClass{
// inner class
}
}
Static nested class:
- 靜態嵌入類不可以訪問外部類的例項成員(instance variables or methods);
- 通過外部類名來引用,並例項化;
OuterClass.StaticNestedClass nestedObject = new OuterClass.StaticNestedClass();
Inner class 本質上就是外部類的例項成員,因此可以訪問 Outer Class 例項(instance)的所有成員,包括私有成員。
要建立 Inner class 的例項,必須先建立一個包裹類的例項,也正因如此,內部類不能定義靜態成員(不然,該怎麼訪問呢?):
OuterClass outerObject = new OuterClass();
OuterClass.InnerClass innerObject = outerObject.new InnerClass();
2. 變數的可見性
當具有相同名稱的變數或方法出現在包裹類和內部類時,內部類的宣告會覆蓋掉包裹類的宣告,這時將不能直接通過名稱來訪問外部類中被覆蓋掉的成員,要使用詳細的訪問路徑才行。
示例
public class ShadowTest {
public int x = 0;
class InnerClass {
public int x = 1;
void methodInInnerClass(int x) {
System.out.println("x = " + x); // input parameter: x
System.out.println("this.x = " + this.x); // innerClass.x
System.out.println("ShadowTest.this.x = " + ShadowTest.this.x); // shadowTest.x
}
}
public static void main(String... args) {
ShadowTest st = new ShadowTest();
ShadowTest.FirstLevel fl = st.new InnerClass(); // 內部類物件的例項化,注意用法
fl.methodInInnerClass(23);
}
}
特別注意!
在內部類呼叫外部類的例項變數,可以通過 OuterClass.this
進行訪問,類似於名稱空間。
3. 區域性類
區域性類(Local Classes)是定義在程式碼塊(Block)中的類,可以理解為區域性類(對應區域性變數)。
Java 中的 Block 是指由 { }
所包裹的程式碼:
class BlockDemo {
public static void main(String[] args) {
boolean condition = true;
if (condition) { // begin block 1
System.out.println("Condition is true.");
} // end block one
else { // begin block 2
System.out.println("Condition is false.");
} // end block 2
}
}
在程式語言中,程式碼塊與變數作用域有很大的關係,但不同語言之間,有很大的差別。比如 JavaScript、Python 允許函式巢狀,可以產生巢狀的函式作用域——閉包;而 Java 中的方法是不能巢狀的,但是類和程式碼塊可以巢狀,併產生巢狀作用域。
JavaScript 中的 { }
並不產生獨立的作用域,與 c/c++/java 不同:
if (1){var xxx=111}
console.log(xxx) // 111
Java 則可以在 { }
中定義區域性類和區域性變數,比如在方法,迴圈語句,條件語句等的 { }
中:
public class OuterClass {
public static void method(String arg1, String arg2) {
final int localVariable = 10;
class LocalClass {
String localNum = null;
LocalClass(String num){
localNum = num;
}
public String getNumber() {
return localNum;
}
}
LocalClass myNumber1 = new LocalClass(arg1);
LocalClass myNumber2 = new LocalClass(arg2);
System.out.println("First number is " + myNumber1.getNumber());
System.out.println("Second number is " + myNumber2.getNumber());
}
public static void main(String... args) {
method("123-456-7890", "456-7890");
}
}
區域性類只能訪問 final or effectively final 的外部變數,否則編譯器會報錯:local variables referenced from an inner class must be final or effectively final
。
4. 匿名類
匿名類(anonymous class)讓程式碼更簡潔,它沒有名稱,在宣告的同時例項化。當局部類只使用一次時,可以考慮使用匿名類。
public class HelloWorldAnonymousClasses {
interface HelloWorld {
public void greet();
}
public void sayHello() {
HelloWorld frenchGreeting = new HelloWorld() {
public void greet() {
greetSomeone("tout le monde");
}
};
frenchGreeting.greet();
}
public static void main(String... args) {
HelloWorldAnonymousClasses myApp = new HelloWorldAnonymousClasses();
myApp.sayHello();
}
}
5. 避免內部類的序列化
內部類、區域性類和匿名類儘量避免進行序列化,因為編譯期在編譯這些特殊的結構時,會建立 synthetic constructs,包括 classses,methods,fields 等等,它們對應的位元組碼是由 Java 編譯器在編譯過程中生成的,不同的編譯器建立的 synthetic constructs 很可能是不同的,因此在反序列化時會有相容性問題。
比如,inner class 原始碼如下:
public class OuterClass {
public class InnerClass { }
}
InnerClass 的建構函式是隱式宣告(implicitly declared)的,但是它的建構函式需要包含特定的引數,當 Java 編譯器在編譯 InnerClass 時,就會生成相應的建構函式,大致如下:
public class OuterClass {
public class InnerClass {
final OuterClass parent;
InnerClass(final OuterClass this$0) {
parent = this$0;
}
}
}
Java 程式語言在語法上允許變數名中包含 $
,但是按照慣例,編碼時儘量不要這樣用。
synthetic 指的是那些由編譯器生成,但在程式碼中既沒有隱式宣告,也沒有顯式宣告的結構,比如:
public class OuterClass {
enum Colors {
RED, WHITE;
}
}
在編譯時,編譯器會生成類似於下面的程式碼:
final class Colors extends java.lang.Enum<Colors> {
public final static Colors RED = new Colors("RED", 0);
public final static Colors BLUE = new Colors("WHITE", 1);
private final static values = new Colors[]{ RED, BLUE };
private Colors(String name, int ordinal) { // the formal parameters (name, ordinal) are synthetic.
super(name, ordinal);
}
public static Colors[] values(){ // implicitly declared
return values;
}
public static Colors valueOf(String name){ // implicitly declared
return (Colors)java.lang.Enum.valueOf(Colors.class, name);
}
}
synthetic constructs 跟編譯器的行為有關,不同編譯器間不保證相容。