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

时间:2013-02-11 09:32:27

标签: java arrays oop class

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

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

  public String[] getArr() 
  {
    return arr;
  }
}

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

Test test = new Test();
test.getArr()[0] ="some value!"; //!!!

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

11 个答案:

答案 0 :(得分:375)

如果您可以使用List而不是数组,Collections provides an unmodifiable list

public List<String> getList() {
    return Collections.unmodifiableList(list);
}

答案 1 :(得分:161)

您必须返回阵列的副本

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

答案 2 :(得分:45)

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

public String [] getArr ()
{
    return arr;
}

为:

public String [] getArr ()
{
    return arr.clone ();
}

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

public String getArrElementAt (int index)
{
    return arr [index];
}

答案 3 :(得分:28)

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

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

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

如果查看Arrays.asListCollections.unmodifiableList的源代码,实际上创建的内容并不多。第一个只是包装数组而不复制它,第二个只是包装列表,使其更改不可用。

答案 4 :(得分: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);
  }
}

答案 5 :(得分:3)

从这个角度来看,你应该使用系统数组副本:

public String[] getArr() {
   if (arr != null) {
      String[] arrcpy = new String[arr.length];
      System.arraycopy(arr, 0, arrcpy, 0, arr.length);
      return arrcpy;
   } else
      return null;
   }
}

答案 6 :(得分:2)

您可以返回数据的副本。选择更改数据的调用者只会更改副本

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

    public String[] getArr() {

        String[] b = new String[arr.length];

        System.arraycopy(arr, 0, b, 0, arr.length);

        return b;
    }
}

答案 7 :(得分:2)

问题的核心是你正在返回一个指向可变对象的指针。哎呀。要么渲染对象不可变(不可修改的列表解决方案),要么返回对象的副本。

一般来说,如果对象是可变的,则对象的终结性不会保护对象不被更改。这两个问题是&#34;亲吻堂兄弟。&#34;

答案 8 :(得分:1)

返回不可修改的列表是个好主意。但是,在调用getter方法期间不可修改的列表仍然可以通过类或派生自类的类来更改。

相反,你应该向扩展该类的任何人明确表示不应该修改列表。

因此,在您的示例中,它可能会导致以下代码:

import java.util.Arrays;
import java.util.Collections;
import java.util.List;

public class Test {
    public static final List<String> STRINGS =
        Collections.unmodifiableList(
            Arrays.asList("1", "2"));

    public final List<String> getStrings() {
        return STRINGS;
    }
}

在上面的示例中,我已将STRINGS字段公开,原则上您可以取消方法调用,因为值已知。

您还可以将字符串分配给在构造类实例期间不可修改的private final List<String>字段。使用常量或实例化参数(构造函数)取决于类的设计。

import java.util.Arrays;
import java.util.Collections;
import java.util.List;

public class Test {
    private final List<String> strings;

    public Test(final String ... strings) {
        this.strings = Collections.unmodifiableList(Arrays
                .asList(strings));
    }

    public final List<String> getStrings() {
        return strings;
    }
}

答案 9 :(得分:0)

是的,你应该返回一个数组的副本:

 public String[] getArr()
 {
    return Arrays.copyOf(arr);
 }

答案 10 :(得分:0)

从 Java 9 开始,也可以从静态工厂方法 List.of() 构造不可变列表,这会导致导入和代码更少:

当可以修改原始 getUsers() 字段时,从 users 返回别名:

class Computer {
    private String[] users = new String[] {"user1", "user2", "user3"};
    public String[] getUsers;

    String[] getUsers() {
        return this.users;
    }
}

Computer c = new Computer();
c.getUsers()[0] = "me";
for (String user: c.getUsers()) {
    System.out.println(user);
}

输出:

me
user2
user3

使用不可变列表:

import java.util.List;

class Computer {
    private String[] users = new String[] {"user1", "user2", "user3"};
    public List<String> getUsers;

    List<String> getUsers() {
        return List.of(this.users);
    }
}

Computer c = new Computer();
c.getUsers().set(0, "me");
for (String user: c.getUsers()) {
    System.out.println(user);
}

输出:

user1
user2
user3