一 final关键字的基本用法
在Java中,final出现是源于代码设计和执行效率考虑而出现的,
final关键字可以用来修饰类、方法和变量(包括成员变量和局部变量)。
下面就从这三个方面来了解一下final关键字的基本用法。
1)修饰类
当用final修饰一个类时,表明这个类不能被继承。也就是说,如果一个类你永远不会让他被继承,
就可以用final进行修饰。final类中的成员变量可以根据需要设为final,
但是要注意final类中的所有成员方法都会被隐式地指定为final方法。
在使用final修饰类的时候,先想清楚了,除非真的出于安全考虑,以后也不想集成这个类,
否则,尽量还是别用final修饰一个类。
2)修饰方法
使用final方法的原因有两个。第一个原因是把方法锁定,以防任何继承类修改它的含义;
第二个原因是效率。在早期的Java实现版本中,会将final方法转为内嵌调用。
但是如果方法过于庞大,可能看不到内嵌调用带来的任何性能提升。
在最近的Java版本中,不需要使用final方法进行这些优化了。
3)修饰变量
对于一个final变量,如果是基本数据类型的变量,则其数值一旦在初始化之后便不能更改;
如果是引用类型的变量,则在对其初始化之后便不能再让其指向另一个对象。
使用final修饰变量的根本目的是不想让变量数据在编译或初始化之后值改变。
eg:
package com.lanhuigu.java.finaldemo;
import java.util.Random;
/**
* final 修饰变量实例:
* 对于一个final变量,如果是基本数据类型的变量,则其数值一旦在初始化之后便不能更改;
* 如果是引用类型的变量,则在对其初始化之后便不能再让其指向另一个对象。
*/
public class FinalData {
private static Random rand = new Random(47);
private String id;
public FinalData(String id) {
this.id = id;
}
/** 编译为常量 */
private final int valueOne = 9;
private static final int VALUE_TWO = 99;
/** 公开的常量类型 */
public static final int VALUE_THREE = 39;
/** 不能编译为常量 */
private final int i4 = rand.nextInt(20);
static final int INT_5 = rand.nextInt(20);
private Value v1 = new Value(11);
private final Value v2 = new Value(22);
private static final Value VAL_3 = new Value(33);
/** 修饰数组 */
private final int[] a = {1, 2, 3, 4, 5, 6};
/** 重写toString()方法 */
@Override
public String toString() {
return id + ": " + "i4 = " + i4 + ", INT_5 = " + INT_5;
}
/**
* 测试调用
*/
public static void main(String[] args) {
FinalData fd1 = new FinalData("fd1");
fd1.v2.i ++;
fd1.v1 = new Value(9);
for (int i = 0; i < fd1.a.length; i ++) {
fd1.a[i]++;
}
System.out.println(fd1);
System.out.println("Creating new FinalData");
FinalData fd2 = new FinalData("fd2");
System.out.println(fd1);
System.out.println(fd2);
}
}
class Value{
int i;
public Value(int i) {
this.i = i;
}
}
程序结果:
fd1: i4 = 15, INT_5 = 18
Creating new FinalData
fd1: i4 = 15, INT_5 = 18
fd2: i4 = 13, INT_5 = 18
程序分析:
code1:
private final int valueOne = 9;
private static final int VALUE_TWO = 99;
valueOne,VALUE_TWO均为final修饰原始数据,直接编译为常量,不可以修改。
code2:
public static final int VALUE_THREE = 39;
VALUE_THREE为public类型的常量,以供包外程序使用,static表示这个值是唯一的,
并且在编译时候就已经初始化,而final表示这个值是不可以改变的,是一个常量。
code3:
private final int i4 = rand.nextInt(20);
static final int INT_5 = rand.nextInt(20);
定义两个随机值变量,static或non-static。从程序结果可以看出,第一次初始化FinalData对象时,
fd1: i4 = 15, INT_5 = 18
而第二次new FinalData对象,
fd2: i4 = 13, INT_5 = 18
从两次结果对比可以看出,i4两次new FinalData的结果是不一样的,二INT_5两次都是一样的,
这是因为INT_5是static修饰的,只初始化一次,无论new FinalData执行多少次,static变量只初始化一次,
所有,第二次new FinalData时,INT_5使用的值还是第一次创建时初始化的值。
而i4每创建一次都会重新赋值一次,所以结果不一样。
code4:
private Value v1 = new Value(11);
private final Value v2 = new Value(22);
private static final Value VAL_3 = new Value(33);
v1是普通引用类型变量,v2为final定义,初始化之后便不能再让其指向另一个对象,
即不能再把v2绑定到别的新建对象上。
二 final关键字其它常见问题
1)类的final变量和普通变量有什么区别?
当用final作用于类的成员变量时,成员变量(注意是类的成员变量,
局部变量只需要保证在使用之前被初始化赋值即可)必须在定义时或者构造器中进行初始化赋值,
而且final变量一旦被初始化赋值之后,就不能再被赋值了。
2)被final修饰的引用变量指向的对象内容可变吗?
在上面提到被final修饰的引用变量一旦初始化赋值之后就不能再指向其他的对象,
那么该引用变量指向的对象的内容可变吗?
eg:
package com.lanhuigu.java.finaldemo;
public class ObjectContentTest {
public static void main(String[] args) {
final MyClass myClass = new MyClass();
System.out.println(++myClass.i);
}
}
class MyClass {
int i = 0;
}
程序结果:
1
程序分析:
从运行结果可以知道,final修饰的引用变量一旦初始化赋值之后就不能再指向其他的对象,
但是,引用变量指向的对象内容是可以改变的。
3)final修饰的方法参数不可变
一个参数被final修饰,这个参数值在对应的作用域内不能改变。
final修饰变量的本意就是在某些情况下,防止传进来的参数值被修改,导致不必要的错误。
网上有些文章扭曲final修饰参数的本意,然后写了一堆反证,其实与final本意
毫不相干,这种反证在final本意面前如小丑在一个高人面前呱呱叫。