Ciao, proverò a spiegarti.
L'errore è molto semplice e cercherò di essere il più chiaro possibile. Perdonami se non ci riuscirò.
Quando dichiari un attributo in una classe, nel tuo caso hai dichiarato
codice:
private ArrayList<String> pila;
all'interno della classe accedi a quell'attributo attraverso
oppure solo attraverso il nome dell'attributo.
Nel tuo primo esempio,
nel costruttore tu fai
codice:
public PilaList() {
ArrayList<String> pila = new ArrayList<String>();
}
ovvero dichiari un'altra variabile di tipo ArrayList<String> che si chiama pila.
Questo dichiarazione varrà solamente all'interno della funzione in cui è fatta, (in questo caso il costruttore) e non varrà al di fuori di essa.
In questo caso quindi, la variabile di nome pila verrà creata all'interno del costruttore, andando peraltro a nascondere un attributo, ma al suo esterno non verrà vista.
Nel frattempo l'attributo della classe, denominato pila, non ha subito nessuna inizializzazione. Ecco perchè quando poi viene richiamato nel metodo aggiungiElemento() solleva l'eccezione nullPointerException, perchè si accede ad un elemento che non esiste.
Nel secondo caso invece, nel costruttore fai
codice:
public PilaList() {
pila = new ArrayList<String>();
}
in questo caso quindi vai a inizializzare l'attributo della classe.
Lo inizializzi all'interno del costruttore, ma esso rimarrà utilizzabile per tutto il ciclo di vita dell'oggeto.
Ecco perchè, quando poi lo vai a usare nel metodo aggiungiElemento, non ti da nessun problema.
Spero di essere stato chiaro,
nel caso, chiedi pure quello che ti serve