區域性類
區域性類是在塊中定義的類,它是一對大括號之間的一組零個或多個語句,你通常會在方法體中找到定義的區域性類。
宣告區域性類
你可以在任何塊中定義區域性類(有關詳細資訊,請參閱表示式、語句和塊),例如,你可以在方法體、for
迴圈或if
子句中定義區域性類。
以下示例LocalClassExample
驗證兩個電話號碼,它在validatePhoneNumber
方法中定義了區域性類PhoneNumber
:
public class LocalClassExample {
static String regularExpression = "[^0-9]";
public static void validatePhoneNumber(
String phoneNumber1, String phoneNumber2) {
final int numberLength = 10;
// Valid in JDK 8 and later:
// int numberLength = 10;
class PhoneNumber {
String formattedPhoneNumber = null;
PhoneNumber(String phoneNumber){
// numberLength = 7;
String currentNumber = phoneNumber.replaceAll(
regularExpression, "");
if (currentNumber.length() == numberLength)
formattedPhoneNumber = currentNumber;
else
formattedPhoneNumber = null;
}
public String getNumber() {
return formattedPhoneNumber;
}
// Valid in JDK 8 and later:
// public void printOriginalNumbers() {
// System.out.println("Original numbers are " + phoneNumber1 +
// " and " + phoneNumber2);
// }
}
PhoneNumber myNumber1 = new PhoneNumber(phoneNumber1);
PhoneNumber myNumber2 = new PhoneNumber(phoneNumber2);
// Valid in JDK 8 and later:
// myNumber1.printOriginalNumbers();
if (myNumber1.getNumber() == null)
System.out.println("First number is invalid");
else
System.out.println("First number is " + myNumber1.getNumber());
if (myNumber2.getNumber() == null)
System.out.println("Second number is invalid");
else
System.out.println("Second number is " + myNumber2.getNumber());
}
public static void main(String... args) {
validatePhoneNumber("123-456-7890", "456-7890");
}
}
該示例通過首先移除電話號碼中除數字0到9之外的所有字元來驗證電話號碼,之後,它會檢查電話號碼是否包含十位數字(北美電話號碼的長度),此示例列印以下內容:
First number is 1234567890
Second number is invalid
訪問封閉類的成員
區域性類可以訪問其封閉類的成員,在前面的示例中,PhoneNumber
建構函式訪問成員LocalClassExample.regularExpression
。
此外,區域性類可以訪問區域性變數,但是,區域性類只能訪問宣告為final
的區域性變數,當區域性類訪問封閉塊的區域性變數或引數時,它會捕獲該變數或引數。例如,PhoneNumber
建構函式可以訪問區域性變數numberLength
,因為它被宣告為final
;numberLength
是捕獲的變數。
但是,從Java SE 8開始,區域性類可以訪問final
或有效final
的封閉塊的區域性變數和引數,在初始化之後其值永遠不會改變的變數或引數實際上是final
,例如,假設變數numberLength
未宣告為final
,並且你在PhoneNumber
建構函式中新增賦值語句,以將有效電話號碼的長度更改為7位數:
PhoneNumber(String phoneNumber) {
numberLength = 7;
String currentNumber = phoneNumber.replaceAll(
regularExpression, "");
if (currentNumber.length() == numberLength)
formattedPhoneNumber = currentNumber;
else
formattedPhoneNumber = null;
}
由於這個賦值語句,變數numberLength
不再是final
,因此,Java編譯器生成類似於“從內部類引用的區域性變數必須是final
或者有效的final
”的錯誤訊息,其中內部類PhoneNumber
嘗試訪問numberLength
變數:
if (currentNumber.length() == numberLength)
從Java SE 8開始,如果在方法中宣告區域性類,它可以訪問方法的引數,例如,你可以在PhoneNumber
區域性類中定義以下方法:
public void printOriginalNumbers() {
System.out.println("Original numbers are " + phoneNumber1 +
" and " + phoneNumber2);
}
printOriginalNumbers
方法訪問validatePhoneNumber
方法的引數phoneNumber1
和phoneNumber2
。
遮蔽和區域性類
在具有相同名稱的封閉範圍內的區域性類遮蔽宣告中型別的宣告(例如變數),有關更多資訊,請參閱遮蔽。
區域性類與內部類相似
區域性類與內部類類似,因為它們無法定義或宣告任何靜態成員,靜態方法中的區域性類,例如在靜態方法validatePhoneNumber
中定義的類PhoneNumber
,只能引用封閉類的靜態成員。例如,如果未將成員變數regularExpression
定義為static
,則Java編譯器會生成類似於“非靜態變數regularExpression
無法從靜態上下文引用”的錯誤。
區域性類是非靜態的,因為它們可以訪問封閉塊的例項成員,因此,它們不能包含大多數型別的靜態宣告。
你不能在一個塊內宣告一個介面;介面本質上是靜態的,例如,以下程式碼片段不會編譯,因為介面HelloThere
是在方法greetInEnglish
的主體內定義的:
public void greetInEnglish() {
interface HelloThere {
public void greet();
}
class EnglishHelloThere implements HelloThere {
public void greet() {
System.out.println("Hello " + name);
}
}
HelloThere myGreeting = new EnglishHelloThere();
myGreeting.greet();
}
你不能在區域性類中宣告靜態初始化或成員介面,以下程式碼片段無法編譯,因為方法EnglishGoodbye.sayGoodbye
被宣告為static
,當遇到此方法定義時,編譯器會生成類似於“修飾符`static
`的錯誤,只允許在常量變數宣告中使用”:
public void sayGoodbyeInEnglish() {
class EnglishGoodbye {
public static void sayGoodbye() {
System.out.println("Bye bye");
}
}
EnglishGoodbye.sayGoodbye();
}
區域性類可以具有靜態成員,前提是它們是常量變數(常量變數是原始型別或型別String
的變數,它被宣告為final
並使用編譯時常量表示式進行初始化,編譯時常量表示式通常是可在編譯時計算的字串或算術表示式,有關更多資訊,請參閱瞭解類成員),以下程式碼片段編譯,因為靜態成員EnglishGoodbye.farewell
是一個常量變數:
public void sayGoodbyeInEnglish() {
class EnglishGoodbye {
public static final String farewell = "Bye bye";
public void sayGoodbye() {
System.out.println(farewell);
}
}
EnglishGoodbye myEnglishGoodbye = new EnglishGoodbye();
myEnglishGoodbye.sayGoodbye();
}