Two references to the same List object
The list variable inside your someVoid method is a local variable, the name of the argument. That local list is a separate distinct variable from the class member field coincidentally named list. So when you say list = null ;, you are clearing that local variable. You did not clear the member field list. The Test object named test still carries its own reference (pointer) to the list.

A few lessons to learn here:
- Watch your naming. Duplicating the same name in various contexts within a piece of code can lead to confusion. In ambiguous situations, consider appending something like
Arg to your argument's name. For example: listArg. But better to use semantic names, with the clearest meaning in each context.
- Speaking of naming,
Test is a poor choice of a name for a class in Java. Unit-testing is common, and such a name is distracting at best and confusing at worst.
- Mark your arguments
final, which should have been the default in the original design of Java. Changing a passed argument is poor practice and entirely unnecessary. Changing that method signature to private void someVoid ( final List < Integer > list ) prompts a compiler alert an the line list = null; telling you that you are changing a passed argument variable. Such a change is illegal if marked final.
- In ambiguous situations, use
this. syntax. For example, this.list.add(0);. In olden days, I used this. on all my code for clarity. Using this. is less necessary nowadays because of the colorizing features in modern IDEs.
- Passing a primitive (
int, float, boolean, and such) in Java passes a copy of the content of that variable. Passing an object in Java is actually passing a copy of a reference to an object, not the object itself. Your code has two references to the same List object, one reference is held in the class member variable named list and another reference to the very same List object is in the local variable list within your method.
Example code
Let's modify your code to accomplish your goal: In a method, add elements to a list stored on a class member field, then eliminate that list entirely. This work makes no sense, but works as a demo.
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() );
}
}
When run, we get a null-pointer exception. This makes sense because we removed the list we had instantiated. So at the point of the println, the class member field references no object, references nothing, is considered null.
Exception in thread "main" java.lang.NullPointerException: Cannot invoke "java.util.List.size()" because "app.numbers" is null
at work.basil.example.Lister.main(Lister.java:26)
Shallow copy
In real work, when handing off a collection, we often want to hand off a shallow copy of that collection. Some objects inside (actually, references to the same objects are inside). But then if the user changes the collection, adding/deleting/sorting, they are not disturbing our original collection.
Likewise, when receiving a collection you may want to make a shallow copy. This is in case the calling programmer did not think to pass a copy rather than the original. Some would argue, correctly I would say, that it is the responsibility of the calling programmer to get their data straight. The programmer of the method being called should not have to predict the failure points of the calling programmer. But you practicality may win out. So I show both the calling and called programmers making defensive copies. Plus, in this example, the calling programmer passes a non-modifiable list, so the called programmer must make a copy in order to add elements.
Tip: List.of and List.copyOf make non-modifiable lists.
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 );
}
}
When run:
report = [42, 0, 1]
Size is: 1 | [42]