Java final修饰符

Mar*_*gus 54 java final

有人告诉我,我误解了final.的影响.final关键字有什么影响?

以下简要概述了我的想法,我知道:

Java final修饰符(又称聚合关系)

原始变量:只能设置一次.(内存和性能增益)
对象变量:可以修改,最终适用于对象引用.
字段:只能设置一次.
方法:不能被覆盖,隐藏.
课程:不能扩展.
垃圾收集:将强制Java世代垃圾收集标记扫描双扫.

Can's和Cant's

  • 可以使克隆失败(这既好又坏)
  • 可以使不可变的原语aka const
  • 可以使空白不可变 - 在创建时初始化也称为readonly
  • 可以使对象浅不可变
  • 可以使范围/可见性不可变
  • 可以使方法调用开销更小(因为它不需要虚拟表)
  • 可以使方法参数用作final(即使你不是)
  • 可以使对象线程安全(如果对象被定义为final,它不会使方法参数最终)
  • 可以进行模拟测试(不是你可以对它做任何事情 - 你可以说错误是有意的)
  • 不能交朋友(与其他朋友互相变化,休息不变)
  • 以后不能将变为不可变的变量(但可以像工厂模式一样修复)
  • 无法使数组元素成为不可变的,也是不可变的
  • 无法创建对象的新实例(这既好又坏)
  • 无法进行序列化工作

没有替代品final,但有包装+私人和枚举.

Ste*_*n C 74

依次回答你的每一点:

原始变量:只能设置一次.(记忆和性能增益)

是的,但没有内存增益,也没有性能提升.(你假设的性能增益来自只设置一次......而不是来自final.)

对象变量:可以修改,最终应用于对象引用.

是.(但是,这个描述忽略了这与Java语言的其余部分处理对象/引用二元性的方式完全一致.例如,当对象作为参数传递并作为结果返回时.)

字段:只能设置一次.

真正的答案是:与变量相同.

方法:不能被覆盖,隐藏.

是.但是请注意,这里发生的是final关键字在不同的语法上下文中使用,表示final对于字段/变量不同的内容.

课程:不能扩展.

是.但也见上面的注释.

垃圾收集:将强制Java世代垃圾收集标记扫描双扫.

这是无稽之谈.该final关键字没有关联任何垃圾收集.你final最终可能会感到困惑......他们是无关的.

但即使是终结者也不会强行进行额外的扫描.发生的情况是,需要完成的对象在一侧设置,直到主GC完成.然后GC在对象上运行finalize方法并设置其标志......并继续.GC下次运行时,该对象被视为普通对象:

  • 如果可以到达则标记并复制
  • 如果无法访问,则不会标记.

(你的特征 - "Java代垃圾收集标记扫描"是乱码.垃圾收集器可以是"标记扫描"或"世代"("复制"的子类).它不能同时存在.通常使用Java世代收集,只会在紧急情况下回落到标记扫描;即当空间不足或低暂停收集器无法跟上时.)

可以使克隆失败(这既好又坏)

我不这么认为.

可以使不可变的原语aka const

是.

可以使空白不可变 - 在创建时初始化也称为readonly

是的......虽然我从未听过之前用过的"空白不可变"这个词.

可以使对象浅不可变

对象可变性是关于可观察状态是否可能改变.因此,声明属性final可能会也可能不会使对象表现为不可变.除了"浅不可变"的概念之外没有明确定义,尤其是因为没有对类语义的深入了解,就无法映射"浅"的概念.

(很明显,变量/字段的可变性是JLS上下文中一个定义明确的概念.从JLS的角度来看,它只是对象可变性的概念.)

可以使范围/可见性不可变

术语错误.可变性是关于对象状态.可见性和范围不是.

可以使方法调用开销更小(因为它不需要虚拟表)

在实践中,这是无关紧要的.如果它们没有被应用程序实际使用的任何类重写,那么现代JIT编译器也会对非final方法执行此优化.(聪明的事情发生......)

可以使方法参数用作final(即使你不是)

咦?我无法解析这句话.

可以使对象线程安全

在某些情况下是的.

(如果对象被定义为final,则不会使方法参数最终)

是的,如果你的意思是上课是最终的.对象不是最终的.

可以进行模拟测试(不是你可以对它做任何事情 - 你可以说错误是有意的)

不解析.

不能交朋友(与其他朋友互相变化,休息不变)

Java没有"朋友".

以后不能将变为不可变的变量(但可以像工厂模式一样修复)

对于第一个是,一个final字段不能从mutable切换到immutable.

目前还不清楚你的意思是第二部分.确实,您可以使用工厂(或构建器)模式来构造不可变对象.但是,如果您final在任何时候都没有使用对象字段,那么该对象将是可变的.

或者,您可以实现使用非最终字段表示不可变状态的不可变对象,并且您可以设计API,以便您可以"翻转开关"以使先前可变对象从现在开始不可变.但是如果你采用这种方法,你需要对同步更加小心......如果你的对象需要是线程安全的.

无法使数组元素成为不可变的,也是不可变的

是的,但是你的术语被打破了; 请参阅上面有关"浅层可变性"的评论.

无法创建对象的新实例(这既好又坏)

没有.没有什么能阻止你用最终字段或最终类或最终方法创建一个对象的新实例.

无法进行序列化工作

编号序列化工作.(当然,final使用自定义readObject方法对字段进行反序列化会带来问题......尽管您可以使用反射方法解决它们.)

最终没有其他选择,

正确.

但有包装+私人

是的,模数(严格地说)非最终字段的非同步getter可能是非线程安全的...... 即使它在对象构造期间被初始化然后从未改变过!

和枚举.

解决了另一个问题.并且enums可以变得可变.

  • 嗯,Java确实有朋友(实际上相当多).它只是在C++使用它们的意义上没有朋友;-). (9认同)
  • 如果通过"可以使克隆失败",他的意思是"可以使深层克隆难以实现"那么这是真的.此外,尽管基本序列化有效,但最终可以使自定义序列化变得困难. (3认同)