elv*_*e14 1 java collections stack jvm heap-memory
someVoid()如果我没有错误地引用传递给它的列表,那么当该方法发生调用时。代码添加后,列表中的2个值和大小相应地变为2。这里的一切对我来说都是可以理解的(如果我犯了错误,请纠正我)。AftersomeVoid()方法将列表的引用更改为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());
}
}
Run Code Online (Sandbox Code Playgroud)
List对象的两个引用该list变量里面你的someVoid方法是一个局部变量,参数的名称。该 locallist是一个与类成员字段分开的不同变量,巧合地命名为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在您的方法中的局部变量中。让我们修改您的代码以实现您的目标:在方法中,将元素添加到存储在类成员字段中的列表中,然后完全消除该列表。这项工作没有任何意义,但可以作为演示使用。
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() );
}
}
Run Code Online (Sandbox Code Playgroud)
运行时,我们得到一个空指针异常。这是有道理的,因为我们删除了我们实例化的列表。因此,就 而言println,类成员字段不引用任何对象,不引用任何内容,被认为是null。
线程“main”中的异常 java.lang.NullPointerException:无法调用“java.util.List.size()”,因为“app.numbers”在 work.basil.example.Lister.main(Lister.java:26) 处为空
在实际工作中,当交出一个集合时,我们经常希望交出该集合的一个浅拷贝。里面的一些对象(实际上,对相同对象的引用在里面)。但是如果用户更改集合,添加/删除/排序,他们不会干扰我们的原始集合。
同样,当收到一个集合时,您可能想要制作一个浅拷贝。这是在调用程序员不认为传递副本而不是原始的情况下。有些人会争辩说,正确地我会说,调用程序员有责任弄清他们的数据。被调用方法的程序员不应该预测调用程序员的故障点。但你的实用性可能会胜出。所以我展示了调用和被调用的程序员制作防御性副本。另外,在这个例子中,调用程序员传递了一个不可修改的列表,所以被调用的程序员必须制作一个副本才能添加元素。
提示:List.of并List.copyOf制作不可修改的列表。
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 );
}
}
Run Code Online (Sandbox Code Playgroud)
运行时:
报告 = [42, 0, 1]
尺寸为:1 | [42]
| 归档时间: |
|
| 查看次数: |
107 次 |
| 最近记录: |