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变量不可以。
通过下⾯的代码可以验证以上的观点:
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⽤来定义⼀个⽅法时,它表⽰这个⽅法不可以被⼦类重写,但是并不影响它被⼦类继承。我们写段代码来验证⼀下:
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语句
System.out.println();
// 测试continue语句
System.out.println();
// 测试break语句
}
}
class ReturnClass {
public ReturnClass() {
System.out.println("执⾏了return语句");
}
}
上⾯这段代码的运⾏结果如下:
执⾏了return语句
java重写和重载的区别执⾏了finally语句
执⾏了finally语句
1
con
执⾏了finally语句
2
执⾏了finally语句
执⾏了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()会被再次调⽤。请看下⾯的⽰例:
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;
<();
}
}
运⾏结果如下:• 执⾏了finalize()⽅法
程序调⽤了java.lang.System类的gc()⽅法,引起GC的执⾏,GC在清理ft对象时调⽤了它的finalize()⽅法,因此才了上⾯的输出结果。调⽤()等同于调⽤下⾯这⾏代码:Runtime().gc();调⽤它们的作⽤只是建议垃圾收集器(GC启动,清理⽆⽤的对象释放内存空间,但是GC的启动并不是⼀定的,这由JAVA虚拟机来决定。直到 JAVA虚拟机停⽌运⾏,些对象的finalize()可能都没被运⾏过,那么怎样保证所对象的这个⽅法在JAVA虚拟机停⽌运⾏之前⼀定被调⽤呢?答案是我们可以调⽤System类的另⼀个⽅法:
public static void FunFinalizersOnExit(boolean value){
//othercode
}
给这个⽅法传⼊true就可以保证对象的finalize()⽅法在JAVA虚拟机停⽌运⾏前⼀定被运⾏了,不过遗憾的是这个⽅法是不安全的,它会导致有⽤的对象finalize()被误调⽤,因此已不被赞成使⽤了。由于finalize()属于Object类,因此所类都这个⽅法,Object的任意⼦类都可以重写(override该⽅法,在其中释放系统资源或者做其它的清理⼯作,如关闭输⼊输出流。通过以上知识的回顾,我想⼤家对于final、finally、finalize的⽤法区别已经很清楚了。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论