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

想象一下我有这样一门课:

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 方法来访问私有字段?(我不想要任何类似番石榴的图书馆。我只是需要知道正确的做法)。

15529 次浏览

必须返回数组的 收到

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

修饰符 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];
}

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

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

如果可以使用 List 代替数组,集合提供不可修改的列表:

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

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

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;
}
}

Collections.unmodifiableList已经被提到了-Arrays.asList()奇怪地没有!我的解决方案也是从外部使用列表,并将数组包装如下:

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

复制数组的问题是: 如果每次访问代码时都要复制数组,而且数组很大,那么肯定会为垃圾收集器创建大量工作。因此,复制是一种简单但非常糟糕的方法-我会说“便宜”,但内存-昂贵!特别是当你拥有的不仅仅是两种元素的时候。

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

问题的关键在于返回一个指向可变对象的指针。哎呀。要么呈现对象不可变(不可修改列表解决方案) ,要么返回对象的副本。

一般来说,如果对象是可变的,那么对象的终结性并不能保护对象不被更改。这两个问题是“亲吻表亲”

在这一点上,您应该使用系统数组副本:

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;
}
}

你也可以使用 ImmutableList,它应该比标准的 unmodifiableList更好。这个类是由 Google 创建的 番石榴库的一部分。

下面是描述:

不像 Collections.unmodfiableList (java.util。List) ,这是一个仍然可以更改的单独集合的视图,ImmutableList 的实例包含自己的私有数据,并且永远不会更改

下面是如何使用它的一个简单例子:

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


public ImmutableList<String> getArr()
{
return ImmutableList.copyOf(arr);
}
}

返回一个不可修改的列表是一个好主意。但是在调用 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;
}
}

从 Java 9开始,也可以用静态工厂方法 List.of()构造一个不可变的列表,这样导入和代码就会少一点:

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

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