Java 基础之(六) final关键字

来源:互联网 发布:比较好的网红的淘宝店 编辑:程序博客网 时间:2024/05/20 09:48

Java中的final关键字非常重要,它可以应用于类、方法以及变量。
由于语境(应用环境)不同, final 关键字的含义可能会稍微产生一些差异。但它最一般的意思就是声明“这个东西不能改变”。之所以要禁止改变,可能是考虑到两方面的因素:设计或效率。
由于这两个原因颇有些区别,所以也许会造成 final 关键字的误用。
接下来,我们将讨论 final 关键字的三种应用场合:变量、方法以及类。

1. final变量

用final修饰的成员变量表示常量,值一旦给定就无法改变!
final变量经常和static关键字一起使用,作为常量。下面是final变量的例子:

public static final String LOAN = "loan";LOAN = new String("loan") //invalid compilation error

final变量是只读的。

定义为 public,则可以被用于包之外;定义为 static 来强调只有一份;定义为 final 来说明它是一个常量。
final标记的变量即成为常量,但这个“常量”也只能在这个类的内部使用,不能在类的外部直接使用。但是当我们用public static final 共同标记常量时,这个常量就成为全局的常量(一个既是static又是final的字段只占据一段不能改变的存储空间)。而且这样定义的常量只能在定义时赋值,其他地方都不行。

下面看个简单的例子:

public class Bat {    final double PI = 3.14; // 在定义时赋值    final int i; // 因为要在构造函数中进行初始化,所以此处便不可再赋值    //在构造函数中为final变量分配初始值    Bat() {       i = 100;    }    public static void main(String[] args) {       Bat b = new Bat();       //b.i=25;       System.out.println("I=" + b.i );       System.out.println("I=" + b.i );    }}
输出结果:I = 100;I= 100;

若去掉程序中注释的那行代码,则编译报错:无法最终变量i分配值。这说明i的值一旦初始化,确实无法更改。

除了在构造器中和定义时为final变量赋值外,也可以在普通初始化块中为其赋初始值。

除了上面的实例变量,final也可以用于修饰类变量。类变量只能在声明该变量时或静态代码块中赋初始值。
定义语法如下:

final static double d = 5.0;

final局部变量

看一个简单的例子:

public class TestFinalLocalVariable{    public void test(final int a)    {        //不能对final修饰的形参赋值,下面语句非法        //a = 5;    }    public static void main(String[] args)     {        //定义final局部变量时指定默认值,则str变量无法重新赋值        final String str = "hello";        //下面赋值语句非法        //str = "Java";        //定义final局部变量时没有指定默认值,则d变量可被赋值一次        final double d;        //第一次赋初始值,成功        d = 5.6;        //对final变量重复赋值,下面语句非法        //d = 3.4;    }}

在上面程序中还示范了final修饰形参的情形。因为形参在调用该方法时,由系统根据传入的参数来进行初始化,因此使用final修饰的形参不能被赋值。

final引用变量

final修饰基本类型变量的时候,不能对基本数据类型变量重新赋值;但对于引用变量而言,它保存的仅仅是一个引用,final只保证这个引用类型变量所引用的地址不会改变,即一直引用同一个对象,但这个对象完全可以发生改变。
看一个例子:

class Person{    private int age;    public Person(){}    public Person(int age)    {        this.age = age;    }    public void setAge(int age)    {        this.age = age;    }    public int getAge()    {         return this.age;    }}public class TestFinalReference{    public static void main(String[] args)     {        //final修饰数组变量,iArr是一个引用变量        final int[] iArr = {5, 6, 12, 9};        System.out.println(Arrays.toString(iArr));        //对数组元素进行排序,合法        Arrays.sort(iArr);        System.out.println(Arrays.toString(iArr));        //对数组元素赋值,合法        iArr[2] = -8;        System.out.println(Arrays.toString(iArr));        //下面语句对iArr重新赋值,非法        iArr = null;        //final修饰Person变量,p是一个引用变量        final Person p = new Person(45);        //改变Person对象的age属性,合法        p.setAge(23);        System.out.println(p.getAge());        //下面语句对p重新赋值,非法        //p = null;    }}

从上面程序可以看出,使用final修饰的引用变量不能被重新赋值,但可以改变引用类型变量所指向对象的内容。

宏变量

对一个final变量来讲,不管它是类field、实例field,还是局部变量,只要该变量满足三个条件,这个final变量就不再是一个变量,而是相当于一个直接量,也称为宏变量。

  • 使用final修饰符修饰
  • 在定义该final变量时指定了初始值
  • 该初始值可以在编译时就被确定下来

    看如下程序:

public class TestFinal{    public static void main(String[] args){        //下面定义了5个宏变量        final int a = 5;        final int i = 5+2;        final double d = 1.2/3.0;        final String str = "hello"+"world";        final String str1 = "hello" + 99.0;        //下面str2变量的值因为调用了方法,因此无法在编译时被确定        final String str2 = "hello" + String.valueOf(99.0);        System.out.println(str1 == "hello99.0");        System.out.println(str2 == "hello99.0");    }}

final修饰符的一个重要作用就是定义“宏变量”,编译器会把该程序中所有用到“宏变量”的地方直接替换成该变量的值。因此,执行下面这行代码时:

System.out.println(str);

实际执行的是System.out.orintln("helloworld");

程序最后两行分别判断str1和str2是否和“hello99.0”相等。由于str1是一个宏变量,它将被直接替换成“hello99.0”,因此返回true。

- final方法

之所以要使用 final 方法,可能是出于对两方面理由的考虑。
第一个是为方法“上锁”,防止任何继承类改变它的本来含义。设计程序时,若希望一个方法的行为在继承期间保持不变,而且不可被覆盖或改写,就可以采取这种做法。
第二个原因是效率。在早期的Java实现版本中,会将final方法转为内嵌调用。但是如果方法过于庞大,可能看不到内嵌调用带来的任何性能提升。在最近的Java版本中,不需要使用final方法进行这些优化了。

因此,如果只有在想明确禁止 该方法在子类中被覆盖的情况下才将方法设置为final的。

  注:类的private方法会隐式地被指定为final方法。

final修饰的方法仅仅是不能被重写,并不是不能被重载。

3. final类

当用final修饰一个类时,表明这个类不能被继承。也就是说,如果一个类你永远不会让他被继承,就可以用final进行修饰。final类中的成员变量可以根据需要设为final,但是要注意final类中的所有成员方法都会被隐式地指定为final方法。
Java中有许多类是final的,譬如String, Interger以及其他包装类。

class SmallBrain {}final class Dinosaur {int i = 7;int j = 1;SmallBrain x = new SmallBrain();void f() {}}//! class Further extends Dinosaur {}// error: Cannot extend final class 'Dinosaur'public class Jurassic {public static void main(String[] args) {Dinosaur n = new Dinosaur();n.f();n.i = 40;n.j++;}} 

注意数据成员既可以是 final,也可以不是,取决于我们具体选择。应用于 final 的规则同样适用于数据成员,无论类是否被定义成 final。将类定义成 final 后,结果只是禁止进行继承—— 没有更多的限制。然而,由于它禁止了继承,所以一个 final 类中的所有方法都默认为 final。因为此时再也无法覆盖它们。所以与我们将一个方法明确声明为 final 一样,编译器此时有相同的效率选择。
可为 final 类内的一个方法添加 final 指示符,但这样做没有任何意义。

4.final的注意事项

  1. final类不能被继承,没有子类,final类中的方法默认是final的。
  2. final方法不能被子类的方法覆盖,但可以被继承。
  3. final成员变量表示常量,只能被赋值一次,赋值后值不再改变。
  4. final不能用于修饰构造方法。

5.final关键字的好处

  1. final关键字提高了性能。JVM和Java应用都会缓存final变量。
  2. final变量可以安全的在多线程环境下进行共享,而不需要额外的同步开销。
  3. 使用final关键字,JVM会对方法、变量及类进行优化。

参考:
http://blog.csdn.net/linchunhua/article/details/5305452

http://www.cnblogs.com/dolphin0520/p/3736238.html

http://lavasoft.blog.51cto.com/62575/18771/

http://www.tuicool.com/articles/qMzi6j

0 0