文档

Java™教程
隐藏目录
本地类
导航: 学习Java语言
课程: 类和对象
章节: 嵌套类

局部类

局部类是在中定义的类,块是在平衡的大括号之间的零个或多个语句组成的。通常,您会在方法体中定义局部类。

本节涵盖以下主题:

声明局部类

您可以在任何块内定义局部类(有关更多信息,请参见表达式、语句和块)。例如,您可以在方法体、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;
        
        // 仅适用于JDK 8和更高版本:
       
        // 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;
            }
            
            // 仅适用于JDK 8和更高版本:

//            public void printOriginalNumbers() {
//                System.out.println("原始号码是 " + phoneNumber1 +
//                    " 和 " + phoneNumber2);
//            }
        }

        PhoneNumber myNumber1 = new PhoneNumber(phoneNumber1);
        PhoneNumber myNumber2 = new PhoneNumber(phoneNumber2);
        
        // 仅适用于JDK 8和更高版本:

//        myNumber1.printOriginalNumbers();

        if (myNumber1.getNumber() == null) 
            System.out.println("第一个号码无效");
        else
            System.out.println("第一个号码是 " + myNumber1.getNumber());
        if (myNumber2.getNumber() == null)
            System.out.println("第二个号码无效");
        else
            System.out.println("第二个号码是 " + myNumber2.getNumber());

    }

    public static void main(String... args) {
        validatePhoneNumber("123-456-7890", "456-7890");
    }
}

这个例子首先将电话号码中除了数字0到9之外的所有字符都删除,然后检查电话号码是否恰好包含十个数字(北美电话号码的长度)。这个例子输出以下内容:

第一个号码是1234567890
第二个号码无效

访问外部类的成员

局部类可以访问其外部类的成员。在上一个例子中,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 的参数 phoneNumber1phoneNumber2

遮蔽和局部类

在局部类中声明一个类型(例如变量)会隐藏与之同名的封闭作用域中的声明。更多信息请参见Shadowing

局部类与内部类类似

局部类与内部类类似,因为它们不能定义或声明任何静态成员。在静态方法中定义的局部类(例如类PhoneNumber,定义在静态方法validatePhoneNumber中)只能引用封闭类的静态成员。例如,如果不将成员变量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();
    }

局部类可以拥有静态成员,前提是它们是常量变量。常量变量是指声明为final并使用编译时常量表达式初始化的原始类型或String类型的变量。编译时常量表达式通常是一个字符串或可以在编译时求值的算术表达式。更多信息请参见了解类成员。以下代码段可以编译,因为静态成员EnglishGoodbye.farewell是一个常量变量:

    public void sayGoodbyeInEnglish() {
        class EnglishGoodbye {
            public static final String farewell = "再见";
            public void sayGoodbye() {
                System.out.println(farewell);
            }
        }
        EnglishGoodbye myEnglishGoodbye = new EnglishGoodbye();
        myEnglishGoodbye.sayGoodbye();
    }

上一页: 内部类示例
下一页: 匿名类