final详解

通达信缠论中枢自动识别与画线实战指南 本文详细介绍了通达信缠论中枢自动识别与画线公式的实战应用。该工具通过严谨的算法逻辑,自动在K线图上标记出符合缠论定义的中枢区间,帮助投资者摆脱繁琐的手工画图,聚焦于价格与中枢的互动关系,从而高效识别趋势延续、背驰及第三类买卖点,提升缠论分析与交易决策的效率。 阅读详情
在java中,final关键字可以有如下的用处: 
  1. final关键字可以被加到类的声明中,final类是不允许继承的;
  2. final关键字可以被加到方法声明中,final方法是不允许重写的(override),这个效果同私有方法一样;
  3. final关键字可以被家到属性或者变量的声明中,final属性或者变量一旦赋值之后就不允许再发生变化。对于基本类型(primitive type),比如int、double、long、byte等,一旦被生命为final,我们就可以将其当作常量来看待,但是对于引用类型或者数组(数组在java中也是对象)来说,则不是。虽然一个引用类型被赋值之后无法发生变化,但是我们仍然可以修改被引用的那个对象或者数组中的元素。因此在java中,常量的定义与其他语言相比可能会有点差异,在java中,常量的定义是:被声明为final的基本类型或者是通过编译时常量初始化的String类型;
  4. 方法的参数可以被声明为final,这些参数一旦初始化之后,在方法体中是不能改变其值的。基本上,在接口中将方法参数声明为final是没有什么意义的,因为java的编译器并没有强制要求在继承接口时,方法的参数也一定要带上final。也就是说,一个方法的参数是否为final并没有被当成是方法签名中的一部分,这个对于类的继承也是一样的。关于这一点,大家可以写个简单的程序测试一下;
  5. 本地类的方法中只能使用final类型的本地变量;
  6. 通常情况下,将方法或者变量生命为final类型有助于提高程序运行时的性能;

下面会对第5,第6点做一个详细的介绍,其他的几点都比较直观,容易理解,第5和第6点涉及到编译器如何产生字节码以及java中对堆区和栈区的使用,会稍微复杂一点。 
1.本地类的方法中使用本地变量  
Java代码   收藏代码
  1. public class FinalField {  
  2. public static void main(String[] args) {  
  3.     final int x = 0;  
  4.     final int y = 0;  
  5.     Foo foo = new Foo() {  
  6.         public void doBar() {  
  7.             int z = x + y;  
  8.             System.out.println(z);  
  9.         }  
  10.     };  
  11.     foo.doBar();  
  12. }  
  13. }  
  14.   
  15. interface Foo {  
  16.     void doBar();  
  17. }  

上面的代码中,定一个了一个Foo接口,在FinalField类中,在main方法中以匿名类的方式创建了一个Foo接口的实现,然后赋值给foo变量。在这里,我们创建的这个匿名的Foo接口的实现就是一个本地类。在这个本地类中,我们使用了在main方法中定义的两个变量x和y,将它们相加之后输出到控制台。 
为了在本地类的doBar方法中使用x和y,我们必须将x和y声明成final,否则编译器是会报错的。其原因还要从Java是一个基于栈的语言说起。Java程序执行时,运行时环境会为每一个线程分配一个线程栈,一个线程在执行过程中的每次方法调用都会在这个栈中分配一个栈帧,而方法中使用到的参数、变量都会在这个栈帧中进行分配。我们可以通过配置JVM的参数来指定线程栈占用空间的最大值,由于每次方法调用都需要在线程栈中分配一个栈帧,因此线程栈的大小直接关系到我们可以执行几次方法调用。一般来说线程栈的大小默认为4K,足够一个线程正常地执行所有的方法调用。但是,对于需要递归调用的方法来说,由于受到线程栈大小的限制,其计算能力也会受到影响。比如,比较经典的斐波那契数的计算就是一个递归的算法,理论上是可以计算任何输入的参数的,但是由于受到线程栈大小的影响,真正可计算的数值的大小是有限制的。 
通过下面这个简单的程序及其字节码,我们来体验一下Java程序是如何利用栈来执行操作的。 
Java代码   收藏代码
  1. public class ThreadStack {  
  2.     public int run() {  
  3.         int x = 0;  
  4.         int y = 1;  
  5.           
  6.         int z = x + y;  
  7.           
  8.         return z;  
  9.     }  
  10. }  

上面这段代码的字节码如下,这里为了简单起见只给出了run方法的字节码。 
Java代码   收藏代码
  1. iconst_0  
  2. istore_1  
  3. iconst_1  
  4. istore_2  
  5. iload_1  
  6. iload_2  
  7. iadd  
  8. istore_3  
  9. iload_3  
  10. ireturn  


字节码中的第0和1行对应源代码中的第3行,iconst指令的含义是将常数0压栈,istore指令的含义是从栈顶弹出一个值,然后赋值给变量x,字节码的第2和第3行是给变量y赋值,对应与源代码中的第4行,同样使用了iconst和istore指令。完成了对x和y变量的赋值之后,字节码的第4和第5行执行了两遍iload指令,这个指令的含义是将本地变量的值压入栈中,通过两次调用就是分别将x和y的值压入栈中。字节码第6行是一个加法指令,这个指令会从栈中弹出两个值,然后执行加法操作,然后将结果值再压入栈中。字节码的第7行是从栈顶弹出一个值然后赋值给变量z,字节码的第8行则是将变量z的值压入栈中,最后的ireturn指令则是从栈中弹出栈顶元素,然后压入调用这个方法的调用者的栈帧中。假设我们在main方法中调用了ThreadStack的run方法,那么这个返回值就会被压入main方法所在栈帧的顶部。一个方法结束之后,这个方法对应的栈帧也就消失了,留下的空间会分配给其他的方法调用所对应的栈帧。 
回过头来再说本节开头的那个例子,main方法调用结束之后,它所对应的栈帧就被回收了,在main方法中声明的x和y变量也就消失了。而我们知道,在Java中,所有的对象都是在堆中被分配的,也就是说,foo所指向的那个对象是在堆中,而不是在栈中的。由于存在与堆中的对象的生命周期与存在与栈中的变量的生命周期不同(堆中对象的声明周期都是比栈中变量的声明周期要长的),因此Java是不允许堆中的对象直接使用栈中分配的变量的。碰到本节开头的例子中的情况,Java其实是将x和y复制了一份给foo所指向的那个对象使用的。这就要求x和y在后面的执行过程中不能够发生任何的变化,否则会就会造成执行上的错误。这就是为什么本地对象只能使用被声明成final的本地变量。 
另外,在复制final类型的变量给本地方法使用的时候,Java针对引用类型和基本数值类型所采用的方法是不同的。我们在前面也提到过,本声明成final的基本数值类型可以被当作编译期常量来使用,因此java的编译器可以直接把这些数值放入到字节码中。而对于引用类型,编译器则是通过生成构造函数的形式来完成复制的。感兴趣的朋友可以通过改写本节开头的类,将x和y声明成String类型,然后用javap -verbose来看看生成的字节码有何不同。 
2.为什么final有助与程序的性能  
还是先来看一段程序, 
Java代码   收藏代码
  1. public class FinalField {  
  2.     public static void main(String[] args) {  
  3.         ValueHolder vh = new ValueHolder();  
  4.         int v = vh.v;  
  5.         System.out.println(v);  
  6.     }  
  7.       
  8.     public static class ValueHolder {  
  9.         private int v = 0;  
  10.     }  
  11. }  

这个程序在FinalField类中定一个了一个子类,这样就可以在FinalField的任何方法中直接使用这个子类中的属性,代码会简单一些,同时也足够用来说明问题。 
上面这个版本中,ValueHolder的v属性没有被声明成final,我们来看下编译器为我们生成的FinalField类的字节码中,是如何来访问ValueHolder中的v属性的。在源代码中是第4行。 
Java代码   收藏代码
  1. invokestatic    #19; //Method com/taobao/tianxiao/FinalField2$ValueHolder.access$0:(Lcom/taobao/tianxiao/FinalField2$ValueHolder;)I  
  2. istore_2  

我们会看到生成的字节码中有这么两条语句,第一条语句执行一个invokestatic指令,这个指令是调用静态方法的指令,而被调用的方法是FinalField2$ValueHolder的access$0方法,调用完成之后,将栈顶的值赋值给变量v。这就奇怪了,我们并没有在ValueHolder中定一个叫做access$0的方法,这是怎么会是呢?我们先来看下ValueHolder的字节码,打开之后可以发现果然有一个叫做access$0的方法定义存在,如下所示。那么既然这个方法不是我们自己定义的,那肯定就是编译器帮我们自动生成的。 
Java代码   收藏代码
  1. static int access$0(com.taobao.tianxiao.FinalField2$ValueHolder);  
  2.   Code:  
  3.    Stack=1, Locals=1, Args_size=1  
  4.        aload_0  
  5.        getfield #12; //Field v:I  
  6.        ireturn  
  7.   LineNumberTable:   
  8.    line 11: 0  

生成的access$0中的字节码很简单,就是去取传进来的ValueHolder对象中的v属性,然后返回。 
从上面的介绍可以看到,虽然我们在源代码中只是简单的写了一句int v=vh.v,但是编译器生成的代码中,是执行了一次方法调用的。那么如果把ValueHolder中的v声明成final,会是什么情况呢? 
Java代码   收藏代码
  1. iconst_0  
  2. istore_2  

从生成的字节码来看,已经没有了之前对access$0方法的调用了,取而代之的是一条iconst_0指令,也就是直接将0压入栈顶了。通过检查ValueHolder的字节码,发现将v设置成声明成final之后,编译器也确实没有为我们生成access$0方法。从这里可以看出,将ValueHolder的v声明成final之后,会将原本需要方法调用的地方,替换成直接压常量入栈,由于减少了方法调用,程序的性能自然会提高一下。但是仔细观察FinalField的字节码会发现,在将ValueHolder的v声明成final之后,与原来相比却多了如下的两行代码, 
Java代码   收藏代码
  1. invokevirtual   #19; //Method java/lang/Object.getClass:()Ljava/lang/Class;  
  2. pop  
  3. iconst_0  
  4. istore_2  

着两行代码被放置在iconst_0指令之前,意思是调用一下vh这个变量所指向的ValueHolder对象的getClass()方法,之后又将返回值直接丢弃掉(pop的意思就是直接将栈顶元素弹出)。这两行代码似乎是没有什么任何意义的,因为不管怎么样,v的值都会被设置成0。 想来想去,只有一个解释是正确的,那就是用来验证一下vh这个变量是不是null,由于后面直接用了常量,因此对vh变量的null检查就需要额外的步骤来完成。 那么有没有办法去掉这个检查,真正地让编译器直接使用常量呢,答案是将ValueHolder中的v属性声明成static final。这里就不在列出字节码了,感兴趣的话可以自己试一下。 
上面的讨论针对的是基本数值类型,对通过编译器常量初始化String对象也是适用的,那么引用类型又会是什么情况呢?让我们来改一下本节最开始的时候的那段程序,如下, 
Java代码   收藏代码
  1. public class FinalField {  
  2.     public static void main(String[] args) {  
  3.         ValueHolder vh = new ValueHolder();  
  4.         String v = vh.v;  
  5.         System.out.println(v);  
  6.     }  
  7.       
  8.     final public static class ValueHolder {  
  9.         public String v = new String();  
  10.     }  
  11. }  

通过查看字节码,正如我们所预期的,main函数中对ValueHolder的v属性的访问是通过access$0这个由编译器自动为我们生成的函数来完成的。字节码如下: 
Java代码   收藏代码
  1. invokestatic    #19; //Method com/taobao/tianxiao/FinalField2$ValueHolder.access$0:(Lcom/taobao/tianxiao/FinalField2$ValueHolder;)Ljava/lang/String;  


那么将ValueHolder的v声明成final又会是什么情况呢?答案是没有任何变化,main函数对ValueHolder中的v属性的访问仍然是通过access$0来完成的。 
综上所述,我们可以得出如下几点结论: 
  1. 将类中的引用类型的属性声明成final不会对程序生成的字节码造成任何的改变,仅仅可以帮助编译器确定这个属性在被赋值之后不会被修改;
  2. 将类中的基本数值类型以及用编译器常量初始化的String类型的属性声明成final,确实会让编译器对访问这些属性的操作进行优化,直接使用常量值,而不是通过自动生成访问函数来完成,从而可以减少一次方法调用。但是,由于还是为需要判断引用是否为null而调用一次getClass()方法,因此性能上的提高有限;

除了final属性或者变量之外,很多资料上也会提到final方法对程序的性能也是由帮助的。但是本文没有谈到final方法,因为编译器对final方法能够做的优化很有限,可以说基本是干不了什么事情的。这是由继承引起的问题,由于子类在覆写父类的方法时,是可以将final关键字抹去的,因此编译器是没有足够多的信息来优化final方法的。final方法的优化是在运行期由虚拟机根据程序的执行情况来完成的,优化采用的方法本质同本文说的一样,就是减少方法调用,书面化一点也就是内联。
LIS2DH三轴加速度传感器原理图设计,已量产(加速度传感器) 最近在做一款可穿戴设备原型时,选了 LIS2DH 这款加速度计 —— 比起之前常用的 ADXL345,它在低功耗和稳定性上的优化刚好戳中了可穿戴设备 “续航 + 可靠” 的核心需求。下面结合这次画的原理图,聊聊这个小模块的硬件设计细节。 阅读详情

相关推荐

el-transfer和el-tree进行结合搞一个树形穿梭框

由于业务需求需要在穿梭框里使用树形结构,但是本身element里并不支持,于是参考了别的大佬发的文章作为思路及后续自己新增了一些处理功能。

bbkhhvv的博客 6306

Java final关键字详解

在java中,final关键字可以有如下的用处: [list=1] [*]final关键字可以被加到类的声明中,final类是不允许继承的; [*]final关键字可以被加到方法声明中,final方法是不允许重写的(override),这个效果同私有方法一样; [*]final关键字可以被家到属性或者变量的声明中,final属性或者变量一旦赋值之后就不允许再发生变化。对于基本类型(prim...

To Be the OnE 222

Globus 数据下载

Globus 大数据下载流程~

weixin_43978558的博客 6624

一文带你搞懂Java-final关键字

上述代码存在并发安全问题,writer和reader同时进行,writer线程进行类的初始化,此时JVM可能会进行指令的重排序,将a,flag等变量的初始化赋值重排序到构造函数之外,导致reader读取的a,flag变量是基础变量的初始值即0和false(指令顺序不一定发生,并且需要特定的硬件和JVM环境)。在构造函数内对一个final修饰的对象的成员域的写入,与随后在构造函数之外把这个被构造的对象的引用赋给一个引用变量,这两个操作是不能被重排序的。编译期常量和非编译期常量。final修饰引用变量。

qq_41124175的博客 1125

泛微OA与金蝶云星空深度集成实战:审批与表单双场景解析

本文深入解析泛微OA与金蝶云星空的深度集成实战,聚焦审批流程与表单对接两大核心场景。通过具体案例,详细阐述了如何实现业务数据与审批流程的自动联动,打破数据孤岛,提升企业运营效率与数据一致性,并分享了接口设计、安全策略及实战避坑指南。

4k5l6j7h8的博客 466

机器视觉最全面试题总结

空洞卷积(Atrous Convolution)也叫做膨胀卷积、扩张卷积,最初的提出是为了解决图像分割在用下采样(池化、卷积)增加感受野时带来的特征图缩小,后再上采样回去时造成的精度上的损失。空洞卷积通过引入了一个扩张率的超参数,该参数定义了卷积核处理数据时各值的间距。可以在增加感受野的同时保持特征图的尺寸不变,从而代替下采样和上采样a. 是普通的卷积过程(dilation rate = 1),卷积后的感受野为3b. 是dilation rate = 2的空洞卷积,卷积后的感受野为5。

@bangbang的博客 9206

7.final

文章目录1. final的指令重排1.1. 赋值`final属性`的指令重排1.2. 读取`final属性`的指令重排1.3. `final属性`为引用类型 1. final的指令重排 对于 final 域,编译器和处理器要遵守两个重排序规则。 在构造函数内对一个final变量赋值,与随后把这个被构造对象的引用赋值给一个变量,这两个操作之间不能重排序 初次读一个包含final变量的对象,与随后初...

苏曼的博客 315

批量导入经纬度点到奥维地图中

随便说说 最近出来出差,天天东奔西跑的,跑完了回到酒店还得整理一天的成果,基本上晚上9点才能整理完,累的基本上倒头就睡,没时间鼓捣自己喜欢的东西了。但想着也得写点啥吧,想来想去就那最近工作中用到的东西吧。因为我们现在的工作内容呢,基本上就是按照着上面给的地理位置找到地方开始检查。所以呢,就需要批量的把手上的经纬点信息导入到地图中,安排好路线,利用导航高效的找到地方。给的坐标就是gps点,wgs-84的,使用奥维地图最方便了,不用做坐标转换直接能用。所以作为组里的gis担(苦)当(力),这个导坐标的活就由我来

lionhenryzxxy的博客 3万+

final的深入理解 - final数据

先通过例子看一看: 第一个类: public class Value { int i; public Value(int i){ this.i = i; } } 第二个类: import java.util.Random; public class FinalData { private static Random rand = new Ran

踏雁寻花 3230

Linux系统安全学习手册

Linux反弹shell Linux系统应急响应 Linux服务器安全加固 Linux下的用户、组和权限 Linux中的gcc Linux中su和sudo的用法和区别 Linux系统python2与python3共存 Linux中grep工具的使用 Linux中环境变量的设置 Linux中常见的150个命令(干货) Linux中编写Shell脚本 LNMP环境搭建Wordpress博客 LAMP环境搭建一个Discuz论坛 Linux运维比较常用的一些脚本 Linux系统中安装软

谢公子的博客 7532

通过 MATLAB/Simulink + Simscape Battery,构建了锂电池老化模型与容量衰减仿真系统

本文基于MATLAB/Simulink平台,构建锂电池老化模型与容量衰减仿真系统,研究储能电池健康管理技术。通过搭建电化学-老化耦合模型,模拟循环和日历老化机制,分析温度、充放电倍率、DOD等因素对电池寿命的影响。系统实现了SOH在线估计和RUL预测功能,仿真了标准日历老化、高倍率循环和峰谷套利等典型工况下的电池衰减行为。结果表明:高温和深充放会显著加速电池老化,而精准的SOH管理可提升储能系统经济性30%以上。文章还探讨了数字孪生、机器学习等高级优化方向,为储能系统长周期运营提供技术支撑。

xiaoheshang_123的博客 398

final修饰的对象一定是线程安全吗?

final修饰的对象不一定是线程安全的

清晨qc的博客 1516

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

java final 详解

1、概念 定义:Java中,final 表示最终,也可以称为完结器,表示对象是最终形态的,不可改变的意思。 用途:final 应用于类、方法和变量时意义是不同的,但本质是一样的,都表示不可改变。 使用注意事项: 1)final 修饰变量,表示变量的值不可改变,此时该变量可被称为常量。 2)final 修饰方法,表示方法不能被子类重写; 重写概念:子类中如果创建了一个与父类中相同名称、相同返回值类型、相同参数列表的方法,只是方法体中的实现不同,以实现不同于父类的功能,这种方式被称为方法重写,又称为

春风化雨 4万+

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

Java final类详解

1.什么是final类?Java中有一些类,如String,Math等,就是final类的典型例子。虽然在Java编程中并不经常使用final类和final方法,但它们有着与众不同的特点,即final类不能被继承,不能被覆盖,以及final类在执行速度方面比一般类快。2.为何不能继承final类?有时需要对继承加以限制。例如某些处理特殊运算和操作的类,为了安全理由,不允许被其他类所继承。final...

子航 2万+

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

java final 详解_java final本质详解

导读正文含义:final 含义是【仅可以被赋值一次】。赋值位置:在【声明时赋值】、在【构造方法中赋值】举例:package com.dada.data;public class TestFinal {// 1.声明时赋值private final int i = 0;private final int j;public TestF() {// 2.构造方法中赋值j = 10;}}注意:我们说 fi...

weixin_32526561的博客 181

Java 之 final 详解

在Java中,final 表示“最终的、不可改变的、完结的”,它也是一种修饰符,可以修饰变量、方法和类。final 修饰变量、方法和类时的意义是不同的,但本质是一样的,都表示不可改变,类似 C#里的 sealed 关键字。final 修饰的变量叫做最终变量,也就是常量,修饰的方法叫做最终方法,修饰的类叫做最终类。

流华追梦的专栏 1万+

JAVA final 详解

摘要: final方法可以被重载(同一类中方法名相同但参数不同),但不能被重写(子类覆盖父类实现)。重载是编译时多态,不受final限制;重写是运行时多态,final明确禁止修改父类方法。final方法可被子类继承调用,但不能覆盖。private方法隐式final,static方法也不能被重写。使用final方法可防止核心行为被修改、确保方法安全,早期还能优化性能。构造函数和接口方法不能为final。关键区别在于重载(编译时)和重写(运行时)的不同机制。

萧曳丶 1016

java final 详解_java中Final详解

Java中的final关键字非常重要,它可以应用于类、方法以及变量。这篇文章中我将带你看看什么是final关键字?将变量,方法和类声明为final代表了什么?使用final的好处是什么?最后也有一些使用final关键字的实例。final经常和static一起使用来声明常量,你也会看到final是如何改善应用性能的。final关键字的含义?final在Java中是一个保留的关键字,可以声明成员变量、...

weixin_35741812的博客 147

final关键字详解

注:文章内容由网络及相关书籍整理而来,如此只为共享知识,给予帮助。 final在Java中并不常用,然而它却为我们提供了诸如在C语言中定义常量的功能,不仅如此,final还可以让你控制你的变量、方法或者是一个类是否可被覆写或继承等功能,这些特点使final在Java中拥有了一个不可或缺的地位,也是学习Java时必须要知道和掌握的关键字之一。 一、final变量 ...

dianmeiwu1724的博客 219

Java final 详解

一 final 基础使用 1.1 修饰类 当某个类的整体定义为 final 时,就表明了你不能打算继承该类,而且也不允许别人这么做。即这个类是不能有子类的。 注意:final 类中的所有方法都隐式为 final,因为无法覆盖他们,所以在 final 类中给任何方法添加 final 关键字是没有任何意义的。 这里顺道说说 final 类型的类如何拓展? 比如 String 是 final 类型,我们想写个 MyString 复用所有 String 中方法,同时增加一个新的 toMyString() 的方法,应

liuwg1226的专栏 1万+

java中final详解_Java中final用法与详解

Java中final用法与详解final作为Java中经常用到的关键字,了解final的使用方法是非常有必要的。这里从final关键字在数据域、方法和类中三个方面分析final关键字的主要用法。final应用于基本数据类型1、对于基本数据类型final使数值恒定不变。2、一个既是static又是final的域只占据一段不能改变的存储空间。(public static final int VAL...

weixin_42361018的博客 1691

final关键字详解与final,finally,finalize的区别

final 用于定义常量、禁止重写或继承,是一种修饰符。finally 用于异常处理,确保代码块中的内容得到执行,无论是否有异常抛出。finalize 是一个方法,用于对象在被垃圾回收前的清理,但不再推荐使用。

weixin_53066120的博客 6003
上一篇: 在多线程下的long和double
下一篇: 通过Return键控制虚拟键盘消失
weeweee
博客等级 码龄16年 10粉丝 44原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值