频道栏目
首页 > 资讯 > Java > 正文

Java中的final关键字解析

18-06-17        来源:[db:作者]  
收藏   我要投稿

我们有时会发现jdk的源码中经常出现final这个关键字,那么它到底有什么作用呢?只是一种规范?还是说在某种场景下有性能上的优化?

首先我们从字面意义上进行初步阐述,final的英文意思是:最终的,不可更改的。也就是说被final关键字修饰的“东西”至少具有某种不变的性质。事实上,我们可根据“东西”的类别来分别描述final关键字的作用。

一、修饰(非抽象类)

final关键字修饰类,则表示该类不能被继承,该类的成员方法无法被覆盖,但并不会默认加上final修饰符(反编译后或者利用反射机制可验证),只是说有提升效执行效率(被final修饰的效果之一)的作用。在设计类时候,如果这个类不需要有子类继承,那么就设计为final类。

二、修饰方法(非抽象方法)

final关键字修饰方法,则表示该方法不能被子类覆盖(或者说被子类重写),但是可以被继承。此外,若方法被final修饰,那么编译器在遇到调用该方法时候会转入内嵌机制,以便提高执行效率。值得注意的是,类的私有成员方法也并不会默认加上final修饰符(反编译后或者利用反射机制可验证),只有提升效执行效率的作用(java早期版本中的私有方法必须显示加上final关键字来达到内嵌的效果,而在java2之后则无需加上final关键字,也会默认转入内嵌机制)。

三、修饰变量

final修饰变量是比较常用的,也是需要重点分析的。如果一个变量被final修饰,那么该变量在其作用域内不能被第二次赋值。变量可分为基本数据类型和引用类型。如果变量是基本数据类型,那么被final修饰后,该变量的值不能再更改;若变量是引用类型,则该变量的引用不可更改,但是被引用对象的属性可以更改。同时我们根据作用域的不同,大概可以分为如下几种情况:

1、作用域在类加载时

此种情况是成员变量同时被 staticfinal修饰,该成员变量成为常量。比如,类Car中的成员变量wheelNum(轮子数量),其值在类加载时就已初始化并不可更改(这个也很容易理解,因为汽车在生产之前便设计成固定的4个轮子)。

public class Car {
	public static final int wheelNum = 4;
}

2、作用域在生成类对象时

此种情况是成员变量仅被final修饰,该成员变量只是在对象生成后不可第二次赋值,但生成不同的对象时可以有不同的初始值。同样用汽车作为例子,类Car中的成员变量power(马力),其值可在生成不同对象时具有不同的初始值,一旦对象初始化后再试图对其值进行更改则会编译报错。

public class Car {

	public static final int wheelNum = 4;
	
	final double power = 100*Math.random() + 100;
	
	public static void main(String[] args) {
		
		Car car1 = new Car();
		System.out.println("car1-->wheelNum = " + car1.wheelNum);
		System.out.println("car1-->power = " + car1.power);
		
		Car car2 = new Car();
		System.out.println("car2-->wheelNum = " + car2.wheelNum);
		System.out.println("car2-->power = " + car2.power);
		
		//编译报错
		//car1.power = 120.00;
	}
}
运行结果:
car1-->wheelNum = 4
car1-->power = 186.2119546789537
car2-->wheelNum = 4
car2-->power = 184.25954192641169

3、作用域在方法内时

此种情况是局部变量仅被final修饰,该变量在方法内不能进行第二次赋值。除此之外,对于基本数据类型或字符串类型的变量,若被final修饰后,则该变量在编译时就已确定,也就是编译时常量,这时编译器可针对其所关联的代码进行内联优化,只是性能提升很微小。下面是网上比较通用的例子,用以分析局部变量是否被final修饰的进一步区别(除了赋值约束的区别)。

public class Test {
	
	public static void main(String[] args) {
		
		String s1 = "hello2";
		String s2 = "hello";
		final String s3 = "hello";
		
		String s4 = s2 + 2;
		String s5 = s3 + 2;
		
		System.out.println(s1 == s4);
		System.out.println(s1 == s5);
	}
}
运行结果:
false
true

为什么会出现这样的结果?我们首先回顾一下字符串常量池的知识:在执行String s1 = "hello2"语句的时候,“hello2”被压入字符串常量池中,并返回引用给s1;在执行String s4 = s2 + 2语句的时候,由于s2不是常量,编译器会new一个StringBuilder来分别添加s2和2的值,此时s4的值并不会添加到常量池中,其引用也不是字符串常量池中的地址,所以s1和s4并不是同一个对象,因此第一个判断为false。然而在执行String s5 = s3 + 2;语句的时候,由于s3是编译时常量,此时编译器意识到是常量与常量的连接,编译器会试图将连接后的值(“hello2”)压入字符串常量池中,如果该值在字符串常量池中已存,则返回该值在字符串常量池中的引用,所以s1和s5引用的是常量池中的同一个对象(“hello2”),因此第二个判断为true。

总结:

final关键字修饰类和变量的主要目的是想规范行为(性能提升可忽略,除非存在大量的基本数据类型运算,此时可用final修饰变量来达到性能提升的效果)。比如,被final修饰的类不可被子类继承,被final修饰的变量在其作用域内不可进行第二次赋值等等;而final关键字修饰方法,除了表示该方法无法被子类覆盖外,主要目的是为了使该方法转入内嵌机制,以便提高执行效率。但是,java2之后,类的私有成员方法无需加上final关键字,也会默认转入内嵌机制。因此,代码中使用final关键字其主要目的并不是提高代码的执行效率,而是想达到某种规范的效果。

相关TAG标签
上一篇:JAVA语言实现excel转pdf文件
下一篇:jps命令的格式和使用介绍
相关文章
图文推荐

关于我们 | 联系我们 | 广告服务 | 投资合作 | 版权申明 | 在线帮助 | 网站地图 | 作品发布 | Vip技术培训 | 举报中心

版权所有: 红黑联盟--致力于做实用的IT技术学习网站