Java 教程是为 JDK 8 编写的。本页中描述的示例和实践未利用在后续版本中引入的改进。
局部类是在 块 中定义的类,它是平衡大括号之间的一组零个或多个语句。你通常会在方法体中找到定义的局部类。
本节包括以下主题:
你可以在任何块内定义局部类(有关更多信息,请参阅 Expressions, Statements, and Blocks)。例如,你可以在方法体中,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 的局部变量。当局部类访问封闭块的局部变量或参数时,它 captures (捕获) 该变量或参数。例如,PhoneNumber
构造函数可以访问局部变量 numberLength
,因为它被声明为 final; numberLength
是 捕获变量。
但是,从 Java SE 8 开始,局部类可以访问 final 或 effectively 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
不再是 effectively final 的。因此,当内部类 PhoneNumber
尝试访问 numberLength
变量时,Java 编译器生成类似于 "从内部类引用的局部变量必须是 final 或 effectively final 的" 的错误消息:
if (currentNumber.length() == numberLength)
从 Java SE 8 开始,如果在方法中声明局部类,它可以访问方法的参数。例如,你可以在 PhoneNumber
局部类中定义以下方法:
public void printOriginalNumbers() { System.out.println("Original numbers are " + phoneNumber1 + " and " + phoneNumber2); }
方法 printOriginalNumbers
访问方法 validatePhoneNumber
的参数 phoneNumber1
和 phoneNumber2
。
在局部类中声明类型(例如变量),会遮蔽封闭范围内具有相同名称的声明。有关更多信息,请参见 遮蔽。
局部类与内部类类似,因为它们无法定义或声明任何静态成员。静态方法中的局部类(例如类 PhoneNumber
)在静态方法 validatePhoneNumber
中定义,只能引用封闭类的静态成员。例如,如果未将成员变量 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(); }