# 本地类

> 原文： [https://docs.oracle.com/javase/tutorial/java/javaOO/localclasses.html](https://docs.oracle.com/javase/tutorial/java/javaOO/localclasses.html)

本地类是在 _ 块 _ 中定义的类，它是一组平衡括号之间的零个或多个语句。您通常会在方法体中找到定义的本地类。

本节包括以下主题：

*   [声明本地类](#declaring-local-classes)
*   [访问封闭类的成员](#accessing-members-of-an-enclosing-class)
    *   [阴影和本地类](#shadowing-and-local-classes)
*   [本地类与内部类相似](#local-classes-are-similar-to-inner-classes)

您可以在任何块内定义本地类（有关详细信息，请参阅[表达式，语句和块](../../java/nutsandbolts/expressions.html)）。例如，您可以在方法体，`for`循环或`if`子句中定义本地类。

以下示例 [`LocalClassExample`](examples/LocalClassExample.java) 验证两个电话号码。它定义方法`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 开始，本地类可以访问最终的封闭块的局部变量和参数，或者 _ 有效地终结 _。在初始化之后其值永远不会改变的变量或参数实际上是最终的。例如，假设变量`numberLength`未声明为 final，并在`PhoneNumber`构造函数中添加突出显示的赋值语句，以将有效电话号码的长度更改为 7 位数：

```
PhoneNumber(String phoneNumber) {
    numberLength = 7;
    String currentNumber = phoneNumber.replaceAll(
        regularExpression, "");
    if (currentNumber.length() == numberLength)
        formattedPhoneNumber = currentNumber;
    else
        formattedPhoneNumber = null;
}
```

由于这个赋值语句，变量`numberLength`不再是有效的最终结果。因此，Java 编译器生成类似于“从内部类引用的局部变量必须是最终的或有效最终的”的错误消息，其中内部类`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`。

封闭范围中具有相同名称的本地类影子声明中的类型（例如变量）的声明。有关详细信息，请参阅 [Shadowing](../../java/javaOO/nested.html#shadowing) 。

本地类与内部类类似，因为它们无法定义或声明任何静态成员。静态方法中的本地类，例如在静态方法`validatePhoneNumber`中定义的类`PhoneNumber`，只能引用封闭类的静态成员。例如，如果未将成员变量`regularExpression`定义为静态，则 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 并用编译时常量表达式初始化。编译时常量表达式通常是一个字符串或一个算术表达式，可以在编译时进行评估。有关更多信息，请参阅[了解类成员](classvars.html)。）以下代码摘录编译，因为静态成员`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();
    }
```