Java中final关键字详解


Posted in PHP onAugust 10, 2015

谈到final关键字,想必很多人都不陌生,在使用匿名内部类的时候可能会经常用到final关键字。另外,Java中的String类就是一个final类,那么今天我们就来了解final这个关键字的用法。
主要介绍:一.final关键字的基本用法。二.深入理解final关键字

一.final关键字的基本用法

在Java中,final关键字可以用来修饰类、方法和变量(包括成员变量和局部变量)。下面就从这三个方面来了解一下final关键字的基本用法。

1.修饰类

当用final修饰一个类时,表明这个类不能被继承。也就是说,如果一个类你永远不会让他被继承,就可以用final进行修饰。final类中的成员变量可以根据需要设为final,但是要注意final类中的所有成员方法都会被隐式地指定为final方法。

Java中final关键字详解

在使用final修饰类的时候,要注意谨慎选择,除非这个类真的在以后不会用来继承或者出于安全的考虑,尽量不要将类设计为final类。

2.修饰方法

下面这段话摘自《Java编程思想》第四版第143页:

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

因此,如果只有在想明确禁止 该方法在子类中被覆盖的情况下才将方法设置为final的。

注:类的private方法会隐式地被指定为final方法。

3.修饰变量

修饰变量是final用得最多的地方,也是本文接下来要重点阐述的内容。首先了解一下final变量的基本语法:

对于一个final变量,如果是基本数据类型的变量,则其数值一旦在初始化之后便不能更改;如果是引用类型的变量,则在对其初始化之后便不能再让其指向另一个对象。

举个例子:

Java中final关键字详解

上面的一段代码中,对变量i和obj的重新赋值都报错了。

二.深入理解final关键字

在了解了final关键字的基本用法之后,这一节我们来看一下final关键字容易混淆的地方。

1.类的final变量和普通变量有什么区别?

当用final作用于类的成员变量时,成员变量(注意是类的成员变量,局部变量只需要保证在使用之前被初始化赋值即可)必须在定义时或者构造器中进行初始化赋值,而且final变量一旦被初始化赋值之后,就不能再被赋值了。

那么final变量和普通变量到底有何区别呢?下面请看一个例子:

public class Test {
  public static void main(String[] args) {
    String a = "hello2"; 
    final String b = "hello";
    String d = "hello";
    String c = b + 2; 
    String e = d + 2;
    System.out.println((a == c));
    System.out.println((a == e));
  }
}

true
false

 大家可以先想一下这道题的输出结果。为什么第一个比较结果为true,而第二个比较结果为fasle。

这里面就是final变量和普通变量的区别了,当final变量是基本数据类型以及String类型时,如果在编译期间能知道它的确切值,则编译器会把它当做编译期常量使用。也就是说在用到该final变量的地方,相当于直接访问的这个常量,不需要在运行时确定。这种和C语言中的宏替换有点像。

因此在上面的一段代码中,由于变量b被final修饰,因此会被当做编译器常量,所以在使用到b的地方会直接将变量b 替换为它的  值。而对于变量d的访问却需要在运行时通过链接来进行。

想必其中的区别大家应该明白了,不过要注意,只有在编译期间能确切知道final变量值的情况下,编译器才会进行这样的优化,比如下面的这段代码就不会进行优化:

public class Test {
  public static void main(String[] args) {
    String a = "hello2"; 
    final String b = getHello();
    String c = b + 2; 
    System.out.println((a == c));
  }
  public static String getHello() {
    return "hello";
  }
}

这段代码的输出结果为false

2.被final修饰的引用变量指向的对象内容可变吗?

在上面提到被final修饰的引用变量一旦初始化赋值之后就不能再指向其他的对象,那么该引用变量指向的对象的内容可变吗?看下面这个例子:

public class Test {
  public static void main(String[] args) {
    final MyClass myClass = new MyClass();
    System.out.println(++myClass.i);
  }
}
class MyClass {
  public int i = 0;
}

这段代码可以顺利编译通过并且有输出结果,输出结果为1。这说明引用变量被final修饰之后,虽然不能再指向其他对象,但是它指向的对象的内容是可变的。

3.final和static

很多时候会容易把static和final关键字混淆,static作用于成员变量用来表示只保存一份副本,而final的作用是用来保证变量不可变。看下面这个例子:

public class Test {
  public static void main(String[] args) {
    MyClass myClass1 = new MyClass();
    MyClass myClass2 = new MyClass();
    System.out.println(myClass1.i);
    System.out.println(myClass1.j);
    System.out.println(myClass2.i);
    System.out.println(myClass2.j);
  }
}
class MyClass {
  public final double i = Math.random();
  public static double j = Math.random();
}

运行这段代码就会发现,每次打印的两个j值都是一样的,而i的值却是不同的。从这里就可以知道final和static变量的区别了。

4.匿名内部类中使用的外部局部变量为什么只能是final变量?

这个问题请参见上一篇博文中《Java内部类详解》中的解释,在此处不再赘述。

5.关于final参数的问题

关于网上流传的”当你在方法中不需要改变作为参数的对象变量时,明确使用final进行声明,会防止你无意的修改而影响到调用方法外的变量“这句话,我个人理解这样说是不恰当的。

因为无论参数是基本数据类型的变量还是引用类型的变量,使用final声明都不会达到上面所说的效果。

看这个例子就清楚了:

Java中final关键字详解

上面这段代码好像让人觉得用final修饰之后,就不能在方法中更改变量i的值了。殊不知,方法changeValue和main方法中的变量i根本就不是一个变量,因为java参数传递采用的是值传递,对于基本类型的变量,相当于直接将变量进行了拷贝。所以即使没有final修饰的情况下,在方法内部改变了变量i的值也不会影响方法外的i。

再看下面这段代码:

public class Test {
  public static void main(String[] args) {
    MyClass myClass = new MyClass();
    StringBuffer buffer = new StringBuffer("hello");
    myClass.changeValue(buffer);
    System.out.println(buffer.toString());
  }
}
class MyClass {
  void changeValue(final StringBuffer buffer) {
    buffer.append("world");
  }
}

运行这段代码就会发现输出结果为 helloworld。很显然,用final进行修饰并没有阻止在changeValue中改变buffer指向的对象的内容。

有人说假如把final去掉了,万一在changeValue中让buffer指向了其他对象怎么办。有这种想法的朋友可以自己动手写代码试一下这样的结果是什么,如果把final去掉了,然后在changeValue中让buffer指向了其他对象,也不会影响到main方法中的buffer,原因在于java采用的是值传递,对于引用变量,传递的是引用的值,也就是说让实参和形参同时指向了同一个对象,因此让形参重新指向另一个对象对实参并没有任何影响。

以上内容就是Java中final关键字详解,希望对大家有所帮助。

PHP 相关文章推荐
PHP如何编写易读的代码
Jul 10 PHP
生成php程序的php代码
Apr 07 PHP
PHP下利用shell后台运行PHP脚本,并获取该脚本的Process ID的代码
Sep 19 PHP
PHP遍历某个目录下的所有文件和子文件夹的实现代码
Jun 28 PHP
ThinkPHP权限认证Auth实例详解
Jul 22 PHP
PHP实现的浏览器检查类
Apr 11 PHP
PHP通过微信跳转的Code参数获取用户的openid(关键代码)
Jul 06 PHP
PHP实现一个简单url路由功能实例
Nov 05 PHP
php四种定界符详解
Feb 16 PHP
PHP基于堆栈实现的高级计算器功能示例
Sep 15 PHP
PHP利用缓存处理用户注册时的邮箱验证,成功后用户数据存入数据库操作示例
Dec 31 PHP
ThinkPHP6.0如何利用自定义验证规则规范的实现登陆
Dec 16 PHP
php生成二维码
Aug 10 #PHP
PHP的文件操作与算法实现的面试题示例
Aug 10 #PHP
PHP的关于变量和日期处理的一些面试题目整理
Aug 10 #PHP
6个超实用的PHP代码片段
Aug 10 #PHP
解读PHP中的垃圾回收机制
Aug 10 #PHP
php实现多城市切换特效
Aug 09 #PHP
php基于双向循环队列实现历史记录的前进后退等功能
Aug 08 #PHP
You might like
php守护进程 加linux命令nohup实现任务每秒执行一次
2011/07/04 PHP
通过dbi使用perl连接mysql数据库的方法
2014/04/16 PHP
php for 循环使用的简单实例
2016/06/02 PHP
解决PHP 7编译安装错误:cannot stat ‘phar.phar’: No such file or directory
2017/02/25 PHP
JQUERY 实现窗口滚动搜索框停靠效果(类似滚动停靠)
2013/03/27 Javascript
jQuery中find()方法用法实例
2015/01/07 Javascript
微信小程序 WXDropDownMenu组件详解及实例代码
2016/10/24 Javascript
概述javascript在Google IE中的调试技巧
2016/11/24 Javascript
JavaScript Ajax实现异步通信
2016/12/14 Javascript
创建简单的node服务器实例(分享)
2017/06/23 Javascript
js中的事件委托或是事件代理使用详解
2017/06/23 Javascript
H5基于iScroll实现下拉刷新和上拉加载更多
2017/07/18 Javascript
jquery animate动画持续运动的实例
2017/11/29 jQuery
Vue实现本地购物车功能
2018/12/05 Javascript
js实现旋转的星空效果
2019/11/01 Javascript
详解javascript中var与ES6规范中let、const区别与用法
2020/01/11 Javascript
js动态生成表格(节点操作)
2021/01/12 Javascript
Django Admin实现上传图片校验功能
2016/03/06 Python
Python编程实现的简单神经网络算法示例
2018/01/26 Python
django数据库migrate失败的解决方法解析
2018/02/08 Python
python 读入多行数据的实例
2018/04/19 Python
解决Python requests库编码 socks5代理的问题
2018/05/07 Python
Python中的groupby分组功能的实例代码
2018/07/11 Python
搞定这套Python爬虫面试题(面试会so easy)
2019/04/03 Python
python调用动态链接库的基本过程详解
2019/06/19 Python
python 实现保存最新的三份文件,其余的都删掉
2019/12/22 Python
python读写文件write和flush的实现方式
2020/02/21 Python
opencv python在视屏上截图功能的实现
2020/03/05 Python
澳大利亚免息网上购物:Shop Zero
2016/09/17 全球购物
印度最大的网上花店:Ferns N Petals(鲜花、礼品和蛋糕)
2017/10/16 全球购物
怎样填写就业意向
2014/04/02 职场文书
药品营销专业毕业生自荐信
2014/07/02 职场文书
课内比教学心得体会
2014/09/09 职场文书
武当山导游词
2015/02/03 职场文书
教师廉洁自律个人总结
2015/02/10 职场文书
python内置进制转换函数的操作
2021/06/02 Python