多应用+插件架构,代码干净,二开方便,首家独创一键云编译技术,文档视频完善,免费商用码云13.8K 广告
### [final 数据](https://lingcoder.gitee.io/onjava8/#/book/08-Reuse?id=final-%e6%95%b0%e6%8d%ae) 许多编程语言都有某种方法告诉编译器有一块数据是恒定不变的。恒定是有用的,如: 1. 一个永不改变的编译时常量。 2. 一个在运行时初始化就不会改变的值。 对于编译时常量这种情况,编译器可以把常量带入计算中;也就是说,可以在编译时计算,减少了一些运行时的负担。在 Java 中,这类常量必须是基本类型,而且用关键字**final**修饰。你必须在定义常量的时候进行赋值。 一个被**static**和**final**同时修饰的属性只会占用一段不能改变的存储空间。 当用**final**修饰对象引用而非基本类型时,其含义会有一点令人困惑。对于基本类型,**final**使数值恒定不变,而对于对象引用,**final**使引用恒定不变。一旦引用被初始化指向了某个对象,它就不能改为指向其他对象。但是,对象本身是可以修改的,Java 没有提供将任意对象设为常量的方法。(你可以自己编写类达到使对象恒定不变的效果)这一限制同样适用数组,数组也是对象。 下面例子展示了**final**属性的使用: ~~~ // reuse/FinalData.java // The effect of final on fields import java.util.*; class Value { int i; // package access Value(int i) { this.i = i; } } public class FinalData { private static Random rand = new Random(47); private String id; public FinalData(String id) { this.id = id; } // Can be compile-time constants: private final int valueOne = 9; private static final int VALUE_TWO = 99; // Typical public constant: public static final int VALUE_THREE = 39; // Cannot be compile-time constants: 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); // Arrays: private final int[] a = {1, 2, 3, 4, 5, 6}; @Override public String toString() { return id + ": " + "i4 = " + i4 + ", INT_5 = " + INT_5; } public static void main(String[] args) { FinalData fd1 = new FinalData("fd1"); //- fd1.valueOne++; // Error: can't change value fd1.v2.i++; // Object isn't constant fd1.v1 = new Value(9); // OK -- not final for (int i = 0; i < fd1.a.length; i++) { fd1.a[i]++; // Object isn't constant } //- fd1.v2 = new Value(0); // Error: Can't //- fd1.VAL_3 = new Value(1); // change reference //- fd1.a = new int[3]; System.out.println(fd1); System.out.println("Creating new FinalData"); FinalData fd2 = new FinalData("fd2"); System.out.println(fd1); System.out.println(fd2); } } ~~~ 输出: ~~~ fd1: i4 = 15, INT_5 = 18 Creating new FinalData fd1: i4 = 15, INT_5 = 18 fd2: i4 = 13, INT_5 = 18 ~~~ 因为**valueOne**和**VALUE\_TWO**都是带有编译时值的**final**基本类型,它们都可用作编译时常量,没有多大区别。**VALUE\_THREE**是一种更加典型的常量定义的方式:**public**意味着可以在包外访问,**static**强调只有一个,**final**说明是一个常量。 按照惯例,带有恒定初始值的**final****static**基本变量(即编译时常量)命名全部使用大写,单词之间用下划线分隔。(源于 C 语言中定义常量的方式。) 我们不能因为某数据被**final**修饰就认为在编译时可以知道它的值。由上例中的**i4**和**INT\_5**可以看出,它们在运行时才会赋值随机数。示例部分也展示了将**final**值定义为**static**和非**static**的区别。此区别只有当值在运行时被初始化时才会显现,因为编译器对编译时数值一视同仁。(而且编译时数值可能因优化而消失。)当运行程序时就能看到这个区别。注意到**fd1**和**fd2**的**i4**值不同,但**INT\_5**的值并没有因为创建了第二个**FinalData**对象而改变,这是因为它是**static**的,在加载时已经被初始化,并不是每次创建新对象时都初始化。 **v1**到**VAL\_3**变量说明了**final**引用的意义。正如你在`main()`中所见,**v2**是**final**的并不意味着你不能修改它的值。因为它是引用,所以只是说明它不能指向一个新的对象。这对于数组具有同样的意义,数组只不过是另一种引用。(我不知道有什么方法能使数组引用本身成为**final**。)看起来,声明引用为**final**没有声明基本类型**final**有用。