Edit: It appears I answered the wrong question. As the OP mentioned in comments below:
Sorry, my question may not be clear enough, what I am truly confusing is that when executing "firstNode = null", wether firstNode.data = null or firstNode.nextNode = null because firstNode is a object of type ListNode, which has int data and listNode type instances.
After the assignment firstNode = null, there is no .data or .nextNode. You'd have to assign firstNode to a new ListNode() first.
Once you do that, its data member will be initialized to 0 and its nextNode member will be initialized to null, as those are the default initial values for member fields if no explicit initialization is done. But again, you need to instantiate a ListNode and assign it to firstNode first, otherwise you'll get a NullPointerException if you try and access it while it is null.
Original answer, re: firstNode = lastNode = null
It assigns both firstNode and lastNode to null.
It is a commonly used syntactic trick that takes advantage of the fact that an assignment expression as a whole evaluates to the value of the variable after assignment:
a = b = 1 => a = (b = 1) (b = 1) assigns b then evaluates to 1 => a = 1
You can chain as many together as you want, as long as the types are compatible:
a = b = c = d = 1 => a = b = c = (d = 1) (d = 1) assigns d then evaluates to 1 => a = b = (c = 1) (c = 1) assigns c then evaluates to 1 => a = (b = 1) (b = 1) assigns b then evaluates to 1 => a = 1
Incompatible types will result in an error:
Integer x; Double y; x = y = null; // error: cannot convert from Double to Integer
It's the same trick you use when you do stuff like:
int ch; while ((ch = input.read()) != -1) { ... }
From JLS 15.26:
At run time, the result of the assignment expression is the value of the variable after the assignment has occurred.