I think you have a concurrency issue. I'm assuming this is your situation:
- You have an execution context (let's call it A), where you query the database and get a list of records
- While Context A is still running, another execution context (let's call it B), deletes a record from the database (one of the records that is kept in memory in A).
If this is the case, your record will remain in memory in context A. It will not be replaced by a null. But at this point, context A is stale (its in-memory representation does not match the database state) because of concurrency. You can reference the deleted record and display its information. However, any attempt to persist it (DML) will fail.
However, if context A ends the execution, and you call the same method again (let's say that the user clicks the same button that started A to begin with), you will have a new execution context (let's call it A') that runs after B has finished.
A' will not have a reference to the deleted object (it will not even be a null... the record will simply not be returned by the query).
Edit (after your question update):
- I've just seen that your list (L) is static
- Since it's a trigger calling a method and then the trigger recursively, everything happens in the same execution context (unless you're using futures/async)
- In either case, if you don't requery your list whenever you enter your method, you'll have stale information (ie: L[0].Name will still be "ABC"). If you requery it, then it will probably be something else.
On a different note, unless there is a very good reason for this, recursive triggers are a bad idea. They often cause more harm than good. I would try to reengineer your code to avoid the recursivity unless you really know what you're doing. There's a good resource on how to avoid recursive triggers here.