初步了解
这是一道非常典型的面试题,而它也是有一系列的典型回答的:
final可以用来修饰类、方法、变量,分别有不同的意义所在,final修饰的class代表不可继续扩展(继承),final修饰的变量代表不可修改,final修饰的方法代表不可重写。
finally则是java保证某一段重点代码一定要被执行的修饰符,例如:我们需要用try块让JDBC保证连接,保证unlock锁等动作
finalize是基础类java.lang.Object的一个方法,它的设计目的是为了保证对象在垃圾回收之前完成特定资源的回收
final
在java中,final可以用来修饰类,方法和变量(成员变量或局部变量)。下面将对其详细介绍。
修饰类
当用final修饰类的时,表明该类不能被其他类所继承。当我们需要让一个类永远不被继承,此时就可以用final修饰。
注意:final类中所有的成员方法都会隐式的定义为final方法。
修饰方法
使用final方法的原因主要有两个:
(1) 把方法锁定,以防止继承类对其进行更改。
(2) 效率,在早期的java版本中,会将final方法转为内嵌调用。但若方法过于庞大,可能在性能上不会有多大提升。因此在最近版本中,不需要final方法进行这些优化了。
final方法意味着“最后的、最终的”含义,即此方法不能被重写。
注意:若父类中final方法的访问权限为private,将导致子类中不能直接继承该方法,因此,此时可以在子类中定义相同方法名的函数,此时不会与重写final的矛盾,而是在子类中重新地定义了新方法。
class A{
private final void getName(){
}
}
public class B extends A{
public void getName(){
}
public static void main(String[]args){
System.out.println("OK");
}
}
修饰变量
final成员变量表示常量,只能被赋值一次,赋值后其值不再改变。类似于C++中的const。
当final修饰一个基本数据类型时,表示该基本数据类型的值一旦在初始化后便不能发生变化;如果final修饰一个引用类型时,则在对其初始化之后便不能再让其指向其他对象了,但该引用所指向的对象的内容是可以发生变化的(参考示例一)。本质上是一回事,因为引用的值是一个地址,final要求值,即地址的值不发生变化。
final修饰一个成员变量(属性),必须要显示初始化。这里有两种初始化方式:
- 在变量声明的时候初始化;
- 在声明变量的时候不赋初值,但是要在这个变量所在的类的所有的构造函数中对这个变量赋初值。
知识扩展
1、final不是immutable(不可变)
public static void main(String[] args) {
final List<String> bookList = new ArrayList<>();
bookList.add("西游记");
bookList.add("水浒传");
//重新给bookList赋值编辑器会报错
// bookList = new ArrayList<>();
}
由例子可见,虽然bookList被final修饰,但是还是可以改变其长度。但是如果重新给bookList赋值编辑器就会报错。
finally
finally作为异常处理的一部分,它只能用在try/catch语句中,并且附带一个语句块,表示这段语句最终一定会被执行(不管有没有抛出异常),经常被用在需要释放资源的情况下。(错!这句话其实存在一定的问题)
很多人都认为finally语句块一定会执行,但真的是这样么?答案是否定的,例如下面这个例子:
public static int test(){
int i = 1;
// if (i == 1){
// return 0;
// }
System.out.println("The previous statement of try block");
i = i /0;
try{
System.out.println("try block");
return i;
}
finally {
System.out.println("finally block");
}
}
public static void main(String[] args) {
System.out.println("return value of test():"+test());
}
执行结果:
如果去掉代码中的注释,执行结果:
为什么在以上两种情况下都没有执行finally语句呢,说明什么问题?
只有与finally对应的try语句块得到执行的情况下,finally语句块才会执行。以上两种情况在执行try语句块之前已经返回或抛出异常,所以try对应的finally语句并没有执行。
但是,在某些情况下,即使try语句执行了,finally语句也不一定执行。例如以下情况:
public static int test(){
int i = 1;
try{
System.out.println("try block");
System.exit(0);
return i;
}
finally {
System.out.println("finally block");
}
}
public static void main(String[] args) {
System.out.println("return value of test():"+test());
}
执行结果:
finally 语句块还是没有执行,为什么呢?因为我们在 try 语句块中执行了 System.exit (0) 语句,终止了 Java 虚拟机的运行。那有人说了,在一般的 Java 应用中基本上是不会调用这个 System.exit(0) 方法的。OK !没有问题,我们不调用 System.exit(0) 这个方法,那么 finally 语句块就一定会执行吗?
再一次让大家失望了,答案还是否定的。当一个线程在执行 try 语句块或者 catch 语句块时被打断(interrupted)或者被终止(killed),与其相对应的 finally 语句块可能不会执行。还有更极端的情况,就是在线程运行 try 语句块或者 catch 语句块时,突然死机或者断电,finally 语句块肯定不会执行了。可能有人认为死机、断电这些理由有些强词夺理,没有关系,我们只是为了说明这个问题。
知识扩展
在try-catch-finally语句中执行return语句,如下代码:
public static int test(String str){
try{
return str.charAt(0);
}catch (NullPointerException e1){
return 1;
}catch (StringIndexOutOfBoundsException e2){
return 2;
}catch (Exception e3){
return 3;
}finally {
return 4;
}
}
public static void main(String[] args) {
System.out.println(test(null)+","+test("0")+","+test(""));
}
执行结果:
首先finally语句在改代码中一定会执行,从运行结果来看,每次return的结果都是4(即finally语句),仿佛其他return语句被屏蔽掉了。
事实也确实如此,因为finally用法特殊,所以会撤销之前的return语句,继续执行最后的finally块中的代码。
finalize
finalize()是在java.lang.Object里定义的,也就是说每一个对象都有这么个方法。这个方法在gc启动,该对象被回收的时候被调用。其实gc可以回收大部分的对象(凡是new出来的对象,gc都能搞定,一般情况下我们又不会用new以外的方式去创建对象),所以一般是不需要程序员去实现finalize的。
特殊情况下,需要程序员实现finalize,当对象被回收的时候释放一些资源,比如:一个socket链接,在对象初始化时创建,整个生命周期内有效,那么就需要实现finalize,关闭这个链接。
使用finalize还需要注意一个事,调用super.finalize();
一个对象的finalize()方法只会被调用一次,而且finalize()被调用不意味着gc会立即回收该对象,所以有可能调用finalize()后,该对象又不需要被回收了,然后到了真正要被回收的时候,因为前面调用过一次,所以不会调用finalize(),产生问题。 所以,推荐不要使用finalize()方法,它跟析构函数不一样。
参考:https://blog.csdn.net/qq_41936805/article/details/90983377