CodeGym /Cursos /JAVA 25 SELF /Paso de parámetros por valor y por referencia

Paso de parámetros por valor y por referencia

JAVA 25 SELF
Nivel 8 , Lección 4
Disponible

1. Introducción

En distintos lenguajes de programación existen dos formas principales de pasar parámetros a funciones (o métodos):

  • Por valor: la función recibe una copia del valor de la variable. Si la función modifica el parámetro, el original no cambia.
  • Por referencia: la función recibe una «referencia» (puntero) al objeto original. Los cambios dentro de la función se reflejan en el original.

¡En Java TODOS los parámetros se pasan por valor!
Pero hay un matiz: cuando pasamos tipos primitivos (int, double, boolean, etc.), se copia el valor en sí. Y cuando pasamos tipos de referencia (por ejemplo, arrays u objetos), se copia el valor de la referencia (es decir, la «dirección» del objeto en memoria), pero no el objeto en sí.

Si sientes que el cerebro empieza a derretirse, no te preocupes — ¡ahora lo pondremos todo en orden y lo mostraremos con ejemplos!

2. Tipos primitivos: se copia el valor

Empecemos por lo sencillo: ¿qué ocurrirá si intentamos modificar el valor de una variable de tipo int dentro de un método?

public class Demo 
{
    public static void main(String[] args) 
    {
        int number = 5;
        changeValue(number);
        System.out.println(number); // ¿Qué imprimirá?
    }

    public static void changeValue(int n) 
    {
        n = 42;
    }
}

Resultado: se imprimirá 5.

¿Por qué?
Al método changeValue se le pasa UNA COPIA del valor de la variable number. Dentro del método solo cambiamos la copia (n); la variable original number en main permanece intacta.

Analogía
Imagina que le das a un amigo una fotocopia de tu pasaporte, no el original. Tu amigo puede dibujarle bigotes a la copia todo lo que quiera — ¡tu pasaporte verdadero seguirá igual!

3. Tipos de referencia: se copia la referencia

Ahora probemos lo mismo con un array:

public class Demo 
{
    public static void main(String[] args) 
    {
        int[] numbers = {1, 2, 3};
        changeFirst(numbers);
        System.out.println(numbers[0]); // ¿Qué imprimirá?
    }

    public static void changeFirst(int[] arr) 
    {
        arr[0] = 99;
    }
}

Resultado: se imprimirá 99.

¿Por qué?
Al método changeFirst se le pasa una copia de la REFERENCIA al array numbers. Ambos nombres (numbers en main y arr en el método) apuntan al mismo array en memoria. Por eso, si cambiamos un elemento del array dentro del método, el cambio se «ve» desde fuera.

Visualización

main: numbers ──► [1, 2, 3]
                   ▲
                   │
changeFirst: arr ──┘
La copia de la referencia apunta al mismo objeto en memoria

Al modificar arr[0], en realidad estamos cambiando numbers[0], porque ambos apuntan al mismo objeto.

4. Pero si cambias la propia referencia — ¡el objeto original no se modifica!

Probemos ahora lo siguiente:

public class Demo 
{
    public static void main(String[] args) 
    {
        int[] numbers = {1, 2, 3};
        replaceArray(numbers);
        System.out.println(numbers[0]); // ¿Qué imprimirá?
    }

    public static void replaceArray(int[] arr) 
    {
        arr = new int[] {10, 20, 30};
    }
}

Resultado: imprimirá 1.

¿Por qué?
En el método replaceArray la variable arr pasa a apuntar a un array nuevo, pero eso no afecta a la variable numbers en main.
Antes estaban vinculadas por la misma referencia, pero cuando dentro del método asignamos arr = ..., es un cambio solo local.

Analogía
Es como si tú y tu amigo tuvierais la misma llave de un piso. Tu amigo se hace una llave nueva, pero tu llave antigua sigue siendo la misma, y la puerta también.

5. Ejemplos para afianzar: no se pueden «intercambiar» dos int mediante un método

Muchos principiantes intentan escribir un método para intercambiar dos números, pero no les funciona:

public class Demo 
{
    public static void main(String[] args) 
    {
        int a = 5, b = 10;
        swap(a, b);
        System.out.println(a + " " + b); // Esperado: 10 5; ¿y en realidad?
    }

    public static void swap(int x, int y) 
    {
        int temp = x;
        x = y;
        y = temp;
    }
}

Resultado: se imprimirá 5 10.

¿Por qué?
Porque a swap llegan copias de las variables a y b. Todo lo que hacemos dentro de swap afecta solo a esas copias, no a los originales.

6. Pero con los arrays se puede cambiar el contenido

Veamos la forma correcta de intercambiar valores en un array:

public class Demo 
{
    public static void main(String[] args) 
    {
        int[] arr = {5, 10};
        swap(arr);
        System.out.println(arr[0] + " " + arr[1]); // Esperado: 10 5
    }

    public static void swap(int[] arr) 
    {
        int temp = arr[0];
        arr[0] = arr[1];
        arr[1] = temp;
    }
}

Resultado: imprimirá 10 5.

¿Por qué?
Porque estamos cambiando el contenido del objeto (el array) al que apuntan tanto la variable original como el parámetro del método.

7. Demostración: objetos y sus campos

Con los arrays ya lo hemos visto. ¿Y qué hay de los objetos de nuestras clases? ¡Exactamente igual!

class Box 
{
    int value;
}

public class Demo
{
    public static void main(String[] args) 
    {
        Box box = new Box();
        box.value = 7;
        changeBox(box);
        System.out.println(box.value); // ¿Qué imprimirá?
    }

    public static void changeBox(Box b) 
    {
        b.value = 42;
    }
}

Resultado: imprimirá 42.

¿Por qué?
Porque estamos cambiando el campo del objeto al que apunta la referencia.

Qué ocurre al pasar parámetros a los métodos

Qué pasamos Cómo se copia ¿Se puede modificar dentro del método? ¿Se ven los cambios desde fuera?
int, double y otros Copia del valor Sí, pero solo la copia No
Array Copia de la referencia Sí, si se cambian los elementos
Objeto (clase) Copia de la referencia Sí, si se cambian los campos
Array/objeto Copia de la referencia No, si se asigna una nueva referencia No

8. Errores típicos

Error n.º 1: esperar que un int cambie tras llamar al método. Muchos principiantes creen que si pasan una variable de tipo int a un método, podrán cambiar su valor dentro del método. Esto es falso: ¡solo se modifica la copia!

Error n.º 2: intentar intercambiar dos int mediante un método. Como ya vimos, es imposible — solo se intercambian las copias de las variables; los originales no se tocan.

Error n.º 3: intentar «sustituir» un array desde el método. Si dentro del método asignas al parámetro-array un array nuevo (arr = new int[]{...}), eso no afectará al array original. Para «sustituir» un array, hay que devolver el array nuevo desde el método y asignarlo a la variable en el código que llama.

Error n.º 4: olvidar que se puede modificar el contenido del objeto. Si pasas un objeto (o un array), no olvides que cualquier cambio en sus campos/elementos será visible desde fuera.

Error n.º 5: confusión con null. Si pasas al método una referencia igual a null y tratas de acceder a sus campos, obtendrás NullPointerException. ¡Ten cuidado!

Comentarios
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION