当一个电话打给 someVoid()
方法如果我没有错的话就把引用的列表传递给它。代码相加后,2值进入列表,大小相应变为2。这里的一切对我来说都是可以理解的(如果我犯了错误,请纠正我)。之后 someVoid()
方法将列表的引用更改为 null
. 问题是:在这些操作之后,为什么列表的大小仍然是2?提前谢谢。
public class Test {
List<Integer> list;
public Test() {
this.list = new ArrayList<>();
someVoid(list);
}
private void someVoid(List<Integer> list) {
list.add(0);
list.add(1);
list = null;
}
public static void main(String[] args) {
Test test = new Test();
System.out.println("Size is: " + test.list.size());
}
}
1条答案
按热度按时间zwghvu4y1#
对同一列表对象的两个引用
这个
list
内部变量someVoid
方法是一个局部变量,即参数的名称。那个地方list
是与类成员字段同时命名的单独的不同变量list
. 所以当你说list = null ;
,您正在清除该局部变量。您没有清除成员字段list
. 这个Test
名为的对象test
仍然携带自己对列表的引用(指针)。这里有一些经验教训:
注意你的名字。在一段代码的不同上下文中重复相同的名称可能会导致混淆。在模棱两可的情况下,可以考虑附加
Arg
以你的论点的名义。例如:listArg
. 但最好使用语义名称,在每个上下文中都有最清晰的含义。说到命名,
Test
在java中,类的名称选择不当。单元测试是很常见的,这样的名称最好是分散注意力,最坏是令人困惑。标记你的论点
final
,这应该是java原始设计中的默认值。改变已通过的论点是不好的做法,完全没有必要。将方法签名更改为private void someVoid ( final List < Integer > list )
提示编译器在该行发出警报list = null;
告诉您正在更改传递的参数变量。这样的更改如果被标记则是非法的final
.在模棱两可的情况下,使用
this.
语法。例如,this.list.add(0);
. 在过去,我用this.
在我所有的代码上。使用this.
由于现代ide的着色特性,现在不太需要了。传递基本体(
int
,float
,boolean
,等)传递该变量内容的副本。在java中传递一个对象实际上是向一个对象传递一个引用的副本,而不是对象本身。您的代码有两个引用相同List
对象,一个引用保存在名为list
另一个同样的例子List
对象位于局部变量中list
在你的方法之内。示例代码
让我们修改代码以实现您的目标:在方法中,将元素添加到存储在类成员字段上的列表中,然后完全消除该列表。这项工作毫无意义,但作为一个示范工程。
运行时,我们得到一个空指针异常。这是有意义的,因为我们删除了示例化的列表。所以在
println
,类成员字段未引用任何对象,未引用任何内容null
.线程“main”java.lang.nullpointerexception中出现异常:无法调用“java.util.list.size()”,因为“app.numbers”在work.basil.example.lister.main(lister)中为null。java:26)
浅拷贝
在实际工作中,当我们交出一份藏品时,我们常常想要交出一份浅薄的藏品。一些对象在里面(实际上,对相同对象的引用在里面)。但是如果用户更改了集合,添加/删除/排序,它们就不会干扰我们原来的集合。
同样地,当你收到一个集合时,你可能想要做一个浅拷贝。这是在这样的情况下调用程序员没有想到传递一个副本,而不是原来的。我想说的没错,有些人会争辩说,调用程序的程序员有责任把他们的数据弄清楚。被调用方法的程序员不必预测调用程序员的失败点。但你可能会赢。所以我展示了调用和被调用的程序员制作防御副本。另外,在本例中,调用的程序员传递一个不可修改的列表,因此被调用的程序员必须复制一个列表才能添加元素。
提示:
List.of
以及List.copyOf
制作不可修改的列表。运行时:
报告=[42,0,1]
尺寸为:1 |[42]