本地类
本地类指的是定义在一个代码块里的类,代码块指写在一组大括号里的代码。最典型的本地类是定义在方法内部
声明本地类
可以在任意代码块内定义一个本地类,比如方法体、for循环、if语句等。
下面是一个例子,LocalClassExample,验证电话号码。这它的方法validatePhoneNumber里定义本地类PhoneNumber
class LocalClassExample {
static String reg = "[^0-9]";
public static void main(String[] args) {
validatePhoneNumber("123-456-7890");
}
public static void validatePhoneNumber(String phoneNumber){
int numberLength = 10;
class PhoneNumber{
String formattedPhoneNumber = null;
PhoneNumber(String phoneNuber){
String currentNumber = phoneNuber.replaceAll(reg,"");
if(currentNumber.length() == numberLength)
formattedPhoneNumber = currentNumber;
else
formattedPhoneNumber = null;
}
public String getNumber(){
return formattedPhoneNumber;
}
public String getOriNum(){
return phoneNumber;
}
}
PhoneNumber number = new PhoneNumber(phoneNumber);
if(number.formattedPhoneNumber == null)
System.out.println("number is invalid");
else{
System.out.println("number is "+number.formattedPhoneNumber);
}
}
}
结果是:number is 1234567890
获取外部类的成员
本地类可以获得外部类的成员。上面那个例子中,PhoneNumber构造方法获得了外部类的属性reg
另外本地类可以获得本地变量(本地类所属代码块中定义的变量)。但是它只能使用使用那些被声明为final的本地变量(其实使用非final本地变量不影响代码运行,不过这样容易引起歧义)。
然而,从javase 8以后,本地方法可以使用final或者实际上final的本地变量和外部代码块的参数(比如上面validatePhoneNumber方法的参数phoneNumber)。所谓的实际上final是指这个变量或参数的值在初始化之后就不再发生改变。例如上面例子中的变量numberLength没有声明final,然后在PhoneNumber的构造方法中加上一行代码numberLength = 7;由于这个语句,变量numberLength就不是实际上final。那么,java编译器就会生成一个“local variables referenced from an inner class must be final or effectively final”的错误信息。
javase8之后,如果你在一个方法里定义了本地类,就能获得这个方法的方法参数,比如上面例子中内部类的方法getOriNum()就使用了外部方法validatePhoneNumber的参数phoneNumber
本地类与内部类比较
本地类跟内部类很相似,都不能定义或声明任何静态成员。在静态方法中定义的本地类,只能使用外部类的静态成员。比如上面例子中如果reg没有声明为static,那么编译器会生成“non-static variable regularExpression
cannot be referenced from a static context."这样的错误信息。
本地类都是非静态的,因为他们要获得外部代码块的实例成员,因此本地类不能包含任何静态声明。(个人认为这是通过编译器审查硬性规定的,因为如果允许声明静态成员,就必须确保这个静态成员跟外部代码块实例成员无关,倒不如一刀切都不许定义为static)
不能再代码块中声明接口。接口天生就是静态的
但是有一个例外,本地类是可以有static成员的,只要他是常量变量(常量变量指被声明为final的基本数据类型或String类型变量,并且它的初始化值必须是编译期常量表达式,编译期常量表达式通常是字符串或者算术表达式这些能在编译器就评估值的。也就是说值是确定的不能用其他变量来给他赋值)
下面代码就是可行的
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();
}
本地类实现原理
本地类跟内部类一样都会生成一个新的class对象,不同的是新生成的class的构造函数参数除了外部类实例引用外还包括了本地类引用了的外部代码块中的变量,同时本地类多了几个跟这几个引用变量对应的final属性