这个问题主要不是关于字符串.出于学术上的好奇心,我想知道final变量上的修饰符如何改变程序的行为.以下示例显示了它是可能的.
打印这些线条 true
final String x = "x";
System.out.println(x + x == "xx");
Run Code Online (Sandbox Code Playgroud)
但这些线条打印出来 false
String x = "x";
System.out.println(x + x == "xx");
Run Code Online (Sandbox Code Playgroud)
除了String实习之外,如果final从变量声明中删除修饰符,还有其他任何可能导致程序行为发生变化的事情吗?我假设程序使用或不使用修饰符进行编译.
请不要投票将其关闭为比较使用==在Java中声明为final的字符串的副本.我理解这个String例子.
我问是否有任何其他原因删除final修饰符可以有所作为.请有人链接到答案或回答问题.谢谢.
所述final改性剂仅确保该变量明确赋值,并禁止任何重新分配,并从该变量.
原始类型或类型String的变量是final,并使用编译时常量表达式(第15.28节)初始化,称为常量变量.
变量是否是常量变量可能对类初始化(第12.4.1节),二进制兼容性(第13.1节,第13.4.9节)和明确赋值(第16节)有影响.
有JLS阅读,像样的数目,并且覆盖主穴: 通过JLS§13.4.9,你会不会遇到在任何不良影响消除的final修正.
但是,通过JLS 17.5,如果您依赖于线程的保证只能看到它可以观察到的对象中明确赋值的变量,那么删除该final变量将导致这些变量不再对另一个线程可见.
因此,如果我们首先查看类初始化,如果字段是静态的而不是常量变量,则存在围绕类初始化的规则:
类或接口类型T将在第一次出现以下任何一个之前立即初始化:
- T是一个类,并且创建了T的实例.
- T是一个类,并且调用由T声明的静态方法.
- 分配由T声明的静态字段.
- 使用由T声明的静态字段,该字段不是常量变量(第4.12.4节).
在JLS§13.1中,明确指出更改字段final可以破坏二进制兼容性:
对作为常量变量的字段(第4.12.4节)的引用在编译时被解析为表示的常量值.二进制文件中的代码中不应存在对此类字段的引用(包含该字段的类或接口除外,该字段将具有初始化它的代码).这样的字段必须总是看似已经初始化(§12.4.2); 绝不能遵守此类字段类型的默认初始值.有关讨论,请参见§13.4.9.
从13.4.9开始:
如果未声明为final的字段更改为声明为final,则它可能会破坏与预先存在的二进制文件的兼容性,这些二进制文件尝试将新值分配给该字段.
删除关键字final或更改字段初始化的值不会破坏与现有二进制文件的兼容性.
如果字段是常量变量(§4.12.4),则删除关键字final或更改其值不会破坏与预先存在的二进制文件的兼容性,导致它们不能运行,但是它们不会看到任何新的用法值该字段除非重新编译.即使用法本身不是编译时常量表达式(第15.28节),也是如此.
这个结果是决定支持条件编译的副作用,如§14.21结尾所述.
所以仅从这一点开始,要小心突然改变领域final. 删除该字段是安全的.
......但这仅适用于单线程世界.来自JLS 17.5:
声明为final的字段初始化一次,但在正常情况下从未更改过.最终字段的详细语义与普通字段的语义略有不同.特别是,编译器有很大的自由来跨越同步障碍移动最终字段的读取,并调用任意或未知的方法.相应地,允许编译器将最终字段的值保存在寄存器中,而不是在必须重新加载非最终字段的情况下从内存重新加载它.
final字段还允许程序员在没有同步的情况下实现线程安全的不可变对象.线程安全的不可变对象被所有线程视为不可变的,即使使用数据争用传递线程之间的不可变对象的引用也是如此.这可以提供安全保证,防止错误或恶意代码滥用不可变类.必须正确使用最终字段以提供不可变性的保证.
当构造函数完成时,对象被认为是完全初始化的.在该对象完全初始化之后只能看到对象引用的线程可以保证看到该对象的最终字段的正确初始化值.
因此,如果您的程序依赖于上述保证以使其正常运行,那么删除final关键字将对线程产生影响.