一、final修饰符
1、final修饰变量
2、final修饰方法
3、final修饰类
1、final修饰变量:
当在类中定义变量时,在其前面加上final关键字,这个变量一旦被初始化便不可改变,即不可以被再次赋值,这里的不可改变的意思对基本类型来说
是其值不可变,而对于对象变量来说是其引用不可在变。
问:使用final关键字修饰一个变量时,是引用不能变,还是引用的对象不能变?
答:使用final关键字修饰一个变量时,是指引用变量不能变,引用变量所指向的对象中的内容还是可以改变的。例如,对于如下语句:
final StringBuffer a=new StringBuffer("immutable");
执行如下语句将报告编译期错误:
a=new StringBuffer(""); //这里多了一个new,说明引用变量a已经被重新创建了。
但是,执行如下语句则可以通过编译:
a.append(" broken!");
有人在定义方法的参数时,可能想采用如下形式来阻止方法内部修改传进来的参数对象:
public void method(final StringBuffer param){
}
实际上,这是办不到的,在该方法内部仍然可以增加如下代码来修改参数对象:
param.append("a");
被final修饰的实例变量必须显式指定初始值,只能在如下3个位置指定初始值:
(1)定义final实例变量时指定初始值
(2)在非静态初始化块中为final实例变量指定初始值
(3)在构造器中为final实例变量指定初始值
下面的代码体现了上面的初始化形式:
package programmer;
public class FinalTest1 {
final int var1 = 1; // 定义final实例变量时指定初始值
final int var2;
final int var3;
{
var2 = 2; // 在非静态初始化块中为final实例变量指定初始值
}
public FinalTest1() {
this.var3 = 3; // 在构造器中为final实例变量指定初始值
}
public static void main(String[] args) {
FinalTest1 ft1 = new FinalTest1();
System.out.println("var1:" + ft1.var1);
System.out.println("var2:" + ft1.var2);
System.out.println("var3:" + ft1.var3);
}
}
对于被final修饰的 类变量,同样必须显式指定初始值,而且final类变量只能在2个地方指定初始值:
(1)定义final类变量时指定初始值
(2)在静态初始化块中为final类变量指定初始值
下面也给出相应的初始化代码:
package programmer;
public class FinalTest2 {
final static int var1 = 1; //定义final类变量时指定初始值
final static int var2;
static {
var2 = 2; //在静态初始化块中为final类变量指定初始值
}
public static void main(String[] args) {
System.out.println(FinalTest2.var1);
System.out.println(FinalTest2.var2);
}
}
也可以在方法体里面定义final变量,但是要求还是一样的,一样要被显式地赋值,且被赋值之后,以后再也不能对final局部变量重新赋值。
final修饰的变量的初始值其实在编译时就已经确定下来了,所以运行时不可以更改它的值。
下面看几个程序:
package programmer;
public class FinalTest3 {
public static void main(String[] args) {
String s1 = "helloworld";
String s2 = "hello" + "world";
System.out.println(s1 == s2);
String s11 = "hello";
String s22 = "world";
String s3 = s11 + s22;
System.out.println(s1 == s3);
}
}
运行结果如下:
package programmer;
public class FinalTest4 {
final String s1;
final String s2;
final String s3 = "hello";
{
s1 = "hello";
}
public FinalTest4() {
s2 = "hello";
}
public void display() {
System.out.println(s1 + s1 == "hellohello");
System.out.println(s2 + s2 == "hellohello");
System.out.println(s3 + s3 == "hellohello");
}
public static void main(String[] args) {
FinalTest4 ft4 = new FinalTest4();
ft4.display();
}
}
运行结果如下:
package programmer;
public class FinalTest5 {
final static String s1;
final static String s2 = "hello";
static {
s1 = "hello";
}
public static void main(String[] args) {
System.out.println(s1 + s1 == "hellohello");
System.out.println(s2 + s2 == "hellohello");
}
}
运行结果如下:
从上面上个程序我们可以很明显的看出来,java在编译时是无法计算出s1+s2类似的式子的,只有到运行时才可以计算出来,而判断也正好在运行时进行,所以类似s1+s1=="hellohello"是无法判断它是正确的,但是“hello"+"hello"=="hellohello“则是可以的,因为这个式子可以在编译时做相关的优化。
对于final修饰的变量,只有在定义final变量的时候指定初始值才可以当作”编译时优化“,否则无法当成”编译时“优化”。
2、final修饰方法:
当final修饰某个方法,则此方法不可被它的子类重写。
将方法声明为final有如下两个原因:
(1)已经知道这个方法提供的功能已经满足要求,不需要进行扩展,并且也不允许任何从此类继承的类来重写这个方法,但是继承子类仍然可以继承这个方法,可以直接使用。
(2)允许编译器将所有的对此方法的调用转化为inline(行内)调用的机制,它会在调用final方法时,直接将方法主体插入到调用处。这样会使程序效率有所提高。
package programmer;
public class FinalTest6 {
public static void main(String[] args) {
FinalInvoke fi = new FinalInvoke();
fi.info();
}
}
class FinalInvoke {
final void info() {
System.out.println("This is final method!");
}
}
上面的代码显示了性质(1).
3、final修饰类:
当使用final修饰类时,要仔细考虑,因为一个final类是无法被任何类继承的,那就意味着此类在一个继承树中是一个叶子类,并且此类的设计已经被认为很完美不需要进行修改或者扩展。对于final类中的成员,可以定义为final,也可以不是final,而对于方法,由于所属为final的关系,自然也就成了final型方法。
注意:在任何内部类中访问局部变量都应该使用final修饰。
二、finally:
finally关键字是对Java异常处理模型的最佳补充,finally结构使代码总会执行,不管有无异常发生,使用finally可以维护对象的内部状态,并可以清理非内存资源,如果没有finally,你的代码看起来很费解。
三、finalize:
finalize是Object类的一个方法,在垃圾收集器执行的时候会调用被回收对象的此方法,可以覆盖此方法提供垃圾收集时的其他资源回收,例如关闭文件等。JVM不保证此方法总被调用。