En este artículo revisaremos en profundidad el paso por valor y por referencia de variables en Java. Lo realizaremos a través de ejemplos con el objetivo de que cualquier lector después de leer el artículo sea capaz de entender ambos conceptos. Vamos al lio!
¿ Qué es el paso por valor de una variable ?
El paso por valor de una variable se realiza cuando al llamar a una función con parámetros de entrada se crea una copia de esta variable y por lo tanto, la variable original no se modifica, solo se modifica la copia de la variable, vamos a ver un ejemplo de código fuente para entender mejor el concepto:
public static void main(String[] args) {
//Creamos una variable de tipo entero y le damos un valor
int miNumero = 5;
//LLamamos a una función que modifica el valor de la variable
modificarNumero(miNumero);
//Imprimimos el valor de la variable
System.out.println(miNumero);
}
private static void modificarNumero(int miNumero) {
//Modificamos el valor de la variable a 7
miNumero = 7;
}
Vamos a explicar primero el código fuente:
- Creamos una variable de tipo entero y le asignamos el valor de 5.
- Llamamos a una función a la cuál le pasamos la variable como parámetro. Esta función modifica el valor de la variable pasada por parámetro a 7.
- Imprimimos el contenido de la variable que hemos declarado en el método main.
¿ Cuál creéis que será el resultado? ¿5 o 7?
Veamos la salida de nuestro programa
5
¿ Por qué el resultado es «5» si en la función modificamos el valor a «7» ?
La respuesta es porque se trata de un paso de variable por valor, es decir, cuando llamamos a la función no estamos pasando la variable «miNumero», sino una copia de la variable «miNumero», por eso, al salir de la función el valor de nuestra variable no se modificado.
Los pasos que seguiría la máquina virtual de Java serían los siguientes:
- Se crea la variable «miNumero», la máquina virtual de Java reserva memoria y le asigna un valor de 5. Digamos que dicha variable está en la posición 0x00.
- Se llama a la función «modificarNumero», la máquina virtual de Java crea una copia de la variable «miNumero», la pone en otra dirección de memoria, digamos en 0x04 y se la pasa a la función.
- Por esta razón dentro de la función el valor de la variable es «7», pero fuera de ella sigue siendo «5». Estamos modificando diferentes direcciones de memoria.
Vamos a modificar ahora nuestro programa para mostrar el valor de la variable en la función.
public static void main(String[] args) {
//Creamos una variable de tipo entero y le damos un valor
int miNumero = 5;
//LLamamos a una función que modifica el valor de la variable
modificarNumero(miNumero);
//Imprimimos el valor de la variable
System.out.println(miNumero);
}
private static void modificarNumero(int miNumero) {
//Modificamos el valor de la variable a 7
miNumero = 7;
System.out.println(miNumero);
}
La salida de nuestro programa como es de esperar es:
7
5
- Primero se muestra el valor de la copia de la variable «miNumero».
- Por último se muestra el valor de la variable original «miNumero».
El paso por valor de variables en Java se realiza con todos los tipos primitivos de Java (int, float, double, boolean, char, etc… e incluso con algunos Objeto que posteriormente veremos que son especiales).
¿ Qué es el paso por referencia de una variable ?
Llamamos paso por referencia de una variable cuando se pasa la variable original sin que la máquina virtual de Java realice ninguna copia de dicha variable, o lo que es lo mismo, se pasa directamente su dirección de memoria.
¿ Qué ocurre con los Arrays, se trata de un paso por valor o por referencia?
Los Arrays se consideran en Java un tipo de datos intermedio entre primitivos y objetos, y su paso siempre se realiza por referencia, es decir, aquí no se crea una copia de nuestra variable, se pasa la dirección de memoria original. Veamos un ejemplo:
public static void main(String[] args) {
//Creamos un Array de enteros de 5 posiciones
int [] misNumeros = new int[5];
//Asignamos valores a nuestro Array
misNumeros[0] = 10;
misNumeros[1] = 20;
misNumeros[2] = 30;
misNumeros[3] = 40;
misNumeros[4] = 50;
//Modificamos en valor de nuestro Array en la función
modificarArray(misNumeros);
//Imprimimos el contenido de nuestro Array
for (int numero: misNumeros) {
System.out.println(numero);
}
}
private static void modificarArray(int [] misNumeros) {
misNumeros[0] = 11;
misNumeros[1] = 21;
misNumeros[2] = 31;
misNumeros[3] = 41;
misNumeros[4] = 51;
}
¿ Cuál será la salida de nuestro programa ?
¿La salida de nuestro programa será 10, 20, 30, 40 y 50 o 11,21,31,41 y 51?
Veamos la salida de nuestro programa:
11
21
31
41
51
¿ Por qué ocurre esto ?
Porque cuando llamamos a la función modificarArray no se pasa como parámetro una copia del Array, por el contrario, se pasa el Array original, es decir, su dirección de memoria.
Vamos a ver el flujo completo:
- Primero se crea y se reserva memoria para el Array «misNumeros». Supongamos que se posiciona en la dirección de memoria 0x08.
- Insertamos elementos en el Array.
- Llamamos a la función «modificarArray» y la máquina virtual de Java pasa como parámetro el Array original, es decir, la dirección de memoria 0x08.
- Modificamos todos los números, al ser un paso por referencia, estamos modificando los valores de nuestro Array original.
- Por último, mostramos el valor de nuestro Array en el método «main», y como es de esperar se muestran los valores modificados.
¿ Cómo se comporta Java con los Objetos ?
El paso de un parámetro a una función de Java de un objeto es siempre por referencia. Veámoslo con un ejemplo:
Nuestra clase Coche
public class Coche {
private int numPuertas;
private String marca;
private String modelo;
private int numeroCaballos;
private int cilindrada;
private double precio;
public Coche() {
}
public Coche(int numPuertas, String marca, String modelo, int numeroCaballos, int cilindrada, double precio) {
super();
this.numPuertas = numPuertas;
this.marca = marca;
this.modelo = modelo;
this.numeroCaballos = numeroCaballos;
this.cilindrada = cilindrada;
this.precio = precio;
}
public int getNumPuertas() {
return numPuertas;
}
public void setNumPuertas(int numPuertas) {
this.numPuertas = numPuertas;
}
public String getMarca() {
return marca;
}
public void setMarca(String marca) {
this.marca = marca;
}
public String getModelo() {
return modelo;
}
public void setModelo(String modelo) {
this.modelo = modelo;
}
public int getNumeroCaballos() {
return numeroCaballos;
}
public void setNumeroCaballos(int numeroCaballos) {
this.numeroCaballos = numeroCaballos;
}
public int getCilindrada() {
return cilindrada;
}
public void setCilindrada(int cilindrada) {
this.cilindrada = cilindrada;
}
public double getPrecio() {
return precio;
}
public void setPrecio(double precio) {
this.precio = precio;
}
}
Nuestro código de ejemplo
public static void main(String[] args) {
Coche c = new Coche(5,"Opel", "Astra", 85, 1200, 14000);
modificarCoche(c);
System.out.println("Numero de puertas: " + c.getNumPuertas());
System.out.println("Marca: " + c.getMarca());
System.out.println("Modelo: " + c.getModelo());
System.out.println("Caballos: " + c.getNumeroCaballos());
System.out.println("Cilindrada: " + c.getCilindrada());
System.out.println("Precio: " + c.getPrecio());
}
private static void modificarCoche(Coche c) {
c.setCilindrada(1400);
c.setPrecio(17000);
c.setMarca("Citroen");
c.setModelo("C3");
c.setNumeroCaballos(92);
c.setNumPuertas(3);
}
La salida de nuestro programa
Numero de puertas: 3
Marca: Citroen
Modelo: C3
Caballos: 92
Cilindrada: 1400
Precio: 17000.0
Como podéis observar se realiza un paso por referencia, en la función estamos modificando el Coche que hemos creado en el método «main» debido a que cuando se llama a la función «modificarCoche» se pasa el objeto original, es decir, se pasa su dirección de memoria.
Existen algunas excepciones
En el apartado anterior hemos dicho que todos los objetos se pasan por referencia en Java, pero esto no es 100 % cierto, existen algunas excepciones, sobre todo con los objetos que son inmutables.
¿ Qué es un objeto inmutable ?
Un objeto inmutable es aquel objeto en el que cada vez que se realiza una modificación sobre el mismo se crea una copia automáticamente del mismo, es decir, es la máquina virtual de Java la que crea la copia por nosotros. El típico ejemplo de un Objeto inmutable en Java es el objeto «String». Podéis ver más información acerca de la inmutabilidad en el siguiente enlace de wikipedia.
¿ Cómo se pasan los objetos inmutables como String ?
public static void main(String[] args) {
String miCadena = "Hola Mundo";
modificarCadena(miCadena);
System.out.println(miCadena);
}
private static void modificarCadena(String miCadena) {
miCadena = "Esto es una modificación de un String";
}
¿ Cuál será la salida de nuestro programa ?
Hola Mundo
Por lo tanto, queda demostrado que existen excepciones con algunos objetos, pero son los menos casos, y solo pasa con aquellos objetos que son inmutables en Java.
Si te ha gustado el artículo deja un comentario en la caja de comentarios para apoyar el Blog.
También puedes revisar otras entradas para profundizar en algunos conceptos, sobre todo nuestra entrada de Arrays.
Excelente muchas gracias, dudas aclaradas.
Muy buenas.
Un alumno me mostró este artículo y perdona pero en el mismo hay un error de concepto. En java, no existe el paso de parámetros por referencia, sólo se pasa por valor. Siempre se crea una nueva variable con el contenido de la variable original. La diferencia, que las variables de objetos u arryas (que en java son objetos), son referencias, es decir, almacenan una dirección de memoria, luego, el paso por valor, copia la dirección de memoria original en la nueva variable y se altera el contenido del objeto.
Si se pasasen por referencia:
private static void modificarCoche(Coche c) {
c = new Coche();
c.setCilindrada(1400);
c.setPrecio(17000);
c.setMarca(«Citroen»);
c.setModelo(«C3»);
c.setNumeroCaballos(92);
c.setNumPuertas(3);
}
esto debería modificar el original, sin embargo, podrás comprobar que el original permanece intacto porque el paso es por valor.
Un saludo.
Entiendo lo que dices, soy consciente de ello, pero creo que tu prisma está sesgado por una persona que solo concibe el paso por referencia de otros lenguajes como C y C++. En el mundo de Java, el concepto de paso por referencia existe, pero no funciona igual que en otros lenguajes como C y C++ como bien has descrito. Creo que esto no aporta nada para cualquier persona que lea el artículo, ya que lo que se pretende con él es entender que no se crea una copia del valor al cuál apunta la referencia cuando se pasa una referencia en Java. Los detalles de que la referencia se copie en contraposición con C y C++ no aportan mucho valor si están aprendiendo Java y no aprendiendo Java sabiendo C o C++. Keep Things Simple. Un saludo y buena información.