【Java基础提高】深入分析final关键字(一)

 Java的关键字final通常是指被它修饰的数据是不能被改变的,不想改变可能出于两种理由:设计或效率。以下是本文主要大纲:

  • final数据的使用
  • final参数的使用
  • final方法的使用
  • final类不能被继承

 一、final数据

   1.1 final修饰变量   

  • 修饰的变量必须是基本数据类型:告知编译器这一块数据是不变的,这样可以在执行计算时,减少一些运行时的负担。
  • 无论什么情况,final修饰的变量必须初始化。

package com.game.lll;

import java.util.Random;

public class FinalT {
	private static Random random = new Random(20);
	private final int VALUE_A = 10;
	private static final int VALUE_B = 20;
	public static final int VALUE_C = random.nextInt(10);
	public  final int VALUE_D = random.nextInt(10);
	public static void main(String[] args) {
		FinalTest test = new FinalTest();
		//test.VALUE_A = 5;//Error:不可以这样做
		//test.VALUE_B  =21;//Error:不可以这样做
		
		FinalT finalT = new FinalT();
		FinalT finalT1 = new FinalT();
		
		System.out.println("VALUE_C:"+VALUE_C);
		System.out.println("VALUE_C:"+finalT.VALUE_C);
		System.out.println("VALUE_C:"+finalT1.VALUE_C);
		System.out.println("---------");
		System.out.println("VALUE_D:"+finalT.VALUE_D);
		System.out.println("VALUE_D:"+finalT1.VALUE_D);
	}
	
}

运行结果:
+------------------第13、15行编译错误-------------------------------+
The final field FinalTest.a cannot be assigned
+--------------------static final ---------------------------------+

VALUE_C:3

VALUE_C:3

VALUE_C:3

+-----------------------final--------------------------------------+

VALUE_D:6

VALUE_D:1

+------------------------------------------------------------------+

  以上结果得出一点我们不能因为某数据时final的就认为在编译时可以知道它的值。在运行时使用随机生成的VALUE_C和VALUE_D说明了这一点。示例代码展示了final数值定义为static和非static的区别。此区别只有当数值在运行时内被初始化时才会显现,这是因为编译器对编译时数值一视同仁(并且他们可能因为优化而消失)。使用static final 的数值是不可以通过一个新的对象而改变的。这是因为在装载时已经被初始化,而不是每次创建新对象时初始化。

   1.2 final用于引用

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

package com.game.lll;

public class FinalTest {
	private final int a = 10;
	
	private final Value v1 = new Value(10);
	

	public static void main(String[] args) {
		FinalTest test = new FinalTest();
		//test.a = 5;//不可以这样做
		test.v1.value++;
		//test.v1 = new Value(12);//Error:不可以这样做
		System.out.println("对象内的数据:"+test.v1.value);
	}

	class Value{
		int value; 
		public Value(int value) {
			this.value = value;
		}
	}
}
运行结果:
+------------------------------------------------------------------+
对象内的数据:11
+------------------------------------------------------------------+

   1.3 final用于数组

  final用于数组和引用时一样的,数组只不过是另一种引用,对于这个变量的引用是不能被重新赋值,但是对象本身是可以修改的。代码如下:

package com.game.lll;  

public class FinalTest {  
	private final Value v1 = new Value(10);  

	private final int[] values = { 1, 2, 3, 4, 5, 6 };  

	public static void main(String[] args) {  
		FinalTest test = new FinalTest();  
		//test.a = 5;//不可以这样做  
		test.v1.value++;  
		//test.v1 = new Value(12);//Error:不可以这样做
		test.values[0] = 100;//对象本身是可以修改的  
		for(int i = 0; i < test.values.length;i++){  
			System.out.println(test.values[i]++);//对于这个变量的引用是不能被重新赋值  
		}  
	}  
	
	class Value{  
		int value;   
		public Value(int value) {  
			this.value = value;  
		}  
	}  
}  
运行结果:
+------------------------------------------------------------------+

100 2 3 4 5 6

+------------------------------------------------------------------+

   1.4 空白final

  Java允许生成“空白final”,空白final是指被声明为final但又给未定初值的域。代码如下:

package com.game.lll;

public class BlankFinal {
	private final int a;
	
	public BlankFinal(int i)
	{
		this.a = i;
	}
	
	
	public static void main(String[] args) {
		BlankFinal blankFinal = new BlankFinal(5);
		System.out.println(blankFinal.a);
	}
}
  虽然未对a直接赋值,但是在构造函数中对a进行了初始化。所以无论什么情况,都要确保final在使用前被初始化。

 二、final参数

   2.1final参数用在普通类中

  Java允许在函数参数列表中使用final。不过在使用final后你不能更改参数引用所指向的对象,但是可以修改对象里面的值。代码如下:

package com.game.lll;

public class FinalArguments {

	private Person person = new Person(3);
	
	private void run(final Person p)
	{
		//p = new Person();//Error 不要这样做
		
		System.out.println("修改前a="+p.a);
		p.a = 6;
		System.out.println("修改后a="+p.a);
	}

	public static void main(String[] args) {
		FinalArguments arguments = new FinalArguments();
		arguments.run(arguments.person);
	}

	 class Person{
		 int a;
		public Person(int a)
		{
			this.a = a;
		}
	}
}
运行结果:
+------------------------------------------------------------------+

修改前a=3

修改后a=6

+------------------------------------------------------------------+

   2.2 final参数用在匿名内部类中

  如果定义一个匿名内部类,并且希望它使用一个在其外部定义的对象,那么编译器会要求其参数引用是final的。

package com.game.lll;

/**
 * final在匿名内部类的使用
 * @author liulongling
 *
 */
public class FinalInClass{
	
	public abstract class AbstractFinalClass<Result> {
		protected abstract Result onExecute(int a);

		public Result execute(int a)
		{
			return this.onExecute(a);
		}
	}
	
	private Integer a(final int value)
	{
		return new AbstractFinalClass<Integer>() {
			@Override
			protected Integer onExecute(int a) {
				a = value;
				return a;
			}
		}.execute(3);
	}
	
	public static void main(String[] args) {
		FinalInClass inClass = new FinalInClass();
		System.out.println(inClass.a(2));
	}
}

  三、final方法

   3.1 final和private关键字

  使用final方法的原因是把方法锁定,以防任何继承类修改它的含义。同时如果父类中的这个方法使用了private那么在子类也是不能重写这个方法。代码示例如下:

package com.game.lll;

public class FinalOverriding {
	class WithFinals {
		private final void a()
		{
			System.out.println("WithFinals a");
		}
		
		private void b()
		{
			System.out.println("WithFinals b");
		}
	}
	
	
	class OverridingFinal extends WithFinals{
		private final void a()
		{
			System.out.println("OverridingFinal a");
		}
		
		private void b()
		{
			System.out.println("OverridingFinal b");
		}
	}
	
	class OverridingFinal2 extends OverridingFinal{
		private final void a()
		{
			System.out.println("OverridingFinal2 a");
		}
		
		private void b()
		{
			System.out.println("OverridingFinal2 b");
		}
	}
	
	private OverridingFinal2 of2 = new OverridingFinal2();
	
	public static void main(String[] args) {
		FinalOverriding finalOverriding = new FinalOverriding();
		finalOverriding.of2.a();
		finalOverriding.of2.b();
		
		OverridingFinal of = finalOverriding.of2;
		of.a();
		of.b();
		
		WithFinals wf = of;
		wf.a();
		wf.b();
		
		
	}
}
运行结果:
+------------------------------------------------------------------+

OverridingFinal2 a OverridingFinal2 b

OverridingFinal a OverridingFinal b

WithFinals a WithFinals b

+------------------------------------------------------------------+

  从结果上来看,子类OverridingFinal和OverridingFinal2都没有重写父类方法。在Java中类中private方法只能在供内部访问,所以子类是无法覆盖它。对private方法添加final和不加结果都是一样并没有任何意义。。下面对它做一些修改
  第一步:将WithFinals的方法a()改为public

public final void a()
{
	System.out.println("WithFinals a");
}

  第二部:将OverridingFinal的方法a()也改为public

public final void a()
{
	System.out.println("OverridingFinal a");
}
结果是编译错误:
+------------------------------------------------------------------+

Cannot override the final method from FinalOverriding.WithFinals

+------------------------------------------------------------------+

 

  将private修改为public后,结果证明使用final修饰的方法是不能被重写的。如果我们把OverridingFinal和OverridingFinal2的b()方法改为public呢?那么OverridingFinal2方法b将会覆盖OverridingFinal的方法b。运行结果如下:

运行结果:
+------------------------------------------------------------------+
OverridingFinal2 a
OverridingFinal2 b

OverridingFinal a OverridingFinal2 b

WithFinals a WithFinals b +------------------------------------------------------------------+

  四、final类

  当你将某个类定义为final class时,那么子类就无法继承该类。

本章参考文章和书籍
<<Thinking in Java>>


作者: 小毛驴,一个游戏人 
梦想:世界和平   
原文地址: http://blog.csdn.net/liulongling
若有错误之处,请多多谅解并欢迎批评指正。     
本博客中未标明转载的文章归作者 小毛驴所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

  • 6
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值