final说明
- 修饰类:表示类不可被继承
- 修饰方法:表示方法不可被子类覆盖,但是可以重载
- 修饰变量:表示变量一旦被赋值就不可以更改它的值
-
修饰成员变量
- 如果final修饰的是类变量,只能在静态初始化块中指定初始值或者声明该类变量时指定初始值。
- 如果final修饰的是成员变量,可以在非静态初始化块、声明该变量或者构造器中执行初始值。
-
修饰局部变量
系统不会为局部变量进行初始化,局部变量必须显示初始化。因此使用final修饰局部变量时,即可在定义时指定默认值(后面的代码不能对变量再赋值),也可以不指定默认值,而在后面的代码中对final变量赋初始值(仅一次)
package com.dameng; /** * @author Allen */ public class FinalVar { /** * 声明时就要赋值,或者在静态代码块中赋值 */ private final static int A = 0; // static { // A = 1; // } /** * 声明时、代码块或者构造函数赋值 */ private final int b = 0; // { // b = 1; // } // public FinalVar() { // b = 2; // } public static void main(String[] args) { // 局部变量只声明不初始化,不会报错 final int localA; // 在使用之前一定要赋值 localA = 0; // 报错,不允许修改 // localA = 1; } }
-
修饰基本类型数据和引用类型数据
- 如果是基本类型的变量,则其数值一旦在初始化之后便不能更改;
- 如果是引用类型的变量,则初始化之后不能再指向另一个对象,但引用值可以修改。
package com.dameng; /** * @author Allen */ public class FinalReferenceTest { public static void main(String[] args) { final int[] arr = {1, 2, 3, 4}; arr[2] = 3; // 报错,不能对arr重新赋值 // arr = null; final Person person = new Person(); person.setAge(24); // 报错,不能对arr重新赋值 // person = null; } private static class Person { private int age; public int getAge() { return age; } public void setAge(int age) { this.age = age; } } }
为什么局部内部类和匿名内部类只能访问局部final变量(<=jdk7)?
匿名内部类
package com.dameng;
/**
* 编译之后会生成两个class文件,Test.class Test$1.class
*
* @author Allen
*/
public class Test {
public static void main(String[] args) {
}
public void test(final int b) {
final int a = 10;
new Thread() {
@Override
public void run() {
System.out.println(a);
System.out.println(b);
}
}.start();
}
}
package com.dameng;
public class Test {
public Test() {
}
public static void main(String[] args) {
}
public void test(final int b) {
int a = true;
(new Thread() {
public void run() {
System.out.println(10);
System.out.println(b);
}
}).start();
}
}
package com.dameng;
class Test$1 extends Thread {
Test$1(Test this$0, int var2) {
this.this$0 = this$0;
this.val$b = var2;
}
public void run() {
System.out.println(10);
System.out.println(this.val$b);
}
}
局部内部类
package com.dameng;
/**
* 编译之后生成两个class文件,Test.class Test$1InClass.class
*
* @author Allen
*/
public class Test {
private int age = 12;
public static void main(String[] args) {
}
public void outPrint(final int x) {
class InClass {
public void inPrint() {
System.out.println(x);
System.out.println(age);
}
}
new InClass().inPrint();
}
}
package com.dameng;
public class Test {
private int age = 12;
public Test() {
}
public static void main(String[] args) {
}
public void outPrint(final int x) {
class InClass {
InClass() {
}
public void inPrint() {
System.out.println(x);
System.out.println(Test.this.age);
}
}
(new InClass()).inPrint();
}
}
package com.dameng;
class Test$1InClass {
Test$1InClass(Test this$0, int var2) {
this.this$0 = this$0;
this.val$x = var2;
}
public void inPrint() {
System.out.println(this.val$x);
System.out.println(this.this$0.age);
}
}
内部类和外部类是处于同一个级别的,内部类不会因为定义在方法中就随着方法的执行结束而被销毁。
而当外部类的方法结束时,外部类的局部变量会被销毁,但内部类对象可能还存在(只有没有被调用时才会死亡),这时就出现一个矛盾:内部类对象访问了一个不存在的变量。为了解决这个问题,就将局部变量复制了一份作为内部类的成员变量,这样当局部变量死亡后,内部类还可以访问它(实际访问的是局部变量的copy),这样就好像延长了局部变量的生命周期。
将局部变量复制为内部类的成员变量时,必须保证这两个变量是一样的,也就是如果我们在内部类中修改了成员变量,方法中的局部变量也跟着改变,怎么解决这个问题呢?
就将局部变量设置为final,对它初始化后,就无法再修改这个变量,就保证了内部类的成员变量和方法的局部变量的一致性。这实际上也是一种妥协,使得局部变量与内部类建立的拷贝保持一致。
jdk8以后匿名内部类可以直接访问局部变量
匿名内部类
package com.dameng;
/**
* 编译之后只生成一个class文件,Test.class
*
* @author Allen
*/
public class Test {
public static void main(String[] args) {
}
// final可有可无
public void test(final int b) {
// final可有可无
final int a = 10;
new Thread(() -> {
System.out.println(a);
System.out.println(b);
}).start();
}
}
package com.dameng;
public class Test {
public Test() {
}
public static void main(String[] args) {
}
public void test(int b) {
int a = true;
(new Thread(() -> {
System.out.println(10);
System.out.println(b);
})).start();
}
}
局部内部类
package com.dameng;
/**
* 编译之后生成两个class文件,Test.class Test$1InClass.class
*
* @author Allen
*/
public class Test {
private int age = 12;
public static void main(String[] args) {
}
// final可有可无
public void outPrint(final int x) {
class InClass {
public void inPrint() {
System.out.println(x);
System.out.println(age);
}
}
new InClass().inPrint();
}
}
package com.dameng;
public class Test {
private int age = 12;
public Test() {
}
public static void main(String[] args) {
}
public void outPrint(final int x) {
class InClass {
InClass() {
}
public void inPrint() {
System.out.println(x);
System.out.println(Test.this.age);
}
}
(new InClass()).inPrint();
}
}
package com.dameng;
class Test$1InClass {
Test$1InClass(Test this$0, int var2) {
this.this$0 = this$0;
this.val$x = var2;
}
public void inPrint() {
System.out.println(this.val$x);
System.out.println(this.this$0.age);
}
}