Sorgente definitiva per Java
Come i commenti hanno già indicato, c'è solo una specifica ufficiale per il linguaggio Java, ovvero Specifiche della lingua Java rese disponibili da Oracle.
La risposta alla tua particolare domanda sul comportamento di chiamata si trova in sezione 8.4.1 :
When the method or constructor is invoked (§15.12), the values of the actual argument expressions initialize newly created parameter variables
Fonte della confusione
Si noti, tuttavia, che le persone che scrivono il JLS potrebbero non condividere la stessa definizione di termini degli altri. In particolare, la discussione sul fatto che Java sia chiamata per valore o qualcos'altro deriva in genere dall'analisi di altre definizioni.
Call-by-value viene in genere definito come il passaggio di una copia dell'argomento alla funzione. Questo comportamento è più esposto in C ++, dove puoi scegliere se vuoi chiamare per valore o chiamare per riferimento per argomento e per chiamare per valore hai anche cose come costruttori di copie.
Questa idea generale, che call-by-reference ti permette di cambiare l'oggetto originale, mentre call-by-value lo lascia intatto è un po 'inutile in Java (almeno se ignori i tipi primitivi). A differenza della famiglia di linguaggi C, Java non ha alcun concetto di puntatore a qualche oggetto, quindi ogni singola variabile è come un puntatore a qualche oggetto. Questo dà l'impressione alle persone che hanno familiarità con C, che Java fa sempre passare dei riferimenti (dopotutto, se si passa un oggetto in una funzione, non si sa se quella funzione modifica gli attributi interni dell'oggetto).
Puoi argomentare contro questo argomento accettando la nozione di puntatori, che a sua volta significa, che una chiamata di funzione sta ancora usando call-by-value, perché crea semplicemente una copia di quel puntatore, che finirà per puntare a lo stesso oggetto di nuovo.
La cosa più confusa è che passare un oggetto a un metodo in Java non creerà mai una copia completa (profonda !?) di quell'oggetto, nonostante il fatto che Java sia chiamata per valore. Altri linguaggi (come C ++) chiamano la loro funzione chiamando anche la chiamata alla semantica, ma in quelle lingue la semantica effettiva è diversa.
Esempi di codice
Supponi di avere i seguenti due metodi nella tua classe:
private void f(C c) {
c.setSomething();
c = new C();
c.setSomething2();
}
private void g(final C c) {
c.setSomething();
}
Ora sul sito di chiamata trovi qualcosa del genere:
C myC = // get some C instance
f(myC); // passes the value
Il metodo f
ora modificherà gli attributi dell'oggetto myC
, perché funziona esattamente sulla stessa istanza. Al contrario, la variabile c
può essere assegnata a un'istanza diversa, in modo tale che la chiamata a setSomething2
non influenzi l'istanza a cui myC
si riferisce.
Se si scambia f
per g
sul sito di chiamata, un tipico errore di rookie dai programmatori C / C ++ che è diventato programmatore Java è supporre che final
in qualche modo garantisce che l'oggetto non venga modificato. Naturalmente, il codice sopra funziona ancora e modifica l'oggetto. Ciò che non funziona più a causa di final
è semplicemente, che la variabile c
viene nuovamente assegnata a un'istanza C
diversa all'interno di quel metodo.
Questo comportamento attira anche gli sviluppatori Java più esperti. Mentre pochi dimenticano che un argomento final List<C>
è autorizzato a modificare la lista, non così pochi incorrere in bug, dopo aver scritto un metodo che restituisce un List<C>
(sia definitivo che non, in quanto ciò non fa alcuna differenza rilevante) e viene morso dal codice del sito di utilizzo che lo modifica.