对同一个 List 对象的两个引用
list 变量inside 你的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.。由于现代IDEs 中的着色功能,如今使用this. 的必要性已降低。
- 在 Java 中传递原语(
int、float、boolean 等)传递该变量内容的副本。在 Java 中传递对象实际上是将 reference 的副本传递给对象,而不是对象本身。您的代码有对同一个 List 对象的两个引用,一个引用保存在名为 list 的类成员变量中,另一个对同一个 List 对象的引用保存在局部变量 @ 987654353@ 在您的方法中。
示例代码
让我们修改您的代码以实现您的目标:在一个方法中,将元素添加到存储在类成员字段中的列表中,然后完全删除该列表。这项工作毫无意义,但可以作为演示。
package work.basil.example;
import java.util.ArrayList;
import java.util.List;
public class Lister
{
List < Integer > numbers;
public Lister ( )
{
this.numbers = new ArrayList <>();
this.sillyMethodToAddToListAndThenEliminateList();
}
private void sillyMethodToAddToListAndThenEliminateList ( )
{
this.numbers.add( 0 );
this.numbers.add( 1 );
this.numbers = null; // Clearing the *local* reference to the `List` object in memory. The class member field continues to point to the `List` object.
}
public static void main ( String[] args )
{
Lister app = new Lister();
System.out.println( "Size is: " + app.numbers.size() );
}
}
运行时,我们得到一个空指针异常。这是有道理的,因为我们删除了我们实例化的列表。所以在println这一点上,类成员字段不引用任何对象,什么也不引用,被认为是null。
线程“main”java.lang.NullPointerException 中的异常:无法调用“java.util.List.size()”,因为“app.numbers”为空
在 work.basil.example.Lister.main(Lister.java:26)
浅拷贝
在实际工作中,在交付集合时,我们通常希望交付该集合的浅拷贝。里面的一些对象(实际上,对相同对象的引用在里面)。但是,如果用户更改了集合,添加/删除/排序,他们不会打扰我们原来的集合。
同样,在接收集合时,您可能需要进行浅拷贝。这是为了以防调用程序员不考虑传递副本而不是原件。有些人会争辩说,我会说正确的是,调用程序员有责任把他们的数据弄直。被调用方法的程序员不必预测调用程序员的故障点。但你的实用性可能会胜出。所以我展示了调用和被调用的程序员制作defensive 的副本。另外,在这个例子中,调用程序员传递了一个不可修改的列表,所以被调用的程序员必须复制一份才能添加元素。
提示:List.of 和 List.copyOf 创建 non-modifiable 列表。
package work.basil.example;
import java.util.ArrayList;
import java.util.List;
public class Lister
{
List < Integer > numbers;
public Lister ( )
{
this.numbers = new ArrayList <>();
this.numbers.add( 42 );
this.sendReport( List.copyOf( this.numbers ) ); // Share a shallow copy of collection.
}
private void sendReport ( final List < Integer > fodderForReport )
{
List < Integer > data = new ArrayList <>( fodderForReport ); // Make defensive and modifiable copy of passed collection.
data.add( 0 );
data.add( 1 );
String report = data.toString();
System.out.println( "report = " + report );
// … send report
}
public static void main ( String[] args )
{
Lister app = new Lister();
System.out.println( "Size is: " + app.numbers.size() + " | " + app.numbers );
}
}
运行时:
报告 = [42, 0, 1]
大小为:1 | [42]