【Java从入门到放弃 之 final 关键字】

final 关键字

  • [final 关键字](#final 关键字)
    • [final 字段](#final 字段)
    • [final 函数列表中的参数](#final 函数列表中的参数)
    • [final 方法](#final 方法)
    • [final 类](#final 类)

final 关键字

Java中里面有final这个关键字,这个关键字总体上是用来表达" 不能被改变" 这个意思的。我们使用这个关键字表达不能被改变,有两种使用场景,有三个使用位置。

使用场景

  1. 设计上就不允许改变的
  2. 为了效率不允许被改变

使用位置

  1. 字段
  2. 方法

我们将从使用位置着手,帮助大家了解final关键字的使用。

final 字段

很多时候,我们需要表达常量这个概念。我们都知道Java是面向对象的编程语言,在Java中一切都是对象Object;但是除此之外,Java还有8中基本数据类型。

所以final修饰的字段要么是8中基本数据类型,要么是Object。修饰基本类型的时候,代表的是修饰变量的值不能变化;修饰Object引用的时候,代表的是这个变量代表的引用不能改变(引用不能改变,但是引用指向的对象是可以改变的)

那既然是常量,不能被改变。那么我们怎么赋予其初始值呢?

  1. 直接初始化 : 可以在声明的时候直接给final变量赋值。这是最简单的方式,适用于你知道变量的确切值并且它不会改变的情况。
java 复制代码
public class Example {
    public final int MAX_SIZE = 100;
}
  1. 构造器初始化 : 如果你需要根据不同的构造条件来设置final变量的值,那么可以在构造器中初始化它们。每个构造器都必须对所有的final实例变量进行初始化,否则编译器会报错。
java 复制代码
public class Example {
    private final int maxSize;

    public Example(int size) {
        this.maxSize = size; // 必须在所有构造函数中初始化
    }
}

静态final变量 初始化也有两种

  1. 直接初始化
java 复制代码
public class Example {
    public static final int MAX_SIZE = 100;
}
  1. 静态块初始化
java 复制代码
public class Example {
    public static final int[] VALUES;

    static {
        // 更复杂的初始化逻辑
        VALUES = new int[3];
        VALUES[0] = 1;
        VALUES[1] = 2;
        VALUES[2] = 3;
    }
}

fianl修饰变量 - 代码案例

java 复制代码
public class Final {

    private static final int VALUE_ONE = 1;
    private final int VALUE_TWO = 2;
    public static final int VALUE_THREE = 3;

    private final int int4 = Random.class.newInstance().nextInt(20);

    private Value v1 = new Value("1");
    private final Value v2 = new Value("2");
    private static final Value v3 = new Value("3");

    private final int[] array = {1, 2, 3, 4, 5};

    public Final() throws InstantiationException, IllegalAccessException {
    }

    public static void main(String[] args) throws InstantiationException, IllegalAccessException {
        Final f = new Final();
        f.v2.value = "tes";
        f.v1 = new Value("new");
        //f.v2 = new Value("test2");
        for (int i = 0; i < f.array.length; i++) {
            f.array[i]++;
        }
        for (int i = 0; i < f.array.length; i++) {
            System.out.print(f.array[i]);
        }
    }
}

class Value {
    String value;

    public Value(String value) {
        this.value = value;
    }
}

final 函数列表中的参数

Java允许你用final 修饰参数列表中的形参,这个意味着,在这个函数中不能修改这个形参。如果这个形参代表的是基本参数类型,意味着函数体中不能修改参数值;如果修饰的是引用,代表我们不能修改引用。

java 复制代码
public class Test8 {

    public static void main(String[] args) {
        System.out.println(add(1, 2));
        System.out.println(add(new Tmp1(1), new Tmp1(2)));
    }

    public static int add(final int a, final int b) {
        //a++;
        return a + b;
    }

    public static int add(final Tmp1 a, final Tmp1 b) {
        //a = new Tmp1("5");
        a.a = 5;
        b.a = 10;
        return a.a + b.a;
    }
}

class Tmp1 {
    int a;

    public Tmp1(int a) {
        this.a = a;
    }
}

final 方法

使用final修饰方法主要有两个方面的考虑

  1. 当一个方法被声明为final时,它不能被子类重写或覆盖,确保了父类的特定行为不会被改变,从而维护了父类的行为一致性。这对于那些你希望保持不变的核心功能特别有用。
  2. 编译器知道final方法不会被覆盖,因此可以在某些情况下进行内联优化。内联意味着直接将方法体插入到调用点,减少方法调用的开销,提升执行效率。

** final 和 private**

final关键字跟private关键字也有关系。任何类中的私有方法跟final差不多。因为我们没有获取私有方法的权限,这也就意味着,我们不能改变父类中的私有方法的实现。

private final 代码案例展示

java 复制代码
public class Test9 {

    public static void main(String[] args) {
        OverRiddingBaseClass1 overRiddingBaseClass1 = new OverRiddingBaseClass1();
        overRiddingBaseClass1.f();
        overRiddingBaseClass1.f1();

//        BaseClass baseClass = overRiddingBaseClass1;
//        baseClass.f();
//        baseClass.f1();
//        OverRiddingBaseClass overRiddingBaseClass = overRiddingBaseClass1;
//        overRiddingBaseClass.f();
//        overRiddingBaseClass.f1();
    }
}

class BaseClass {

    private final void f() {
        System.out.println("BaseClass.f()");
    }

    private void f1() {
        System.out.println("BaseClass.f1()");
    }
}

class OverRiddingBaseClass extends BaseClass {

    private final void f() {
        System.out.println("OverRiddingBaseClass.f()");
    }

    private void f1() {
        System.out.println("OverRiddingBaseClass.f1()");
    }
}

class OverRiddingBaseClass1 extends OverRiddingBaseClass {
    public final void f() {
        System.out.println("OverRiddingBaseClass1.f()");
    }

    public void f1() {
        System.out.println("OverRiddingBaseClass1.f1()");
    }
}

Overriding 只会发生在父类非私有的方法中,上面代码案例中我们在最下面的子类把f()改成自己的实现,实际上这个方法是public,父类的是private;子类中的方法只是同名,不能算是父类的Overriding。

final 类

在Java中,使用final关键字修饰类(class)意味着该类不能被继承。这也就是说,任何试图创建此类子类的尝试都将导致编译错误。final类是不可扩展的,它提供了一些重要的好处,同时也带来了一定的限制。

  1. 防止继承:
  • 定义:当一个类被声明为final时,它不能作为其他类的父类。
  • 好处:确保了该类的行为和状态不会被改变或扩展,从而维护了类的完整性和一致性。
java 复制代码
public final class FinalClass {
    // 类成员和方法
}
  1. 提高安全性
  • 防止恶意扩展:final类可以防止其他开发人员通过继承来修改类的行为,特别是在库或框架中公开的API中,这对于保护敏感逻辑或数据非常重要。
  • 避免意外破坏:减少了因继承带来的潜在错误风险,例如子类可能无意间改变了父类的关键行为。
  1. 促进编译器优化
  • 性能提升:由于final类的方法不能被重写,编译器可以在某些情况下对这些方法进行内联调用等优化操作,从而提高程序执行效率。
  • 静态绑定:对于final类的方法调用,编译器可以直接确定要调用的方法版本,无需在运行时动态查找最合适的版本。
  1. 表达设计意图
  • 清晰的设计语义:通过将类声明为final,开发者明确表达了不允许对该类进行任何扩展的设计意图。这有助于其他开发人员理解代码库中的不可变规则和限制条件。
  • 文档化:final关键字起到了一种自我文档的作用,告诉其他程序员哪些类是固定的,哪些是可以自由扩展的。
  1. 支持不可变对象
  • 构建不可变类:为了实现不可变性,通常会将所有方法都设为final,并且让类本身也成为final。这样可以确保一旦对象创建后其状态不会发生变化,有助于构建线程安全的对象,因为在多线程环境中,不可变对象天生就是线程安全的。
  1. 简化调试和维护
  • 减少复杂度:当类不能被继承时,追踪其行为变得更为简单,因为你不需要考虑不同子类中可能存在的各种重写版本。
  • 降低维护成本:由于final类的行为是固定的,所以在进行代码审查或维护时,可以更放心地依赖这些类的一致性表现,减少了需要考虑的变量和可能性。
  1. 限制
  • 灵活性降低:final类不能被继承,这意味着如果以后需要扩展类的功能,必须通过组合或其他设计模式来实现,而不是直接继承。
  • 代码复用受限:因为不能继承,所以不能利用继承机制来复用final类中的代码。

final修饰class使用场景

  • 核心类库:如Java标准库中的String、Integer等包装类都是final的,以保证它们的安全性和一致性。
  • 工具类:当类主要包含静态方法,并且不打算被实例化时,可以将其声明为final,如Math类。
  • 安全敏感类:涉及敏感信息处理或系统资源管理的类应该考虑设为final,以防止未经授权的修改。
  • 不可变类:为了实现线程安全和不可变性,类及其成员应尽可能设为final。
相关推荐
jackson凌5 分钟前
【Java学习笔记】运算符
java·笔记·学习
前端开发张小七13 分钟前
每日一练:3统计数组中相等且可以被整除的数对
前端·python
程序员总部14 分钟前
Python正则表达式有哪些常用匹配字符?
python·mysql·正则表达式
咸鱼求放生18 分钟前
网络请求只到前端页面接口报200并到不到后端接口
java
天天进步201520 分钟前
Python项目--基于Python的自然语言处理文本摘要系统
开发语言·python·自然语言处理
只会AI搜索得coder25 分钟前
sqlite3 sqlcipher加密,解密,集成springboot,读取sqlcipher加密工具
java·spring boot·sqlite
小麦果汁吨吨吨33 分钟前
Flask快速入门
后端·python·flask
NetX行者35 分钟前
详解正则表达式中的?:、?= 、 ?! 、?<=、?<!
开发语言·前端·javascript·正则表达式
流云一号37 分钟前
Python实现贪吃蛇三
开发语言·前端·python
小白教程40 分钟前
如何处理Python爬取视频时的反爬机制?
开发语言·python·音视频·python爬虫