浅析final关键字

一 final关键字的基本用法

    在Java中,final出现是源于代码设计和执行效率考虑而出现的,

final关键字可以用来修饰类、方法和变量(包括成员变量和局部变量)。

下面就从这三个方面来了解一下final关键字的基本用法。

1)修饰类

    当用final修饰一个类时,表明这个类不能被继承。也就是说,如果一个类你永远不会让他被继承,

就可以用final进行修饰。final类中的成员变量可以根据需要设为final,

但是要注意final类中的所有成员方法都会被隐式地指定为final方法。


在使用final修饰类的时候,先想清楚了,除非真的出于安全考虑,以后也不想集成这个类,

否则,尽量还是别用final修饰一个类。

2)修饰方法

    使用final方法的原因有两个。第一个原因是把方法锁定,以防任何继承类修改它的含义;

第二个原因是效率。在早期的Java实现版本中,会将final方法转为内嵌调用。

但是如果方法过于庞大,可能看不到内嵌调用带来的任何性能提升。

在最近的Java版本中,不需要使用final方法进行这些优化了。

3)修饰变量

    对于一个final变量,如果是基本数据类型的变量,则其数值一旦在初始化之后便不能更改;

如果是引用类型的变量,则在对其初始化之后便不能再让其指向另一个对象。

使用final修饰变量的根本目的是不想让变量数据在编译或初始化之后值改变。

eg:

package com.lanhuigu.java.finaldemo;

import java.util.Random;

/**
 * final 修饰变量实例:
 * 对于一个final变量,如果是基本数据类型的变量,则其数值一旦在初始化之后便不能更改;
 * 如果是引用类型的变量,则在对其初始化之后便不能再让其指向另一个对象。
 */
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};

    /** 重写toString()方法 */
    @Override
    public String toString() {
        return  id + ": " + "i4 = " + i4 + ", INT_5 = " + INT_5;
    }

    /**
     * 测试调用
     */
    public static void main(String[] args) {
        FinalData fd1 = new FinalData("fd1");

        fd1.v2.i ++;
        fd1.v1 = new Value(9);
        for (int i = 0; i < fd1.a.length; i ++) {
            fd1.a[i]++;
        }

        System.out.println(fd1);
        System.out.println("Creating new FinalData");
        FinalData fd2 = new FinalData("fd2");
        System.out.println(fd1);
        System.out.println(fd2);
    }
}

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

程序结果:

fd1: i4 = 15, INT_5 = 18
Creating new FinalData
fd1: i4 = 15, INT_5 = 18

fd2: i4 = 13, INT_5 = 18

程序分析:

code1:

private final int valueOne = 9;
private static final int VALUE_TWO = 99;

valueOne,VALUE_TWO均为final修饰原始数据,直接编译为常量,不可以修改。

code2:

public static  final int VALUE_THREE = 39;

VALUE_THREE为public类型的常量,以供包外程序使用,static表示这个值是唯一的,

并且在编译时候就已经初始化,而final表示这个值是不可以改变的,是一个常量。

code3:

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

定义两个随机值变量,static或non-static。从程序结果可以看出,第一次初始化FinalData对象时,

fd1: i4 = 15, INT_5 = 18

而第二次new FinalData对象,

fd2: i4 = 13, INT_5 = 18

从两次结果对比可以看出,i4两次new FinalData的结果是不一样的,二INT_5两次都是一样的,

这是因为INT_5是static修饰的,只初始化一次,无论new FinalData执行多少次,static变量只初始化一次,

所有,第二次new FinalData时,INT_5使用的值还是第一次创建时初始化的值。

而i4每创建一次都会重新赋值一次,所以结果不一样。

code4:

private Value v1 = new Value(11);
private final Value v2 = new Value(22);
private static final Value VAL_3 = new Value(33);

v1是普通引用类型变量,v2为final定义,初始化之后便不能再让其指向另一个对象,

即不能再把v2绑定到别的新建对象上。

二 final关键字其它常见问题

1)类的final变量和普通变量有什么区别?

    当用final作用于类的成员变量时,成员变量(注意是类的成员变量,

局部变量只需要保证在使用之前被初始化赋值即可)必须在定义时或者构造器中进行初始化赋值,

而且final变量一旦被初始化赋值之后,就不能再被赋值了。

2)被final修饰的引用变量指向的对象内容可变吗?

在上面提到被final修饰的引用变量一旦初始化赋值之后就不能再指向其他的对象,

那么该引用变量指向的对象的内容可变吗?

eg:

package com.lanhuigu.java.finaldemo;


public class ObjectContentTest {
    public static void main(String[] args)  {
        final MyClass myClass = new MyClass();
        System.out.println(++myClass.i);
    }
}

class MyClass {
    int i = 0;
}

程序结果:

1

程序分析:

从运行结果可以知道,final修饰的引用变量一旦初始化赋值之后就不能再指向其他的对象,

但是,引用变量指向的对象内容是可以改变的。

3)final修饰的方法参数不可变

一个参数被final修饰,这个参数值在对应的作用域内不能改变。

final修饰变量的本意就是在某些情况下,防止传进来的参数值被修改,导致不必要的错误。

网上有些文章扭曲final修饰参数的本意,然后写了一堆反证,其实与final本意

毫不相干,这种反证在final本意面前如小丑在一个高人面前呱呱叫。



猜你喜欢

转载自blog.csdn.net/yhl_jxy/article/details/80873505