Posts 「Java」深入理解Final关键字
Post
Cancel

「Java」深入理解Final关键字

深入理解final关键字

Created: Feb 15, 2021 5:36 PM Tags: Java, final

修饰类


final修饰一个类时,表明这个类不能被继承。

1
2
3
4
5
6
final class Father{

}
class Son extends Father{   //编译报错,不能继承final修饰的类

}

修饰方法


final修饰方法,方法不可以重写,但是可以被子类访问 【前提:方法不是 private 类型】。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Fu{
    public final void speak(){
        System.out.println("Father");
    }
}
class  Zi extends Fu{
    //直接编译失败,被final修饰的方法不能被重写
//    public void speak(){
//        System.out.println("Son");
//    }
}

public class EmbellishMethod {
    public static void main(String[] args) {

        Zi z =new Zi();
        z.speak();  //访问final修饰的方法
    }
}

运行结果 Father

修饰变量


如果被final修饰的是基本数据类型的变量,则其数值一旦在初始化之后便不能更改;如果是引用类型的变量,则在对其初始化之后便不能再让其指向另一个对象。

1
2
3
4
5
6
7
8
9
10
11
public class EmbellishVariable {
    public final int a=1;

    public void method(){
       // final修饰基本数据类型的变量,其数值一旦在初始化之后便不能更改
        a=2; //编译失败
       // final修饰引用类型的变量,其初始化之后便不能再让其指向另一个对象
        final String str=new String();
        str=new String(); //编译失败
    }
}

Final变量修饰变量(成员变量、局部变量)

Final修饰成员变量


成员变量必须在定义时或者构造器中进行初始化赋值

1
2
3
4
5
public class FinalAndVariable {
    public int t; //编译成功
    public final int b; //编译失败
    public final int c = 1; //编译成功
}

如果在定义成员变量的时候不初始前提是在构造方法中将成员变量b进行初始化。

1
2
3
4
5
6
7
8
9
public class FinalAndVariable {
    public int t;
    public final int b; //编译成功
    public final int c = 1; //编译成功

    public FinalAndVariable() {  //构造方法
       b=2;  //在构造方法中将成员变量b进行初始化
    }
}

final变量一旦被初始化赋值之后,就不能再被赋值了。【注意是成员变量】

1
2
3
4
5
6
7
8
9
10
11
public class EmbellishVariable {
    public final int a=1;

    public void method(){
    // final修饰基本数据类型的变量,其数值一旦在初始化之后便不能更改
        a=2; //编译失败
    // final修饰引用类型的变量,其初始化之后便不能再让其指向另一个对象
        final String str=new String();
        str=new String();//编译失败
    }
}

final修饰局部变量


只需要保证在使用之前被初始化赋值即可

final变量和普通变量的区别


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class FinalAndVariableDifference {
    public static void main(String[] args)  {
        String a = "helloWord1";

        final String b = "helloWord";
        String F = "helloWord";

        String c = b + 1;
        String e = F + 1;
        System.out.println((a == c));
        System.out.println((a == e));
    }
}

//结果
true
false

当final变量修饰基本数据类型以及String类型时,编译期间能知道它的确切值时,编译器会把它当做编译期常量使用。也就是说在用到该final变量的地方,相当于直接访问的这个常量,不需要在运行时确定。

分析上面代码:由于变量b被final修饰,因此会被当做编译器常量,所以在使用到b的地方会直接将变量b 替换为它的值(这种情况我们成为编译器的优化)。而对于变量F的访问却需要在运行时才能连接确定,所以返回false

被final修饰的引用变量一旦初始化赋值之后指向的对象不可变但该对象的内容可变

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class AA{
    int i=1;
}
public class EmbellishQuote {
    public static void main(String[] args) {
        
       final AA a = new AA();  //final修饰引用变量
        
       a=new AA(); //编译失败,说明被final修饰的引用变量一旦初始化赋值之后指向的对象不可变

       System.out.println( ++a.i ); //输出值为2,说明内容可变
    }
}

//运行结果: 2

Final和Static的关系


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class FinalDemo {
    public final double i = Math.random();
    public static double t = Math.random();
}

public class DemoDemo {
    public static void main(String[] args) {

        FinalDemo demo1 = new FinalDemo();
        FinalDemo demo2 = new FinalDemo();
        System.out.println("final修饰的  i=" + demo1.i);
        System.out.println("static修饰的 t=" + demo1.t);
        System.out.println("final修饰的  i=" + demo2.i);
        System.out.println("static修饰的 t=" + demo2.t);

        System.out.println("t+1= "+ ++demo2.t );
//      System.out.println( ++demo2.i );//编译失败
      }
}
运行结果
	final修饰的  i=0.7282093281367935
	static修饰的 t=0.30720545678577604
	final修饰的  i=0.8106990945706758
	static修饰的 t=0.30720545678577604
	t+1= 1.307205456785776

final修饰基本数据类型的变量时,则其数值一旦在初始化之后便不能更改。

上面代码中被final修饰的变量是在运行时才初始化的,并没有在编译期就被初始化!由于值为随机数,运行时被初始化是不确定的一个值,也就是个随机数,仅仅当运行之后被初始化之后他的值才会不变!

关于final修饰参数的争议


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Parameter{
    public void method(final int a){  //使用final修饰参数
  //      a++;  //编译失败
  //      a=1;  //编译失败
        System.out.println(a);
    }
}
public class ParameterAndFinal {
    public static void main(String[] args) {

        Parameter par=new Parameter();
        int a=2;
       par.method(a);
        int b=4;
       par.method(b);
    }
}
运行结果 
2
4
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Parameter{
    public void method(int a){  //不使用final修饰参数
        a++;
        a=6;
        System.out.println(a);
    }
}
public class ParameterAndFinal {
    public static void main(String[] args) {

        Parameter par=new Parameter();
        int a=999;
        par.method(a);
        int b=777;
        par.method(b);
    }
}
运行结果 
        6  
        6

使用final修饰的程序1中,因为使用了final,所以不会影响到你在外部传递的参数,而不使用final修饰的程序1中,因为没使用final,不管你传递啥,没无效。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class BB {
    public void method(final StringBuffer buffer) {
        buffer.append("波波小菜鸡");
    }
}
public class ParameterAndFinal {
    public static void main(String[] args)  {
        BB b = new BB();
        StringBuffer buffer = new StringBuffer("乾坤未定你我皆是");
        b.method(buffer);
        System.out.println(buffer.toString());
    }

}

//运行结果:  乾坤未定你我皆是波波小菜鸡
This post is licensed under CC BY 4.0 by the author.