final修饰的实例变量
被final修饰的实例变量必须显示指定初始值,而且只能在如下3个位置指定初始值: 1.定义final实例变量时指定初始值; 2.在非静态初始化块中为final实例变量指定初始值 3.在构造器中为final实例变量指定初始值 对于普通实例变量,Java程序可以对它执行默认的初始化,也就是将实力变量的值指定为默认的初始值0或null,但对于final实例变量,则必须由程序员显示指定初始值。 final实例变量必须显示地被赋初始值,而且本质上final实例变量只能在构造器中被赋初始值。在定义final实例变量时指定初始值,和在初始化块中为final实例变量指定初始值本质上是一样的。除此之外,final实例变量将不能被再次赋值。
final修饰的类变量
对于final类变量而言,同样必须显示指定初始值,而且final类变量只能在2个地方指定初始值: 定义final类变量时指定初始值;在静态初始化块中为final类变量指定初始值; 这两种方式都会被抽取到静态初始化块中赋初始值。定义final类变量时指定初始值和在静态初始化块中为final类变量指定初始值,本质是一样的。除此之外final类变量将不能被再次赋值。
final修饰局部变量
final修饰的局部变量一样需要被显式地赋初始值,因为Java本来就要求局部变量必须被显式地赋初始值。与普通变量不同的是,final修饰的局部变量被赋初始值之后,将不能再被重新赋值。
final修饰符的第一简单的功能就是一旦被赋初始值,将不可改变。 final的另一个简单的功能就是在定义了该final类变量时指定了初始值,且该初始值可以在编译时就被确定下来,系统将不会在静态初始化块中对该类变量赋初始值,而将是在类定义中直接使用该初始化值代替该final变量。
对于一个使用final修饰的变量而言,如果定义该final变量时就指定初始值,而且这个初始值可以在编译时就确定下来,那么这个final变量将不再是一个变量,系统会将其变成“宏变量”处理。所有出现该变量的地方,系统将直接把它当成对应的值处理。
执行“宏替换”的变量
对于一个final变量,不管它是类变量、实例变量还是局部变量,定义了该变量时使用了final修饰符修饰,并在定义该final类变量时指定了初始值,而且该初始值可以在编译时就被确定下来,那么这个final变量本质上已经不再是变量,而是想当于一个直接的变量。 final修饰符的一个重要用途就是定义“宏变量“,当定义final变量时就为该变量指定了初始值,而且该初始值可以在编译的时候就确定下来,那么这个final变量本质上就是一个”宏变量“,编译器会把程序中所用到该变量的地方直接替换成该变量的值。如果被赋的表达式只是基本的算术运算表达式或字符串连接运算,没有访问普通变量,调用方法,Java编译器同样会将这种final变量当成”宏变量“处理。
对于实例变量而言,可以在定义该变量时赋初始值之外,还可以在非静态初始化块、构造器中对它赋初始值,在这三个地方指定初始值的效果基本一样。但对于final实例变量而言,只有在定义该变量时指定初始值才会有”宏变量“的效果,在非静态初始化块、构造器中为final实例变量指定初始值则不会有这种效果。对于普通类变量而言,在定义时指定初始值,在静态初始化块中赋初始值的效果基本一样。但对于final类变量而言,只有在定义final类变量时指定初始值,系统才会对该final类变量执行”宏替换“
final方法不能重写
当final修饰某个方法时, 用于限制该方法不可被它的子类重写。 如果父类中某个方法使用了final修饰符进行修饰,这个方法将不可能被子类访问到,因此这个方法也不可能被它的子类重写。private和final同时修某个方法是没有太大意义,但是被Java语法允许。 如果父类和子类没有处于同一个包下,父类中包含的某个方法不使用访问控制符(相当于包访问权限)或者仅使用private访问控制符,那子类也是无法重写该方法的。
内部类中的局部变量
不仅匿名内部类,即使是普通内部类,在任何内部类中访问的局部变量都应该使用final修饰。 此处说的内部类指的是局部内部类,只有局部内部类(包括匿名内部类)才可以访问局部变量,普通静态内部类、非静态内部类不可能访问方法体内的局部变量。Java要求所有被内部类访问的局部变量都使用final修饰,对于普通局部变量而言,它的作用域就是停留在该方法内,当方法执行结束,该局部变量也随之消失。但内部类则可能产生隐式的”闭包“闭包将使得局部变量脱离它所在的方法继续存在。 匿名内部类的实例生命周期没有结束的话,将一直可以访问局部变量的值,这就是内部类会扩大局部变量作用域的实例。 由于内部类可能扩大局部变量的作用域,如果再加上这个被内部类访问的局部变量没有使用final修饰,也就是说该变量的值可以随意改变,就会引起大乱。因此Java编译器要求所有被内部类访问的局部变量必须使用final修饰。