局部类
局部类是定义在一个代码块里的类。 那么什么是一个代码块呢? 在一组花括号里包含一个或者多个语句的代码片段就可以叫做代码块。局部类通常定义在一个方法的内部。
声明局部类
局部类可以定义在任何代码块里,它可以出现在在方法内、一个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"); } } 验证电话号码的逻辑是首先把里面的非数字字符去掉,然后看看号码的长度是不是刚好10位 (北美的电话号码长度),这个例子会输出以下结果: First number is 1234567890 Second number is invalid访问包装类的成员
一个局部类是可以访问包装它的类的成员的, 在上面的例子中,PhoneNumber的构造方法就访问了包装类的成员:LocalClassExample.regularExpression
而且局部类也可以访问局部变量,然而局部类只能访问final的局部变量。当局部类访问局部变量或者封闭块的参数时,它会留存(可以复制一个副本来使用的,因为我知道你不会变动)那个变量或者参数。例如,PhoneNumber的构造方法可以访问局部变量numberLength,就是因为它是final的, numberLength是一个可以留存的变量。
然而从Java 8开始,局部类除了可以访问final的局部变量和参数,还可以访问事实上是final的变量(尽管没有加final修饰符)。 如果一个变量或者参数从它初始化之后就从来没有修改过,那么事实它就是一个final的变量或者参数。例如,假定numberLength没有加final修饰符,你又在PhoneNumber的构造方法里对它做了点小手脚,改动了它。
PhoneNumber(String phoneNumber) { <strong>numberLength = 7;</strong> String currentNumber = phoneNumber.replaceAll( regularExpression, ""); if (currentNumber.length() == numberLength) formattedPhoneNumber = currentNumber; else formattedPhoneNumber = null; } 好吧,就是因为你动了手脚,改变了它的值,变量 numberLength就不在被看作是纯洁的也就是final的了。因此,编译就会报错:内部类引用的局部变量必须是final的或者事实上是final的。
从Java 8开始,如果局部类是定义在一个方法内部的,它是可以访问方法的参数的。例如你可以在局部类PhoneNumber内定义方法
public void printOriginalNumbers() { System.out.println("Original numbers are " + phoneNumber1 +" and " + phoneNumber2); }方法 printOriginalNumbers就访问了方法validatePhoneNumber的参数phoneNumber1和phoneNumber2遮蔽和局部类
如果在局部类里声明的类型(例如变量)和它的包装类的类型名字一样,那么局部类的变量就会遮蔽包装类的变量作用范围, 如果只用名字访问,只会对当前局部类生效。
局部类和内部类相似
局部类和内部类一样,不能定义声明静态的成员。如果局部类出现在了静态方法里 (例如刚才的例子PhoneNumber是定义在静态方法validatePhoneNumber中的)只能引用它的包装类的静态成员。 假如你没有把成员变量regularExpression 定义成静态的,编译就会报错:非静态变量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是静态的
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(); }