Java final关键字用法注意点

Java final关键字用法注意点

Scroll Down

Java final关键字用法注意点

(按照Java代码惯例,final变量就是常量,而且通常常量名要大写:)

  • final关键字声明的static变量(属于类)必须在声明、类初始化(static{})的时候初始化,而且不能重复赋值;而非static变量(属于对象),必须在声明、对象初始化({}方法)或者构造函数里面初始化,也不可以重复赋值。(有例子)
  • final static作为常量的时候,jvm会对其进行优化(有例子)
  • final变量不可变的是句柄的地址,其实里面的值可以改变(有例子)

一:final变量的声明和初始化

final关键字声明的static变量(属于类)必须在声明、类初始化(static{})的时候初始化,我们举一个例子说明:

class Final{
    static final String I = "helloworld";
    static {
        System.out.println("Final's static function!类初始化。。。。。。");
        //I = "helloWorld";
    }
}



class Final{
    static final String I ;
    static {
        System.out.println("Final's static function!类初始化。。。。。。");
        I = "helloWorld";
    }
}

但是对于而非static的final变量(属于对象),必须在声明、对象初始化({}方法)或者构造函数里面初始化,也不可以重复赋值。

class Final {
    final String S = "two";

    {
        System.out.println("Final's function!对象初始化。。。。。。");
        //S = "hello;";
    }

    public Final(String helloWorld) {
        //S = helloWorld;
    }
}


class Final {
    final String S ;

    {
        System.out.println("Final's function!对象初始化。。。。。。");
        S = "hello;";
    }

    public Final(String helloWorld) {
        //S = helloWorld;
    }
}

而且有一点都要记住:都不可以重复赋值。

二:final static作为常量的时候,jvm会对其进行优化

一个类中有声明为static final的变量,jvm会对类的加载进行优化,先来看一个对比的例子:

例一:

img

img

**解析:**因为s是static final变量,且它等于helloWorld,在编译的时候就可以知道它的值,所以直接访问s的值不会引起Final类的初始化。作为表现,也就是static静态代码块不会被加载。

例二:

img

img

**解析:**因为s是static final变量,因为s在编译的时候无法知道它的确切的值,所以只有等到运行的时候才能知道,所以访问s的值会引起Final类的初始化。作为表现,也就是static静态代码块会被加载。

三:final变量不可变的是句柄的地址,其实里面的值可以改变

还是通过例子来说明问题。

public class FinalTest {
    public static final List TEMPEXM = new ArrayList();

    public static void main(String[] args) {
        FinalTest.TEMPEXM.add("hello ");
        FinalTest.TEMPEXM.add("world !");
        System.out.println(FinalTest.TEMPEXM);
    }
}

img

final变量可以安全的在多线程环境下进行共享,而不需要额外的同步开销。

class Final{
    static int i = 0;
}

public class FinalTest {
    public static void main(String[] args) {
        Final f = new Final();
        changFinalClass(f);
    }
    public static void changFinalClass(final Final f) {
        //f = new Final();//出错
        f.i = 100;
    }
}

目的是不允许将引用变量进行重定向,但是引用的值却是可以变化的。

JAVA局部变量加final修饰的好处

一般来说有以下这几种用法:

  1. for循环中,使用局变量来保存循环数次,并用final修饰,而非直接用getCount()、getSize()、lenght等
  2. 需要访问集合中的某个对象时,使用局部变量来引用,并用final修饰,而非直接引用
  3. 需要访问外部某个对象时,使用局部变量来引用,并用final修饰,而非直接引用
  4. 其它情况

个人理解的好处有:

  1. 访问局部变量要比访问成员变量要快
  2. 访问局部变量要比每次调用方法去获取对象要快
  3. 使用final修饰可以避免变量被重新赋值(引用赋值)
  4. 使用final修饰时,JVM不用去跟踪该引用是否被更改?

以下是网上看到的一些见解:

Accessing a local variable is faster than accessing a field. It's best to keep field accesses out of performance-critical inner loops when possible. (Profile first, of course, to see if it matters.) In theory the JVM could "inline" the field to a local variable automatically under the right conditions, but don't count on it.

A final local variable is not any different from a normal local variable at runtime. The "final" keyword on a local variable expresses a constraint on the source code (that it is assigned to once) which the compiler can easily check.

Final fields do allow additional optimizations, and static final fields with primitive values (as in "public static final int MY_CONSTANT = 3") are treated as compile-time constants and inlined.