Java Getter&Setter 系列之二:常见错误

4. 实现Getter/Setter方法常见错误

没有无过错的人,没有无bug的代码。本节将描述实现getter/setter时的常见错误,以及解决方案。

常见错误一

实现了getter/setter,但是变量声明访问类型不够严格

考虑如下代码片段:

public String firstName;

public void setFirstName(String fname) {
    this.firstName = fname;
}

public String getFirstName() {
    return this.firstName;
}

变量firstName被声明为public,所以可以使用.运算符直接访问,因此getter/setter就没有实际意义了。一个解决方案是使用更加严格的访问修饰词,比如protectedprivate

private String firstName;

在《Effective Java》一书中,Joshua Bloch在第14条中指出这个问题:

"In public classes, use accessor methods, not public fields."

常见错误二

在setter中直接分配对象引用。

考虑如下代码片段:

private int[] scores;
public void setScores(int[] scr) {
    this.scores = scr;
}

如下示例代码说明了问题:

int[] myScores = {5, 5, 4, 3, 2, 4};
setScores(myScores);
displayScores();   
myScores[1] = 1;
displayScores();

一个int类型的数组myScores,使用6个元素初始化后执行第2行代码setScores()方法。displayScores()方法将数据元素简单打印出来:

public void displayScores() {
    for (int i = 0; i < this.scores.length; i++) {
        System.out.print(this.scores[i] + " ");
    }
    System.out.println();
}

第3行代码将打印如下结果:

5 5 4 3 2 4

数组myScores所有元素被打印出来。现在,通过第4行代码,我们修改数组myScores中的第二个元素:

myScores[1] = 1;

如果我们通过第5行代码再次调用displayScores()方法会发生什么呢?好,让我们看下输出结果:

5 1 4 3 2 4

可以看到,通过第四行的赋值语句,第二个元素的值从5变成了1。这就意味着在setter方法以外的代码也可以修改数据,这违反了setter方法封装变量的目的。为什么会出现这种情况呢?让我们再看看setScores()方法:

public void setScores(int[] scr) {
    this.scores = scr;
}

变量scores被直接赋值为方法入参变量scr。这意味着这两个变量都指向内存中的同一个对象——myScores数组。所以无论是对scores还是myScores变量的修改实际上都是在同一个对象上的。

这种情况的一个解决方案是逐一拷贝scr数组中的元素到scores数组中。修改后的setter方法如下:

public void setScores(int[] scr) {
    this.scores = new int[scr.length];
    System.arraycopy(scr, 0, this.scores, 0, scr.length);
}

这有什么不同呢?现在,变量scores不再指向scr变量所指向的对象了。而是scores数组被初始化为与scr数组相同大小的一个新数组。然后,我们使用System.arraycopy()方法,将scr数组中的元素都拷贝到scores数组中。

再次运行示例代码,得到如下输出结果:

5 5 4 3 2 4
5 5 4 3 2 4

现在,两次displayScores()方法调用产生了相同的结果。这就意味着scores数组独立于通过setter方法赋值的scr数组,因此我们调用如下语句:

myScores[1] = 1;

不会影响到scores数组。

所以,拇指规则是:如果传递一个。对象引用到setter方法中,不要直接把引用拷贝给对象的内部变量。而是应该使用一些方法将传递过来的对象的值拷贝给对象内部变量,就像我们使用System.arraycopy()方法将一个数组的元素拷贝给另外一个数组一样。

常见错误三

直接在getter方法中返回对象引用。

考虑如下的getter方法:

private int[] scores;
public int[] getScores() {
    return this.scores;
}

然后再看下面一段示例代码:

int[] myScores = {5, 5, 4, 3, 2, 4};
setScores(myScores);
displayScores();
int[] copyScores = getScores();
copyScores[1] = 1;
displayScores();

将会产生如下输出结果:

5 5 4 3 2 4
5 1 4 3 2 4

可以看到,通过第5行示例代码,数组scores的第二个元素被setter方法以外的代码修改了。因为getter方法直接返回了内部变量scores的引用,外部代码就可以获取该变量引用并修改这个内部变量。

这个问题的一个解决方案是,在getter方法中不要直接返回对象的引用,而是应该返回对象的一个副本。这样外部代码只能获取到对象的副本,而不是内部变量对象本身。

我们将getter方法修改如下:

public int[] getScores() {
    int[] copy = new int[this.scores.length];
    System.arraycopy(this.scores, 0, copy, 0, copy.length);
    return copy;
}

所以,拇指规则是:不要在getter方法中直接返回源对象的引用,而是应该返回其对象的副本。

系列文章

  1. Java Getter&Setter 系列之一:基础
  2. Java Getter&Setter 系列之二:常见错误
  3. Java Getter&Setter 系列之三:最佳实践
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容