一. 什么是final关键字?

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

凡是对成员变量或者本地变量(在方法中的或者代码块中的变量称为本地变量)声明为final的都叫作final变量。final变量经常和static关键字一起使用,作为常量。下面是final变量的例子:

public static final String LOAN = "loan";

LOAN = new String("loan") //invalid compilation error

final变量是只读的,只能一次赋值,后边不能改变

在Java的继承体系中,子类可以在父类的基础上根据自己的需求来修改父类的内容来使子类更具个性,但是出于一些其他方面的考虑,我们不可能让子类随意的修改父类的内容,为了避免子类随意改写父类的内容,java提供了final关键字来修饰不可变的内容, final关键字可以修饰变量、方法、类。接下来从这三个方面来理解final关键字。

1.final关键字修饰类

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

final class PersonalLoan{

}

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

}

下面来看一个例子

final class A{

A(){

......

}

TestA(){

System.out.println("A是父类");

}

}

Class B extends A{ ///////    编译报错 Cannot inherit from final 'A'

TestB(){

System.out.println("B是子类");

}

}

由上面例子可以看出由final修饰的类不能被继承,在编译阶段会直接报错。Java中有许多类是final的,例如String, Interger以及其他Number包装类。在使用final修饰类的时候,要注意谨慎选择,除非这个类真的在以后不会用来继承或者出于安全的考虑,尽量不要将类设计为final类

2.final关键字修饰方法

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

}

}

“使用 final 方法的原因有两个。第一个原因是把方法锁定,以防任何继承类修改它的含义;第二个原因是效率。在早期的Java实现版本中,会将final方法转为内嵌调用。但是如果方法过于庞大可能看不到内嵌调用带来的任何性能提升。在最近的 Java 版本中,不需要使用 final 方法进行这些优化了。“

例如:

class A {

public final void Test() {

// public修饰,子类可以继承到此方法,导致重写了父类的final方法,编译出错

}

}

public class B extends A {

public static void main(String[] args) {

}

public void Test() { /////Error:Test()' cannot override 'Test()'; overridden method is final

}

}

Error:///// java:B中的Test()无法覆盖A中的Test(),被覆盖的方法为final然而对于下面的例子:

class A {

private final void Test() {

// private修饰,子类不可以继承到此方法

}

}

public class B extends A {

public static void main(String[] args) {

}

public void Test() {

//该方法是子类独有的

}

}

编译通过,由于private修饰的方法不能被子类所继承,所以子类的Test方法相当于子类自己的成员方法,固然可以执行。

final关键字修饰方法和类相似,被final修饰的方法可以认为是足够完整,则不可以被子类去修改,并且final方法是静态绑定的,在编译时期就确定好了是哪个类的方法,所以final方法是比非final方法快的。

注意:因为覆盖的前提是子类可以从父类中继承此方法,如果父类中final修饰的方法同时访问控制权限为private,将会导致子类中不能直接继承到此方法,因此,此时可以在子类中定义相同的方法名和参数,此时不再产生重写与final的矛盾,而是在子类中重新定义了新的方法。对于java来说类的private方法会隐式地被指定为final方法。

3. final关键字修饰局部变量

凡是对成员变量或者局部(或者在方法中的或者代码块中的变量称为本地变量)声明为final的都叫作final变量。final变量经常和static关键字一起使用,作为常量。final变量是只读的。当final修饰一个基本数据类型时,表示该基本数据类型的值一旦在初始化后便不能发生变化;如果final修饰一个引用类型时,则在对其初始化之后便不能再让其指向其他对象了,相当于该变量只能只向该对象,不能指向其他对象,但该引用所指向的对象的内容是可以发生变化的。

final修饰普通局部变量:

public class A {

public static void main(String[] args) {

// 声明变量,使用final修饰

final int a;

// 第一次赋值

a = 10;

// 第二次赋值

a = 20; // 报错,不可重新赋值

// 声明变量,直接赋值,使用final修饰

final int b = 10;

// 第二次赋值

b = 20;

// 报错,不可重新赋值

}

}

基本类型的局部变量,被final修饰后,只能赋值一次,不能再更改

final修饰引用类型的局部变量如下:

public class FinalDemo2 {

public static void main(String[] args) {

// 创建 User 对象

final   User u = new User();

// 创建 另一个 User对象

u = new User(); // 报错,指向了新的对象,地址值改变。

// 调用setName方法

u.setName("张三"); // 可以修改

}

}

引用类型的局部变量,被final修饰后,只能指向一个对象,地址不能再更改。但是不影响对象内部的成员变量值的修改。

4. final关键字修饰成员变量

final关键字修饰成员变量和局部变量类似,不过涉及到一个初始化问题。初始化方式有两种,只能二选一:

初始化方式1:显示初始化

public class User {

final String USERNAME = "张三";

private int age;

}

初始化方式2:构造方法初始化

public class User {

final String USERNAME ;

private int age;

public User(String username, int age) {

this.USERNAME = username;

this.age = age;

}

}

注意:在一般的书写规范中,final修饰的常量名称,所有字母都大写。

二.深入理解final关键字修饰String变量。

请看下面代码:

Class A{

public class Main {

public static void main(String[] args) {

String a = "huhahei111";

final String b = "huhahei";

String d = "huhahei";

String c = b + 111;

String e = d + 111;

System.out.println((a == c)); ////true

System.out.println((a == e)); ////false

}

}

}

程序在运行的时候会创建一个字符串常量池,程序会把字符串常量(程序中的huhahei)即a放入字符串常量池中.

由于变量 b 是 final 修饰的,变量 b 的值在编译时候就已经确定了外汇返佣它的值,也就是提前知道了变量 b 的内容到底是个啥,所以b也相当于一个常量;

创建字符串的时候,程序首先会在这个字符串常量池寻找相同值的对象,a,b,d先被放到了池中,在c被创建的时候,由于b是一个编译期常量,2也是一个常量,所以程序找到了具有相同值的 a,将c的引用指向了a所引用的对象"huhahei111"。所以比较a==c由于两个是指向同一个对象,所以为true。

d 是指向常量池中 huhahei,但由于 d 不是 final 修饰,也就是说在使用 d 的时候不会提前知道 d 的值是什么,所以在计算 e 的时候就不一样了,e的话由于使用的是 d 的引用计算,变量d的访问却需要在运行时通过链接来进行,所以这种计算会在堆内存上生成 huhahei111,所以最终 e 指向的是堆上的 huhahei111 , 所以 a 和 e所在内存不同,所以指向不同的对象,结果为false。

特点

1、final从字面上理解含义为“最后的,最终的”。

2、final可以用来修饰变量(包括类属性、对象属性、局部变量、形参),方法(包括类方法和对象方法)以及类。

用法

1、用来修饰数据,包括成员变量和局部变量,该变量只能被赋值一次且他的值不能再被改变。对于成员变量来说,我们必须在声明或构造方法中对它赋值。

2、用来修饰方法参数,表示在变量的生存期间它的值不能被改变。

3、修饰方法,表示该方法不能被重写。(因为重写的前提是子类可以从父类中继承此方法,如果父类中final修饰的方法同时访问控制权限为private,将会导致子类中不能直接继承到此方法。

因此,此时可以在子类中定义相同的方法名和参数,此时不再产生重写与final的矛盾,而是在子类中重新定义了新的方法。)

public class B extends A {

public static void main(String[] args) {

}

public void getName() {

}

}

class A {

/**

* 因为private修饰,子类中不能继承到此方法,因此,子类中的getName方法是重新定义的、

* 属于子类本身的方法,编译正常

*/

private final void getName() {

}

/* 因为pblic修饰,子类可以继承到此方法,导致重写了父类的final方法,编译出错

public final void getName() {

}

*/

}

4、修饰类,表示该类无法被继承,即不能拥有自己的子类。

注意

1、final关键字可以用于成员变量、本地变量、方法以及类。

2、final成员变量必须在声明的时候初始化或者在构造器中初始化,否则就会报编译错误。

3、你不能够对final变量再次赋值。

4、本地变量必须在声明时赋值。

5、在匿名类中所有变量都必须是final变量。

6、final方法不能被重写。

7、final类不能被继承。

8、final关键字不同于finally关键字,后者用于异常处理。

9、final关键字容易与finalize()方法搞混,后者是在Object类中定义的方法,是在垃圾回收之前被JVM调用的方法。

10、接口中声明的所有变量本身是final的。

11、final和abstract这两个关键字是反相关的,final类就不可能是abstract的。

12、final方法在编译阶段绑定,称为静态绑定(static binding)。

13、没有在声明时初始化final变量的称为空白final变量(blank final variable),它们必须在构造器中初始化,或者调用this()初始化。不这么做的话,编译器会报错“final变量(变量名)需要进行初始化”。

14、将类、方法、变量声明为final能够提高性能,这样JVM就有机会进行估计,然后优化。

15、按照Java代码惯例,final变量就是常量,而且通常常量名要大写

final关键字的好处

下面总结了一些使用final关键字的好处

final关键字提高了性能。JVM和Java应用都会缓存final变量。

final变量可以安全的在多线程环境下进行共享,而不需要额外的同步开销。

使用final关键字,JVM会对方法、变量及类进行优化。

不可变类

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

关于final的重要知识点

final关键字可以用于成员变量、本地变量、方法以及类。

final成员变量必须在声明的时候初始化或者在构造器中初始化,否则就会报编译错误。

你不能够对final变量再次赋值。

本地变量必须在声明时赋值。

在匿名类中所有变量都必须是final变量。

final方法不能被重写。

final类不能被继承。

final关键字不同于finally关键字,后者用于异常处理。

final关键字容易与finalize()方法搞混,后者是在Object类中定义的方法,是在垃圾回收之前被JVM调用的方法。

接口中声明的所有变量本身是final的。

final和abstract这两个关键字是反相关的,final类就不可能是abstract的。

final方法在编译阶段绑定,称为静态绑定(static binding)。

没有在声明时初始化final变量的称为空白final变量(blank final variable),它们必须在构造器中初始化,或者调用this()初始化。不这么做的话,编译器会报错“final变量(变量名)需要进行初始化”。

将类、方法、变量声明为final能够提高性能,这样JVM就有机会进行估计,然后优化。

按照Java代码惯例,final变量就是常量,而且通常常量名要大写:

private final int COUNT = 10;

对于集合对象声明为final指的是引用不能被更改,但是你可以向其中增加,删除或者改变内容。譬如:

private final List Loans = new ArrayList();

list.add(“home loan”);  //valid

list.add("personal loan"); //valid

loans = new Vector();  //not valid

我们已经知道final变量、final方法以及final类是什么了。必要的时候使用final,能写出更快、更好的代码的。

最新文章

  1. 深入理解C++对象模型
  2. COGS14. [网络流24题] 搭配飞行员
  3. HTML 浏览器显示控制
  4. Windows Phone 8.1中处理后退键的HardwareButtons.BackPressed事件
  5. [CSS] vertical-align
  6. Tutorial: Triplet Loss Layer Design for CNN
  7. MySql 用户 及权限操作
  8. ajax返回正个页面
  9. [Locked] Group Shifted Strings
  10. 超实用 JS 代码段笔记(一)
  11. POJ 2182 解题报告
  12. nginx cookie 会话保持功能
  13. C#使用RSA证书文件加密和解密
  14. java 日志体系(三)log4j从入门到详解
  15. Kotlin入门(21)活动页面的跳转处理
  16. Jira的搭建
  17. Confluence 6 避免和清理垃圾
  18. linux 生成密钥,并向git服务器导入公钥
  19. kubernetes1.5新特性(二):支持Photon卷插件
  20. 模块 import 与from

热门文章

  1. 转帖 java使用poi.3.10读取excel 2010
  2. python利用ConfigParser读写配置文件
  3. Qt QSS图片样式切割,三种状态normal,hover,pressed
  4. eclipse中服务器找不到项目怎么解决
  5. securityDemo依赖
  6. IP地址的定义和划分
  7. AcWing 214. Devu和鲜花 (容斥)打卡
  8. idea 配置idk
  9. selenium自动化之验证,判断总结(持续更新)
  10. HTTP超详细总结