Java 7之基础 - final、finally和finilize关键字

时间:2021-02-23 18:21:58

 1、final 关键字


1.1  final关键字的使用

(1)修饰类  如果一个类被声明为final,意味着它不能再派生出新的子类,不能作为父类被继承。因此一个类不能同时声明为abstract final或者interface final的。
(2)修饰方法  将方法声明为final,可以保证它们在使用中不被改变。被声明为final的方法也同样只能使用,不能重载。但是子类可以继承父类的final方法。
(3)修饰变量  表示属性值第一次初始化后不能被修改。final属性可以直接初始化或在构造函数中初始化
如果属性是直接初始化,则其值不能被其它函数(包括构造函数)修改。
public final int a=2;public BaseType(){// 构造方法
a=3; // 编译报错
}
(4)修饰方法参数    参数值不能被修改
(5)修饰方法中的局部变量    局部变量被第一次初始化后不能被修改
 public void hello() {       final String name;       name = "hi";        // ok       name = "hello";     // error}

1.2  常见final的错误应用

class Something {    final int i;    public void doSomething() {        System.out.println("i = " + i);    }}

final int i是个final的实例变量。final的实例变量没有默认值,必须在constructor(构造器)结束之前被赋予一个明确的值。可以修改为"final int i = 0;"

再举一个例子:
interface Playable {    void play();}interface Bounceable {    void play();}interface Rollable extends Playable, Bounceable {    Ball ball = new Ball("PingPang"); // 默认为public static final类型的}class Ball implements Rollable {    private String name;    public String getName() {        return name;    }    public Ball(String name) {        this.name = name;            }   public void play() {        ball = new Ball("Football");        System.out.println(ball.getName());    }}
任何在interface里声明的成员变量,默认为public static final。
Ball ball = new Ball("PingPang"); // 等价于 public static final Ball ball = new Ball("PingPang");
在Ball类的Play()方法中,改变了ball的引用,而这里的ball来自Rollable interface,因此编译器将在"ball = new Ball("Football");"这里显示有错。

从如上的两个例子可以看出,final使得被修饰的变量”不变”,但是由于对象型变量的本质是”引用”,使得”不变”也有了两种含义:引用本身的不变和引用指向的对象不变。
                final StringBuffer a= new StringBuffer("immutable");		final StringBuffer b= new StringBuffer("not immutable");		//a=b;                 // 编译期错误		a.append(" xx");       // 改变引用的内容		System.out.println(a); // 结果为:immutable xx
当a=b时会出现编译错误,不允许改变引用,但是我们却可以改变其引用的内容,如a.append("  xx");就改变了其引用指向的内容。所以经常有人会范如下的错误:
public class Something {   public static void main(String[] args) {       Other o = new Other();       new Something().addOne(o);   }   public void addOne(final Other o) {       o.i++;   }}class Other {   public int i;}
想通过添加final字段来阻止Other类中i的改变,其实这是办不到的,如上的程序依然能够通过o.i++来改变i的值。


如果是为一个参数对象添加final关键字呢?例如:
public void test(final Dog dog){      // 省略}


1.3  使用final的意义

     第一,为方法“上锁”,防止任何继承类改变它的本来含义和实现。设计程序时,若希望一个方法的行为在继承期间保持不变,而且不可被覆盖或改写,就可以采取这种做法。
      第二,提高程序执行的效率,将一个方法设成final后,编译器就可以把对那个方法的所有调用都置入“嵌入”调用里(内嵌机制)。

1.4  Java7新特性 - final重抛

在Java7以前,经常可以看到如下的代码片段:

        try{        	int a=0,x;        	x=3/a;        }catch(Exception e){        	throw e;        }
如上会将新抛出的异常类型java.lang.ArithmeticException声明为Exception,而真实具体的确被覆盖了。使用Java7新语法:
try{        	int a=0,x;        	x=3/a;        }catch(final Exception e){        	throw e;        }

只需要加个final关键字字即可,这样就不会抛出笼统的异常类型,这叫“final重抛‘。

2、finally关键字


 再异常处理时提供 finally 块来执行任何清除操作。如果抛出一个异常,那么相匹配的 catch 子句就会执行,然后控制就会进入 finally 块。 所以说finally块是一定会被执行的。
     finally 语句块是在 try 或者 catch 中的 return 语句之前执行的。更加一般的说法是,finally 语句块应该是在控制转移语句之前执行, 控制转移语句除了 return 外,还有 break 和 continue。
    另外,throw 语句也属于控制转移语句。虽然 return、throw、break 和 continue 都是控制转移语句,但是它们之间是有区别的。其中 return 和 throw 把程序控制权转交给它们的调用者(invoker),而 break 和 continue 的控制权是在当前方法内转移。
public class Test {    public static void main(String[] args) {        System.out.println("return value of getValue(): " + getValue());    }     public static int getValue() {        try {            return 0;        } finally {            return 1;        }    }}
执行结果为:return value of getValue(): 1。因为finally 语句块是在 try 或者 catch 中的 return 语句之前执行的。
public class Test {    public static void main(String[] args) {        System.out.println("return value of getValue(): " + getValue());    }     public static int getValue() {        int i = 1;        try {            return i;        } finally {            i++;        }    }}
其执行结果为:
return value of getValue(): 1
    究其原因,我们来分析一下其执行顺序:分为正常执行(没有 exception)和异常执行(有 exception)两种情况。我们先来看一下正常执行的情况。打开.class文件,代码如下:
 public static int getValue();     0 iconst_1            1 istore_0 [i]     2 iload_0 [i]     3 istore_2     4 iinc 0 1 [i]     7 iload_2     8 ireturn     9 astore_1    10 iinc 0 1 [i]    13 aload_1    14 athrow      Exception Table:        [pc: 2, pc: 4] -> 9 when : any      Line numbers:        [pc: 0, line: 9]        [pc: 2, line: 11]        [pc: 4, line: 13]        [pc: 7, line: 11]        [pc: 9, line: 12]        [pc: 10, line: 13]        [pc: 13, line: 14]      Local variable table:        [pc: 2, pc: 15] local: i index: 0 type: int      Stack map table: number of frames 1        [pc: 9, full, stack: {java.lang.Throwable}, locals: {int}]}
其执行图如下所示。
Java 7之基础 - final、finally和finilize关键字
可以看到,如上的结果为什么是1而不是2了。
继续来看下一道题:
public class Test {    public static void main(String[] args) {        System.out.println(test());    }   public static String test() {        try {            System.out.println("try block");            return test1();// 相当于代码String tmp=test1();return temp;        } finally {            System.out.println("finally block");        }    }     public static String test1() {        System.out.println("return statement");         return "after return";    }}
运行结果为:
try block 
return statement
finally block 
after return


其实finally在项目开发中的主要作用就是关闭资源,如数据库连接对象Connection、输入流/输出流等。在JDK7中对此做了改变。把资源的作用域限定在代码块内,当程序离开这个代码块时,资源会被自动关闭,这就防止了未关闭而造成资源浪费等一些问题。
Java 7之基础 - final、finally和finilize关键字
资源在处理完毕后,会自动关闭br。



3、finalize 方法名

     
       Java 技术允许使用 finalize()方法在垃圾收集器将对象从内存中清除出去之前做必要的清理工作。这个方法是由垃圾收集器在确定这个对象没有被引用时对这个对象调用的。它是在 Object 类中定义的,因此所有的类都继承了它。子类覆盖 finalize() 方法以整理系统资源或者执行其他清理工作。finalize()方法是在垃圾收集器删除对象之前对这个对象调用的。