我一直在努力解决Java中的一个问题并且它已经开始找到我了,我真的想不出一个正确的解决方法.
我有一个最终的对象属性,但是动态的.也就是说,我希望值在指定后保持不变,但每个运行时的值可以不同.所以我在类的开头声明了类级变量 - 比如说private final FILE_NAME;.然后,在构造函数中,我为它赋值 - 比方说FILE_NAME = buildFileName();
当我在buildFileName()抛出异常的方法中有代码时,问题就开始了.所以我在构造函数中尝试这样的东西:
try{
FILE_NAME = buildFileName();
}
catch(Exception e){
...
System.exit(1);
}
Run Code Online (Sandbox Code Playgroud)
现在我有一个错误 - "空白的最终字段FILE_NAME可能尚未初始化." 这是我开始对Java的严格编译器感到有些恼火的地方.我知道这不会是一个问题,因为如果它到达catch,程序将退出...但编译器不知道,因此不允许此代码.如果我尝试在catch中添加一个虚拟赋值,我会得到 - "可能已经分配了最终字段FILE_NAME." 我显然不能在try-catch之前分配一个默认值,因为我只能分配一次.
有任何想法吗...?
多年来,我一直在使用Java Memory Model进行日常工作.我认为我对数据竞争的概念以及避免它们的不同方法(例如,同步块,易变变量等)有很好的理解.但是,仍然有一些我认为我完全不了解内存模型的东西,这是类的最终字段应该是线程安全的方式而没有任何进一步的同步.
所以根据规范,如果一个对象被正确初始化(也就是说,没有引用对象在其构造函数中以某种方式转义,使得引用可以被另一个线程看到),那么,在构造之后,任何看到该对象的线程对象将保证看到对象的所有最终字段的引用(在它们构造时的状态),没有任何进一步的同步.
特别是,标准(http://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.4)说:
最终字段的使用模型很简单:在该对象的构造函数中设置对象的最终字段; 并且在对象的构造函数完成之前,不要在另一个线程可以看到的地方写入对正在构造的对象的引用.如果遵循此原因,那么当另一个线程看到该对象时,该线程将始终看到该对象的最终字段的正确构造版本.它还将看到那些最终字段引用的任何对象或数组的版本,这些字段至少与最终字段一样是最新的.
他们甚至给出了以下示例:
class FinalFieldExample {
final int x;
int y;
static FinalFieldExample f;
public FinalFieldExample() {
x = 3;
y = 4;
}
static void writer() {
f = new FinalFieldExample();
}
static void reader() {
if (f != null) {
int i = f.x; // guaranteed to see 3
int j = f.y; // could see 0
}
}
}
Run Code Online (Sandbox Code Playgroud)
其中线程A应该运行"reader()",并且线程B应该运行"writer()".
到目前为止,显然是如此的好.
我主要担心的是......这在实践中真的有用吗?据我所知,为了使线程A(运行"reader()")看到对"f"的引用,我们必须使用一些同步机制,例如使f volatile,或者使用lock来同步访问F.如果我们不这样做,我们甚至不能保证"reader()"将能够看到初始化的"f",也就是说,由于我们没有同步访问"f",读者可能会看到" null"而不是由writer线程构造的对象.这个问题在http://www.cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html#finalWrong中说明,这是Java内存模型的主要参考之一[大胆强调我的]:
现在,已经说过所有这些,如果在一个线程构造一个不可变对象(即一个只包含最终字段的对象)之后,你想确保所有其他线程都能正确看到它,你仍然通常需要使用同步.例如,没有其他方法可以确保第二个线程可以看到对不可变对象的引用.程序从最终字段获得的保证应该仔细调整,仔细了解如何在代码中管理并发.
因此,如果我们甚至不能保证看到对"f"的引用,那么我们必须使用典型的同步机制(volatile,lock等),并且这些机制确实已经导致数据争用消失,最终的需求是我甚至都不会考虑的事情.我的意思是,如果为了使"f"对其他线程可见,我们仍然需要使用volatile或synchronized块,并且它们已经使内部字段对其他线程可见......有什么意义(在线程安全术语中)首先在场上进行决赛?
请看以下示例:
public void init() {
final Environment env = new Environment();
Runtime.getRuntime().addShutdownHook(new Thread() {
public void run() {
env.close();
}
});
}
Run Code Online (Sandbox Code Playgroud)
首先,env存储在哪里?是吗:
我的猜测是第一个选择.
其次,执行这样做会产生任何性能问题(而不是简单地创建env类的成员变量并引用它),特别是如果要创建大量引用最终局部变量的内部类构造.
虽然在Java中使用'final'关键字的原因有很多,但我一遍又一遍地听到的一个原因是它使您的代码更安全.虽然这在这个微不足道的案例中似乎有意义:
public class Password
{
public final String passwordHash;
...
}
Run Code Online (Sandbox Code Playgroud)
使用final关键字,您可以预期没有恶意代码可以更改变量passwordHash.但是,使用反射可以更改passwordHash字段的最终修饰符.
"最终"提供任何真正的安全性,还是只是安慰剂?
编辑: 有一些非常有趣的讨论,我希望我能接受不止一个答案.谢谢各位的意见.
在通过构造函数传递给匿名类的最终变量中,Jon Skeet提到变量通过自动生成的构造函数传递给匿名类实例.在这种情况下,为什么我无法使用反射来查看构造函数:
public static void main(String... args) throws InterruptedException {
final int x = 100;
new Thread() {
public void run() {
System.out.println(x);
for (Constructor<?> cons : this.getClass()
.getDeclaredConstructors()) {
StringBuilder str = new StringBuilder();
str.append("constructor : ").append(cons.getName())
.append("(");
for (Class<?> param : cons.getParameterTypes()) {
str.append(param.getSimpleName()).append(", ");
}
if (str.charAt(str.length() - 1) == ' ') {
str.replace(str.length() - 2, str.length(), ")");
} else
str.append(')');
System.out.println(str);
}
}
}.start();
Thread.sleep(2000);
Run Code Online (Sandbox Code Playgroud)
}
输出是:
100
constructor : A$1()
Run Code Online (Sandbox Code Playgroud) 代码优先.
#include <iostream>
using namespace std;
struct A final {};
struct B {};
int main()
{
cout << is_final<A>::value << endl; // Output true
cout << is_final<B>::value << endl; // Output false
return 0;
}
Run Code Online (Sandbox Code Playgroud)
如何实现类is_final?
我碰巧在我的工作场所遇到过Java代码.这是场景:有两个类 - ClassA和ClassB.
ClassA除了4个公共静态最终字符串值之外什么都没有.它的目的是使用那些值ClassA.variable(不要问我为什么,这不是我的代码).
ClassB进口ClassA.我编辑了字符串值ClassA并对其进行了编译.当我跑步时,ClassB我可以看到它使用的是旧值 - 而不是新值.我必须重新编译ClassB才能使用新的值ClassA!(我不得不重新编译其他导入的类ClassA!)
这只是因为JDK 1.6或我之前应该知道重新编译的ClassB!开导我.:)
编译完代码后,我遇到了两个错误.
错误是:
1.
local variable input is accessed within inner class;
needs to be declared final
String name = input.getText();
Run Code Online (Sandbox Code Playgroud)
2.
local variable c_age is accessed within inner class;
needs to be declared final
Object child_age = c_age.getSelectedItem();
Run Code Online (Sandbox Code Playgroud)
这是我的代码:
import javax.swing.*;
import java.awt.event.*;
public class GUI
{
public static void main(String[] args)
{
JFrame frame = new JFrame("Try GUI");
JLabel l1 = new JLabel("Please Enter Your Child's Name");
JTextField input = new JTextField("",10);
JLabel l2 = new JLabel("Choose Your Child's …Run Code Online (Sandbox Code Playgroud) final int a = 1;
final int b;
b = 2;
final int x = 0;
switch (x) {
case a:break; // ok
case b:break; // compiler error: Constant expression required
}
/* COMPILER RESULT:
constant expression required
case b:break;
^
1 error
*/
Run Code Online (Sandbox Code Playgroud)
为什么我会遇到这种错误?如果我愿意final int b = 2,一切正常.
听起来有点奇怪,但是可以final在运行时添加修改器吗?
我有一个标记为的变量public static int/short.在某些时候我想阻止改变它的值,我希望将其可访问性保持为标准静态值(ClassName.field).
public class Main {
private static int a = 0;
public static void addFinalMod(Field f) {
Field modifiersField = null;
try {
modifiersField = Field.class.getDeclaredField("modifiers");
}
catch (NoSuchFieldException e) {
e.printStackTrace();
}
modifiersField.setAccessible(true);
try {
modifiersField.setInt(f, f.getModifiers() & Modifier.FINAL);
}
catch (IllegalAccessException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
System.out.println(a);
try {
Field f = Main.class.getDeclaredField("a");
addFinalMod(f);
}
catch (NoSuchFieldException e) {
e.printStackTrace();
}
a = …Run Code Online (Sandbox Code Playgroud) final ×10
java ×9
c++ ×1
c++11 ×1
case ×1
constants ×1
constructor ×1
exception ×1
modifier ×1
performance ×1
public ×1
recompile ×1
security ×1
static ×1
type-traits ×1