想象一下,我有这个课程:
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这样的库。我只需要知道正确的方法来做到这一点。)
答案 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.asList
和Collections.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