为什么要在 Java 中的方法参数上使用关键字“final”?

2024-03-25

我不明白在哪里final关键字是really当它用在方法参数上时很方便。

如果我们排除匿名类的使用、可读性和意图声明,那么它对我来说几乎毫无价值。

强制某些数据保持不变并不像看起来那么有力。

  • 如果参数是原语,那么它将没有任何效果,因为参数作为值传递给方法,并且更改它在范围之外不会产生任何影响。

  • 如果我们通过引用传递参数,那么引用本身就是一个局部变量,如果从方法内部更改引用,则不会对方法范围之外产生任何影响。

考虑下面的简单测试示例。 尽管该方法更改了给定它的引用的值,但该测试通过了,但没有任何效果。

public void testNullify() {
    Collection<Integer> c  = new ArrayList<Integer>();      
    nullify(c);
    assertNotNull(c);       
    final Collection<Integer> c1 = c;
    assertTrue(c1.equals(c));
    change(c);
    assertTrue(c1.equals(c));
}

private void change(Collection<Integer> c) {
    c = new ArrayList<Integer>();
}

public void nullify(Collection<?> t) {
    t = null;
}

停止变量的重新分配

虽然这些答案在智力上很有趣,但我还没有读过简短的简单答案:

使用关键字final当你希望编译器阻止 变量被重新分配给不同的对象。

无论变量是静态变量、成员变量、局部变量还是自变量/参数变量,效果都是完全相同的。

Example

让我们看看实际效果。

考虑这个简单的方法,其中两个变量 (arg and x) 都可以重新分配不同的对象。

// Example use of this method: 
//   this.doSomething( "tiger" );
void doSomething( String arg ) {
  String x = arg;   // Both variables now point to the same String object.
  x = "elephant";   // This variable now points to a different String object.
  arg = "giraffe";  // Ditto. Now neither variable points to the original passed String.
}

将局部变量标记为final。这会导致编译器错误。

void doSomething( String arg ) {
  final String x = arg;  // Mark variable as 'final'.
  x = "elephant";  // Compiler error: The final local variable x cannot be assigned. 
  arg = "giraffe";  
}

相反,我们将参数变量标记为final。这也会导致编译器错误。

void doSomething( final String arg ) {  // Mark argument as 'final'.
  String x = arg;   
  x = "elephant"; 
  arg = "giraffe";  // Compiler error: The passed argument variable arg cannot be re-assigned to another object.
}

故事的道德启示:

如果要确保变量始终指向同一个对象, 标记变量final.

切勿重新分配参数

作为良好的编程实践(任何语言),您应该never将参数/参数变量重新分配给调用方法传递的对象以外的对象。在上面的例子中,永远不应该写这一行arg = 。既然人类都会犯错误,而程序员也是人,那么我们就让编译器来帮助我们吧。将每个参数/参数变量标记为“最终”,以便编译器可以找到并标记任何此类重新分配。

回想起来

正如其他答案中指出的那样…… 鉴于 Java 最初的设计目标是帮助程序员避免读取超出数组末尾的愚蠢错误,Java 应该被设计为自动强制所有参数/参数变量为“final”。换句话说,参数不应该是变量。但事后看来,这是 20/20 的愿景,Java 设计者当时正忙得不可开交。

所以,总是添加final所有的论点?

我们应该添加final声明的每个方法参数?

  • 理论上是的。
  • 实际上,没有。
    ➥ Add final仅当方法的代码很长或很复杂时,参数可能会被误认为是局部变量或成员变量,并且可能会被重新分配。

如果您坚持从不重新分配参数的做法,您将倾向于添加一个final每一个。但这很乏味,并且使声明有点难以阅读。

对于简短的简单代码,其中参数显然是一个参数,而不是局部变量或成员变量,我不费心添加final。如果代码非常明显,我或任何其他程序员都不可能在进行维护或重构时意外地将参数变量误认为是参数以外的东西,那么就不必费心了。在我自己的工作中,我添加了final仅在较长或涉及较多的代码中,参数可能会被误认为局部变量或成员变量。

#为了完整性添加了另一个案例

public class MyClass {
    private int x;
    //getters and setters
}

void doSomething( final MyClass arg ) {  // Mark argument as 'final'.
  
   arg =  new MyClass();  // Compiler error: The passed argument variable arg  cannot be re-assigned to another object.

   arg.setX(20); // allowed
  // We can re-assign properties of argument which is marked as final
 }

record

Java 16 带来了新的records https://openjdk.java.net/jeps/395特征。记录是定义类的一种非常简短的方式,其中心目的只是不可变且透明地携带数据。

您只需声明类名及其成员字段的名称和类型。编译器隐式提供了构造函数、getters、equals & hashCode, and toString.

这些字段是只读的,没有设置器。所以一个record是一种不需要标记参数的情况final。它们实际上已经是最终版本。事实上,编译器禁止使用final当声明记录的字段时。

public record Employee( String name , LocalDate whenHired )  // ???? Marking `final` here is *not* allowed.
{
}

如果您提供可选的构造函数,那么您can mark final.

public record Employee(String name , LocalDate whenHired)  // ???? Marking `final` here is *not* allowed.
{
    public Employee ( final String name , final LocalDate whenHired )  // ???? Marking `final` here *is* allowed.
    {
        this.name = name;
        whenHired = LocalDate.MIN;  // ???? Compiler error, because of `final`. 
        this.whenHired = whenHired;
    }
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

为什么要在 Java 中的方法参数上使用关键字“final”? 的相关文章

  • 我是否需要安装 SQLite 才能使 SQLiteJDBC 正常工作?

    我想我只是没有 明白 如果我的计算机上尚未安装 SQLite 并且我想编写一个使用嵌入式数据库的 Java 应用程序 并且我将 SQLiteJDBC JAR 下载 导入到我的项目中 那么这就是我所需要的吗 或者 我是否需要先安装 SQLit
  • Netbeans 8.1 Gnome 3 GTK+ UI 字体和选项卡高度

    我刚刚在运行 GNOME 3 桌面的 Ubuntu 16 04 上安装了 NetBeans 8 1 如果可能的话 我想继续使用 IDE 的 GTK 外观和感觉 但 UI 上的字体 尤其是选项卡中的字体 太小且重叠 我尝试添加 fontsiz
  • 什么是抽象类? [复制]

    这个问题在这里已经有答案了 当我了解抽象类时 我说 WT H 问题 创建一个无法实例化的类有什么意义呢 为什么有人想要这样的课程 什么情况下需要抽象类 如果你明白我的意思 最常见的是用作基类或接口 某些语言有单独的interface构建 有
  • 如何在 JPQL 或 HQL 中进行限制查询?

    在 Hibernate 3 中 有没有办法在 HQL 中执行相当于以下 MySQL 限制的操作 select from a table order by a table column desc limit 0 20 如果可能的话 我不想使用
  • 从 MATLAB 调用 Java?

    我想要Matlab程序调用java文件 最好有一个例子 需要考虑三种情况 Java 内置库 也就是说 任何描述的here http docs oracle com javase 6 docs api 这些项目可以直接调用 例如 map ja
  • Mockito 使用 @Mock 时将 Null 值注入到 Spring bean 中?

    由于我是 Spring Test MVC 的新手 我不明白这个问题 我从以下代码中获取了http markchensblog blogspot in search label Spring http markchensblog blogsp
  • 如何在java中将日期格式从YYMMDD更改为YYYY-MM-DD? [关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 我从机器可读代码中获取日期格式为 YYMMDD 如何将其更改为 YYYY MM DD 例如我收到 871223 YYMMDD 我想把它改成
  • Java Applet 中的 Apache FOP - 未找到数据的 ImagePreloader

    我正在研究成熟商业产品中的一个问题 简而言之 我们使用 Apache POI 库的一部分来读取 Word DOC 或 DOCX 文件 并将其转换为 XSL FO 以便我们可以进行标记替换 然后 我们使用嵌入到 Java 程序中的 FOP 将
  • 将人类日期(当地时间 GMT)转​​换为日期

    我正在服务器上工作 服务器正在向我发送 GMT 本地日期的日期 例如Fri Jun 22 09 29 29 NPT 2018在字符串格式上 我将其转换为日期 如下所示 SimpleDateFormat simpleDateFormat ne
  • 如何在 ant 中为 junit 测试设置 file.encoding?

    我还没有完全完成file encoding 和 ant https stackoverflow com questions 1339352 how do i set dfile encoding within ants build xml
  • Akka 与现有 java 项目集成的示例

    如果我已经有现有的javaWeb 应用程序使用spring and servlet容器 将 Akka 集成到其中的正确方法是什么 就像我将会有Actor1 and Actor2互相沟通的 开始使用这些演员的切入点是什么 例如 1 把它放在那
  • 如何在.NET中使用java.util.zip.Deflater解压缩放气流?

    之后我有一个转储java util zip Deflater 可以确认它是有效的 因为 Java 的Inflater打开它很好 并且需要在 NET中打开它 byte content ReadSample sampleName var inp
  • 在Java中运行bat文件并等待

    您可能会认为从 Java 启动 bat 文件是一项简单的任务 但事实并非如此 我有一个 bat 文件 它对从文本文件读取的值循环执行一些 sql 命令 它或多或少是这样的 FOR F x in CD listOfThings txt do
  • 如何在JPanel中设置背景图片

    你好 我使用 JPanel 作为我的框架的容器 然后我真的想在我的面板中使用背景图片 我真的需要帮助 这是我到目前为止的代码 这是更新 请检查这里是我的代码 import java awt import javax swing import
  • 使用 Elastic Beanstalk 进行 Logback

    我在使用 Elastic Beanstalk 记录应用程序日志时遇到问题 我正在 AWS Elastic Beanstalk 上的 Tomcat 8 5 with Corretto 11 running on 64bit Amazon Li
  • 轻松的反应

    我有一个与这里描述的类似的案例 动态更改RESTEasy服务返回类型 https stackoverflow com questions 3786781 dynamically change resteasy service return
  • Spring @Cacheable 和 @Async 注解

    我需要缓存一些异步计算的结果 具体来说 为了克服这个问题 我尝试使用 Spring 4 3 缓存和异步计算功能 作为示例 我们采用以下代码 Service class AsyncService Async Cacheable users C
  • 将 JavaFX FXML 对象分组在一起

    非常具有描述性和信息性的答案将从我这里获得价值 50 声望的赏金 我正在 JavaFX 中开发一个应用程序 对于视图 我使用 FXML
  • 部署 .war 时出现 Glassfish 服务器错误:部署期间发生错误:准备应用程序时出现异常:资源无效

    我正在使用以下内容 NetBeans IDE 7 3 内部版本 201306052037 爪哇 1 7 0 17 Java HotSpot TM 64 位服务器虚拟机 23 7 b01 NetBeans 集成 GlassFish Serve
  • 将 JScrollPane 添加到 JFrame

    我有一个关于向 Java 框架添加组件的问题 我有一个带有两个按钮的 JPanel 和一个添加了 JTable 的 JScrollPane 我想将这两个添加到 JFrame 中 我可以将 JPanel 添加到 JFrame 或将 JScro

随机推荐

  • 如何在flutter中将图像从base64字符串保存到文件?

    我有 64 进制图像字符串 如何将该图像保存到用户的移动设备 这是我尝试过的 var response await http post Utility serverUrl body body final encodedStr data re
  • SQL(Java,h2):检索我刚刚插入数据库的单个项目的唯一 ID 的最佳方法是什么? [复制]

    这个问题在这里已经有答案了 我目前的方法是这样的 SELECT TOP 1 ID FROM DATAENTRY ORDER BY ID DESC 这假设最新插入的项目始终具有最高的唯一 ID 主键 自动增量 这里有些不对劲 备择方案 如果
  • 不间断地播放多部分视频 (HTML5)

    我想将视频分成几部分 但在浏览器中将其作为单个视频播放 在我最初的尝试中 我使用 ffmpeg 分割视频 并使用 onending 事件来播放下一部分 缺点是它会在下一部分开始之前中断 此外 不再可能使用内置控件进行搜索 因此 我想知道是否
  • 使用 Android 模拟器捕获和保存图像时出现空指针异常

    我正在尝试通过 Android 模拟器捕获并保存图像 正在捕获图像 但保存的文件已损坏 可能是什么原因造成的 谁能帮我找出可能的错误点 下面是我的代码 public void onCreate Bundle savedInstanceSta
  • ILogger (ASP.NET Core) 日志被调用甚至 IsEnabled 返回 false

    我试图理解的目的已启用 https learn microsoft com en us aspnet core api microsoft extensions logging ilogger Microsoft Extensions Lo
  • RStudio 达到最大文件大小

    我已经下载了 RStudio 在打开我的代码所在的文件时 我似乎已经达到了容量限制 The file is 2 3MB the maximum file size is 2MB The file is too large to open a
  • 使用 jQuery 从 ASP.NET 向用户显示消息

    在 ASP NET 中开发各种 Web 应用程序时 我发现自己需要在执行各种操作后将消息发送回用户 例如 文件是否已成功上传或数据库记录已更新 另外 如果有错误我想通知用户 到目前为止 我一直在创建包含要向用户显示的消息的服务器端变量 然后
  • 如何捕获 socket.io 错误并防止它们出现在控制台中?

    我在node js 上运行socket io 并在Apache 网站上运行socket io 客户端 如果我不启动 node js 服务器并加载客户端页面 则会触发错误事件并显示空错误消息 这会导致以下控制台输出 GET http exam
  • Android:快速按下按钮会导致多个意图实例

    目前 我遇到一个错误 如果用户快速点击按钮 该按钮所附加的意图将多次触发 从而导致需要再次回溯的该意图堆栈 我怎样才能避免这种情况或补救这种情况 谢谢 k 这是 onClickListener 的内部 我在这里设置了布尔值 然后在过程结束时
  • 使用 Spreadsheetml 换行文本的样式

    我使用 xslt 生成电子表格 并想要一个带有换行文本的单元格 或者更好 带有换行符的文本 所以我的 xslt 为工作表数据生成以下 XML
  • 在 osx 上批量将 .py(文本文件)转换为 .pdf

    我教授 Python 课程 并且有大量提交用于考试的 py 文件 我想将所有这些内容转换为 pdf 以便我可以在 ipad 上的 IAnnotate 中打开它们 对其进行标记并将其返回给学生 我怎样才能在批处理模式下进行这样的转换 在小牛队
  • 在以破折号开头的目录中查找

    find将文件名开头的破折号解释为选项的开头 使用熟悉的 技巧不起作用 因为选项位于文件名之后 引用无效 并将第一个破折号替换为 也不行 通常鼓励用户在此类文件名之前添加 但是如果我不知道给定的路径是绝对路径还是相对路径 我该怎么办 编辑
  • rdstate 和 rdbuf 中的 rd 代表什么?

    C 标准I O库中有两个名称 rdstate and rdbuf 我知道 state 和 buf 但是 rd 是什么 PS 我相信我知道如何使用rdstate and rdbuf 不要教我那个 我认为它们代表 read 类似于大多数人使用
  • 如何使用 dbus-send 调用 org.bluez.Adapter1.StartDiscovery?

    我正在尝试使用 Bluez 5 44 进行交互dbus send https dbus freedesktop org doc dbus send 1 html命令行工具 我似乎无法让它正确启动发现 尽管当我使用时它工作得很好bluetoo
  • 使用 POI Apache 从 Excel 读取数据时将数据添加到 ArrayList

    我正在尝试使用 POI Apache 从 Excel 工作表中读取数据 我遇到的问题是我想同时读取一行中所有单元格的数据并将其存储在 Type Class 的 ArrayList 中 但输出只是逐个单元格 这是打开 Excel 工作表并逐个
  • 从 Windows 媒体库获取目录列表

    有没有什么方法可以以编程方式查找当前在 Windows 媒体库上设置的目录列表 例如 假设我有以下库 我为葡萄牙语表示歉意 但你会明白的 我怎样才能以编程方式获得这些三个目录路径列于视频库 D Filmes D Series D Video
  • 如何使用 Scala 中的任何库将通用潜在嵌套映射 Map[String, Any] 转换为案例类?

    我对反射并没有太多的兴趣 这个答案在某些情况下使用无形的作品 但似乎有很多边缘情况 将 Map String Any 转换为 case 类的无形状代码无法处理可选子结构 https stackoverflow com questions 5
  • 检测全屏模式

    现代桌面版 IE 10 始终是全屏的 有一个居住规范 https dvcs w3 org hg fullscreen raw file tip Overview html for fullscreenW3 上的伪类 但是当我尝试使用 jQu
  • pandas 中日期时间索引的算术运算

    在 pandas 中 您可以通过基于经典整数位置 行的索引或基于日期时间的索引来访问时间序列的特定位置 可以使用基本算术运算来操作基于整数的索引 例如如果我有一个integer index对于频率为 12 小时的时间序列 并且我想恰好在此之
  • 为什么要在 Java 中的方法参数上使用关键字“final”?

    我不明白在哪里final关键字是really当它用在方法参数上时很方便 如果我们排除匿名类的使用 可读性和意图声明 那么它对我来说几乎毫无价值 强制某些数据保持不变并不像看起来那么有力 如果参数是原语 那么它将没有任何效果 因为参数作为值传