Java基础之深入理解final关键字

1 含义

  final在Java中是一个保留的关键字,可以声明成员变量、方法、类以及本地变量。一旦你将引用声明作final,你将不能改变这个引用了,编译器会检查代码,如果你试图将变量再次初始化的话,编译器会报编译错误。final变量是只读的。

2 分类

2.1 什么是final变量?

  凡是对成员变量或者本地变量声明为final的都叫作final变量。final变量经常和static关键字一起使用,作为常量。下面是final变量的例子:

public static final String LOAN = "loan";

2.2 什么是final方法?

  final也可以声明方法。方法前面加上final关键字,代表这个方法不可以被子类的方法重写。如果你认为一个方法的功能已经足够完整了,子类中不需要改变的话,你可以声明此方法为final。final方法比非final方法要快,因为在编译的时候已经静态绑定了,不需要在运行时再动态绑定。

class PersonalLoan{
    public final String getName(){
        return "personal loan";
    }
}
class CheapPersonalLoan extends PersonalLoan{
    @Override
    public final String getName(){
        return "cheap personal loan"; //compilation error: overridden method is final
    }
}

2.3 什么是final类?

  使用final来修饰的类叫作final类。final类通常功能是完整的,它们不能被继承。Java中有许多类是final的,譬如String, Interger以及其他包装类。

final class PersonalLoan{

}
class CheapPersonalLoan extends PersonalLoan{  
        //compilation error: cannot inherit from final class
}

3 final关键字的好处

(1)final关键字提高了性能。JVM和Java应用都会缓存final变量。
(2)final变量可以安全的在多线程环境下进行共享,而不需要额外的同步开销。
(3)使用final关键字,JVM会对方法、变量及类进行优化。

4 不可变类

  创建不可变类要使用final关键字。不可变类是指它的对象一旦被创建了就不能被更改了。String是不可变类的代表。不可变类有很多好处,譬如它们的对象是只读的,可以在多线程环境下安全的共享,不用额外的同步开销等等。

4.1 为什么String类设计成final?

(1)官方解析:Strings are constant; their values cannot be changed after they are created. String buffers support mutable strings. Because String objects are immutable they can be shared.
(2)效率:String类被频繁的使用,申明为final可以提高程序的性能。如果一个类申明为final的,那么它所有的方法都是final的,jvm编译的时候会寻找机会内联那些final的方法,所以容易构造,测试与使用,提高了效率。
(3)安全:防止String内被继承和重写里面的方法。因为java不是操作系统本地语言,String类中的很多方法是用被的操作系统语言实现的,如果不申明为final而重写这些方法时搞破坏,产生所谓的病毒了。
(4)真正不可变对象都是线程安全。

5 为什么Java匿名内部类访问的外部局部变量或参数需要被final修饰

abstract class ABSClass{
        public abstract void print();
    }

    public class Test2{
        public static void test(final String s){//一旦参数在匿名类内部使用,则必须是final
            ABSClass c = new ABSClass() {
                public void print(){
                    System.out.println(s);
                }
            };
            c.print();
        }
        public static void main(String[] args){
            test("Hello World!");
        }
    }

(1)原因是编译程序实现上的困难
(2)内部类对象的生命周期会超过局部变量的生命周期。
  解析:局部变量的生命周期:当该方法被调用时,该方法中的局部变量在栈中被创建,当方法调用结束时,退栈,这些局部变量全部死亡。而内部类对象生命周期与其它类一样:自创建一个匿名内部类对象,系统为该对象分配内存,直到没有引用变量指向分配给该对象的内存,它才会死亡(被JVM垃圾回收)。所以完全可能出现的一种情况是:成员方法已调用结束,局部变量已死亡,但匿名内部类的对象仍然活着。
  解决方法:匿名内部类对象可以访问同一个方法中被定义为final类型的局部变量。定义为final后,编译程序的实现方法:对于匿名内部类对象要访问的所有final类型局部变量,都拷贝成为该对象中的一个数据成员。这样,即使栈中局部变量已死亡,但被定义为final类型的局部变量的值永远不变,永远指向同一个对象。
(3)在内部类的回调方法中,s既不可能是静态变量、临时变量、方法参数,它不可能作为根,在内部类中也没有变量引用它,它的根在内部类外部的那个方法中。如果这时外面变量s重指向其它对象,则回调方法中的这个对象s就失去了引用,可能被回收。而由于内部类回调方法大多数在其它线程中执行,可能还要在回收后还会继续访问它,出问题。

6 参考博客,如果侵权请告知

深入理解Java中的final关键字

为什么Java匿名内部类访问的外部局部变量或参数需要被final修饰

为什么匿名内部类和局部内部类只能访问final变量

猜你喜欢

转载自blog.csdn.net/chenliguan/article/details/79522875