final修饰的变量

被final修饰的实例变量必须显式指定初始值,而且只能在如下3个位置指定初始值.

1.定义final实例变量时指定初始值;

2.在非静态初始化块中为final实例变量指定初始值;

3.在构造器中为final实例变量指定初始值.

对于普通实例变量,Java程序可以对它执行默认的初始化,也就是将实例变量的值指定为默认的初始值0或null;但对于final实例变量,则必须由程序显式指定初始值.

package com.lic.array;

public class FinalInstanceVaribaleTest {
        
    public static void main(String[] args) {
        FinalInstanceVaribaleTest fiv = new FinalInstanceVaribaleTest();
        System.out.println(fiv.var1);
        System.out.println(fiv.var2);
        System.out.println(fiv.var3);
    } 

    // 定义final实例变量时赋初始值
    final int var1 = "疯狂Java讲义".length();
    final int var2;
    final int var3;
    // 在初始化块中为var2赋初始值
    {
        var2 = "轻量级Java EE企业应用实战".length();
    }
    // 在构造器中为var3赋初始值
    public FinalInstanceVaribaleTest(){
        this.var3 = "疯狂XML讲义".length();
    }
}
上面的程序定义了3个final修饰的实例变量.var1,var2,var3,分别在定义是时为var1赋初始值,在初始化块中为var2指定初始值,在构造器中为var3指定初始值.但是经过编译器的处理,这三种方式都会抽取到构造器中赋初始值.如果使用javap工具来分析:

Compiled from "FinalInstanceVaribaleTest.java"
public class com.lic.array.FinalInstanceVaribaleTest {
  final int var1;

  final int var2;

  final int var3;

  public static void main(java.lang.String[]);
    Code:
       0: new           #1                  // class com/lic/array/FinalInstance
VaribaleTest
       3: dup
       4: invokespecial #2                  // Method "<init>":()V
       7: astore_1
       8: getstatic     #3                  // Field java/lang/System.out:Ljava/
io/PrintStream;
      11: aload_1
      12: getfield      #4                  // Field var1:I
      15: invokevirtual #5                  // Method java/io/PrintStream.printl
n:(I)V
      18: getstatic     #3                  // Field java/lang/System.out:Ljava/
io/PrintStream;
      21: aload_1
      22: getfield      #6                  // Field var2:I
      25: invokevirtual #5                  // Method java/io/PrintStream.printl
n:(I)V
      28: getstatic     #3                  // Field java/lang/System.out:Ljava/
io/PrintStream;
      31: aload_1
      32: getfield      #7                  // Field var3:I
      35: invokevirtual #5                  // Method java/io/PrintStream.printl
n:(I)V
      38: return

  public com.lic.array.FinalInstanceVaribaleTest();
    Code:
       0: aload_0
       1: invokespecial #8                  // Method java/lang/Object."<init>":
()V
       4: aload_0
       5: ldc           #9                  // String 疯狂Java讲义
       7: invokevirtual #10                 // Method java/lang/String.length:()
I
      10: putfield      #4                  // Field var1:I
      13: aload_0
      14: ldc           #11                 // String 轻量级Java EE企业应用实战
      16: invokevirtual #10                 // Method java/lang/String.length:()
I
      19: putfield      #6                  // Field var2:I
      22: aload_0
      23: ldc           #12                 // String 疯狂XML讲义
      25: invokevirtual #10                 // Method java/lang/String.length:()
I
      28: putfield      #7                  // Field var3:I
      31: return
}
从上面分析结果可以看出:final实例变量必须显式地被赋初始值,而且本质上final实例变量只能在构造器中被赋初始值.当然,就程序员变成来说,还可在定义final实例变量时指定初始值,也可以初始化块中为final实例变量指定初始值,但它们本质上是一样的.除此之外,final实例变量将不能被再次赋值.

对于final类变量而言,同样必须显式指定初始值,而且final类变量只能在2个地方指定初始值:

1.定义final类变量时指定初始值;

2.在静态初始化块中为final类变量指定初始值.

package com.lic.array;

public class Demo22 {

	// 定义final类变量时赋初始值
	final static int var1 = "疯狂Java讲义".length();
	final static int var2;
	// 在静态初始化块中为var2赋初始值
	static{
		var2 = "轻量级Java EE企业应用实战".length();
	}
	public static void main(String[] args) {
		System.out.println(Demo22.var1);
		System.out.println(Demo22.var2);
	}

}
上面程序中定义了2个final类变量var1和var2,在定义var1时为其赋初始值,在静态初始化块中为var2指定初始值.需要指出的是,经过编译器的处理,这2中方式都会被抽取到静态初始化块中赋初始值.如果使用javap工具来肥西改程序:

Compiled from "Demo22.java"
public class com.lic.array.Demo22 {
  static final int var1;

  static final int var2;

  public com.lic.array.Demo22();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":
()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: getstatic     #2                  // Field java/lang/System.out:Ljava/
io/PrintStream;
       3: getstatic     #3                  // Field var1:I
       6: invokevirtual #4                  // Method java/io/PrintStream.printl
n:(I)V
       9: getstatic     #2                  // Field java/lang/System.out:Ljava/
io/PrintStream;
      12: getstatic     #5                  // Field var2:I
      15: invokevirtual #4                  // Method java/io/PrintStream.printl
n:(I)V
      18: return

  static {};
    Code:
       0: ldc           #6                  // String 疯狂Java讲义
       2: invokevirtual #7                  // Method java/lang/String.length:()
I
       5: putstatic     #3                  // Field var1:I
       8: ldc           #8                  // String 轻量级Java EE企业应用实战
      10: invokevirtual #7                  // Method java/lang/String.length:()
I
      13: putstatic     #5                  // Field var2:I
      16: return
}
上面程序为final类变量赋初始值.可以看到,var1,var2两个类变量的赋初始值过程都是放在静态初始化块内完成的.由此可见,final类变量必须显式地被赋初始值,而且本质上final实例变量只能在静态初始化块中被赋初始值.当然就程序员编程来说,还可在定义final类变量时指定初始值.也可以在静态初始化块中为final类变量指定初始值,但它们本质上是一样的.除此之外,final类变量将不能被再次赋值.

final修饰局部变量的情形则比较简单----Java本来就要求局部变量必须被显式地赋初始值,final修饰的局部变量一样需要被显式的赋初始值.与普通初始变量不同的是:final修饰的局部变量被赋初始值之后,以后再也不能对final局部变量重新赋值.

经过上面介绍,大致可以发现final修饰符的第一个简单功能:被final修饰的变量一旦被赋初始值,final变量的值以后将不会被改变.

除此之外,final修饰符还有一个功能.

package com.lic.array;

public class Demo23 {

	public static void main(String[] args) {
		// 通过Price的INSTANCE访问currentPrice实例变量
		System.out.println(Price_23.INSTANCE.currentPrice);
		// 显式创建Price实例
		Price_23 p = new Price_23(2.8);
		// 通过显式创建的Price实例访问currentPrice实例变量
		System.out.println(p.currentPrice);
	}

}
class Price_23{
	// 类成员是Price实例
	final static Price_23 INSTANCE = new Price_23(2.8);
	// 再定义一个类变量
	final static double initPrice = 20;
	// 定义该Price的currentPrice实例变量
	double currentPrice;
	public Price_23(double discount){
		// 根据静态变量计算实例变量
		currentPrice = initPrice - discount;
	}
}
你猜输出啥?

很明显,这是程序中增加了final修饰符的缘故.再次使用javap工具来分析下:

Compiled from "Demo23.java"
class com.lic.array.Price_23 {
  static final com.lic.array.Price_23 INSTANCE;

  static final double initPrice;

  double currentPrice;

  public com.lic.array.Price_23(double);
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: aload_0
       5: ldc2_w        #3                  // double 20.0d
       8: dload_1
       9: dsub
      10: putfield      #5                  // Field currentPrice:D
      13: return

  static {};
    Code:
       0: new           #2                  // class com/lic/array/Price_23
       3: dup
       4: ldc2_w        #6                  // double 2.8d
       7: invokespecial #8                  // Method "<init>":(D)V
      10: putstatic     #9                  // Field INSTANCE:Lcom/lic/array/Price_23;
      13: return
}
如果不使用final修饰程序中的initPrice类变量,看看javap的分析结果
Compiled from "Demo23.java"
class com.lic.array.Price_23 {
  static final com.lic.array.Price_23 INSTANCE;

  static double initPrice;

  double currentPrice;

  public com.lic.array.Price_23(double);
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: aload_0
       5: getstatic     #2                  // Field initPrice:D
       8: dload_1
       9: dsub
      10: putfield      #3                  // Field currentPrice:D
      13: return

  static {};
    Code:
       0: new           #4                  // class com/lic/array/Price_23
       3: dup
       4: ldc2_w        #5                  // double 2.8d
       7: invokespecial #7                  // Method "<init>":(D)V
      10: putstatic     #8                  // Field INSTANCE:Lcom/lic/array/Price_23;
      13: ldc2_w        #9                  // double 20.0d
      16: putstatic     #2                  // Field initPrice:D
      19: return
}
对比上面两个输出结果,不难发现当使用final修饰符变量时,如果定义该final类变量时指定了初始值,而且该初始值可以在编译时就被确定下来,系统将不会在静态初始化块中对该类变量赋初始值,而将是在类定义中直接使用该初始值代替该final变量.

对于一个使用final修饰的变量而言,如果定义该final变量时就指定初始值,而且这个初始值可以在编译时就确定下来,那么这个final变量将不再是一个变量,系统会将其当成"宏变量"处理.也就是说,所有出现该变量的地方,系统将直接把它当成对应的值处理.

对于上面的Price类而言,由于使用了final关键字修饰initPrice类变量,因此Price类的构造器中执行currentPrice = initPrice - discount; 代码时,程序直接会将initPrice替换成20.因此,执行该代码的效果相当于currentPrice = 20 - discount;.

  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Zerlinda_Li

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值