`

回望Java中的final关键字

阅读更多

final关键字可以理解为“这个东西不能改变”。之所以要禁止改变,可能是因为实际业务情况需要或者效率或者设计因素,比如说应用中的全局变量我们经常将其用static加final关键字声明保证不能改变且该类的所有对象只有一份。在声明为final的地方有成员变量、方法、类。

 

final成员变量:

往往我们在用final声明为是常数数据的时候,可以分为编译期的常数数据和运行期的常数数据。

什么叫编译期的常数数据?是指在程序在编译期间已经将数据通过过程的形式存在了class文件中了,这部分数据在不需要在运行期间去执行,相当于节省了一部分开销,这类数据必须是属于基本数据类型(boolean、byte、char、short、int、float、long、double)且必须赋予一个初始值这样一经初始化就在编译期间设定了不能再改变。这类变量的声明我们习惯性的用大写来命名。

public class FinalTest {
	public static final int A = 5;

	public static void main(String[] args) {
		//FinalTest.A = 5; 编译不能通过 因为final声明的数据一旦指定不能改变
	}
}

 

什么叫运行期初始化的常数?是指该变量的初始化数据必须在运行期间才能被指定,一旦指定后就不会改变,在这里包括基本数据类型的指定和对象句柄的指定。基本数据类型的指定后是不可改变的,对象句柄的指定后也是不可改变的,但他的不同点是该句柄始终指向到一个具体的对象,而且该句柄始终指向这一对象,但是这一对象本身是可以改变的。

class Student {
	private int age;
	public Student(int age){
		this.age = age;
	}
	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

}

public class FinalTest {
	public static final int A = 5;
	final int b = (int) (Math.random() * 4);
	static final int staticb = (int) (Math.random() * 4);
	final Student p = new Student(20);

	public static void main(String[] args) {
		FinalTest test = new FinalTest();
		FinalTest test2 = new FinalTest();
		System.out.println(test.b);//b在运行期被初始化,一旦初始化后就不会改变
		System.out.println(test2.b);//b在运行期被初始化,一旦初始化后就不会改变

		System.out.println(test.staticb);//staticb在运行期被初始化,一旦初始化后就不会改变
		System.out.println(test2.staticb);//staticb在运行期被初始化,一旦初始化后就不会改变 与b不同的是加了static关键字所以test2.staticb值与test.b值一样
		
		//test.p = new Student(15);//编译不通过 ,对象句柄的指定后也是不可改变的,该句柄始终指向到一个具体的对象(就是之前初始化的那个对象)
		System.out.println(test.p.getAge());
		test.p.setAge(21);//但是值是指向的对象本身是可以改变的
		System.out.println(test.p.getAge());
	}
}

 

在《thinking in Java》中的中文翻译中这句话个人觉得是有问题的:无论static还是final字段,都只能存储一个数据,而且不得改变。

英文中是:A field that is both static and final  has only one piece of storage that   cannot be changed .

我的理解是只有final声明的字段是只能存储一个数据而且不得改变,事实上static单独声明的字段是可以改变数据的。英文的正确翻译应该是“字段同时由static和final声明后都只能存储一个数据,而且不得改变”。

 

final方法:

之所以要使用final方法,可能是出于对两方面理由的考虑。第一个是为方法“上锁”,防止任何继承类改变它的本来含义。设计程序时,若希望一个方法的行为在继承期间保持不变,而且不可被覆盖或改写,就可以采取这种做法。

class FinalFather  {
	public final void say(String str){
		System.out.println(str);
	}
}

public class FinalTest extends FinalFather{
	public void say(String str){//编译不通过
		System.out.println(str);
	}
}

 采用final方法的第二个理由是程序执行的效率。只要编译器发现一个final方法调用,就会(根据它自己的判断)忽略为执行方法调用机制而采取的常规代码插入方法(将自变量压入堆栈;跳至方法代码并执行它;跳回来;清除堆栈自变量;最后对返回值进行处理)。相反,它会用方法主体内实际代码的一个副本来替换方法调用。这样做可避免方法调用时的系统开销。当然,若方法体积太大,那么程序也会变得雍肿,可能受到到不到嵌入代码所带来的任何性能提升。因为任何提升都被花在方法内部的时间抵消了。Java编译器能自动侦测这些情况,并颇为“明智”地决定是否嵌入一个final方法。然而,最好还是不要完全相信编译器能正确地作出所有判断。通常,只有在方法的代码量非常少,或者想明确禁止方法被覆盖的时候,才应考虑将一个方法设为final。

 

final类:

 

如果说整个类都是final(在它的定义前冠以final关键字),就表明自己不希望从这个类继承,或者不允许其他任何人采取这种操作。换言之,出于这样或那样的原因,我们的类肯定不需要进行任何改变;或者出于安全方面的理由,我们不希望进行子类化(子类处理)。
将类定义成final后,结果只是禁止进行继承——没有更多的限制。然而,由于它禁止了继承,所以一个final类中的所有方法都默认为final,但是成员变量默认是没加的。因为此时再也无法覆盖它们。所以与我们将一个方法明确声明为final一样,编译器此时有相同的效率选择。

final class FinalFather  {
	//final int a = 5;
	int a = 5;
	public  void say(String str){//默认也是带有final的
		System.out.println(str);
	}
}

//class FinalChild extends FinalFather{}//编译不通过

public class FinalTest{
	public static void main(String[] args) {
		FinalFather f = new FinalFather();
		f.a=10;//如果把FinalFather中的a声明为final则编译不通过说明final类的成员变量是没有加自动加上fianl的只有方法自动加上了final限制
		System.out.println(f.a);
	}
}

 

设计一个类时,往往需要考虑是否将一个方法设为final。可能会觉得使用自己的类时执行效率非常重要,没有人能覆盖自己的方法。这种想法在某些时候是正确的。但要慎重作出自己的假定。通常,我们很难预测一个类以后会以什么样的形式再生或重复利用。常规用途的类尤其如此,若将一个方法定义成final,就杜绝了在其他程序员的项目中对自己的类进行继承的途径。



 

 

1
2
分享到:
评论
2 楼 hegang 2012-09-11  
总结的不错啊
1 楼 eve 2012-09-03  
both and 跟 either or都分不清的人 怎么翻译书的哇 what a daddy trap!!!

相关推荐

    回望期权.doc

    回望期权 回望期权是一种强路径依赖期权,其收益依赖于期权有效期内标的资产的价格的最大值或最小值。一般分为回望看涨期权和回望看跌期权。 回望期权的概述: 回望期权是一种期权 phái生工具,它的价值取决于...

    java面试多线程高并发相关回家技巧(超经典)

    3. **线程同步**:为了解决多线程环境中的数据安全问题,Java提供了synchronized关键字、volatile变量、Lock接口(如ReentrantLock)等同步机制。 二、线程控制 1. **线程的启动、暂停与停止**:start()方法启动...

    java面试常问基础知识总结(超经典)

    5. **异常处理**:Java中的异常分为检查异常和运行时异常,理解如何使用try-catch-finally语句块进行捕获和处理,以及如何声明和抛出异常,是面试中常见的问题。 6. **IO流**:Java的IO系统包括字节流(InputStream...

    55个手机JAVA全屏触屏游戏

    今天,当我们回望过去,55个手机JAVA全屏触屏游戏的集合,不仅仅是对那个时代的怀旧,更是对技术发展和移动游戏历史的一次致敬。 这些游戏之所以特别,首先因为它们是基于JAVA平台开发的。JAVA(Java 2 Platform, ...

    跳-扩散价格过程下有交易成本的回望期权定价研究

    本文研究了在跳-扩散价格过程背景下,交易成本对回望期权定价的影响。在金融市场中,期权是一种重要的衍生金融工具,它给予持有者在特定时间以特定价格买卖标的资产的权利。根据到期日执行时间的不同,期权分为欧式...

    回望专用记事本源码.e.rar

    5. 高亮显示:为了增强代码可读性,源码可能实现了对不同编程语言关键字的高亮显示,这涉及到字符串匹配和颜色设置。 6. 其他功能:如自动缩进、括号匹配、代码折叠等,这些都是提高编码效率的重要特性。 通过学习...

    shout.m_回望期权_SHOUT_shoutoption_

    描述中提到“回望期权的定价”,这暗示了该程序可能实现了某种期权定价模型,可能是经典的Black-Scholes模型的扩展,也可能是更适用于回望期权的其他模型,如Fleming-Wilmott模型或Rogers-Shepp模型。这些模型通常...

    Java笔试(超经典)时可能出现问题及其答案

    3. **类与对象**:面向对象编程的三大特征——封装、继承和多态在Java中得到充分体现。理解类的定义、对象的创建,以及如何使用构造函数是基础,而接口和抽象类的运用则是进阶内容。 4. **封装**:封装是将数据和...

    Java面试SQL查询语句突击练习

    在Java中,我们通常通过JDBC(Java Database Connectivity)接口来与MySQL交互。以下是一些关键的SQL知识点: 1. **基本查询**:包括SELECT语句,用于从表中获取数据。如`SELECT * FROM table_name`可以获取表中的...

    高中历史之图说历史回望太平天国素材

    高中历史之图说历史回望太平天国素材

    monte carlo_optionpricing_回望期权_障碍期权_

    本文将详细探讨蒙特卡洛方法在奇异期权,特别是美式期权、回望期权和障碍期权定价中的应用。 首先,我们要理解什么是蒙特卡洛方法。这是一种基于随机抽样或统计试验的计算方法,用于解决那些在解析解难以求得或者...

    完整版回望专用记事本源码.rar

    - “回望专用记事本”可能是用C++、C#、Java或Python等常见编程语言编写的,每个语言都有其特定的编程风格和库。源码中可能使用了常见的GUI库,如Qt、wxWidgets或WinAPI,来创建图形用户界面。 4. **文本处理技术*...

    回望专用记事本源码.rar

    5. **编程语言和框架**:根据源码的扩展名,我们可以推测其使用的编程语言,如C++、Java、Python或C#等。此外,可能还使用了特定的框架或库来加速开发,如Qt、wxWidgets或.NET Framework。 深入研究《回望专用...

    餐饮行业深度分析-回望美国餐饮历程,看中国餐饮发展趋势.docx

    餐饮行业深度分析-回望美国餐饮历程,看中国餐饮发展趋势.docx

    Java,Jvm 的设计理念和Android的基础和贯彻

    回望Java——设计为Jvm一体的特性,Java是Jvm与编程人员的或窃窃私语,或雅望高谈(不过由于Java与Jvm背负了太多历史,GraalVM出来探路) Jvm的设计理念 平台无关性 和 语言无关性 Android设计理念 全面贴合TCP/IP...

    完整版回望专用记事本源码.e.rar

    《回望专用记事本源码》是一款专为回顾与学习设计的文本编辑器源代码。这个压缩包中包含了该记事本应用的全部源代码,对于编程爱好者和IT从业者来说,是一个很好的学习和研究资源。以下是这款记事本源码的一些关键...

    CPU历史之旅--回望过去的脚步.mht

    CPU历史之旅--回望过去的脚步.mht CPU历史之旅--回望过去的脚步.mht CPU历史之旅--回望过去的脚步.mht CPU历史之旅--回望过去的脚步.mht CPU历史之旅--回望过去的脚步.mht

Global site tag (gtag.js) - Google Analytics