如何防止修改类中的私有字段?

Hos*_*ein 164 java arrays oop class

想象一下,我有这个课程:

public class Test
{
  private String[] arr = new String[]{"1","2"};    

  public String[] getArr() 
  {
    return arr;
  }
}
Run Code Online (Sandbox Code Playgroud)

现在,我有另一个使用上述类的类:

Test test = new Test();
test.getArr()[0] ="some value!"; //!!!
Run Code Online (Sandbox Code Playgroud)

所以这就是问题:我从外面访问了一个类的私有字段!我怎么能阻止这个?我的意思是如何让这个数组不可变?这是否意味着使用每种getter方法,您都可以通过自己的方式访问私有字段?(我不想要任何像Guava这样的库.我只需要知道正确的方法来做到这一点).

sp0*_*00m 375

如果您可以使用List而不是数组,则Collections提供了一个不可修改的列表:

public List<String> getList() {
    return Collections.unmodifiableList(list);
}
Run Code Online (Sandbox Code Playgroud)

  • 请注意,如果数组的元素本身是不可变的,就像`String`那样,这是有效的,但是如果它们是可变的,则必须要做一些事情以防止调用者更改它们. (2认同)

Old*_*eon 161

您必须返回阵列的副本.

public String[] getArr() {
  return arr == null ? null : Arrays.copyOf(arr, arr.length);
}
Run Code Online (Sandbox Code Playgroud)

  • 我想提醒大家的是,虽然这个问题已被选为问题的正确答案,但问题的最佳解决方案**实际上是[sp00m](http://stackoverflow.com/a/14809392/823393)说 - 返回一个`Unmodifiable List`. (120认同)
  • 如果你确实需要返回一个数组,那就没有了. (47认同)
  • 但要确保它是数据的深层副本,而不是浅层副本.对于字符串,它没有区别,对于像Date这样的其他类,可以修改实例的内容,它可以有所作为. (20认同)
  • @Svish我应该更加激烈:如果你从API函数返回一个数组,你做错了.在库中的私有函数中,它*可能是正确的.在API中(防止可修改性发挥作用),它从不**. (16认同)
  • 实际上,所讨论的**问题的最佳解决方案通常是@MikhailVladimirov所说: - 提供或返回数组或集合的**视图**. (8认同)
  • @Svish那是多少次?几乎肯定不需要返回数组. (2认同)
  • @KonradRudolph无论多久都没关系.有时它是,并且因为问题涉及到一个我认为答案也是合适的:) (2认同)
  • @XaviLópez:那又怎样?甚至`java.lang.String`也可以通过反射来搞乱.请记住:封装是关于保护*代码*免受*代码*的影响,而不是保护*代码*来自*编码器*. (2认同)

Mik*_*rov 45

修饰符private仅保护字段本身不被其他类访问,但不保护此字段的对象引用.如果您需要保护引用的对象,请不要将它放弃.更改

public String [] getArr ()
{
    return arr;
}
Run Code Online (Sandbox Code Playgroud)

至:

public String [] getArr ()
{
    return arr.clone ();
}
Run Code Online (Sandbox Code Playgroud)

或者

public int getArrLength ()
{
    return arr.length;
}

public String getArrElementAt (int index)
{
    return arr [index];
}
Run Code Online (Sandbox Code Playgroud)

  • 本文不反对在数组上使用clone,仅针对可以子类化的对象. (5认同)

mic*_*l_s 28

Collections.unmodifiableList已经提到-的Arrays.asList()奇怪不是!我的解决方案也是使用外部列表并按如下方式包装数组:

String[] arr = new String[]{"1", "2"}; 
public List<String> getList() {
    return Collections.unmodifiableList(Arrays.asList(arr));
}
Run Code Online (Sandbox Code Playgroud)

复制数组的问题是:如果你每次访问代码并且数组很大时都这样做,那么你肯定会为垃圾收集器创建大量的工作.所以副本是一个简单但非常糟糕的方法 - 我会说"便宜",但内存昂贵!特别是当你拥有超过2个元素时.

如果你看看源代码Arrays.asList,并Collections.unmodifiableList有其实不多创建.第一个只是包装数组而不复制它,第二个只是包装列表,使其更改不可用.


iTe*_*ech 6

您也可以使用ImmutableList哪个应该比标准更好unmodifiableList.该类是由Google创建的Guava库的一部分.

这是描述:

与Collections.unmodifiableList(java.util.List)不同,它是一个可以更改的单独集合的视图,ImmutableList的实例包含自己的私有数据,永远不会更改

这是一个如何使用它的简单示例:

public class Test
{
  private String[] arr = new String[]{"1","2"};    

  public ImmutableList<String> getArr() 
  {
    return ImmutableList.copyOf(arr);
  }
}
Run Code Online (Sandbox Code Playgroud)