转 Java中final、finally、finalize的区别与用法 Java中final、finally、finalize的区别与用法

Java中final、finally、finalize的区别与用法

 

1.简单区别:
final用于声明属性,方法和类,分别表示属性不可交变,方法不可覆盖,类不可继承。
finally是异常处理语句结构的一部分,表示老是执行。
finalize是Object类的一个方法,在垃圾收集器执行的时候会调用被回收对象的此方法,供垃圾收集时的其余资源回收,例如关闭文件等。
2.中等区别:
虽然这个单词在Java中都存在,可是并没太多关联:
final:java中的关键字,修饰符。
A).若是一个类被声明为final,就意味着它不能再派生出新的子类,不能做为父类被继承。所以,一个类不能同时被声明为abstract抽象类的和final的类。
B).若是将变量或者方法声明为final,能够保证它们在使用中不被改变.
  1)被声明为final的变量必须在声明时给定初值,而在之后的引用中只能读取,不可修改。
  2)被声明final的方法只能使用,不能重载。
finally:java的一种异常处理机制。
  finally是对Java异常处理模型的最佳补充。finally结构使代码总会执行,而无论无异常发生。使用finally能够维护对象的内部状态,并能够清理非内存资源。特别是在关闭数据库链接这方面,若是程序员把数据库链接的close()方法放到finally中,就会大大下降程序出错的概率。
finalize:Java中的一个方法名。
Java技术使用finalize()方法在垃圾收集器将对象从内存中清除出去前,作必要的清理工做。这个方法是由垃圾收集器在肯定这个对象没被引用时对这个对象调用的。它是在Object类中定义的,所以所的类都继承了它。子类覆盖finalize()方法以整理系统资源或者执行其余清理工做。finalize()方法是在垃圾收集器删除对象以前对这个对象调用的。
3.详细区别:
这是一道再经典不过的面试题了,咱们在各个公司的面试题中几乎都能看到它的身影。final、finally和finalize虽然长得像孪生兄弟同样,可是它们的含义和用法倒是截然不同。
final关键字咱们首先来讲说final。它能够用于如下四个地方:
1).定义变量,包括静态的和非静态的。
2).定义方法的参数。
3).定义方法。
4).定义类。
定义变量,包括静态的和非静态的。定义方法的参数
第一种状况:
  若是final修饰的是一个基本类型,就表示这个变量被赋予的值是不可变的,即它是个常量;
  若是final修饰的是一个对象,就表示这个变量被赋予的引用是不可变的
这里须要提醒你们注意的是,不可改变的只是这个变量所保存的引用,并非这个引用所指向的对象。
第二种状况:final的含义与第一种状况相同。
实际上对于前两种状况,一种更贴切的表述final的含义的描述,那就是,若是一个变量或方法参数被final修饰,就表示它只能被赋值一次,可是JAVA虚拟机为变量设定的默认值不记做一次赋值。被final修饰的变量必须被初始化。初始化的方式如下几种:    
1.在定义的时候初始化。    
2.final变量能够在初始化块中初始化,不能够在静态初始化块中初始化。
3.静态final变量能够在定义时初始化,也能够在静态初始化块中初始化,不能够在初始化块中初始化。    
4.final变量还能够在类的构造器中初始化,可是静态final变量不能够。
经过下面的代码能够验证以上的观点:html

public class FinalTest{
  public final int A=10; //在定义时初始化
  public final int B;{B=20;} //在初始化块中初始化

  //非静态final变量不能在静态初始化块中初始化    
  //public final int C;static{//C=30; }

  //静态常量,在定义时初始化
  public static final int STATIC_D=40;

   //静态常量,在静态初始化块中初始化
  public static final int STATIC_E;static{STATIC_E = 50;}

  //静态变量不能在初始化块中初始化    
  //public static final int  STATIC_F;{STATIC_F=60;}

  public final int G;

  //静态final变量不能够在构造器中初始化    
  //public static final int STATIC_H;

  //在构造器中初始化         
  public finalTest(){
    G=70;
    //静态final变量不能够在构造器中初始化
    //STATIC_H=80;

    //给final的变量第二次赋值时,编译会报错
    //A=99;
    //STATIC_D=99;
  }

  //final变量未被初始化,编译时就会报错
  //public final int L;

  //静态final变量未被初始化,编译时就会报错
  //public static final int STATIC_J;
}

咱们运行上面的代码以后出了能够发现final变量(常量和静态final变量(静态常量被初始化时,编译会报错。
用final修饰的变量(常量比非final的变量(普通变量拥更高的效率,所以咱们在际编程中应该尽量多的用常量来代替普通变量。
定义方法
当final用来定义一个方法时,它表示这个方法不能够被子类重写,可是并不影响它被子类继承。咱们写段代码来验证一下:java

public class ParentClass{
    public final void TestFinal(){
        System.out.println("父类--这是一个final方法");
    }
}
public class SubClass extends ParentClass{
    //子类没法重写(override父类的final方法,不然编译时会报错
    /* public void TestFinal(){
           System.out.println("子类--重写final方法");
    } */   
    public static void main(String[]args){
        SubClass sc = new SubClass();
        sc.TestFinal();
    }
}    

这里须要特殊说明的是,具备private访问权限的方法也能够增长final修饰,可是因为子类没法继承private方法,所以也没法重写它。编译器在处理private方法时,是照final方来对待的,这样能够提升该方法被调用时的效率。不过子类仍然能够定义同父类中private方法具一样结构的方法,可是这并不会产生重写的效果,并且它们之间也不存在必然联系。
定义类
最后咱们再来回顾一下final用于类的状况。这个你们应该也很熟悉了,由于咱们最经常使用的String类就是final的。因为final类不容许被继承,编译器在处理时把它的所方法都看成final的,所以final类比普通类拥更高的效率。而由关键字abstract定义的抽象类含必须由继承自它的子类重载实现的抽象方法,所以没法同时用final和abstract来修饰同一个类。一样的道理,
final也不能用来修饰接口。 final的类的所方法都不能被重写,但这并不表示final的类的属性(变量值也是不可改变的,要想作到final类的属性值不可改变,必须给它增长final修饰,请看下面的例子:程序员

public final class FinalTest{
    int i =20;
    final int j=50;
    public static void main(String[] args){
          FinalTest ft = new FinalTest();
          ft.i = 99;/*final类FinalTest的属性值 i是能够改变的,由于属性值i前面没final修饰*/
          //ft.j=49;//报错....由于j属性是final的不能够改变。
          System.out.println(ft.i);
    }
}        

运行上面的代码试试看,结果是99,而不是初始化时的10。
finally语句
接下来咱们一块儿回顾一下finally的用法。finally只能用在try/catch语句中而且附带着一个语句块,表示这段语句最终老是被执行。请看下面的代码:面试

public final class FinallyTest{
    public static void main(String[] args){
        try{
            throw new NullPointerException();
        }catch(NullPointerException e){
            System.out.println("程序抛出了异常");
        }finally{
            //这里总会被执行,不受break,return影响另如数据库链接的close()通常写在这里,能够下降程序的出错概率
            System.out.println("执行了finally语句块");
        }
    }
}

运行结果说明了finally的做用:数据库

1.程序抛出了异常编程

2.执行了finally语句块请你们注意,捕获程序抛出的异常以后,既不加处理,也不继续向上抛出异常,并非良好的编程习惯,它掩盖了程序执行中发生的错误,这里只是方便演示,请不要学习。
那么,没一种状况使finally语句块得不到执行呢?
return、continue、break这个能够打乱代码顺序执行语句的规律。那咱们就来试试看,这个语句是否能影响finally语句块的执行:安全

public final class FinallyTest {
    //测试return语句
    //结果显示:编译器在编译return new ReturnClass();时,
    //将它分红了两个步骤,new ReturnClass()和return,前一个建立对象的语句是在finally语句块以前被执行的,
    //然后一个return语句是在finally语句块以后执行的,也就是说finally语句块是在程序退出方法以前被执行的
    public ReturnClass testReturn() {
        try {
            return new ReturnClass();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            System.out.println("执行了finally语句");
        }
        return null;
    }

    //测试continue语句
    public void testContinue(){
        for(int i=0; i<3; i++){
            try {
                System.out.println(i);
                if(i == 1){
                    System.out.println("con");
                }
            } catch(Exception e) {
                e.printStackTrace();
            } finally {
                System.out.println("执行了finally语句");
            }
        }
    }
    //测试break语句
    public void testBreak() {
        for (int i=0; i<3; i++) {
            try {
                System.out.println(i);
                if (i == 1) {
                    break;
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                System.out.println("执行了finally语句");
            }
        }
    }

    public static void main(String[] args) {
        FinallyTest ft = new FinallyTest();
        // 测试return语句
        ft.testReturn();
        System.out.println();
        // 测试continue语句
        ft.testContinue();
        System.out.println();
        // 测试break语句
        ft.testBreak();
    }
}

class ReturnClass {
    public ReturnClass() {
        System.out.println("执行了return语句");
    }
}
上面这段代码的运行结果以下:
执行了return语句
执行了finally语句

0
执行了finally语句
1
con
执行了finally语句
2
执行了finally语句

0
执行了finally语句
1
执行了finally语句

很明显,return、continue和break都没能阻止finally语句块的执行。从输出的结果来看,return语句彷佛在finally语句块以前执行了,事实真的如此吗?咱们来想一想看,return语句的做用是什么呢?是退出当前的方法,并将值或对象返回。若是 finally语句块是在return语句以后执行的,那么return语句被执行后就已经退出当前方法了,finally语句块又如何能被执行呢?所以,正确的执行顺序应该是这样的:编译器在编译return new ReturnClass();时,将它分红了两个步骤,new ReturnClass()和return,前一个建立对象的语句是在finally语句块以前被执行的,然后一个return语句是在finally语句块以后执行的,也就是说finally语句块是在程序退出方法以前被执行的。一样,finally语句块是在循环被跳过(continue和中断(break以前被执行的
finalize方法
最后,咱们再来看看finalize,它是一个方法,属于java.lang.Object类,它的定义以下:protected void finalize()throws Throwable{}众所周知,finalize()方法是GC(garbagecollector运行机制的一部分,在此咱们只说说finalize()方法的做用是什么呢?finalize()方法是在GC清理它所从属的对象时被调用的,若是执行它的过程当中抛出了没法捕获的异常(uncaughtexception,GC将终止对改对象的清理,而且该异常会被忽略;直到下一次GC开始清理这个对象时,它的finalize()会被再次调用。请看下面的示例:ide

public final class FinallyTest{
    //重写finalize()方法
    protected void finalize() throws Throwable{
         System.out.println("执行了finalize()方法");
    }
    public static void main(String[] args){
          FinallyTest ft = new FinallyTest();
          ft = null;
          System.gc();
    }
}

运行结果以下:• 执行了finalize()方法
程序调用了java.lang.System类的gc()方法,引发GC的执行,GC在清理ft对象时调用了它的finalize()方法,所以才了上面的输出结果。调用System.gc()等同于调用下面这行代码:Runtime.getRuntime().gc();调用它们的做用只是建议垃圾收集器(GC启动,清理无用的对象释放内存空间,可是GC的启动并非必定的,这由JAVA虚拟机来决定。直到 JAVA虚拟机中止运行,些对象的finalize()可能都没被运行过,那么怎样保证所对象的这个方法在JAVA虚拟机中止运行以前必定被调用呢?答案是咱们能够调用System类的另外一个方法:post

public static void FunFinalizersOnExit(boolean value){
    //othercode
}  

给这个方法传入true就能够保证对象的finalize()方法在JAVA虚拟机中止运行前必定被运行了,不过遗憾的是这个方法是不安全的,它会致使有用的对象finalize()被误调用,所以已不被同意使用了。因为finalize()属于Object类,所以所类都这个方法,Object的任意子类均可以重写(override该方法,在其中释放系统资源或者作其它的清理工做,如关闭输入输出流。经过以上知识的回顾,我想你们对于final、finally、finalize的用法区别已经很清楚了。学习

相关文章
相关标签/搜索