java final 关键字的使用,第一部分 final 数据(final data)

本文详细介绍了Java中的final关键字,讲解了其用于数据变量时的功能和使用场景。final可以用于声明常量,提高编译效率,并确保对象引用不变。通过实例展示了final修饰基本类型、对象引用及数组的区别,帮助读者深入理解final关键字在Java编程中的应用。
摘要由CSDN通过智能技术生成

Java 的基础知识,final 关键字。想毕从事 Java 开发工作的朋友对 final 关键字都不会陌生,但是对它有非常全面清晰认识的,我觉得不多。final 有哪些功能,为何要使用 final 数据,何时使用 final 数据呢?可以带着疑问来看此文。内容比较细致,可能有些啰嗦,请耐心的看完,希望能对大家有所帮助,读后有所解惑。

Java 的 final 关键字根据上下文应用场景的不同而含义略有不同,但总的来说,用它修饰定义,则表示被修饰的目标不可更改。通常出于两个原因想要禁止目标的更改:设计或效率。因为这两个原因完全不同,所以可能会误用 final 关键字。

Java 程序编写中可以使用 final 关键字修饰的三类目标:数据方法

请注意,此文我仅讲述对 final 关键字对数据变量定义的修饰使用。

像许多其他编程语言一样,java 编程语言也有方式告诉编译器一份数据是“常数”,就是所谓的常量。之所以使用常量,看重的是它本身的特点,有两个原因:

它可以是一个永远不会改变的编译时常量。

它可以是在运行时初始化且不可再更改的值。

当你有想让你程序里的数据满足上述两种情况的需求时,就可以使用 final 关键字了。

对于编译时常量,允许编译器将常量值“封装”到使用它的任何计算中;也就是说,编译时常量,在编译期间就可以计算使用,这样可以节省一些运行时的开销。在 Java 编程语言中,编译时常量的数据类型, 必须是 final 关键字修饰的基本数据类型。在进行定义时必须给一个初始值。

当同时使用 static 和 final 两关键字修饰定义一个变量时,表示这个变量在整个程序中,只有一份数据存储,是不可变的。这里 static 强调只有一份,final 表示常量,不可变。~有点 C 语言静态数据的味道~

Java 语言中的变量,包括类对象变量、基本数据类型变量、数组变量等,抽象概括的说就是两种:一种称为类对象变量,另一种基本数据类型变量。

当用 final 关键字修饰定义变量的数据类型是类类型,而不是基本数据类型时,此时的语义容易混淆,最终结果是,前者将对象引用对象引用,可理解为访问对象的地址。类似地,windows c++系统开发中,也称之为句柄。)定义成了常数,便于理解可以叫对象引用常量,后者将变量值定义成了常数。这里,一旦此对象引用被初始化指向一个对象,它将永远不再被允许指向其他的对象,但是对象本身的成员数据是可以改变的。Java 没有提供一种使得任意对象本身是常量的方法(但我们可以自己编写代码,达到让对象本身是常量的效果)。这里注意,数组类型数据,加 final 关键字定义,成为常量的部分是对象引用,因为数组同样可以理解是类类型,不是基本数据类型。

最好通过例子来体会一下,

import java.util.*;
class Value {
  int i; // 包内可访问
  public Value(int i) { 
    this.i = i; 
  }
}

public class FinalData {
  private static Random rand = new Random(47);
  private String id;
  public FinalData(String id) { 
    this.id = id; 
  }
  // 可以是编译时常量:
  private final int valueOne = 9;
  private static final int VALUE_TWO = 99;
  // 典型的公共常量:
  public static final int VALUE_THREE = 39;
  // 不能是编译时常量:
  private final int i4 = rand.nextInt(20);
  static final int INT_5 = rand.nextInt(20);
  private Value v1 = new Value(11);
  private final Value v2 = new Value(22);
  private static final Value VAL_3 = new Value(33);
  // 数组:
  private final int[] a = { 1, 2, 3, 4, 5, 6 };
  public String toString() {
    return id + ": " + "i4 = " + i4 + ", INT_5 = " + INT_5;
  }
public static void main(String[] args) {
    FinalData fd1 = new FinalData("fd1");
    //! fd1.valueOne++; // 错误: 不能改变值
    fd1.v2.i++; // 不是常量!
    fd1.v1 = new Value(9); // OK -- not final
    for(int i = 0; i < fd1.a.length; i++) {
      fd1.a[i]++; // 不是常量!
    }
    //! fd1.v2 = new Value(0); // 错误: 不能改变对象引用
    //! fd1.VAL_3 = new Value(1); // 错误: 不能改变对象引用
    //! fd1.a = new int[3]; // 错误: 不能改变对象引用
    System.out.println(fd1);
    System.out.println("创建新的 FinalData 对象");
    FinalData fd2 = new FinalData("fd2");
    System.out.println(fd1);
    System.out.println(fd2);
  }
} 
/* Output:
fd1: i4 = 15, INT_5 = 18
创建新的 FinalData 对象
fd1: i4 = 15, INT_5 = 18
fd2: i4 = 13, INT_5 = 18
*///:~

看一下程序中的 final 关键字的各种使用情况:

由于 valueOne 和 VALUE_TWO 是具有编译时值的最终原语,因此它们都可以用作编译时常量,并且在任何重要方面都没有区别。VALUE_THREE 是定义此类常量的更典型方式:public 表示它们可以在包外使用,static 强调只有一个,final 表示它是一个常量。请注意,具有恒定初始值(即编译时常量)的最终静态原语按照约定以全部大写字母命名,单词之间用下划线分隔。 (这就像 C 常量一样。)

有 final 的定义,并不意味着它的值在编译时就是已知的。这通过在运行时使用随机生成的数字初始化 i4 和 INT_5 可以来说明。示例的这一部分还显示了将最终值设为静态或非静态之间的区别。这种差异仅在运行时初始化值时才会出现,因为编译器编译时对值的处理方式是相同的。运行程序时才会显示差异。请注意, 对 fd1 和 fd2 来说, i4 的值是分别唯一的,就是说, fd1 的 i4 的值是唯一的, fd2 的 i4 的值是唯一的,但因 static 的原因 INT_5 的值不会通过创建第二个 FinalData 对象而更改。这是因为它是静态的,并且在加载时只初始化一次,而不是每次创建新对象都进行初始化。

变量 v1 到 VAL_3 展示了 final 引用的含义。正如你在 main() 中看到的,仅仅因为 v2 是 final 并不意味着你不能改变它的值。因为此出实际上 final 的是一个对象引用,所以 final 意味着不能将 v2 重新绑定到新对象,但是可以改变v2 对象本身。还可以看到,同样的语义对数组也是一样的,可以理解它只是另一种的对象引用

通过上面的例子演示,是不是更容易理解一些。多动动手~

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值