为什么Java中的最终实例类变量?

如果实例变量设置为final,则其值不能更改为

public class Final { private final int b; Final(int b) { this.b = b; } int getFinal() { return b = 8; // COMPILE TIME ERROR } } 

在代码的某处我看到实例类变量HashMap被声明为final

  private final Map cacheMap = new HashMap(); 

我不明白为什么这样宣布? 通常在这种情况下它被声明。 这是否意味着如果我放入哈希映射然后我无法更改其值?

编辑:
如果声明为final的cacheMap作为参数传递给另一个类,那么如果我更改其引用,则不会显示final的错误。 为什么会这样?

  class CacheDTO { private Map conditionMap; public Map getConditionMap() { return conditionMap; } public void setConditionMap(Map conditionMap) { this.conditionMap = conditionMap; } } 

然后

 private final Map cacheMap = new HashMap(); CacheDTO cc = new CacheDTO(); cc.setConditionMap(cacheMap); Map cacheMapDeclaredAsFinal = cc.getConditionMap(); Map newMap = new HashMap(); cacheMapDeclaredAsFinal = newMap; // In this case no error is shown. Though cacheMapDeclaredAsFinal reference is obtained by calling cc.getConditionMap() and cacheMapDeclaredAsFinal refers to final. 

你不能改变篮子。 你仍然可以改变里面的水果。

来自语言规范#14.12.4

一旦分配了最终变量,它总是包含相同的值。 如果最终变量包含对对象的引用,则可以通过对对象的操作来更改对象的状态,但该变量将始终引用同一对象。

声明fieldreference final时,必须在构造函数退出时设置一次值。

您只能在构造函数中为该变量赋值。

  private final Map CacheMap = new HashMap(); 

在这里你可以做到

 CacheMap.put(..... 

在课堂上。

但你做不到

 CacheMap = something. //compile error. 

你应该知道valuereference之间的区别。

编辑

这里

  Map cachemapdeclaredasfinal = cc.geConditionMap(); Map newMap = new HashMap(); cachemapdeclaredasfinal = newMap; // In this case no error is shown 

原因,

由于cachemapdeclaredasfinal不是新映射,因此它是conditionMap的另一个引用

当你创建这样的新实例时

  Map cachemapdeclaredasfinal = new HashMap(cc.geConditionMap()); 

那个错误消失了。 既然你用了新的

编辑2:

  private Map conditionMap; public void setConditionMap(Map ConditionMap) { this.conditionMap = conditionMap; } private final Map CacheMap = new HashMap(); CacheDto cc = new CacheDto(); cc.setConditionMap(CacheMap); Map cachemapdeclaredasfinal = cc.geConditionMap(); Map newMap = new HashMap(); cachemapdeclaredasfinal = newMap; 

在这里,你困惑的是。

您将一个final声明的map分配给一些正常(非finalmap 。 当您检索到该正常时,只有您正在获取并且不是final因此您可以进一步使用/ assign它。

简而言之

 normalMap= finalMap; //no error since normalMap is not final finalMap =normalMap;// compiler error since normalMap is final 

final与变量所引用的对象的内容无关。 您将无法更改变量的值并使其引用另一个对象。

我认为你在“最终”和“不可变”对象之间感到困惑。

 public class Final { private final int b; Final(int b) { this.b = b; } int getFinal() { return b = 8; // COMPILE TIME ERROR } } 

最终意味着您无法更改对象的引用。 如果是基元,则表示您无法更改该值。 因此,当您尝试将b设置为8时,会出现编译时错误。

 cc.setConditionMap(cacheMap); public void setConditionMap(Map conditionMap) { this.conditionMap = conditionMap; } 

在Java中 – “对象的引用按值传递”(正如Bruce Eckel在他的书“Thinking in Java”中所说的那样)。 所以,您传递的是参考文献的副本。 因此,您现在有2个对同一个cacheMap的引用。

因此,您可以使用任何引用更改cacheMap。 但是你只能将“复制”的引用重新分配给另一个对象,因为它不是最终的(不是原始的,原始的是最终的,不能指向另一个对象)。

正如其他答案所指定的那样,您无法使最终变量引用另一个对象。

引用Java语言规范 :

4.12.4。 最终变量

最终变量只能分配给一次…如果最终变量保存对象的引用,则可以通过对对象的操作来更改对象的状态,但变量将始终引用同一对象。

在您的问题的已编辑部分中,该规则未被违反:

  • 您已将CacheMap声明为final,并且您不会在任何地方重新分配新值。 如果你能够做到这一点,那就违反了。

  • cachemapdeclaredasfinal引用 CacheMap引用的相同内容,而不是 final本身。

正如Suresh提到的upthread,如果您阅读Java中的值和引用,将会有所帮助。 一个很好的起点是这个主题: Java是“通过引用传递”吗? 。 确保你理解为什么Java总是按值传递并且永远不会通过引用传递 – 这就是为什么CacheMap的“最终性”没有被传递的原因。

这意味着无法更改hasmap。 hashmap中的元素与最终分隔符无关。

 private static final HashMap map = new HashMap(); public void foo(K k, V v) { map.push(k, v); //This is allowed map = new HashMap //This is not allowed } 

这个问题可能会对您有所帮助: http : //www.stackoverflow.com/questions/40480/is-java-pass-by-reference

根据我的理解,这就是实际发生的事情:当您使用java将对象传递给方法时,您基本上是将指针传递给该对象。 因此,当你调用cc.geConditionMap()时,你基本上得到了指针。 当你改变它时,你实际上并没有改变对象。 您正在将指针的副本指向另一个地图。

由于您将副本存储到非最终变量,因此您的指针副本不受最终保护。

在“C / C ++”术语中:

 Thing * a; Thing * const b; Thing const * c; Thing const * const d; 

Java中的“final”最接近“b”。 “b”是指向Thing的常量指针。 “b”不能改为指向不同的东西,但是事物本身可能会改变。

Java没有“c”和“d”的表示。 “c”是指向常数Thing的指针。 “c”可能指向其他东西,但它指向的东西不能改变(至少,不是通过“c”本身)

“d”结合“b”和“c”:“d”是指向常数Thing的常量指针。

哦,“a”当然没什么特别的。

嗯…在Java中,并非所有东西都是对象,因此规则略有不同。

 final int f = 9; 

其中,在C中很像

 int const f = 9; 

这意味着您无法更改“f”或其整数值。

注意:

 int const f; const int g; 

两者意味着相同的事情,但“f”恕我直言具有更清晰的意义。 不幸的是,“g”很常见。

这是我所知道的关键字final的用法:

在class级宣言中

这意味着该类:不能分类

 public final class SomeClass { //... } 

在一个全局变量中

这意味着一旦为其分配了值,它就无法更改。

 public class SomeClass { private final int value = 5; } 

如果不指定值,则会出现编译错误,但您可以使用合成来赋值。

 public class SomeClass { private final int value; public SomeClass(int value) { this.value=value } } 

在对象参数中

这意味着无法更改传递的对象

 public class SomeClass { public void someMethod(final String value) { //... } } 

在局部变量中

这意味着一旦分配,该值就不会改变

 public class SomeClass { public void someMethod(final String value) { final double pi = 3.14; } } 

在方法中

这意味着该方法无法覆盖

  public class SomeClass { public final void someMethod() { //... } } 

在馆藏和地图中

这意味着集合不能重新初始化, 但并不意味着元素是不可变的 ,每个元素都不会被关键字final所感染

  public class SomeClass { final HashMap someMap = new HashMap(); } 

当引用为final时,它不能链接到任何其他对象。

可以更改对象的值,因此可以向地图添加值,但不能更改地图的对象。

比方说,最终Map map = new HashMap(); new:负责在堆中创建保存值的对象

“map”引用将在堆栈中创建,这是最终的。

 The value of "map" reference is real object created in heap. As "map" reference is final, it can not have any other value in it. When we pass "map" reference, Actually we pass the value of map which is nothing but reference of object created in heap. In the called method, another reference "map" will be created in stack which holds the same reference of object in heap. The same concept is coded in this example 

import java.util.HashMap; import java.util.Map;

public class FinalExample {public static void main(String [] args){

  // Please see this example in case of normal variable and go through the // comment Final1 f1 = new Final1(); f1.fun2(); // Please see this example in case of Map Object and go through the // comment Final2 f2 = new Final2(); f2.fun2(); } 

}

class Final1 {final int a = 10;

 void fun1(int a) { a += 20; System.out.println(a); } void fun2() { // Here we are passing just content of final variable "a" but not the // block "a" itself. // When method fun1 is called another local block "a" will be created // This local "a" has nothing to do with instance final "a". Both are // different // We can change the value of local a it has nothing to do with instance // "a" fun1(a); } 

}

class Final2 {final static Map map = new HashMap();

 static { map.put("1", "Nandeshwar"); map.put("2", "Sah"); } void fun1(Map map) { map.put("3", "John"); map.put("4", "Nash"); System.out.println(map); } void fun2() { // Here (in fun1) we pass the content of final map. The content of final // map is // the refernece of real object which holds the value // "1" "Nandeshwar // "2" "Sah". // When we call fun1, Another object "map(Map)" will be created. this // newly created object "map" will also // indicate the same reference as instance map refers // So the local object "map" and instance object "map" both is // different. But indicates the real Object which holds the value fun1(map); } 

}

注意: – 当您将对象引用声明为final时,这意味着它将始终指向初始化它的堆上的同一对象。

如果声明为final的cacheMap作为参数传递给另一个类,那么如果我更改其引用,则不会显示final的错误。 为什么会这样?

1.)Java是按值传递的,所以当你编写cc.setConditionMap(cacheMap )时, cacheMap是一个对象引用(注意: – cacheMap本身不是一个对象,它只是一个引用)然后你只是传递了它的地址对象在setConditionMap(cacheMap) ,现在在setConditionMap(cacheMap)conditionMapcacheMap的值初始化(缓存Map的值是Map Object的地址)。 现在,在此步骤之后, conditionMapcacheMap引用堆上的同一对象。 但问题是你可以再次设置conditionMap的值,使其指向堆上的其他一些map对象,但cacheMap将始终指向相同的Map对象。

2.)将变量声明为final并不意味着堆上的对象是最终的(完全没有意义,对吧?)而是意味着它变量总是指向它被初始化的对象,没人能改变它。