6942 votos

¿Es Java "pass-by-reference" o "pass-by-value"?

Siempre pensé que Java era pase por referencia Sin embargo, he visto un par de publicaciones en blogs (por ejemplo, este blog ) que afirman que no lo es. Creo que no entiendo la distinción que hacen.

¿Cuál es la explicación?

6205voto

erlando Puntos 5802

Java es siempre pass-by-value. Lo difícil puede ser entender que Java pasa objetos como referencias y esas referencias se pasan por valor.

Es así:

Dog aDog = new Dog("Max");
foo(aDog);
aDog.getName().equals("Max"); // true

public void foo(Dog d) {
  d.getName().equals("Max"); // true
  d = new Dog("Fifi");
  d.getName().equals("Fifi"); // true
}

En este ejemplo aDog.getName() seguirá devolviendo "Max" . d no se sobrescribe en la función ya que la referencia al objeto se pasa por valor.

Igualmente:

Dog aDog = new Dog("Max");
foo(aDog);
aDog.getName().equals("Fifi"); // true

public void foo(Dog d) {
  d.getName().equals("Max"); // true
  d.setName("Fifi");
}

3237voto

Scott Stanchfield Puntos 15863

Acabo de darme cuenta de que has hecho referencia a mi artículo ;)

La especificación de Java dice que todo en Java es pass-by-value. En Java no existe el "pase por referencia".

La clave para entender esto es que algo como

Dog myDog;

es no un Perro; en realidad es un puntero a un perro.

Lo que significa, es que cuando tienes

Dog myDog = new Dog("Rover");
foo(myDog);

esencialmente estás pasando el dirección de los creados Dog al objeto foo método.

(Digo esencialmente porque los punteros de Java no son direcciones directas, pero es más fácil pensar en ellos de esa manera)

Supongamos que el Dog reside en la dirección de memoria 42. Esto significa que pasamos 42 al método.

si el Método se definiera como

public void foo(Dog someDog) {
    someDog.setName("Max");     // AAA
    someDog = new Dog("Fifi");  // BBB
    someDog.setName("Rowlf");   // CCC
}

veamos lo que está sucediendo.

  • el parámetro someDog se fija en el valor 42
  • en la línea "AAA"
    • someDog se sigue al Dog que señala (el Dog objeto en la dirección 42)
    • que Dog (el de la dirección 42) se le pide que cambie su nombre por el de Max
  • en la línea "BBB"
    • un nuevo Dog se crea. Digamos que está en la dirección 74
    • asignamos el parámetro someDog a 74
  • en la línea "CCC"
    • someDog es seguido por el Dog que señala (el Dog objeto en la dirección 74)
    • que Dog (el de la dirección 74) se le pide que cambie su nombre por el de Rowlf
  • entonces, devolvemos

Ahora pensemos en lo que ocurre fuera del método:

En myDog ¿cambio?

Ahí está la clave.

Teniendo en cuenta que myDog es un puntero y no un verdadero Dog la respuesta es NO. myDog sigue teniendo el valor 42; sigue apuntando al original Dog .

Es perfectamente válido seguir una dirección y cambiar lo que está al final de la misma; sin embargo, eso no cambia la variable.

Java funciona exactamente igual que C. Puedes asignar un puntero, pasar el puntero a un método, seguir el puntero en el método y cambiar los datos a los que apuntaba. Sin embargo, no puedes cambiar a dónde apunta ese puntero.

En C++, Ada, Pascal y otros lenguajes que soportan pass-by-reference, se puede cambiar la variable que se pasó.

Si Java tuviera una semántica de paso por referencia, el foo que definimos anteriormente habría cambiado donde myDog apuntaba cuando asignaba someDog en la línea BBB.

Piensa que los parámetros de referencia son alias de la variable pasada. Cuando ese alias se asigna, también lo hace la variable que se pasó.

¿Ayuda eso? (Tendré que añadir esto como anexo a mi artículo...)
-- Scott

1909voto

Eng.Fouad Puntos 44085

Java siempre pasa los argumentos por valor NO por referencia.


Permítanme explicar esto a través de un ejemplo :

public class Main{
     public static void main(String[] args){
          Foo f = new Foo("f");
          changeReference(f); // It won't change the reference!
          modifyReference(f); // It will modify the object that the reference variable "f" refers to!
     }
     public static void changeReference(Foo a){
          Foo b = new Foo("b");
          a = b;
     }
     public static void modifyReference(Foo c){
          c.setAttribute("c");
     }
}

Lo explicaré por pasos:

  1. Declarar una referencia llamada f de tipo Foo y asignarlo a un nuevo objeto de tipo Foo con un atributo "f" .

    Foo f = new Foo("f");

    enter image description here

  2. Del lado del método, una referencia de tipo Foo con un nombre a se declara y se asigna inicialmente a null .

    public static void changeReference(Foo a)

    enter image description here

  3. Al llamar al método changeReference la referencia a se asignará al objeto que se pasa como argumento.

    changeReference(f);

    enter image description here

  4. Declarar una referencia llamada b de tipo Foo y asignarlo a un nuevo objeto de tipo Foo con un atributo "b" .

    Foo b = new Foo("b");

    enter image description here

  5. a = b es reasignar la referencia a NO f al objeto cuyo atributo es "b" .

    enter image description here


  6. Como usted llama modifyReference(Foo c) método, una referencia c se crea y se asigna al objeto con el atributo "f" .

    enter image description here

  7. c.setAttribute("c"); cambiará el atributo del objeto que referencia c apunta a él, y es el mismo objeto que referencia f lo señala.

    enter image description here

Espero que ahora entiendas cómo funciona el paso de objetos como argumentos en Java :)

752voto

Gevorg Puntos 4218

Esto le dará una idea de cómo funciona realmente Java, hasta el punto de que en su próxima discusión sobre el paso por referencia o el paso por valor en Java simplemente sonreirá :-)

Primer paso, por favor, borra de tu mente esa palabra que empieza por 'p' "_ _ _ _ _ _", especialmente si vienes de otros lenguajes de programación. Java y 'p' no pueden escribirse en el mismo libro, foro o incluso txt.

Segundo paso, recuerda que cuando pasas un objeto a un método estás pasando la referencia del objeto y no el objeto mismo.

  • Estudiante : Maestro, ¿significa esto que Java es pass-by-reference?
  • Maestro : Grasshopper, No.

Ahora piensa en lo que hace/es la referencia/variable de un objeto:

  1. Una variable contiene los bits que indican a la JVM cómo llegar al objeto referenciado en la memoria (Heap).
  2. Cuando se pasan argumentos a un método NO ESTÁ pasando la variable de referencia, sino una copia de los bits de la variable de referencia . Algo así: 3bad086a. 3bad086a representa una forma de llegar al objeto pasado.
  3. Así que sólo estás pasando 3bad086a que es el valor de la referencia.
  4. Estás pasando el valor de la referencia y no la referencia misma (y no el objeto).
  5. Este valor es realmente COPIADO y dado al método .

En lo siguiente (por favor, no intente compilar/ejecutar esto...):

1. Person person;
2. person = new Person("Tom");
3. changeName(person);
4.
5. //I didn't use Person person below as an argument to be nice
6. static void changeName(Person anotherReferenceToTheSamePersonObject) {
7.     anotherReferenceToTheSamePersonObject.setName("Jerry");
8. }

¿Qué ocurre?

  • La variable persona se crea en la línea 1 y es nulo al principio.
  • En la línea 2 se crea un nuevo objeto Persona, que se almacena en memoria, y la variable persona recibe la referencia al objeto Persona. Es decir, su dirección. Digamos 3bad086a.
  • La variable persona que contiene la dirección del objeto se pasa a la función en la línea 3.
  • En la línea #4 puedes escuchar el sonido del silencio
  • Comprueba el comentario de la línea 5
  • Una variable local del método - otraReferenciaAlMismoObjetoPersona - se crea y luego viene la magia en la línea #6:
    • La variable/referencia persona se copia bit a bit y se pasa a otraReferenciaAlMismoObjetoPersona dentro de la función.
    • No se crean nuevas instancias de Persona.
    • Ambos " persona " y " otraReferenciaAlMismoObjetoPersona " mantienen el mismo valor de 3bad086a.
    • No intentes esto pero persona==otraReferenciaAlMismoObjetoPersona sería verdadero.
    • Ambas variables tienen COPIAS IDÉNTICAS de la referencia y ambas se refieren al mismo Objeto Persona, el MISMO Objeto en el Heap y NO UNA COPIA.

Una imagen vale más que mil palabras:

Pass by Value

¡Tenga en cuenta que las flechas anotherReferenceToTheSamePersonObject se dirigen al objeto y no a la variable person!

Si no lo has entendido, confía en mí y recuerda que es mejor decir que Java es pass by value . Bueno, pasar por valor de referencia . Oh, bueno, aún mejor es pasar-por-copiar-el-valor-de-la-variable! ;)

Ahora siéntanse libres de odiarme pero noten que dado esto no hay diferencia entre pasar tipos de datos primitivos y Objetos al hablar de los argumentos del método.

¡Siempre se pasa una copia de los bits del valor de la referencia!

  • Si se trata de un tipo de dato primitivo, estos bits contendrán el valor del propio tipo de dato primitivo.
  • Si se trata de un objeto, los bits contendrán el valor de la dirección que indica a la JVM cómo llegar al objeto.

¡Java es pass-by-value porque dentro de un método puedes modificar el Objeto referenciado tanto como quieras pero por mucho que lo intentes nunca podrás modificar la variable pasada que seguirá referenciando (no p _ _ _ _ _ _ _) el mismo Objeto pase lo que pase!


La función changeName anterior nunca podrá modificar el contenido real (los valores de los bits) de la referencia pasada. En otras palabras, changeName no puede hacer que la persona se refiera a otro objeto.


Por supuesto, puedes acortarlo y decir simplemente que ¡Java es pass-by-value!

738voto

SCdF Puntos 11397

Java siempre pasa por valor, sin excepciones, siempre .

Entonces, ¿cómo es posible que alguien pueda confundirse con esto, y creer que Java es pass by reference, o pensar que tiene un ejemplo de Java actuando como pass by reference? El punto clave es que Java nunca proporciona acceso directo a los valores de los propios objetos , en cualquier circunstancias. El único acceso a los objetos es a través de un referencia a ese objeto. Como los objetos Java son siempre se accede a través de una referencia, en lugar de hacerlo directamente, es común hablar de campos y variables y los argumentos del método como ser objetos cuando pedantemente sólo son referencias a objetos . La confusión se debe a este cambio de nomenclatura (estrictamente hablando, incorrecto).

Así, al llamar a un método

  • Para los argumentos primitivos ( int , long etc.), el valor de paso es el valor real de la primitiva (por ejemplo, 3).
  • En el caso de los objetos, el pase por valor es el valor de la referencia al objeto .

Así que si tienes doSomething(foo) y public void doSomething(Foo foo) { .. } los dos Foos han copiado referencias que apuntan a los mismos objetos.

Naturalmente, pasar por valor una referencia a un objeto es muy parecido (indistinguible en la práctica) a pasar un objeto por referencia.

Iteramos.com

Iteramos es una comunidad de desarrolladores que busca expandir el conocimiento de la programación mas allá del inglés.
Tenemos una gran cantidad de contenido, y también puedes hacer tus propias preguntas o resolver las de los demás.

Powered by:

X