记一次被面试的final问题
---- 前言
今天面试被问到了,我们都知道final修饰的东西是不可变的,那么是值不可变还是其地址不可变?一脸懵逼,回来查阅一番,总结一下
--- final与数据
在日常行为下,一般数据指的都是基本数据变量或者常量。当final修饰的是一个基本的数据变量时,那么该变量的值就相当于一个常数不可变,当final修饰的是一个对象时,那么对于对象句柄,final会将句柄变成一个常数,就是此时不可变的对象的引用。
然后,final修饰的对象是不可以重新指向新的一个对象的,但是自身对象的内容可以进行改变。
举个栗子
package com.xxg; class Value { int i = 1; } public class FinalData { // Can be compile-time constants final int i1 = 9; static final int I2 = 99; // Typical public constant: public static final int I3 = 39; // Cannot be compile-time constants: final int i4 = (int)(Math.random()*20); static final int i5 = (int)(Math.random()*20); Value v1 = new Value(); final Value v2 = new Value(); static final Value v3 = new Value(); //! final Value v4; // Pre-Java 1.1 Error: // no initializer // Arrays: final int[] a = { 1, 2, 3, 4, 5, 6 }; public void print(String id) { System.out.println( id + ": " + "i4 = " + i4 + ", i5 = " + i5); } public static void main(String[] args) { FinalData fd1 = new FinalData(); //! fd1.i1++; // Error: can't change value fd1.v2.i++; // Object isn't constant! fd1.v1.i++; fd1.v1 = new Value(); // OK -- not final for(int i = 0; i < fd1.a.length; i++) fd1.a[i]++; // Object isn't constant! //! fd1.v2 = new Value(); // Error: Can't //! fd1.v3 = new Value(); // change handle //! fd1.a = new int[3]; fd1.print("fd1"); System.out.println("Creating new FinalData"); FinalData fd2 = new FinalData(); fd1.print("fd1"); fd2.print("fd2"); } }
结果:
fd1: i4 = 6, i5 = 11 Creating new FinalData fd1: i4 = 6, i5 = 11 fd2: i4 = 11, i5 = 11
分析:
i1和i2都是具备final属性的基本数据类型变量,并且在编译期都是具备有编译值的,而i3其实也差不多,只是多了public的访问修饰可以允许外边访问以及static强调只有一个
i4和i5则证明了不是所有被final修饰的在编译期就能够确定值为多少,这两个都是在运行时调用随机函数生成数才能确认值。同时也看到了,由于i5是static修饰的,因为它的值并不会由于fd1和fd2对象的创建而发生改变。
举个栗子
public static void main(String[] args) { final FinalString fs = new FinalString("1"); final FinalString fss = new FinalString("333"); fs = fss; }
public static void main(String[] args) { final String[] strs0 = {"123","234"}; final String[] strs1 = {"345","456"}; strs1 = strs0; strs1[1] = "333"; }
分析:
这两个程序的运行会报错“The final local variable fs cannot be assigned”,被final修饰不可变的是变量的引用,而不是引用指向的内容,引用指向的内容是可以改变的,如果将第二程序的 strs1=str0 去掉,那么程序就可以正常运行
--- final与方法
会使用final修饰方法,一方面的情况时为了确保该方法不会被修改或者由于继承类改变它的原本含义,另外一种情况时考虑到代码的运行效率问题,假如已经确定了某个方法是不会在变化了,那么可以通过final修饰,将其直接加载进入方法区,而可以忽略每次使用完方法和使用完后,堆内的压栈等一系列操作,可以减少系统的开销。
类内所有private方法都自动成为final。由于我们不能访问一个private方法,所以它绝对不会被其他方法覆盖(若强行这样做,编译器会给出错误提示)。可为一个private方法添加final指示符,但却不能为那个方法提供任何额外的含义。
---fianl与类
如果说整个类都是final(在它的定义前冠以final关键字),就表明自己不希望从这个类继承,或者不允许其他任何人采取这种操作。换言之,出于这样或那样的原因,我们的类肯定不需要进行任何改变;或者出于安全方面的理由,我们不希望进行子类化(子类处理)。
将类定义成final后,结果只是禁止进行继承——没有更多的限制。然而,由于它禁止了继承,所以一个final类中的所有方法都默认为final。因为此时再也无法覆盖它们。所以与我们将一个方法明确声明为final一样,编译器此时有相同的效率
选择。可为final类内的一个方法添加final指示符,但这样做没有任何意义。
参考文献:《thinging in java》