谈谈Java中的final关键字

前言

本文内容参考自《Thinking in Java》。

final数据

在Java中,编译期常量必须是基本数据类型,并且以关键字final表示。在对这个常量进行定义的时候,必须对其进行赋值。

对于基本类型,final使数值恒定不变;而用于对象引用,final使引用恒定不变。一旦引用被初始化指向一个对象,就无法再把它改为指向另一个对象。然而对象其自身却是可以被修改的。

public class FinalData {
	private final int valueOne = 9;
	private static final VALUE_TWO = 99;
	public static final VALUE_THREE = 39;
}

由于valueOneVALUE_TWO都是带有编译时数值的final基本类型,所以它们二者均可以用作编译期常量,并且没有重大区别。VAL_THREE是一种更加典型的对常量进行定义的方式:定义为public,则可以被用于包之外;定义为static,则强调只有一份;定义为final,则说明它是一个常量。请注意,带有恒定初始值(即,编译期常量)的static final基本类型全用大写字母命名,并且单词与单词之间用下划线隔开(这就像C常量一样,C常量是这一命名传统的发源地)。

public class FinalData {
	private static Random rand = new Random(47);
	private final int i4 = rand.nextInt(20);
	static final int INT_5 = rand.nextInt(20); 
}

我们不能因为某数据是final的就认为在编译时可以知道它的值。在运行时使用随机生成的数值来初始化i4INT_5就说明了这一点。

空白final

Java允许生成“空白final”,所谓空白final是指被声明为final但又未给定初值的域。无论什么情况,编译器都确保空白final在使用前必须被初始化。但是,空白final在关键字final的使用上提供了更大的灵活性,为此,一个类中的final域就可以做到根据对象而有所不同,却又保持其恒定不变的特性。

class Poppet {
	private int i;
	Poppet(int ii) {
		i = ii;
	}
}
public class BlankFinal {
	private final int i = 0;	// Initialized final
	private final int j;	// Blank final
	private final Poppet p;	// Blank final reference
	public BlankFinal() {
		j = 1;	// Initialize blank final 
		p = new Poppet(1);	// Initialize blank final reference
	}
	public BlankFinal(int x) {
		j = x;	// Initialize blank final 
		p = new Poppet(x);	// Initialize blank final reference
	}
	public static void main(String[] args) {
		new BlankFinal();
		new BlankFinal(47);
	}
}

必须在域的定义处或者每个构造器中用表达式对final进行赋值,这正是final域在使用前总是被初始化的原因所在。

final参数

Java允许在参数列表中以声明的方式将参数指明为final。这意味着你无法在方法中更改参数引用所指向的对象。

class Gizmo {
	public void spin() {}
}
public class FinalArguments {
	void with(final Gizmo g) {
		//! g = new Gizmo();	// Illegal -- g is final
	} 
	void without(Gizmo g) {
		g = new Gizmo();	// OK -- g not final
		g.spin();
	}
	void f(final int i) {
		//! i++	// Can't change
	}
	int g(final int i) {
		return i + 1;
	}
	public static void main(String[] args) {
		FinalArguments bf = new FinalArguments();
		bf.without(null);
		bf.with(null);
	}
}

方法f()g()展示了当基本类型的参数被指明为final所出现的结果:你可以读参数,但却无法修改参数。这一特性主要用来向匿名内部类传递数据。

final方法

使用final方法的原因有两个。第一个原因是把方法锁定,以防止任何继承类修改它的含义。这是出于设计的考虑:想要确保在继承中使方法行为保持不变,并且不会被覆盖。过去建议使用final方法的第二个原因是效率。在Java的早期实现中,如果将一个方法指明为final,就是同意编译器将针对该方法的所有调用都转为内嵌调用。这将消除方法调用的开销。现在已经不再需要使用final方法来进行优化了。

final和private关键字

类中所有的private方法都隐式地指定为是final的。由于无法取用private方法,所以也就无法覆盖它。可以对private方法添加final修饰词,但这并不能给方法增加任何额外的意义。

final类

当用final修饰⼀个类时,表明这个类不能被继承。final类中的所有方法都会被隐式地指定为final方法。在final类中可以给方法添加final修饰词,但这不会增添任何意义。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值