final
final可以用来修饰变量,方法和类。
final变量
若是被final修饰后,只能被赋值一次。
-
声明成员变量后赋值 被final修饰后赋值一次后不能再进行修改,再次修改编译器会提示将final关键字去掉,否则编译不通过。 -
声明成员变量后不赋值 声明后若是不赋值,则编译器编译不通过,提示必须在构造方法中进行初始化,而且在后续的操作中不能再对它进行修改。 -
引用类型 引用类型被final修饰后,即使初始值设置为null,也算一次赋值,后续无法再为该引用创建对象。虽然对象引用不能再修改,但是对象的属性可以被修改。
private final Person person = null;
private void test() {
person.setName("final");
}
上诉代码肯定无法运行,因为person为null,会出现 NullPointerException。
private static final Person person = new Person();
public static void main(String[] args) {
System.out.println(person.getName());
person.setName("name1");
System.out.println(person.getName());
person.setName("name2");
System.out.println(person.getName());
}
结果:
null
name1
name2
开始创建对象没有赋值,所以输出null,当赋值为name1时输出name1,修改为name2后输出为name2,说明被final修饰的对象的属性是可以被修改的。
private static final int[] values = {1,2,3,4,4};
public static void main(String[] args) {
values[0] = 10;
for (int value : values) {
System.out.print(value + ",");
}
}
结果:
10,2,3,4,4,
说明final修饰的数组同样不能修改,但是数组里面的元素是可以修改的,该属性常用于内部类中。
public static void main(String[] args) {
final int testValue = 1;
new Thread(() -> {
System.out.println(testValue);
});
}
若在匿名内部类中使用局部变量,该变量需要声明为final,若是想要进行修改,还是需要将变量改成final数组。
public static void main(String[] args) {
final int[] testValue = {1};
new Thread(() -> testValue[0]++);
}
被final修饰后
-
若是基本数据类型,此时变量会变成常量,不能再进行修改; -
若是成员变量则需要在声明后马上赋值,或在构造方法中赋值,否则编译报错; -
若是引用数据类型,则对象或数组的引用无法修改,但对象或数组里面的元素可以修改; -
匿名内部类中使用所有局部变量都需要声明为final。
final方法
父类:
public class Parent {
public final void family(){}
}
子类:
final类
父类:
public final class Parent {
public void family(){}
}
子类:
-
当类被声明为final类,该类就无法进行扩展,也就是无法被继承。 -
若一个类被声明为final类,该类中所有的方法都会自动变成final方法,无法被重写。类不能被继承,里面的方法自然无法被重写。 -
final与abstract互斥,final是禁止扩展的,而abstract是必须扩展的,否则没有意义,所以两者不能同时使用。
修改final变量值
被final修饰的变量只能被赋值一次,若是需要修改可以利用反射进行修改。
private final int value = 1;
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
FinalTest test = new FinalTest();
Field field = FinalTest.class.getDeclaredField("value");
field.setAccessible(true);
field.set(test,2);
System.out.println(test.value);
}
结果:
1
输出结果为1,不能说明修改不成功,而是编译器对于基本数据类型进行的内联优化,也就是代码中的value直接被1取代了,不需要再通过value去查1。查看编译后的class文件可知。
private final int value = 1;
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
FinalTest test = new FinalTest();
Field field = FinalTest.class.getDeclaredField("value");
field.setAccessible(true);
field.set(test, 2);
PrintStream var10000 = System.out;
Objects.requireNonNull(test);
var10000.println(1);
}
编译后的.class文件中value直接被1取代了,所以输出语句 System.out.println(test.value)被直接编译成了 var10000.println(1),所以输出结果为1。 若是想查看被修改后的值,输出语句需要改成System.out.println(field.get(test))。
private final int value = 1;
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
FinalTest test = new FinalTest();
Field field = FinalTest.class.getDeclaredField("value");
field.setAccessible(true);
field.set(test,2);
System.out.println(field.get(test));
}
结果:
2
-
编译器的此次优化只针对基本数据类型,引用数据类型修改后会马上生效。
|