【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。
相关推荐
ramsey171 小时前
python_excel列表单元格字符合并、填充、复制操作
python
重生之绝世牛码1 小时前
Java设计模式 —— 【行为型模式】命令模式(Command Pattern) 详解
java·大数据·开发语言·设计模式·命令模式·设计原则
Ven%2 小时前
如何让后台运行llamafactory-cli webui 即使关掉了ssh远程连接 也在运行
运维·人工智能·chrome·python·ssh·aigc
晚风_END2 小时前
node.js|浏览器插件|Open-Multiple-URLs的部署和使用,实现一键打开多个URL的强大工具
服务器·开发语言·数据库·node.js·dubbo
java排坑日记4 小时前
poi-tl+kkviewfile实现生成pdf业务报告
java·pdf·word
V+zmm101344 小时前
校园约拍微信小程序设计与实现ssm+论文源码调试讲解
java·微信小程序·小程序·毕业设计·ssm
_周游4 小时前
【C语言】_指针与数组
c语言·开发语言
猿来入此小猿4 小时前
基于SpringBoot小说平台系统功能实现四
java·spring boot·毕业设计·毕业源码·在线小说阅读·在线小说平台·免费学习:猿来入此
caron45 小时前
Python--正则表达式
python·正则表达式
SyntaxSage5 小时前
Scala语言的数据库交互
开发语言·后端·golang