JVM常用概念之常量

问题

final修饰的字段就一定是不能重新赋值吗?

基础知识

常量变量是使用常量表达式初始化的原始类型或 String 类型的最终变量。变量是否为常量变量可能对类初始化、二进制兼容性和明确赋值有影响。 —Java 语言规范

实验

用例源码-重新赋值

import java.lang.reflect.Field;public class ConstantValues {final int fieldInit = 42;final int instanceInit;final int constructor;{instanceInit = 42;}public ConstantValues() {constructor = 42;}static void set(ConstantValues p, String field) throws Exception {Field f = ConstantValues.class.getDeclaredField(field);f.setAccessible(true);f.setInt(p, 9000);}public static void main(String... args) throws Exception {ConstantValues p = new ConstantValues();set(p, "fieldInit");set(p, "instanceInit");set(p, "constructor");System.out.println(p.fieldInit + " " + p.instanceInit + " " + p.constructor);}}

执行结果

42 9000 9000

如上述执行结果所示,上面有3个被final关键字修饰的字段,其中的fieldInit字段赋有初值,其他两个没有赋初值,而在后续的通过Java反射机制对上述的3个被final修饰字段重新赋值后,执行结果惊奇的发现赋有初值的fieldInit字段的值没有被修改,其他两个没有赋初值的字段的值发生了修改,那这是因为什么呢?我们可以通过查看通过javac静态编译的字节码一查究竟,如下述代码所示:

$ javap -c -v -p ConstantValues.class
...final int fieldInit;descriptor: Iflags: ACC_FINALConstantValue: int 42  <---- oh...final int instanceInit;descriptor: Iflags: ACC_FINALfinal int constructor;descriptor: Iflags: ACC_FINAL...
public static void main(java.lang.String...) throws java.lang.Exception;descriptor: ([Ljava/lang/String;)Vflags: ACC_PUBLIC, ACC_STATIC, ACC_VARARGSCode:...41: bipush        42   // <--- Oh wow, inlined fieldInit field43: invokevirtual #18  // StringBuilder.append46: ldc           #19  // String " "48: invokevirtual #20  // StringBuilder.append51: aload_152: getfield      #3   // Field instanceInit:I55: invokevirtual #18  // StringBuilder.append58: ldc           #19  // String ""60: invokevirtual #20  // StringBuilder.append63: aload_164: getfield      #4   // Field constructor:I67: invokevirtual #18  // StringBuilder.append70: invokevirtual #21  // StringBuilder.toString73: invokevirtual #22  // System.out.println

通过查看上述字节码可以看出,被初始化赋值的fieldInit字段其实在javac静态编译时已经通过内联操作赋值了,而对于在JVM动态编译时不可能重新重写字节码,所以从此我们可以看出已经进行初始化赋值的由final关键字修饰的字段是不能修改的,而未进行初始化赋值的由final关键字修饰的字段却是可以进行修改的。理论上进行初始化赋值的由final关键字修饰的字段性能表现肯定要比没有进行初始化赋值的由final关键字修饰的字段要好,我们可以通过下面的测试用例进行进一步的验证。

用例源码-是否有final修饰的已初始化字段

@Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
@Fork(3)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@State(Scope.Benchmark)
public class FinalInitBench {// Too lazy to actually build the example class with constructor that initializes// final fields, like we have in production code. No worries, we shall just model// this with naked fields. Right?final int fx = 42;  // Compiler complains about initialization? Okay, put 42 right here!int x  = 42;@Benchmarkpublic int testFinal() {return fx;}@Benchmarkpublic int test() {return x;}
}

执行结果

Benchmark                                  Mode  Cnt   Score    Error  Units
FinalInitBench.test                        avgt    9   1.920 ±  0.002  ns/op
FinalInitBench.test:CPI                    avgt    3   0.291 ±  0.039   #/op
FinalInitBench.test:L1-dcache-loads        avgt    3  11.136 ±  1.447   #/op
FinalInitBench.test:L1-dcache-stores       avgt    3   3.042 ±  0.327   #/op
FinalInitBench.test:cycles                 avgt    3   7.316 ±  1.272   #/op
FinalInitBench.test:instructions           avgt    3  25.178 ±  2.242   #/opFinalInitBench.testFinal                   avgt    9   1.901 ±  0.001  ns/op
FinalInitBench.testFinal:CPI               avgt    3   0.285 ±  0.004   #/op
FinalInitBench.testFinal:L1-dcache-loads   avgt    3   9.077 ±  0.085   #/op  <--- !
FinalInitBench.testFinal:L1-dcache-stores  avgt    3   4.077 ±  0.752   #/op
FinalInitBench.testFinal:cycles            avgt    3   7.142 ±  0.071   #/op
FinalInitBench.testFinal:instructions      avgt    3  25.102 ±  0.422   #/op

由上述通过perform 执行结果可以看出,都进行了初始化的两个字段,有final修饰的字段的性能要更好。那这是因为什么呢?我们可以通过汇编代码进行查证,具体如下:

# test
...
1.02%    1.02%  mov    0x10(%r10),%edx ; <--- get field x
2.50%    1.79%  nop
1.79%    1.60%  callq  CONSUME
...# testFinal
...
8.25%    8.21%  mov    $0x2a,%edx      ; <--- just use inlined "42"
1.79%    0.56%  nop
1.35%    1.19%  callq  CONSUME
...

通过上述的汇编代码可以看出,由final修饰的字段在执行汇编指令过程中并没有进行字段加载,而只是引入字节码中的内联常量,这就是性能提升的关键点。

用例源码-是否有final修饰的未初始化字段

@Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
@Fork(3)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@State(Scope.Benchmark)
public class FinalInitCnstrBench {final int fx;int x;public FinalInitCnstrBench() {this.fx = 42;this.x = 42;}@Benchmarkpublic int testFinal() {return fx;}@Benchmarkpublic int test() {return x;}
}

执行结果

Benchmark                                            Mode  Cnt   Score    Error  Units
FinalInitCnstrBench.test                             avgt    9   1.922 ±  0.003  ns/op
FinalInitCnstrBench.test:CPI                         avgt    3   0.289 ±  0.049   #/op
FinalInitCnstrBench.test:L1-dcache-loads             avgt    3  11.171 ±  1.429   #/op
FinalInitCnstrBench.test:L1-dcache-stores            avgt    3   3.042 ±  0.031   #/op
FinalInitCnstrBench.test:cycles                      avgt    3   7.301 ±  0.445   #/op
FinalInitCnstrBench.test:instructions                avgt    3  25.235 ±  1.732   #/opFinalInitCnstrBench.testFinal                        avgt    9   1.919 ±  0.002  ns/op
FinalInitCnstrBench.testFinal:CPI                    avgt    3   0.287 ±  0.014   #/op
FinalInitCnstrBench.testFinal:L1-dcache-loads        avgt    3  11.170 ±  1.104   #/op
FinalInitCnstrBench.testFinal:L1-dcache-stores       avgt    3   3.039 ±  0.864   #/op
FinalInitCnstrBench.testFinal:cycles                 avgt    3   7.278 ±  0.394   #/op
FinalInitCnstrBench.testFinal:instructions           avgt    3  25.314 ±  0.588   #/op

由上述执行结果可知,对于未进行初始化,不管是否有final关键字修饰的字段,这两种情况执行的性能表现是一样的。

总结

由final关键字修饰的字段需要进行初始化赋值。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/897600.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

数据采集技术之python网络爬虫(中国天气网的爬取)

一、爬取中国天气网所有地区当天的天气数据&#xff08;PyCharm&#xff09;&#xff1a; 网址&#xff1a;https://www.weather.com.cn/ 下面爬取数据&#xff1a; 因为现在已经到了夜间&#xff0c;所以白天的数据已经不见了&#xff0c;但原理是一样的。 二、代码以及详情…

树莓集团落子海南,如何重构数字产业生态体系​

树莓集团在海南的布局&#xff0c;是其整体商业战略中的关键一环。这背后&#xff0c;是对政策机遇、产业协同、以及区域优势的深度考量。 政策机遇 海南自贸港建设带来前所未有的政策红利&#xff0c;包括贸易、投资、资金等方面的自由便利。树莓集团紧抓这一机遇&#xff0…

Ollama本地部署deepseek-r1蒸馏版

Docker安装Ollama 拉取镜像 docker pull ollama/ollama​ 启动-使用GPU docker run -d --gpusall -p 11434:11434 --name ollama ollama/ollamadocker run : Docker 的核心命令&#xff0c;用于创建并启动一个新的容器。 -d : 后台模式&#xff08;detached mode&#xff09…

41.HarmonyOS NEXT Layout布局组件系统详解(八):自定义样式与类

温馨提示&#xff1a;本篇博客的详细代码已发布到 git : https://gitcode.com/nutpi/HarmonyosNext 可以下载运行哦&#xff01; HarmonyOS NEXT Layout 布局组件系统详解&#xff08;八&#xff09;&#xff1a;自定义样式与类 文章目录 HarmonyOS NEXT Layout 布局组件系统详…

【Go | 从0实现简单分布式缓存】-7:增加etcd和gRPC功能

本文目录 1.序2.引入etcd缓存流程项目结构 3.gocachepb.proto4.服务注册register.go5.服务发现discover.go6.gRPC客户端client.gopeers.goclient.go 7.gRPC服务端实现server.go一些问题缓存获取流程缓存设置流程为什么要带超时的上下文&#xff1f; 1.序 GeeCache项目并没有引…

Pytorch系列教程:可视化Pytorch模型训练过程

深度学习和理解训练过程中的学习和进步机制对于优化性能、诊断欠拟合或过拟合等问题至关重要。将训练过程可视化的过程为学习的动态提供了有价值的见解&#xff0c;使我们能够做出合理的决策。训练进度必须可视化的两种方法是&#xff1a;使用Matplotlib和Tensor Board。在本文…

18 | 实现简洁架构的 Handler 层

提示&#xff1a; 所有体系课见专栏&#xff1a;Go 项目开发极速入门实战课&#xff1b;欢迎加入我的训练营&#xff1a;云原生AI实战营&#xff0c;一个助力 Go 开发者在 AI 时代建立技术竞争力的实战营&#xff1b;本节课最终源码位于 fastgo 项目的 feature/s14 分支&#x…

蓝队第三次

1.了解什么是盲注 盲注&#xff08;Blind SQL Injection&#xff09;是SQL注入的一种形式&#xff0c;攻击者无法直接通过页面回显或错误信息获取数据&#xff0c;而是通过观察页面的布尔状态&#xff08;真/假&#xff09;或时间延迟来间接推断数据库信息。例如&#xff0c;通…

sql server 2016 版本补丁说明

包信息和发布类型 Microsoft为创建和分发的 SQL Server 的所有软件更新包采用了标准化命名架构。 软件更新包是一个可执行文件&#xff08;.exe 或 .msi&#xff09;文件&#xff0c;其中包含一个或多个文件&#xff0c;这些文件可能应用于 SQL Server 安装以更正特定问题。 …

STM32之I2C硬件外设

注意&#xff1a;硬件I2C的引脚是固定的 SDA和SCL都是复用到外部引脚。 SDA发送时数据寄存器的数据在数据移位寄存器空闲的状态下进入数据移位寄存器&#xff0c;此时会置状态寄存器的TXE为1&#xff0c;表示发送寄存器为空&#xff0c;然后往数据控制寄存器中一位一位的移送数…

从青铜到王者:六大排序算法实战解析

前言 在编程的世界里,排序算法如同一颗璀璨的明珠,闪耀着智慧的光芒。它不仅是计算机科学的基础知识点,更是每一位程序员必备的技能。今天,就让我们一同走进排序算法的世界,深入探究冒泡排序、选择排序、插入排序、快速排序、归并排序、堆排序这六大经典算法的精髓所在,…

小程序配置webview

1.在微信公众平台配置业务域名 1&#xff09;包括把校验文件放在服务器根目录 2&#xff09;配置域名 2.在小程序中 新建文件 小程序新建页面&#xff1a;web-view json配置&#xff1a;{ "pageOrientation": "landscape", "renderer":&qu…

不用 Tomcat?SpringBoot 项目用啥代替?

在SpringBoot框架中&#xff0c;我们使用最多的是Tomcat&#xff0c;这是SpringBoot默认的容器技术&#xff0c;而且是内嵌式的Tomcat。 同时&#xff0c;SpringBoot也支持Undertow容器&#xff0c;我们可以很方便的用Undertow替换Tomcat&#xff0c;而Undertow的性能和内存使…

线索二叉树构造及遍历算法

线索二叉树构造以及遍历算法 线索二叉树&#xff08;中序遍历版&#xff09;构造线索二叉树构造双向线索链表遍历中序线索二叉树 线索二叉树&#xff08;中序遍历版&#xff09; 中序遍历找到对应结点的前驱&#xff08;土方法&#xff09; #mermaid-svg-eunGO5d2GhjLxCn5 {fo…

基于SpringBoot的“体育购物商城”的设计与实现(源码+数据库+文档+PPT)

基于SpringBoot的“体育购物商城”的设计与实现&#xff08;源码数据库文档PPT) 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;SpringBoot 工具&#xff1a;IDEA/Ecilpse、Navicat、Maven 系统展示 系统总体模块设计 前台用户登录界面 系统首页界面…

数据篇| App爬虫入门(一)

App 的爬取相比 Web 端爬取更加容易,反爬虫能力没有那么强,而且数据大多是以 JSON 形式传输的,解析更加简单。在 Web 端,我们可以通过浏览器的开发者工具监听到各个网络请求和响应过程,在 App 端如果想要查看这些内容就需要借助抓包软件。常见抓包软件有: ‌工具名称‌‌…

go context学习

1.Context接口2.emptyCtx3.Deadline()方法4.Done()方法5.Err方法6.Value方法&#xff08;&#xff09;7.contex应用场景8.其他context方法 1.Context接口 Context接口只有四个方法&#xff0c;以下是context源码。 type Context interface {Deadline() (deadline time.Time, …

在VMware Workstation Pro上轻松部署CentOS7 Linux虚拟机

首先我们需要下载VM虚拟机和Centos7的镜像 下载并安装VMware Workstation Pro 访问VMware Workstation Pro官网下载 https://www.vmware.com/ 第二步&#xff1a;下载centos7镜像 访问centos官网下载 https://www.centos.org/ 开始部署Centos7 点击创建新的虚拟机 这里是Cen…

Jsoup 解析商品信息时需要注意哪些细节?

在使用Jsoup解析商品信息时&#xff0c;需要注意以下细节和最佳实践&#xff0c;以确保爬虫的稳定性和数据的准确性&#xff1a; 1. 检查HTML文档的合法性 在解析之前&#xff0c;需要确认所解析的文档是否是一份合法正确的HTML文档。如果HTML结构不完整或存在错误&#xff0…

Android AudioFlinger(五)—— 揭开AudioMixer面纱

前言&#xff1a; 在 Android 音频系统中&#xff0c;AudioMixer 是音频框架中一个关键的组件&#xff0c;用于处理多路音频流的混音操作。它主要存在于音频回放路径中&#xff0c;是 AudioFlinger 服务的一部分。 上一节我们讲threadloop的时候&#xff0c;提到了一个函数pr…