深入解析Java编程中final关键字的使用_java

在Java中声明属性、方法和类时,可使用关键字final来修饰。final变量即为常量,只能赋值一次;final方法不能被子类重写;final类不能被继承。
1.final成员
声明 final 字段有助于优化器作出更好的优化决定,因为如果编译器知道字段的值不会更改,那么它能安全地在寄存器中高速缓存该值。final 字段还通过让编译器强制该字段为只读来提供额外的安全级别。
 
1.1关于final成员赋值
1)在java中,普通变量可默认初始化。但是final类型的变量必须显式地初始化。
 
2)final 成员能且只能被初始化一次。
 
3)final成员必须在声明时(在final变量定义时直接给其赋值)或者在构造函数中被初始化,而不能在其它的地方被初始化。
示例1 Bat.java

public class Bat {
  final double PI = 3.14; // 在定义时赋值
  final int i; // 因为要在构造函数中进行初始化,所以此处便不可再赋值
  final List<Bat> list; // 因为要在构造函数中进行初始化,所以此处便不可再赋值

  Bat() {
    i = 100;
    list = new LinkedList<Bat>();
  }

  Bat(int ii, List<Bat> l) {
    i = ii;
    list = l;
  }

  public static void main(String[] args) {
    Bat b = new Bat();
    b.list.add(new Bat());
    // b.i=25;
    // b.list=new ArrayList<Bat>();
    System.out.println("I=" + b.i + " List Type:" + b.list.getClass());
    b = new Bat(23, new ArrayList<Bat>());
    b.list.add(new Bat());
    System.out.println("I=" + b.i + " List Type:" + b.list.getClass());
  }
}

 
结果:

I=100 List Type:class java.util.LinkedList
I=23 List Type:class java.util.ArrayList

 
在main方法中有两行语句注释掉了,如果你去掉注释,程序便无法通过编译,这便是说,不论是i的值或是list的类型,一旦初始化,确实无法再更改。然而b可以通过重新初始化来指定i的值或list的类型。
 
1.2 final引用字段的无效初始化
要正确使用final字段有点麻烦,对于其构造子能抛出异常的对象引用来说尤其如此。因为 final 字段在每个构造器中必须只初始化一次,如果 final 对象引用的构造器可能抛出异常,编译器可能会报错,说该字段没有被初始化。编译器一般比较智能化,足以发现在两个互斥代码分支(比如,if...else 块)的每个分支中的初始化恰好只进行了一次,但是它对 try...catch 块通常不会如此“宽容”。
下面这段代码通常会出现问题。

class Thingie {
  public static Thingie getDefaultThingie() {
    return new Thingie();
  }
}

public class Foo {
  private final Thingie thingie;

  public Foo() {
    try {
      thingie = new Thingie();
    } catch (Exception e) {
      thingie = Thingie.getDefaultThingie();//Error:The final field thingie may already have been assigned
    }
  }
}

 
你可以这样修改。

public class Foo {
  private final Thingie thingie;

  public Foo() {
    Thingie tempThingie;
    try {
      tempThingie = new Thingie();
    } catch (Exception e) {
      tempThingie = Thingie.getDefaultThingie();
    }
    thingie = tempThingie;
  }
}

 
1.3关于final成员使用
当你在类中定义变量时,在其前面加上final关键字,那便是说,这个变量一旦被初始化便不可改变,这里不可改变的意思对基本类型来说是其值不可变,而对于对象变量来说其引用不可再变。然而,对象其本身却是可以被修改的,Java并未提供使任何对象恒定不变的途径。这一限制同样适合数组,它也是对象。
示例2

private final int VAL_ONE=9;
private static final int VAL_TWO=99;
public static final int VAL_THREE=999;

由于VAL_ONE 和VAL_TOW 是带有编译期数值的final 原始类型,所以它们二者均可以用作编译期常量,并且没有重大区别。VAL_THREE是一种更加典型的对常量进行定义的方式:定义为 public,则可以被用于包之外;定义为 static 来强调只有一份;定义为 final 来说明它是一个常量。
final标记的变量即成为常量,但这个“常量”也只能在这个类的内部使用,不能在类的外部直接使用。但是当我们用public static final 共同标记常量时,这个常量就成为全局的常量(一个既是static又是final的字段只占据一段不能改变的存储空间)。而且这样定义的常量只能在定义时赋值,其他地方都不行。
示例3

class Value {
  int i;

  public Value(int i) {
    this.i = i;
  }
}

public class FinalData {
  private static Random rand = new Random();

  private String id;

  public FinalData(String id) {
    this.id = id;
  }

  private final int i4 = rand.nextInt(20);

  static final int i5 = rand.nextInt(20);

  public String toString() {
    return id + ":" + "i4:" + i4 + ", i5=" + i5;
  }

  public static void main(String[] args) {
    FinalData fd1 = new FinalData("fd1");
    System.out.println(fd1);
    System.out.println("Creating new FinalData");
    FinalData fd2 = new FinalData("fd2");
    System.out.println(fd1);
    System.out.println(fd2);
  }
}

 
结果

fd1:i4:6, i5=3
Creating new FinalData
fd1:i4:6, i5=3
fd2:i4:17, i5=3

示例部分展示了将final 数值定义为static(i5) 和非static(i4) 的区别。此区别只有在数值在运行期内被初始化时才会显现,这是因为编译器对编译期数值一视同仁。(并且它们可能因优化而消失。)当你运行程序时,就会看到这个区别。请注意,在fd1 和fd2 中, i5 的值是不可以通过创建第二个FinalData 对象而加以改变的。这是因为它是 static,在装载时已被初始化,而不是每次创建新对象时都初始化。
示例4

class Value {
  int i;

  public Value(int i) {
    this.i = i;
  }
}

public class … {
  private Value v1=new Value(11);
  private final Value v2=new Value(22);
  private static final Value v3=new Value(33);
  …
}

public static void main(String[] args) {
  …
  fd1.v2.i++;// OK--Object isn't constant!
  fd1.v1=new Value(9);//OK--not final
  fd1.v2=new Value(0);//Error:Can't change reference
  fd1.v3=new Value(1);//Error:Can't change reference
  …
}

从v1 到v3 的变量说明了final 引用的意义。正如你在main( )中所看到的,不能因为v2 是final 的,就认为你无法改变它的值。由于它是一个引用,final 意味着你无法将v2 再次指向另一个新的对象。
示例5

public class … {
  private final int[] a={1,2,3,4,5,6};
  …
}

public static void main(String[] args) {
  …
  for(int i=0;i<fd1.a.length;i++)
 fd1.a[i]++;// OK--Object isn't constant!
  fd1.a=new int[3];//Error:Can't change reference  …
}

对数组具有同样的意义(可以改变它的值,但不能指向一个新的对象),数组是另一种引用。
 
1.4解决final数组的局限性
尽管数组引用能被声明成 final,但是该数组的元素却不能。这意味着暴露 public final 数组字段的或者通过它们的方法将引用返回给这些字段的类都不是不可改变的。

// Not immutable -- the states array could be modified by a malicious
// callerpublic
class DangerousStates {
  private final String[] states = new String[] { "Alabama", "Alaska", "ect" };

  public String[] getStates() {
    return states;
  }
}

 
同样,尽管对象引用可以被声明成 final 字段,而它所引用的对象仍可能是可变的。如果想要使用 final 字段创建不变的对象,必须防止对数组或可变对象的引用“逃离”你的类。要不用重复克隆该数组做到这一点,一个简单的方法是将数组转变成 List。

// Immutable -- returns an unmodifiable List insteadpublic
class SafeStates {
  private final String[] states = new String[] { "Alabama", "Alaska", "ect" };

  private final List statesAsList = new AbstractList() {
    public Object get(int n) {
      return states[n];
    }

    public int size() {
      return states.length;
    }
  };

  public List getStates() {
    return statesAsList;
  }
}

 
1.5关于final参数使用
还有一种用法是定义方法中的参数为final,对于基本类型的变量,这样做并没有什么实际意义,因为基本类型的变量在调用方法时是传值的,也就是说你可以在方法中更改这个参数变量而不会影响到调用语句,然而对于对象变量,却显得很实用,因为对象变量在传递时是传递其引用,这样你在方法中对对象变量的修改也会影响到调用语句中的对象变量,当你在方法中不需要改变作为参数的对象变量时,明确使用final进行声明,会防止你无意的修改而影响到调用方法。
 
1.6关于内部类中的参数变量
另外方法中的内部类在用到方法中的参变量时,此参数变量必须声明为final才可使用。
示例6 INClass.java

public class INClass {
  void innerClass(final String str) {
    class IClass {
      IClass() {
       System.out.println(str);
      }
    }
    IClass ic = new IClass();
  }

  public static void main(String[] args) {
    INClass inc = new INClass();
    inc.innerClass("Hello");
  }
}

2.final方法
2.1final方法用途
1)为了确保某个函数的行为在继承过程中保持不变,并且不能被覆盖(overridding),可以使用final方法。
 
2)class中所有的private和static方法自然就是final。
 
2.2 final与private关键字
类中所有的private方法都隐式地指定是final的。由于无法取用private方法,所以也就无法覆盖它。
“覆盖”只有在某方法是基类的接口的一部分时才会出现。即,必须能将一个对象向上转型为它的基本类型并调用相同的方法。如果某方法为private,它就不是基类的接口的一部分。它仅是一些隐藏于类中的代码,只不过是具有相同的名称而已。但如果在导出类以相同的方法生成一个public、protected或包访问权限方法的话,该方法就不会产生在基类中出现的“仅具有相同名称”的情况。此时,你并没有覆盖该方法,仅是生成了一个新的方法。由于private方法无法触及而且能有效隐藏,所以除了把它看成是因为它所归属的类的组织结构的原因而存在外,其他任何事物都不需要考虑它。
3.final类
将某个类的整体定义为final 时,该类无法被继承。而且由于final类禁止继承,所以final类中所有的方法都隐式指定为final的,因为无法覆盖它们。
final 用于类或方法是为了防止方法间的链接被破坏。例如,假定类 X 的某个方法的实现假设了方法 M 将以某种方式工作。将 X 或 M 声明成 final 将防止派生类以这种方式重新定义 M,从而导致 X 的工作不正常。尽管不用这些内部相关性来实现 X 可能会更好,但这不总是可行的,而且使用 final 可以防止今后这类不兼容的更改。

PS:final,finally和finallize的区别

  1. final用于申明属性,方法和类,表示属性不可变,方法不可以被覆盖,类不可以被继承。
  2. finally是异常处理语句结构中,表示总是执行的部分。  
  3. finallize表示是object类一个方法,在垃圾回收机制中执行的时候会被调用被回收对象的方法。

以上是小编为您精心准备的的内容,在的博客、问答、公众号、人物、课程等栏目也有的相关内容,欢迎继续使用右上角搜索按钮进行搜索java
final
深入解析wpf编程、深入解析wpf编程 pdf、java final关键字、android final关键字、final关键字,以便于您获取更多的相关知识。

时间: 2024-08-03 19:50:14

深入解析Java编程中final关键字的使用_java的相关文章

深入解析Java编程中final关键字的作用_java

final class当一个类被定义成final class,表示该类的不能被其他类继承,即不能用在extends之后.否则在编译期间就会得到错误. package com.iderzheng.finalkeyword; public final class FinalClass { } // Error: cannot inherit from final class PackageClass extends FinalClass { } Java支持把class定义成final,似乎违背了面

详解Java编程中super关键字的用法_java

通过用static来定义方法或成员,为我们编程提供了某种便利,从某种程度上可以说它类似于C语言中的全局函数和全局变量.但是,并不是说有了这种便利,你便可以随处使用,如果那样的话,你便需要认真考虑一下自己是否在用面向对象的思想编程,自己的程序是否是面向对象的. 好了,现在开始讨论this&super这两个关键字的意义和用法.在Java中,this通常指当前对象,super则指父类的.当你想要引用当前对象的某种东西,比如当前对象的某个方法,或当前对象的某个成员,你便可以利用this来实现这个目的,当

Java面向对象编程中final关键字的使用方法详解_java

在Java中通过final关键字来声明对象具有不变性(immutable),这里的对象包括变量,方法,类,与C++中的const关键字效果类似. immutable指对象在创建之后,状态无法被改变 可以从三个角度考虑使用final关键字: 代码本身:不希望final描述的对象所表现的含义被改变 安全:final对象具有只读属性,是线程安全的 效率:无法修改final对象本身,对其引用的操作更为高效 final 变量定义final Object a,则a只能被初始化一次,一旦初始化,a的数据无法修

详解Java编程中static关键字和final关键字的使用_java

Java static关键字以及Java静态变量和静态方法 static 修饰符能够与变量.方法一起使用,表示是"静态"的. 静态变量和静态方法能够通过类名来访问,不需要创建一个类的对象来访问该类的静态成员,所以static修饰的成员又称作类变量和类方法.静态变量与实例变量不同,实例变量总是通过对象来访问,因为它们的值在对象和对象之间有所不同. 请看下面的例子: public class Demo { static int i = 10; int j; Demo() { this.j

深入解析Java编程中接口的运用_java

接口的本质--接口是一种特殊的抽象类,这种抽象类里面只包含常量和方法的定义,而没有变量和方法的实现. 抽象类所具有的一些东西接口可以具有,假如一个抽象类里面所有的方法全都是抽象的,没有任何一个方法需要这个抽象类去实现,并且这个抽象类里面所有的变量都是静态(static)变量,都是不能改变(final)的变量,这时可以把这样的抽象类定义为一个接口(interface).把一个类定义成一个接口的格式是把声明类的关键字class用声明接口的关键字interface替换掉即可. 接口(interface

深入解析Java编程中的抽象类_java

Java程序用抽象类(abstract class)来实现自然界的抽象概念.抽象类的作用在于将许多有关的类组织在一起,提供一个公共的类,即抽象类,而那些被它组织在一起的具体的类将作为它的子类由它派生出来.抽象类刻画了公有行为的特征,并通过继承机制传送给它的派生类.在抽象类中定义的方法称为抽象方法,这些方法只有方法头的声明,而用一个分号来代替方法体的定义,即只定义成员方法的接口形式,而没有具体操作.只有派生类对抽象成员方法的重定义才真正实现与该派生类相关的操作. 在各子类继承了父类的抽象方法之后,

深入解析Java编程中面向字节流的一些应用_java

文件输入输出流 文件输入输出流 FileInputStream 和 FileOutputStream 负责完成对本地磁盘文件的顺序输入输出操作. [例]通过程序创建一个文件,从键盘输入字符,当遇到字符"#"时结束,在屏幕上显示该文件的所有内容 import java.io.*; class ep10_5{ public static void main(String args[]){ char ch; int data; try{ FileInputStream a=new FileI

详解Java编程中final,finalize,finally的区别_java

final:final可以让你控制你的成员.方法或者是一个类是否可被覆写或继承等功能,这些特点使final在Java中拥有了一个不可或缺的地位,也是学习Java时必须要知道和掌握的关键字之一. final成员 当你在类中定义变量时,在其前面加上final关键字,那便是说,这个变量一旦被初始化便不可改变,这里不可改变的意思对基本类型来说是其值不可变,而对于对象变量来说其引用不可再变.其初始化可以在两个地方,一是其定义处,二是在构造函数中,两者只能选其一. 下面程序很简单的演示了final的常规用法

解析Java编程中对于包结构的命名和访问_java

包的命名包的名字应该避免与其他包冲突,所以选择一个既有意义又唯一的名字是包设计的一个重要方面.但是全球的程序员都在开发包,根本就没有办法获知谁采用了什么包名,因此选择唯一的包名是一个难题.如果我们确定某个包只在我们的组织内部使用,那么我们就可以让内部仲裁者(internal arbiter)来确保项目之间不会发生名字冲突. 但是对于整个世界而言,这种方法是不实际的.包的标识符都是简单的名字,一种比较好的能够确保包名唯一的方法是使用Internet域名.如果我们所就职的公司的名字为Magic.ln