Siempre pensé que Java era paso por referencia .
Sin embargo, he visto un par de publicaciones en el blog (por ejemplo, este blog ) que afirman que no lo es.
No creo entender la distinción que están haciendo.
¿Cuál es la explicación?
java
methods
parameter-passing
pass-by-reference
pass-by-value
usuario4315
fuente
fuente
call-by-value
ycall-by-reference
difieren de una manera muy importante . (Personalmente, prefiero usarcall-by-object-sharing
estos díascall-by-value[-of-the-reference]
, ya que esto describe la semántica en un alto nivel y no crea un conflicto concall-by-value
, que es la implementación subyacente).swap(x,y)
prueba .Respuestas:
Java siempre es paso por valor . Desafortunadamente, cuando pasamos el valor de un objeto, le pasamos la referencia . Esto es confuso para los principiantes.
Dice así:
En el ejemplo anterior
aDog.getName()
aún regresará"Max"
. El valoraDog
dentromain
no se cambia en la funciónfoo
con elDog
"Fifi"
como la referencia del objeto se pasa por valor. Si se pasa por referencia, a continuación, elaDog.getName()
enmain
que volvería"Fifi"
después de la llamada afoo
.Igualmente:
En el ejemplo anterior,
Fifi
es el nombre del perro después de llamar afoo(aDog)
porque el nombre del objeto se estableció dentro defoo(...)
. Cualquier operación que sefoo
lleva a cabo end
son tales que, a efectos prácticos, que se realizan enaDog
, pero es no es posible cambiar el valor de la variableaDog
en sí.fuente
Acabo de notar que hiciste referencia a mi artículo .
La especificación de Java dice que todo en Java es paso por valor. No hay tal cosa como "pasar por referencia" en Java.
La clave para entender esto es que algo así como
no es un perro; en realidad es un puntero a un perro.
Lo que eso significa es cuando tienes
básicamente está pasando la dirección del
Dog
objeto creado alfoo
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
objeto reside en la dirección de memoria 42. Esto significa que pasamos 42 al método.si el Método se definiera como
Echemos un vistazo a lo que está sucediendo.
someDog
se establece en el valor 42someDog
se sigue hastaDog
que señala (elDog
objeto en la dirección 42)Dog
(el de la dirección 42) se le pide que cambie su nombre a MaxDog
Se crea un nuevo . Digamos que está en la dirección 74someDog
a 74Dog
donde apunta (elDog
objeto en la dirección 74)Dog
se le pide a ese (el de la dirección 74) que cambie su nombre a RowlfAhora pensemos en lo que sucede fuera del método:
¿
myDog
Cambio?Ahí está la llave.
Teniendo en cuenta que
myDog
es un puntero , y no realDog
, la respuesta es NO.myDog
todavía tiene el valor 42; sigue apuntando al originalDog
(pero tenga en cuenta que debido a la línea "AAA", su nombre ahora es "Max", sigue siendo el mismo perro;myDog
el valor no ha cambiado).Es perfectamente válido seguir una dirección y cambiar lo que está al final; eso no cambia la variable, sin embargo.
Java funciona exactamente como C. Puede asignar un puntero, pasar el puntero a un método, seguir el puntero en el método y cambiar los datos apuntados. Sin embargo, no puede cambiar dónde apunta ese puntero.
En C ++, Ada, Pascal y otros lenguajes que admiten paso por referencia, en realidad puede cambiar la variable que se pasó.
Si Java tuviera una semántica de paso por referencia, el
foo
método que definimos anteriormente habría cambiado hacia dóndemyDog
apuntaba cuando se asignósomeDog
en la línea BBB.Piense en los parámetros de referencia como alias para la variable que se pasa. Cuando se asigna ese alias, también lo es la variable que se pasó.
fuente
Java siempre pasa argumentos por valor , NO por referencia.
Déjame explicarte esto a través de un ejemplo :
Explicaré esto en pasos:
Declarar una referencia llamada
f
de tipoFoo
y asignarle un nuevo objeto de tipoFoo
con un atributo"f"
.Desde el lado del método, se declara una referencia de tipo
Foo
con un nombrea
y se asigna inicialmentenull
.Cuando llame al método
changeReference
, a la referenciaa
se le asignará el objeto que se pasa como argumento.Declarar una referencia llamada
b
de tipoFoo
y asignarle un nuevo objeto de tipoFoo
con un atributo"b"
.a = b
realiza una nueva asignación a la referenciaa
, nof
, del objeto cuyo atributo es"b"
.A medida que llama al
modifyReference(Foo c)
método,c
se crea una referencia y se le asigna el objeto con atributo"f"
.c.setAttribute("c");
cambiará el atributo del objeto que hace referenciac
a él, y es el mismo objeto que hace referenciaf
a él.Espero que entiendas ahora cómo pasar objetos como argumentos funciona en Java :)
fuente
a
apunta al mismo objeto quef
(y nunca obtiene su propia copia del objetof
), cualquier cambio en el objeto que se hagaa
debe modificarsef
también (ya que ambos trabajan con el mismo objeto ), por lo que en algún momentoa
debe obtener su propia copia de losf
puntos de objeto .Esto le dará una idea de cómo funciona realmente Java hasta el punto de que en su próxima discusión sobre Java pasando por referencia o pasando por valor, simplemente sonreirá :-)
Paso uno, por favor borra de tu mente esa palabra que comienza con 'p' "_ _ _ _ _ _ _", especialmente si vienes de otros lenguajes de programación. Java y 'p' no se pueden escribir en el mismo libro, foro o incluso txt.
Paso dos recuerda que cuando pasas un objeto a un método, estás pasando la referencia del objeto y no el objeto en sí.
Ahora piense en lo que hace / es la referencia / variable de un Objeto:
A continuación (no intente compilar / ejecutar esto ...):
¿Lo que pasa?
Una imagen vale mas que mil palabras:
Tenga en cuenta que las flechas anotherReferenceToTheSamePersonObject se dirigen hacia el objeto y no hacia la persona variable.
Si no lo obtuvo, simplemente confíe en mí y recuerde que es mejor decir que Java es pasar por valor . Bueno, pase por valor de referencia . ¡Oh, bueno, aún mejor es pasar por copia del valor de la variable! ;)
Ahora siéntase libre de odiarme, pero tenga en cuenta que, dado esto, no hay diferencia entre pasar tipos de datos primitivos y objetos cuando se habla de argumentos de métodos.
¡Siempre pasa una copia de los bits del valor de la referencia!
¡Por supuesto, puede acortarlo y solo decir que Java es paso por valor!
fuente
public void foo(Car car){ ... }
,car
es localfoo
y contiene la ubicación del montón del Objeto? Entonces, si cambiocar
el valor porcar = new Car()
, ¿apuntará a un Objeto diferente en el montón? y si cambiocar
el valor de la propiedad porcar.Color = "Red"
,car
se modificará el objeto en el montón señalado por Además, ¿es lo mismo en C #? ¡Por favor responde! ¡Gracias!System.out.println(person.getName());
¿qué aparecerá? "Tom" o "Jerry"? Esto es lo último que me ayudará a superar esta confusión.Java siempre se pasa por valor, sin excepciones, nunca .
Entonces, ¿cómo es que cualquiera puede estar confundido por esto y creer que Java se pasa por referencia, o cree que tiene un ejemplo de Java que actúa como pase por referencia? El punto clave es que Java nunca proporciona acceso directo a los valores de los objetos , en ninguna circunstancia. El único acceso a los objetos es a través de una referencia a ese objeto. Debido a que siempre se accede a los objetos Java a través de una referencia, en lugar de hacerlo directamente, es común hablar de campos y variables y argumentos de métodos como objetos , cuando pedagógicamente solo son referencias a objetos .La confusión surge de este cambio (estrictamente hablando, incorrecto) en la nomenclatura.
Entonces, cuando se llama a un método
int
,long
, etc.), el paso por valor es el valor real de la primitiva (por ejemplo, 3).Entonces, si tiene
doSomething(foo)
ypublic 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 se parece mucho (y es indistinguible en la práctica) a pasar un objeto por referencia.
fuente
Java pasa referencias por valor.
Por lo tanto, no puede cambiar la referencia que se pasa.
fuente
Siento que discutir sobre "pasar por referencia vs pasar por valor" no es muy útil.
Si dice: "Java es pasar por lo que sea (referencia / valor)", en cualquier caso, no proporcionará una respuesta completa. Aquí hay información adicional que con suerte ayudará a comprender lo que está sucediendo en la memoria.
Curso intensivo en la pila / montón antes de llegar a la implementación de Java: los valores entran y salen de la pila de una manera ordenada y agradable, como una pila de platos en una cafetería. La memoria en el montón (también conocida como memoria dinámica) es desordenada y desorganizada. La JVM solo encuentra espacio donde puede y lo libera, ya que las variables que lo usan ya no son necesarias.
Bueno. En primer lugar, las primitivas locales van a la pila. Entonces este código:
resultados en esto:
Cuando declaras e instancias un objeto. El objeto real va al montón. ¿Qué pasa en la pila? La dirección del objeto en el montón. Los programadores de C ++ llamarían a esto un puntero, pero algunos desarrolladores de Java están en contra de la palabra "puntero". Lo que sea. Solo sepa que la dirección del objeto va en la pila.
Al igual que:
Una matriz es un objeto, por lo que también va en el montón. ¿Y qué hay de los objetos en la matriz? Obtienen su propio espacio de almacenamiento dinámico y la dirección de cada objeto va dentro de la matriz.
Entonces, ¿qué pasa cuando llamas a un método? Si pasa un objeto, lo que realmente está pasando es la dirección del objeto. Algunos pueden decir el "valor" de la dirección, y otros dicen que es solo una referencia al objeto. Esta es la génesis de la guerra santa entre los defensores de la "referencia" y el "valor". Lo que llamas no es tan importante como que entiendas que lo que se pasa es la dirección del objeto.
Se crea una cadena y se asigna espacio para ella en el montón, y la dirección de la cadena se almacena en la pila y se le asigna el identificador
hisName
, ya que la dirección de la segunda cadena es la misma que la primera, no se crea una nueva cadena y no se asigna nuevo espacio de almacenamiento dinámico, pero se crea un nuevo identificador en la pila. Luego llamamosshout()
: se crea un nuevo marco de pila y se crea un nuevo identificador,name
y se le asigna la dirección de la Cadena ya existente.Entonces, ¿valor, referencia? Tú dices "papa".
fuente
Solo para mostrar el contraste, compare los siguientes fragmentos de C ++ y Java :
En C ++: Nota: Código incorrecto: ¡pérdidas de memoria! Pero demuestra el punto.
En Java
Java solo tiene los dos tipos de paso: por valor para los tipos integrados y por valor del puntero para los tipos de objeto.
fuente
Dog **objPtrPtr
al ejemplo de C ++, de esa manera podemos modificar lo que el puntero "señala".Java pasa referencias a objetos por valor.
fuente
Básicamente, la reasignación de parámetros de Object no afecta el argumento, por ejemplo,
se imprimirá en
"Hah!"
lugar denull
. La razón por la que esto funciona es porquebar
es una copia del valor debaz
, que es solo una referencia"Hah!"
. Si se tratara de la referencia real en sí, entoncesfoo
habría redefinidobaz
anull
.fuente
No puedo creer que nadie haya mencionado a Barbara Liskov todavía. Cuando diseñó CLU en 1974, se encontró con este mismo problema de terminología e inventó el término llamada por compartir (también conocido como llamada por compartir objetos y llamada por objeto ) para este caso específico de "llamada por valor donde el valor es una referencia".
fuente
El quid de la cuestión es que la palabra referencia en la expresión "pasar por referencia" significa algo completamente diferente del significado habitual de la palabra referencia en Java.
Generalmente en Java, referencia significa una referencia a un objeto . Pero los términos técnicos pasan por referencia / valor de la teoría del lenguaje de programación hablando de una referencia a la celda de memoria que contiene la variable , que es algo completamente diferente.
fuente
En java todo es referencia, así que cuando tienes algo como:
Point pnt1 = new Point(0,0);
Java hace lo siguiente:Java no pasa argumentos de método por referencia; los pasa por valor. Usaré ejemplos de este sitio :
Flujo del programa:
Crear dos objetos Point diferentes con dos referencias diferentes asociadas.
Como resultado esperado será:
En esta línea, el "paso por valor" entra en juego ...
Referencias
pnt1
ypnt2
se pasan por valor al método complicado, lo que significa que ahora las referencias tuyaspnt1
ypnt2
tienen sucopies
nombrearg1
yarg2
.Asípnt1
yarg1
apunta al mismo objeto. (Lo mismo para elpnt2
yarg2
)En el
tricky
método:Siguiente en el
tricky
métodoAquí, primero debe crear la nueva
temp
referencia del punto que apuntar en el mismo lugar comoarg1
referencia. Luego mueve la referenciaarg1
para apuntar al mismo lugar comoarg2
referencia. Finalmentearg2
se apunte al mismo lugar comotemp
.Desde aquí ámbito del
tricky
método se ha ido y que no tienen acceso a los más referencias:arg1
,arg2
,temp
. Pero una nota importante es que todo lo que haga con estas referencias cuando estén 'en la vida' afectará permanentemente el objeto al que apuntan .Entonces, después de ejecutar el método
tricky
, cuando regresamain
, tiene esta situación:Así que ahora, la ejecución completa del programa será:
fuente
arg1.x = 1; arg1.y = 1; arg2.x = 2; arg2.y = 2;
, así, como arg1 ahora mantiene la referencia pnt2 y arg2 mantiene ahora la referencia pnt1, entonces, su impresiónX1: 2 Y1: 2 X2: 1 Y2: 1
Java siempre pasa por valor, no pasa por referencia
En primer lugar, debemos entender qué son los pases por valor y los que pasan por referencia.
Pasar por valor significa que está haciendo una copia en memoria del valor del parámetro real que se pasa. Esta es una copia del contenido del parámetro real .
Pasar por referencia (también llamado pasar por dirección) significa que se almacena una copia de la dirección del parámetro real .
A veces, Java puede dar la ilusión de pasar por referencia. Veamos cómo funciona usando el siguiente ejemplo:
La salida de este programa es:
Vamos a entender paso a paso:
Como todos sabemos, creará un objeto en el montón y devolverá el valor de referencia a t. Por ejemplo, suponga que el valor de t es
0x100234
(no conocemos el valor interno real de JVM, esto es solo un ejemplo).Al pasar la referencia t a la función, no pasará directamente el valor de referencia real de la prueba de objeto, pero creará una copia de t y luego la pasará a la función. Como pasa por valor , pasa una copia de la variable en lugar de la referencia real de la misma. Como dijimos que el valor de t era
0x100234
, tanto t como f tendrán el mismo valor y, por lo tanto, apuntarán al mismo objeto.Si cambia algo en la función utilizando la referencia f, modificará el contenido existente del objeto. Es por eso que obtuvimos el resultado
changevalue
, que se actualiza en la función.Para entender esto más claramente, considere el siguiente ejemplo:
¿Esto arrojará un
NullPointerException
? No, porque solo pasa una copia de la referencia. En el caso de pasar por referencia, podría haber arrojado unNullPointerException
, como se ve a continuación:Esperemos que esto ayude.
fuente
Una referencia siempre es un valor cuando se representa, sin importar el idioma que use.
Para obtener una vista fuera del cuadro, veamos el ensamblaje o alguna administración de memoria de bajo nivel. A nivel de CPU, una referencia a cualquier cosa se convierte inmediatamente en un valor si se escribe en la memoria o en uno de los registros de la CPU. (Es por eso que puntero es una buena definición. Es un valor, que tiene un propósito al mismo tiempo).
Los datos en la memoria tienen una ubicación y en esa ubicación hay un valor (byte, palabra, lo que sea). En Assembly tenemos una solución conveniente para asignar un nombre a cierta ubicación (también conocida como variable), pero al compilar el código, el ensamblador simplemente reemplaza Name con la ubicación designada, al igual que su navegador reemplaza los nombres de dominio con direcciones IP.
Hasta el núcleo, es técnicamente imposible pasar una referencia a cualquier cosa en cualquier idioma sin representarlo (cuando se convierte inmediatamente en un valor).
Digamos que tenemos una variable Foo, su ubicación está en el byte 47 en la memoria y su valor es 5. Tenemos otra variable Ref2Foo que está en el 223 byte en la memoria, y su valor será 47. Este Ref2Foo podría ser una variable técnica , no creado explícitamente por el programa. Si solo mira 5 y 47 sin ninguna otra información, verá solo dos valores . Si los usa como referencias, para llegar a ellos
5
tenemos que viajar:Así es como funcionan las tablas de salto.
Si queremos llamar a un método / función / procedimiento con el valor de Foo, hay algunas formas posibles de pasar la variable al método, dependiendo del idioma y sus diversos modos de invocación:
En todos los casos por encima de un valor, una copia de un valor existente, se ha creado, ahora depende del método de recepción manejarlo. Cuando escribe "Foo" dentro del método, se lee desde EAX, o se desreferencia automáticamente , o se desreferencia doble, el proceso depende de cómo funciona el lenguaje y / o qué dicta el tipo de Foo. Esto queda oculto para el desarrollador hasta que elude el proceso de desreferenciación. Por lo tanto, una referencia es un valor cuando se representa, porque una referencia es un valor que debe procesarse (a nivel de lenguaje).
Ahora hemos pasado Foo al método:
Foo = 9
) solo afecta el alcance local ya que tiene una copia del Valor. Desde el interior del método, ni siquiera podemos determinar en qué lugar de la memoria se encontraba el Foo original.Foo = 11
), podría cambiar Foo globalmente (depende del lenguaje, es decir, Java o como laprocedure findMin(x, y, z: integer;
var m de Pascal: integer);
). Sin embargo, si el idioma le permite eludir el proceso de desreferenciación, puede cambiar47
, digamos que49
. En ese momento, Foo parece haber cambiado si lo lees, porque le has cambiado el puntero local . Y si tuviera que modificar este Foo dentro del método (Foo = 12
), probablemente FUBARÁ la ejecución del programa (también conocido como segfault) porque escribirá en una memoria diferente a la esperada, incluso puede modificar un área que está destinada a contener ejecutable programa y escribir en él modificará el código de ejecución (Foo ahora no está en47
). PERO el valor de Foo de47
no cambió globalmente, solo el que estaba dentro del método, porque47
también era una copia del método.223
dentro del método, crea el mismo caos que en 3. o 4. (un puntero, apuntando a un valor ahora incorrecto, que nuevamente se usa como puntero) pero sigue siendo un local problema, ya que se copió 223 . Sin embargo, si puede desreferenciarRef2Foo
(es decir223
), alcanzar y modificar el valor señalado47
, digamos, a49
, afectará a Foo globalmente , porque en este caso los métodos obtuvieron una copia223
pero47
solo existe una referencia , y cambiar eso to49
llevará cadaRef2Foo
doble desreferenciación a un valor incorrecto.Analizando detalles insignificantes, incluso los lenguajes que hacen referencia pasarán valores a las funciones, pero esas funciones saben que tienen que usarlo para fines de referencia. Este paso de la referencia como valor está oculto para el programador porque es prácticamente inútil y la terminología es solo paso por referencia .
El paso estricto por valor también es inútil, significaría que una matriz de 100 Mbytes debería copiarse cada vez que llamamos a un método con la matriz como argumento, por lo tanto, Java no puede pasar estrictamente por valor. Cada idioma pasaría una referencia a esta gran matriz (como valor) y emplea un mecanismo de copia en escritura si esa matriz se puede cambiar localmente dentro del método o permite que el método (como lo hace Java) modifique la matriz globalmente (desde la vista del llamante) y algunos idiomas permiten modificar el valor de la referencia en sí.
En resumen, y en la propia terminología de Java, Java es paso por valor donde el valor puede ser: un valor real o un valor que es una representación de una referencia .
fuente
addr
no tiene tipo, lo@
hace con tipo y puede modificar la variable referenciada más tarde (excepto las copiadas localmente). Pero no entiendo por qué harías eso. Ese trozo de papel en su ejemplo (Objeto # 24601) es una referencia, su propósito es ayudar a encontrar la matriz en la memoria, no contiene ningún dato de matriz en sí mismo. Si reinicia su programa, la misma matriz puede obtener una ID de objeto diferente, incluso si su contenido será el mismo que en la ejecución anterior.AtomicReference
y no expone la referencia ni su objetivo, sino que incluye métodos para hacer cosas al objetivo; una vez que el código al que se pasó el objeto regresa, elAtomicReference
[al que su creador mantuvo una referencia directa] debe ser invalidado y abandonado. Eso proporcionaría la semántica adecuada, pero sería lento y repulsivo.Java es una llamada por valor
Cómo funciona
¡Siempre pasa una copia de los bits del valor de la referencia!
Si se trata de un tipo de datos primitivos, estos bits contienen el valor del tipo de datos primitivos en sí, por eso si cambiamos el valor del encabezado dentro del método, entonces no refleja los cambios externos.
Si se trata de un tipo de datos de objeto como Foo foo = new Foo () , en este caso la copia de la dirección del objeto pasa como acceso directo de archivo, supongamos que tenemos un archivo de texto abc.txt en C: \ escritorio y supongamos que hacemos un acceso directo de el mismo archivo y colóquelo dentro de C: \ desktop \ abc-shortcut para que cuando acceda al archivo desde C: \ desktop \ abc.txt y escriba 'Stack Overflow' y cierre el archivo y nuevamente abra el archivo desde el acceso directo y luego escribir 'es la comunidad en línea más grande para que los programadores aprendan', luego el cambio total de archivos será 'Stack Overflow es la comunidad en línea más grande para que los programadores aprendan'lo que significa que no importa desde dónde abra el archivo, cada vez que accedemos al mismo archivo, aquí podemos asumir Foo como un archivo y suponer foo almacenado a 123hd7h (dirección original como C: \ desktop \ abc.txt ) dirección y 234jdid (dirección copiada como C: \ desktop \ abc-shortcut que en realidad contiene la dirección original del archivo dentro). Entonces, para una mejor comprensión, crea un archivo de acceso directo y siente ..
fuente
Ya hay excelentes respuestas que cubren esto. Quería hacer una pequeña contribución compartiendo un ejemplo muy simple (que compilará) contrastando los comportamientos entre Pass-by-reference en c ++ y Pass-by-value en Java.
Algunos puntos:
C ++ pase por ejemplo de referencia:
Java pasar "una referencia de Java" por ejemplo de valor
EDITAR
Varias personas han escrito comentarios que parecen indicar que o no están viendo mis ejemplos o no obtienen el ejemplo de c ++. No estoy seguro de dónde está la desconexión, pero adivinar el ejemplo de c ++ no está claro. Estoy publicando el mismo ejemplo en pascal porque creo que el paso por referencia se ve más limpio en pascal, pero podría estar equivocado. Podría estar confundiendo más a la gente; Espero que no.
En pascal, los parámetros pasados por referencia se denominan "parámetros var". En el siguiente procedimiento setToNil, tenga en cuenta la palabra clave 'var' que precede al parámetro 'ptr'. Cuando se pasa un puntero a este procedimiento, se pasará por referencia . Tenga en cuenta el comportamiento: cuando este procedimiento establece ptr en nil (eso es hablar pascal para NULL), establecerá el argumento en nil; no puede hacerlo en Java.
EDITAR 2
Algunos extractos de "THE Java Programming Language" de Ken Arnold, James Gosling (el tipo que inventó Java) y David Holmes, capítulo 2, sección 2.6.5
Continúa haciendo el mismo punto con respecto a los objetos. . .
Y hacia el final de la misma sección, hace una declaración más amplia sobre que Java solo pasa por valor y nunca pasa por referencia.
Esta sección del libro tiene una gran explicación del paso de parámetros en Java y de la distinción entre paso por referencia y paso por valor y es por el creador de Java. Animaría a cualquiera a leerlo, especialmente si todavía no estás convencido.
Creo que la diferencia entre los dos modelos es muy sutil y, a menos que haya hecho la programación en la que realmente utilizó el paso por referencia, es fácil pasar por alto dónde difieren dos modelos.
Espero que esto resuelva el debate, pero probablemente no.
EDITAR 3
Podría estar un poco obsesionado con esta publicación. Probablemente porque siento que los creadores de Java sin saberlo difundieron información errónea. Si en lugar de usar la palabra "referencia" para los punteros hubieran usado otra cosa, digamos dingleberry, no habría habido ningún problema. Se podría decir, "Java pasa dingleberries por valor y no por referencia", y nadie se confundiría.
Esa es la razón por la que solo los desarrolladores de Java tienen problemas con esto. Miran la palabra "referencia" y piensan que saben exactamente lo que eso significa, por lo que ni siquiera se molestan en considerar el argumento opuesto.
De todos modos, noté un comentario en una publicación anterior, que hizo una analogía de globo que realmente me gustó. Tanto es así que decidí unir algunas imágenes prediseñadas para hacer un conjunto de dibujos animados para ilustrar el punto.
Pasar una referencia por valor: los cambios a la referencia no se reflejan en el alcance de la persona que llama, pero los cambios en el objeto sí. Esto se debe a que la referencia se copia, pero tanto el original como la copia se refieren al mismo objeto.
Pasar por referencia : no hay copia de la referencia. La persona que llama y la función que se está llamando comparten la referencia única. Cualquier cambio en la referencia o los datos del Objeto se reflejan en el alcance de la persona que llama.
EDITAR 4
He visto publicaciones sobre este tema que describen la implementación de bajo nivel de paso de parámetros en Java, lo que creo que es genial y muy útil porque hace concreta una idea abstracta. Sin embargo, para mí la pregunta es más sobre el comportamiento descrito en la especificación del lenguaje que sobre la implementación técnica del comportamiento. Este es un ejercicio de la Especificación del lenguaje Java, sección 8.4.1 :
Lo que significa que Java crea una copia de los parámetros pasados antes de ejecutar un método. Como la mayoría de las personas que estudiaron compiladores en la universidad, usé "The Dragon Book", que es EL libro de compiladores. Tiene una buena descripción de "Llamada por valor" y "Llamada por referencia" en el Capítulo 1. La descripción de Llamada por valor coincide exactamente con las especificaciones Java.
Cuando estudié compiladores, en los años 90, utilicé la primera edición del libro de 1986, anterior a Java por unos 9 o 10 años. Sin embargo, ¡acabo de encontrar una copia de la 2da Edición del 2007 que en realidad menciona Java! La sección 1.6.6 denominada "Mecanismos de paso de parámetros" describe el paso de parámetros bastante bien. Aquí hay un extracto bajo el título "Llamada por valor" que menciona Java:
fuente
Hasta donde yo sé, Java solo conoce la llamada por valor. Esto significa que para los tipos de datos primitivos trabajará con una copia y para los objetos trabajará con una copia de la referencia a los objetos. Sin embargo, creo que hay algunas trampas; por ejemplo, esto no funcionará:
Esto llenará Hello World y no World Hello porque en la función de intercambio usa copias que no tienen impacto en las referencias en main. Pero si sus objetos no son inmutables, puede cambiarlo, por ejemplo:
Esto llenará Hello World en la línea de comando. Si cambia StringBuffer en String, producirá solo Hello porque String es inmutable. Por ejemplo:
Sin embargo, podría hacer un contenedor para String como este que lo haría capaz de usarlo con Strings:
editar: creo que esta es también la razón para usar StringBuffer cuando se trata de "agregar" dos cadenas porque puedes modificar el objeto original que no puedes con objetos inmutables como String.
fuente
swap(a, b)
que (1) intercambiaa
yb
desde el punto de vista de la persona que llama, (2) es independiente del tipo en la medida en que lo permite la escritura estática (lo que significa que usarlo con otro tipo no requiere nada más que cambiar los tipos declarados dea
yb
) , y (3) no requiere que la persona que llama pase explícitamente un puntero o nombre, entonces el lenguaje admite pasar por referencia.No, no se pasa por referencia.
Java se pasa por valor de acuerdo con la especificación del lenguaje Java:
fuente
Permítanme tratar de explicar mi comprensión con la ayuda de cuatro ejemplos. Java es paso por valor y no paso por referencia
/ **
Pasar por valor
En Java, todos los parámetros se pasan por valor, es decir, la asignación de un argumento de método no es visible para la persona que llama.
* /
Ejemplo 1:
Resultado
Ejemplo 2
/ ** * * Pasar por valor * * /
Resultado
Ejemplo 3
/ ** Este 'Pase por valor tiene la sensación de' Pasar por referencia '
Algunas personas dicen que los tipos primitivos y 'String' son 'pasar por valor' y los objetos son 'pasar por referencia'.
Pero a partir de este ejemplo, podemos entender que de hecho solo se pasa por valor, teniendo en cuenta que aquí estamos pasando la referencia como valor. es decir: la referencia se pasa por valor. Es por eso que pueden cambiar y aún así es cierto después del alcance local. Pero no podemos cambiar la referencia real fuera del alcance original. lo que eso significa se demuestra en el siguiente ejemplo de PassByValueObjectCase2.
* /
Resultado
Ejemplo 4
/ **
Además de lo mencionado en el Ejemplo 3 (PassByValueObjectCase1.java), no podemos cambiar la referencia real fuera del alcance original ".
Nota: no estoy pegando el código
private class Student
. La definición de clase paraStudent
es la misma que en Example3.* /
Resultado
fuente
Nunca puede pasar por referencia en Java, y una de las formas obvias es cuando desea devolver más de un valor de una llamada al método. Considere el siguiente bit de código en C ++:
A veces quieres usar el mismo patrón en Java, pero no puedes; Al menos no directamente. En cambio, podrías hacer algo como esto:
Como se explicó en respuestas anteriores, en Java está pasando un puntero a la matriz como un valor
getValues
. Eso es suficiente, porque el método modifica el elemento de matriz y, por convención, espera que el elemento 0 contenga el valor de retorno. Obviamente, puede hacer esto de otras maneras, como estructurar su código para que esto no sea necesario o construir una clase que pueda contener el valor de retorno o permitir que se establezca. Pero el patrón simple disponible para usted en C ++ anterior no está disponible en Java.fuente
Pensé que contribuiría con esta respuesta para agregar más detalles de las Especificaciones.
Primero, ¿cuál es la diferencia entre pasar por referencia versus pasar por valor?
O de wikipedia, sobre el tema de la referencia de paso
Y sobre el tema del paso por valor
En segundo lugar, necesitamos saber qué utiliza Java en sus invocaciones de métodos. Los estados de la especificación del lenguaje Java
Por lo tanto, asigna (o enlaza) el valor del argumento a la variable de parámetro correspondiente.
¿Cuál es el valor del argumento?
Consideremos los tipos de referencia, los estados de la especificación de máquina virtual Java
La especificación del lenguaje Java también establece
El valor de un argumento (de algún tipo de referencia) es un puntero a un objeto. Tenga en cuenta que una variable, una invocación de un método con un tipo de referencia tipo de retorno y una expresión de creación de instancia (
new ...
) se resuelven en un valor de tipo de referencia.Entonces
se unen todos el valor de una referencia a un
String
ejemplo de parámetro de nueva creación del método,param
. Esto es exactamente lo que describe la definición de paso por valor. Como tal, Java es paso por valor .El hecho de que pueda seguir la referencia para invocar un método o acceder a un campo del objeto referenciado es completamente irrelevante para la conversación. La definición de paso por referencia fue
En Java, modificar la variable significa reasignarla. En Java, si reasigna la variable dentro del método, pasará desapercibido para la persona que llama. Modificar el objeto al que hace referencia la variable es un concepto completamente diferente.
Los valores primitivos también se definen en la Especificación de máquina virtual Java, aquí . El valor del tipo es el valor de punto flotante o integral correspondiente, codificado apropiadamente (8, 16, 32, 64, etc. bits).
fuente
En Java solo se pasan referencias y se pasan por valor:
Todos los argumentos de Java se pasan por valor (la referencia se copia cuando la usa el método):
En el caso de los tipos primitivos, el comportamiento de Java es simple: el valor se copia en otra instancia del tipo primitivo.
En el caso de los objetos, esto es lo mismo: las variables de objeto son punteros (cubos) que contienen solo la dirección del objeto que se creó con la palabra clave "nueva" y se copian como tipos primitivos.
El comportamiento puede parecer diferente de los tipos primitivos: porque la variable objeto copiada contiene la misma dirección (al mismo Objeto). El contenido / los miembros del Objeto aún podrían modificarse dentro de un método y luego acceder al exterior, dando la ilusión de que el Objeto (que contiene) se pasó por referencia.
Los objetos de "cadena" parecen ser un buen contraejemplo de la leyenda urbana que dice que "los objetos se pasan por referencia":
En efecto, utilizando un método, nunca podrá actualizar el valor de una Cadena pasada como argumento:
Un objeto de cadena contiene caracteres por una matriz declarada final que no puede modificarse. Solo la dirección del Objeto puede ser reemplazada por otra usando "nuevo". El uso de "nuevo" para actualizar la variable no permitirá que se acceda al Objeto desde afuera, ya que la variable se pasó inicialmente por valor y se copió.
fuente
strParam.setChar ( i, newValue )
. Dicho esto, las cadenas, como cualquier otra cosa, se pasan por valor y, dado que String es un tipo no primitivo, ese valor es una referencia a lo que se creó con new, y puede verificar esto usando String.intern () .La distinción, o tal vez solo la forma en que recuerdo como solía estar bajo la misma impresión que el póster original, es esta: Java siempre se pasa por valor. Todos los objetos (en Java, cualquier cosa excepto las primitivas) en Java son referencias. Estas referencias se pasan por valor.
fuente
Como muchas personas lo mencionaron antes, Java siempre es un valor de paso
Aquí hay otro ejemplo que lo ayudará a comprender la diferencia ( el ejemplo de intercambio clásico ):
Huellas dactilares:
Esto sucede porque iA e iB son nuevas variables de referencia local que tienen el mismo valor de las referencias pasadas (apuntan a a y b respectivamente). Por lo tanto, tratar de cambiar las referencias de iA o iB solo cambiará en el ámbito local y no fuera de este método.
fuente
Java solo ha pasado por valor. Un ejemplo muy simple para validar esto.
fuente
obj
(null
) se pasó ainit
, no una referencia aobj
.Siempre pienso en ello como "pasar por copia". Es una copia del valor, ya sea primitivo o de referencia. Si es una primitiva, es una copia de los bits que son el valor y si es un objeto, es una copia de la referencia.
Salida de Java PassByCopy:
Las clases de envoltura primitiva y las cadenas son inmutables, por lo que cualquier ejemplo que use esos tipos no funcionará igual que otros tipos / objetos.
fuente
A diferencia de otros lenguajes, Java no le permite elegir entre pasar por valor y pasar por referencia: todos los argumentos se pasan por valor. Una llamada a un método puede pasar dos tipos de valores a un método: copias de valores primitivos (por ejemplo, valores de int y double) y copias de referencias a objetos.
Cuando un método modifica un parámetro de tipo primitivo, los cambios en el parámetro no tienen efecto sobre el valor del argumento original en el método de llamada.
Cuando se trata de objetos, los objetos en sí no se pueden pasar a los métodos. Entonces pasamos la referencia (dirección) del objeto. Podemos manipular el objeto original usando esta referencia.
Cómo Java crea y almacena objetos: cuando creamos un objeto, almacenamos la dirección del objeto en una variable de referencia. Analicemos la siguiente declaración.
"Cuenta cuenta1" es el tipo y el nombre de la variable de referencia, "=" es el operador de asignación, "nuevo" solicita la cantidad de espacio requerida del sistema. El constructor a la derecha de la palabra clave new que crea el objeto se llama implícitamente por la palabra clave new. La dirección del objeto creado (resultado del valor correcto, que es una expresión llamada "expresión de creación de instancia de clase") se asigna al valor izquierdo (que es una variable de referencia con un nombre y un tipo especificado) utilizando el operador de asignación.
Aunque la referencia de un objeto se pasa por valor, un método aún puede interactuar con el objeto referenciado llamando a sus métodos públicos utilizando la copia de la referencia del objeto. Dado que la referencia almacenada en el parámetro es una copia de la referencia que se pasó como argumento, el parámetro en el método llamado y el argumento en el método de llamada se refieren al mismo objeto en la memoria.
Pasar referencias a matrices, en lugar de los objetos de la matriz en sí, tiene sentido por razones de rendimiento. Debido a que todo en Java se pasa por valor, si se pasan objetos de matriz, se pasaría una copia de cada elemento. Para matrices grandes, esto perdería tiempo y consumiría un almacenamiento considerable para las copias de los elementos.
En la imagen a continuación, puede ver que tenemos dos variables de referencia (estas se llaman punteros en C / C ++, y creo que ese término facilita la comprensión de esta característica) en el método principal. Las variables primitivas y de referencia se guardan en la memoria de la pila (lado izquierdo en las imágenes a continuación). "punto" de las variables de referencia array1 y array2 (como lo llaman los programadores de C / C ++) o referencia a las matrices ayb respectivamente, que son objetos (los valores que contienen estas variables de referencia son direcciones de objetos) en la memoria del montón (lado derecho en las imágenes a continuación) .
Si pasamos el valor de la variable de referencia array1 como argumento al método reverseArray, se crea una variable de referencia en el método y esa variable de referencia comienza a apuntar a la misma matriz (a).
Entonces, si decimos
en el método reverseArray, hará un cambio en la matriz a.
Tenemos otra variable de referencia en el método reverseArray (array2) que apunta a una matriz c. Si tuviéramos que decir
en el método reverseArray, entonces la variable de referencia array1 en el método reverseArray dejaría de apuntar a la matriz a y comenzaría a apuntar a la matriz c (Línea de puntos en la segunda imagen).
Si devolvemos el valor de la variable de referencia array2 como el valor de retorno del método reverseArray y asignamos este valor a la variable de referencia array1 en el método principal, array1 en main comenzará a apuntar a la matriz c.
Así que escribamos todas las cosas que hemos hecho a la vez.
Y ahora que el método reverseArray ha terminado, sus variables de referencia (array1 y array2) se han ido. Lo que significa que ahora solo tenemos las dos variables de referencia en el método principal matriz1 y matriz2 que apuntan a las matrices c y b respectivamente. Ninguna variable de referencia apunta al objeto (matriz) a. Por lo tanto, es elegible para la recolección de basura.
También puede asignar el valor de array2 en main a array1. array1 comenzaría a apuntar a b.
fuente
He creado un hilo dedicado a este tipo de preguntas para cualquier lenguaje de programación aquí .
También se menciona Java . Aquí está el breve resumen:
fuente
Para resumir, los objetos Java tienen algunas propiedades muy peculiares.
En general, Java tiene tipos primitivos (
int
,bool
,char
,double
, etc.) que se transmiten directamente por valor. Entonces Java tiene objetos (todo lo que se deriva dejava.lang.Object
). Los objetos siempre se manejan a través de una referencia (una referencia es un puntero que no puede tocar). Eso significa que, en efecto, los objetos se pasan por referencia, ya que las referencias normalmente no son interesantes. Sin embargo, significa que no puede cambiar a qué objeto se apunta cuando la referencia en sí misma se pasa por valor.¿Suena extraño y confuso? Consideremos cómo C implementa pasa por referencia y pasa por valor. En C, la convención predeterminada es pasar por valor.
void foo(int x)
pasa un int por valor.void foo(int *x)
es una función que no quiere unaint a
, sino un puntero a un int:foo(&a)
. Uno usaría esto con el&
operador para pasar una dirección variable.Lleva esto a C ++, y tenemos referencias. Las referencias son básicamente (en este contexto) azúcar sintáctico que oculta la parte del puntero de la ecuación:
void foo(int &x)
se llama porfoo(a)
, donde el compilador mismo sabe que es una referencia y sea
debe pasar la dirección de la no referencia . En Java, todas las variables que se refieren a objetos son en realidad de tipo de referencia, en efecto forzando la llamada por referencia para la mayoría de las intenciones y propósitos sin el control (y la complejidad) de grano fino que ofrece, por ejemplo, C ++.fuente