Java 基础 - final关键字

总结

  1. (重点)Java中final修饰符既可以修饰变量、方法,也可以修饰类。
    用final修饰的变量一旦被初始化,就不能再修改。
    用final修饰的方法不可被重写(不能被子类覆盖,但可以被继承)
    用final修饰的类不能被继承 (final类中的所有方法默认是final的,但是final类中的成员变量默认不是final的)
  2. final不能用于修饰构造方法。

ref: https://www.cnblogs.com/fangpengchengbupter/p/7858510.html; https://www.cnblogs.com/xiaoxiaoyihan/p/4974273.html

-------------------

final关键字表示的不可变的。下面讨论final关键字使用的三种场合:数据、方法以及类。

final数据

1、final属性

    程序中经常需要用到一些“常数”。常数主要应用于两个方面:

  • 编译期常数,永远不会改变
  • 在运行期初始化一个值,不希望它发生改变。

    对于编译期的常数,计算可以在编译期间提前执行,可以将常数值直接用于程序中。Java中,这种常数必须是基本数据类型。前置关键字final声明。定义时必须提供一个值

class Person {
    final String name;  // name未初始化,编译出错
}

    如果对对象句柄使用final,final会将句柄变成一个常数。进行声明时,必须将句柄初始化到一个具体的对象,而且不能将句柄指向另一个对象。

复制代码
class Person {
    String name = "张三";
}

public class FinalDemo {

    public static void main(String[] args) {
        final Person p = new Person();
        p = new Person();   // Error:无法为最终变量p分配值
    }
}
复制代码

然而,对象本身是可以修改的。

复制代码
class Person {
    String name = "张三";
}

public class FinalDemo {
    public static void main(String[] args) {
        final Person p = new Person();
        p.name = "萧萧弈寒";
    }
}
复制代码

一个可能的结果:

name = fd1,i1 = 0, i2 = 6

name = fd2,i1 = 8, i2 = 6

i1,i2是在运行期间随机产生的数据。

2、空白final

    Java1.1允许创建“空白final”,它们属于特殊字段。尽管被声明为final,但是却未得到一个初始值。即便如此,空白final还是必须在使用之前得到初始化。 示例:

复制代码
class Person {}

public class FinalDemo {
    final int i;
    final Person p;

    FinalDemo() {
        i = 1;
        p = new Person();
    }

    FinalDemo(int x) {
        i = x;
        p = new Person();
    }

    public static void main(String[] args) {
        FinalDemo fd = new FinalDemo();
    }
}
复制代码

现在强行要求对final进行赋值处理,要么在定义字段时使用一个表达式,要么在每个构造器中

3、用final修饰参数

java中也会将参数列表中的参数以声明的方式指明为final。这意味着你无法改变参数所指向的对象。例如:

复制代码
package com.bupt.java.test;

class Gizmo{
    public void spin(String temp){
        System.out.println(temp+" Method call Gizmo.spin()");
    }
}
public class FinalArguments {
    void with(final Gizmo g){
        //g = new Gizmo(); //不能对终态局部变量 g 赋值。它必须为空白,并且不使用复合赋值
    }
    
    void without(Gizmo g) {
        g = new Gizmo();
        g.spin("without");
    }
    
//    void f(final int i){
//        i++;
//    }不能对终态局部变量 i 赋值。它必须为空白,并且不使用复合赋值
    
    int g(final int i){
        return i + 1;
    }
    public static void main(String[] args){
        FinalArguments bf = new FinalArguments();
        bf.without(null);
        bf.with(null);
        System.out.println("bf.g(10)="+bf.g(10));
    }
}
复制代码

运行结果:

without Method call Gizmo.spin()
bf.g(10)=11

final方法

final方法主要有两个方面的作用:一种是防止任何继承类覆盖方法。若希望一个方法的行为在继承期间保持不变,不可被覆盖和改写,就可以采取这种做法。另一种是提高程序执行的效率。将一个方法设成final后,编译器就会忽略为执行方法调用机制而采取的常规代码插入方法(将自变量压入堆栈;跳至方法代码并执行它;跳回来;清除堆栈自变量;最后对返回值进行处理)。它会用方法主体内实际代码的一个副本来替换方法调用。这样可以避免方法调用时的系统开销。若方法体太大,可能效率也得不到提升。

复制代码
class Human {
    public final void show() {
        //...
    }
}
public class Man extends Human{
    public void show() {}   //Cannot override the final method from Human
}
复制代码

    类内所有的private方法都自动成为final。由于不能访问一个private方法,所以它绝对不会被覆盖。

final类

如果整个类都是final,就表明这个类不允许被继承。或者出于安全方面的理由,不希望进行子类化。除此之外,或许还考虑执行效率的问题,确保涉及这个类各对象的所有行动都要尽可能地有效。

final class Human {
}

public class Man extends Human{ // The type Man cannot subclass the final class Human
}

注意:数据成员既可以是final,也可以不是。无论类是否被定义成final,应用于final的规则同样适用于数据成员。

将类定义成final后,结果只是禁止被继承。由于禁止了继承,所以一个final类中的所有方法都默认是final。

猜你喜欢

转载自www.cnblogs.com/frankcui/p/10808900.html