Java final关键字详解

在java中,final的含义在不同的场景下有细微的差别,但总体上来说,它指的是“这是不可变的”。下面,我们来讲final的几种主要用法。

final数据

在编写程序时,我们经常需要说明一个数据是不可变的,我们称为常量。在java中,用final关键字修饰的变量,只能进行一次赋值操作,并且在生存期内不可以再次赋值。更重要的是,final会告诉编译器,这个数据是不会修改的,那么编译器就可能会在编译时期就对该数据进行替换甚至执行计算,这样可以对我们的程序起到一点优化。不过在针对基本类型和引用类型时,final关键字的效果存在细微差别。我们来看下面的例子:

package s1;

public class L {
	public static void main(String[] args) {
		final int i1 = 1;  	//定义变量并直接赋常量,此时为编译时常量
		i1 = 11;			//编译出错,因为final数据只能赋值一次
		final int i2; 		//定义变量,但先不赋值;在后面需要的地方再赋值,此时不是编译时常量
		i2 = 2;		
		i2 = 12;			//编译出错,因为final数据只能赋值一次
		//此处i3虽然是final并赋值,但是赋的并不是常量,所以i3不是编译时常量
		final int i3 = (int) (Math.random() * 10);
		i3 = 13;			//编译出错,因为final数据只能赋值一次
		//上面部分是基本类型的例子,下面看看有关对象
		final Value value1 = new Value();
		value1 = new Value();	//编译出错,因为final数据只能赋值一次
		//下面这样是可以的,因为value1并没有改变(没有再次赋值),改变的仅仅是value1对象里面的value值
		value1.value = 1;		
		value1.value = 1;
	}
}

class Value {
	int value;
}

空白final

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

package s1;

public class L {
	public static void main(String[] args) {
		Value value1 = new Value(10);
		value1.maxValue = 20;	//编译出错,因为final数据只能赋值一次
		Value value2 = new Value(20);
	}
}

class Value {
	final int maxValue;
	Value(int value) {
		this.maxValue = value;
	}
}

编译期常量

对于编译期常量,编译器可以将该常量值带入任何可能用到它的计算式中,也就是说,可以在编译时执行计算,这减轻了一些运行时的负担。在Java中,这类编译期常量必须是基本数据类型(byte、char、short、int、long、float、double、boolean)或者字符串常量("abc"是字符串常量,new String("abc")不是字符串常量),并且以关键字final表示。在对于这个常量定义时,还必须对其进行赋值。如果一个变量赋予一个编译期常量,那么该变量也是编译期常量。

访问编译期常量是不会引起类的初始化,关于类的初始化可参考https://blog.csdn.net/GracefulGuigui/article/details/103856984,下面举个例子:

package s3;

public class A {
	public static void main(String[] args) {
		//请逐行运行,其余行注释掉,因为静态数据只初始化一次
		//int i1 = A1.I1;	//运行结果:A1 static block,因为Integer()不是基本数据类型也不是字符串常量,即I3不是编译期常量
		//int i2 = A1.I2;	//运行结果:		    ,因为I2是编译期常量,所以不初始化,即不执行静态变量的初始化和静态语句块
		//int i3 = A1.I3;	//运行结果:A1 static block,因为Math.random()不是基本数据类型也不是字符串常量,即I3不是编译期常量
		//String s1 = A1.S1;	//运行结果:		    ,因为S1是编译期常量,所以不初始化,即不执行静态变量的初始化和静态语句块
		//String s2 = A1.S2;	//运行结果:		    ,因为S1是编译期常量,所以S2也是编译期常量所以不初始化,即不执行静态变量的初始化和静态语句块
		//String s3 = A1.S3;	//运行结果:A1 static block,因为new String("abc")不是基本数据类型也不是字符串常量,即S3不是编译期常量
		//String s4 = A1.S4;	//运行结果:A1 static block,因为定义S4时候没有直接赋值,即S4不是编译期常量
	}
}

class A1 {
	static final int I1 = new Integer(1);
	static final int I2 = 2;
	static final int I3 = (int) Math.random();
	static final String S1 = "abc";
	static final String S2 = S1;
	static final String S3 = new String("abc");
	static final String S4;
	static {
		S4 = "abc";
		System.out.print("A1 static block");
	}
}

final方法

使用final方法的原因主要是把方法锁起来,以防止继承者修改它的含义。这是出于设计的考虑:想要确保在继承中使方法行为保持不变,并且不会被覆盖。例子:

package s1;

public class L {
	public static void main(String[] args) {
		
	}
}

class L1 {
	final void calculate() {
		System.out.println(getClass().getSimpleName() + "calculate");
	}
}

class L2 extends L1 {
	void calculate() {    //编译出错,不能覆盖L1中的calculate方法,因为它是final的
		
	}
}

final和private关键字

类中的所有private方法都隐式地指定为final方法。由于子类无法调用访问父类的private方法,所以也就无法覆盖它,也就无法实现多态(关于多态,可以参考https://blog.csdn.net/GracefulGuigui/article/details/103869327)。可以对private方法添加final修饰词,但这并不会给该方法增加任何额外的意义。例子:

package s1;

public class L {
	public static void main(String[] args) {
		L1 l1 = new L2();
		l1.test();
	}
}

class L1 {
	void test() {
		calculate1();
		calculate2();
	}
	private final void calculate1() {
		System.out.println("L1 calculate1");
	}
	void calculate2() {
		System.out.println("L1 calculate2");
	}
}

class L2 extends L1 {
        // 不会报错,虽然L1中calculate1方法是final,但是private限制了L2无法访问L1的calculate1,所以可以起同名+同参数列表的方法
	void calculate1() {
		System.out.println("L2 calculate1");
	}
	void calculate2() {
		System.out.println("L2 calculate2");
	}
}

运行结果:

L1 calculate1    //由于是private方法,不支持多态
L2 calculate2    // 因为多态,调用的实际是L2的calculate2

final类

final类比较简单易懂,被final修饰的类,则表明该类无法被其它类继承。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值