首先说一下final,final作为形容词意思是 adj. 最终的;决定性的;不可更改的
主要用在四个地方:
- 修饰变量,包括静态变量和非静态变量
- 修饰方法中的参数
- 修饰方法
- 修饰类
修饰变量时,则表示被修饰的变量是不可更改的。如果变量是基本类型,则表示变量的值不容更改,变成了常量;如果变量是引用类型,则表示此引用不可更改了,就是说这个引用在定义的时候指向谁,以后都不能更改了,不能指向别的对象了,但是并不是说引用所指向的对象的值也没法改变了。也不知道我说清楚没有,看看代码吧:
public static void main(String[] args) { final int a = 5; // a = 6; // Error The final local variable a cannot be assigned. // It must be blank and not using a compound assignment final Student std = new Student("zhangsan", 1); // std = new Student("lisi", 2); }
在用final修饰基本类型变量 a 时,如果我们试图修改 a 的值(第6行),编译时则会报告错误信息(Error The final local variable a cannot be assigned. It must be blank and not using a compound assignment)。当修饰引用类型 Student 时,如果我们试图修改句柄 std 所指向的对象(第8行),则会引起同样的错误,即不能指向其他的对象。但是当我们修改 std 对象中的变量时却是可以的,代码如下:
class Student { String name; int sno; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getSno() { return sno; } public void setSno(int sno) { this.sno = sno; } public Student(String name, int sno) { super(); this.name = name; this.sno = sno; } }
这里在修改时和final其实没有任何关系,因为final修饰的是std这个对象,但是我们改变的是Student类中的变量 name ,如果想要不改变 name 变量我们应该在 Student 类中用 final修饰 name 变量。
另外,静态变量不能在非静态块中初始化,如下,第3行和第8行则会报错(The blank final field name may not have been initialized):
class Student { static final String name; int sno; public Student(String name, int sno) { super(); this.name = name; this.sno = sno; } }
静态块是在加载类的时候就会自动运行,非静态块则不会。可见,Java规定不允许静态常量在非静态块中定义是因为在刚加载类的时候非静态块可能还没有执行,这样如果静态块中需要使用静态常量就得不到其值了,或者非静态块有可能被多次调用,如果允许的话,静态常量岂不是会被多次赋值,破坏了final的定义。同样,非静态常量不能在静态块中初始化,因为静态块不能访问非静态属性和方法。
修饰方法中的参数时,final 的作用其实和上面的 效果是一样的。我们这么来看,函数中的形参作用的有效范围就是函数块,那么这个时候我们用 final 修饰了形参,会引发什么样的效果呢,那就是在它作用的这一块中,它是不能被修改的。如下:
public void change(final int i) { i = 100; }
则在第3行中会报错(The final local variable i cannot be assigned. It must be blank and not using a compound assignment)。如果我们使用一个函数,只是希望用这个函数对传进去的值做一些运算,而不想改变它的值(例如为了安全),我们就可以用 final 来修饰函数的参数。如果传进来的是引用类型,我们用 final 修饰效果是一样的,即不能改变这个参数所指向的对象。
修饰方法时,final 的作用是其修饰的方法子类不能够重写,但是可以使用。这就像王羲之集毕生心血写了一幅字,已经臻于化境了,堪称完美了,为了防止后代狗尾续貂或者恶意篡改,就给后代定下规矩,这幅字,谁也不能改!就这样,这幅画就被 final 了,但是后代虽然不能改,但是可以拿出来看啊。
修饰类时,final 的作用就是这个类因为某种原因不想被任何类继承。例如Java中的 String类,final 类之后不再有子类,所以它处于类树的叶子节点的位置。至于 final 类提高效率的原因在 深沉的船 的这篇博客(http://www.ehelper.com.cn/blog/post/132.html)中讲解了一些,先引用如下,感谢前辈的分享:
提高执行速度
final类可以提高执行速度主要因为如下原因:
l 不涉及继承和覆盖。
l 其地址引用和装载在编译时完成。
l 在运行时不要求JVM执行因覆盖而产生的动态地址引用而花费时间和空间。
l 与继承链上的一般对象相比,垃圾回收器在收回final对象所占据的地址空间时也相对简单快捷。
但在某些情况下使用final方法并不能取得提高执行速度的结果。因为并不是所有final方法其地址的装载和引用在编译时间完成。
假设类C继承了B,B继承了A,在类A中有final方法。对类C来讲,调用A的final方法的确是inline编译,即装载在编译时间完成;但对A和B来讲,可能没有调用final方法。而在执行期间,JVM动态装载的方法有可能并不是C所调用的final方法。这种情况下,则不能够取得提高执行速度的结果。当然,如果final方法在编译时间装载到JVM,而且没有在执行期间覆盖的,可以取得inline效益,提高执行速度。
作者建议是:不能仅仅因为考虑追求提高执行速度而使用final类。在程序设计和代码编写时,应首先考虑这个类所执行的任务和安全因素,是否允许有子类。在这个前提下,尽量提高代码的重复应用性是面向对象设计和编程的宗旨。然后考虑是否使用final类和final方法。