En Java, el paso de parámetros es por valor.

Existe comúnmente la creencia errónea de que en Java es posible pasar parámetros por referencia, y no es así. Java siempre pasa los parámetros por valor. Esta confusión se da debido a que todas las variables de objeto son referencias a objetos [1]. En el libro “The Java Programming Language” de Ken Arnold y James Gosling (autores de Java), sección 2.6.1., tenemos la siguiente cita: «There is exactly one parameter passing mode in Java – pass by value – and that helps keep things simple.» [2] (Existe un solo modo de paso de parámetros en Java – paso por valor – y eso ayuda a mantener las cosas simples.).

Antes de continuar, vamos a recordar cuáles son las definiciones de paso por valor y paso por referencia: [3]:

Paso por valor
significa que cuando un argumento se pasa a una función, la función recibe una copia del valor original. Por lo tanto, si la función modifica el parámetro, sólo la copia cambia y el valor original permanece intacto.

Paso por referencia
significa que cuando un argumento se pasa a una función, la función recibe la dirección de memoria del valor original, no la copia del valor. Por lo tanto, si la función modifica el parámetro, el valor original en el código que llamó a la función cambia.

Vamos a valernos de ejemplos para explicar el mecanismo con el que Java pasa parámetros a los métodos.

Tenemos el siguiente programa Java:

1 public class ValorOReferencia {
2
3 private String param1 = new String();
4
5 /** Creates a new instance of PassValueOrReference */
6 public ValorOReferencia(String param1) {
7 this.setParam1(param1);
8 }
9
10 public static void cambiarObjeto(ValorOReferencia objeto) {
11 objeto = new ValorOReferencia("Este es un nuevo objeto.");
12 System.out.println("Luego de \"reasignar\" pass: " + objeto);
13 }
14
15 public static void cambiarParam1(ValorOReferencia objeto) {
16 objeto.setParam1("Este es un nuevo valor para param1.");
17 }
18
19 public static void main(String[] args) {
20 ValorOReferencia pass =
21 new ValorOReferencia("Objeto inicial.");
22 System.out.println("Entender que Java pasa parámetros por valor: ");
23 System.out.println("Antes de modificar pass es: " + pass);
24 ValorOReferencia.cambiarObjeto(pass);
25 System.out.println("De vuelta en main pass es: " + pass);
26 System.out.println("Ahora vamos a cambiar sólo param1:");
28 ValorOReferencia.cambiarParam1(pass);
29 System.out.println("De seguro param 1 ha cambiado: " + pass);
30 System.out.println("Parece difícil, pero no lo es.");
31 }
32
33 public String getParam1() {
34 return param1;
35 }
36
37 public void setParam1(String param1) {
38 this.param1 = param1;
39 }
40
41 public String toString() {
42 return "[param1 = " + this.getParam1() + "]";
43 }
44
45 }

Remitámonos a línea 20. Declaramos una variable pass, de tipo ValorOReferencia, con su único atributo param1 inicializado con el valor “Objeto inicial.”. En la línea 23, presentamos el objeto en pantalla, y se muestra el valor con el que fue declarado.

Salida del programa:

Entender que Java pasa parámetros por valor:
Antes de modificar pass es: [param1 = Objeto inicial.]

Ahora, en la línea 24 pasamos nuestra variable pass al método cambiarObjeto, método que tiene un parámetro formal de tipo ValorOReferencia. En dicho método, en la línea 11, se realiza una asignación

objeto = new ValorOReferencia("Este es un nuevo objeto.");

, se presenta el objeto “modificado” y el control regresa al método main.

Salida del programa:

Luego de "reasignar" pass: [param1 = Este es un nuevo objeto.]

Suponiendo que el paso de parámetros en Java fuera por referencia, la referencia pass apuntaría ahora a un nuevo objeto con el valor “Este es un nuevo objeto.”. Pero, al regresar al método main, en la línea 25, presentamos de nuevo pass, y vemos que el valor con el que fue originalmente declarado se mantiene.

Salida del programa:

De vuelta en main() pass es: [param1 = Objeto inicial.]

Ahora, vamos a pasar pass y vamos a modificar solamente su único atributo. En la línea 28, pasamos pass al método cambiarParam1, en donde tenemos la sentencia

objeto.setParam1("Este es un nuevo valor para param1.");

en la línea 16. Así, se ha modificado el valor del atributo param1, y al volver al método main, presentamos pass otra vez:

Salida del programa:

Ahora vamos a cambiar sólo param1:
De seguro param 1 ha cambiado: [param1 = Este es un nuevo valor para param1.]
Parece difícil, pero no lo es.

Al ver esta última operación, quizá alguien pueda decir que Java sí pasa parámetros por referencia, ya que se modificó el atributo del objeto, pero estaría equivocado: ¿Por qué en cambiarObjeto la variable pass no sufre ninguna modificación, y en el método cambiarParam1 su atributo se ve efectivamente modificado? Porque Java no pasa objetos como parámetros [4], sino copias de las referencias a esos objetos. Exacto. Java pasa parámetros por valor. Pasa referencias a objetos por valor.

Vamos a explicar lo que hacen nuestros métodos cambiarObjeto(ValorOReferencia objeto) y cambiarParam1(ValorOReferencia objeto).

cambiarObjeto(ValorOReferencia objeto)

En main, declaramos una variable

ValorOReferencia pass = new ValorOReferencia("Objeto inicial.");

Se ha creado un objeto ValorOReferencia en cierta posición de memoria y la forma de acceder a él es usar la referencia pass.

Omito los métodos setters, getters y toString() en el diagrama porque no intervienen en la explicación de este método. Este método tiene un parámetro formal ValorOReferencia objeto. Como Java pasa parámetros por valor tenemos que objeto, el parámetro formal de cambiarObjeto, es una copia de la referencia pass, es un alias, mas no es la referencia pass. Siguiente a la llamada al método lo que tenemos es lo siguiente:

objeto es una copia de la referencia pass, es otra referencia que apunta al mismo lugar. Al ejecutar la sentencia

objeto = new ValorOReferencia("Este es un nuevo objeto.");

lo que hacemos de hecho es esto:

objeto, que originalmente era una copia de la referencia pass, apunta ahora a un nuevo objeto creado en otra posición de memoria. Es por eso que de vuelta al main, el objeto apuntado por pass no ha cambiado.

cambiarParam1(ValorOReferencia objeto)

Desde el método main pasamos a cambiarParam1 la referencia pass:

Incluyo el método setter setParam1(String param1) porque interviene en esta explicación. cambiarParam1 tiene un parámetro formal ValorOReferencia objeto, que es un alias de pass.

Al ejecutar la sentencia

objeto.setParam1("Este es un nuevo valor para param1.");

, lo que estamos haciendo es invocar al método setParam1 del objeto apuntado por la referencia objeto. Es por eso que el atributo param1 del objeto referenciado por pass es efectivamente modificado.

Cuando declaramos variables, por ejemplo,

ValorOReferencia valoRef = new ValorOReferencia();

, no declaramos un objeto ValorOReferencia, sino una referencia a un objeto ValorOReferencia. Java tiene punteros (referencias a objetos), pero no es posible manejarlos con la aritmética con que se manejan en C++ [5]. No obstante, la implementación de estos punteros es similar en ambos lenguajes:

• Una sentencia Java

ValorOReferencia valORef;

es exactamente igual a una sentencia C++

ValorOReferencia *valORef;.

• Una sentencia Java

valORef.cambiarParam1(“Otro valor.”);

es exactamente igual a una sentencia C++

valORef -> cambiarParam1(“Otro valor.”);

. [6]

Hay que tener claro entonces, que cuando se escribe una sentencia del estilo

cualquierFuncion(CualquierTipo argumento);

en Java, lo que se pasa no es un objeto, sino una copia de la referencia al objeto. Es importante no confundirnos. Java pasa objetos por referencia (pasa las referencias a los objetos), pero nunca pasa un objeto como parámetro [7], ni parámetros por referencia.

Referencias:

[1] UNDERSTANDING THAT PARAMETERS ARE PASSED BY VALUE AND NOT BY REFERENCE, DeveloperWorks, Peter Haggar, http://www.ibm.com/developerworks/library/j-praxis/pr1.html
[2] PASS BY VALUE SEMANTICS IN JAVA APPLICATIONS, Peter Haggar, DeveloperWorks http://www.ibm.com/developerworks/java/library/j-passbyval/
[3] PASS BY VALUE SEMANTICS IN JAVA APPLICATIONS, Peter Haggar, DeveloperWorks http://www.ibm.com/developerworks/java/library/j-passbyval/
[4] PASS BY VALUE SEMANTICS IN JAVA APPLICATIONS, Peter Haggar, DeveloperWorks http://www.ibm.com/developerworks/java/library/j-passbyval/
[5] JAVA IS PASS-BY-VALUE, DAMMIT!, JavaDude Articles,
http://javadude.com/articles/passbyvalue.htm
[6] JAVA IS PASS-BY-VALUE, DAMMIT!, JavaDude Articles,
http://javadude.com/articles/passbyvalue.htm
[7] PASS BY VALUE SEMANTICS IN JAVA APPLICATIONS, Peter Haggar, DeveloperWorks http://www.ibm.com/developerworks/java/library/j-passbyval/

9 responses

28 06 2007
En Java, el paso de parámetros es por valor. « My place

[…] [leer más…]  […]

6 04 2008
29 10 2008
Guillermo

Perdona pero a mi entender eso de que todos los objetos en java van por valor me parece erróneo.

1. No es lo mismo pasar un String que cualquier otro objeto. El String sí que se pasa por valor, mientras que un Object, no.

2. Explícame por qué cuando yo paso un Object como parámetro a una función me retorna con el valor modificado (dentro de la función le asigno valores a sus atributos) y no como tú dices, el objeto original que se le pasó.

3. Lo correcto: todo en java es por referencia, salvo los tipos primitivos y la clase String.

29 10 2008
Juan Pablo Angamarca

@Guillermo, creo que te saltaste de leer esta parte:

En el libro “The Java Programming Language” de Ken Arnold y James Gosling (autores de Java), sección 2.6.1., tenemos la siguiente cita: “There is exactly one parameter passing mode in Java – pass by value – and that helps keep things simple.”

23 01 2009
El blog de Jose Dueñas » Paso de variables por Valor y por Referencia en Java

[…] no es cierto, después de consultar varias fuentes puedo concluir que en Java las variables, ya sean simples u objetos, se pasan siempre por […]

26 06 2009
Nelson Guerra Alvarez

Juan Pablo Angamarca. Estas totalmente equivocado. Todo el ejemplo que describiste reafirma que Java pasa parametros por referencia y no por valor (solo los datos primitivos se pasan por valor). La referencia que pasas es a la direccion de memoria del Objeto. Como bien explicaste en la primera figura pass es una REFERENCIA al objeto creado. Es igual que en el lenguaje C cuando tienes una direccion de memoria la cual es apuntada por un apuntador (puntero). el puntero tiene la referencia al objeto, en este caso PASS tiene la referencia al objeto creado. Cuando se llama a la funcion ValorOReferencia.cambiarObjeto(pass) lo que haces es pasar la referencia que tiene pass hacia el objeto, ESO ES PASO DE PARAMETROS POR REFERENCIA. Es igual cuando en el lenguaje C a una funcion le pasas como paraetro el contenido de un apuntador, lo que pasas como parametro es la direccion de memoria a la que apunta el apuntador. En este caso, objeto apunta donde mismo apunta pass, es decir, ambos tienen la misma referencia, la cual pass entrego a objeto.

26 06 2009
Nelson Guerra Alvarez

Para completar la ultima parte.
En este caso, objeto apunta donde mismo apunta pass, es decir, ambos tienen la misma referencia, la cual pass entrego a objeto. Despues en el metodo cambiarObjeto
«public static void cambiarObjeto(ValorOReferencia objeto) {
objeto = new ValorOReferencia(«Este es un nuevo objeto.»);
System.out.println(«Luego de \»reasignar\» pass: » + objeto);

Tu a la variable OBJECTO le asignas otra referencia (haces que objeto apunte a otro espacio de momeria), y por lo que ya la variable PASS y OBJETO NO tienen la misma referencia, la variable OBJETO apunta al nuevo objeto creado.

Si todavia tienes dudas trata de leer un poco un manual de C y se disiparan tus dudas.

23 07 2009
luis

Por favor. Decidanse. Lo importante no es lo que dice el libro
sino loque realmente se da. Me gustaria que me aclaren esa duda.

24 07 2009
Juan Pablo Angamarca

En el libro “The Java Programming Language” de Ken Arnold y James Gosling (autores de Java), sección 2.6.1., tenemos la siguiente cita: “There is exactly one parameter passing mode in Java – pass by value – and that helps keep things simple.”