referencia - paso de parametros en java ejemplo
¿Es Java "paso por referencia" o "paso por valor"? (30)
Siempre pensé que Java era paso por referencia .
Sin embargo, he visto un par de publicaciones de 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?
Una referencia siempre es un valor cuando se representa, independientemente del idioma que utilice.
Al obtener una vista externa de la caja, veamos Ensamblado o alguna administración de memoria de bajo nivel. En el nivel de la 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 el 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 Ensamblaje tenemos una solución conveniente para dar un Nombre a cierta Ubicación (también conocida como variable), pero al compilar el código, el ensamblador simplemente reemplaza Nombre por la ubicación designada al igual que su navegador reemplaza los nombres de dominio con direcciones IP.
Básicamente, es técnicamente imposible pasar una referencia a cualquier cosa en cualquier idioma sin representarlo (cuando se convierte de inmediato 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 223er 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 observa 5 y 47 sin ninguna otra información, verá solo dos valores . Si los usas como referencias entonces para llegar a ellos 5
tenemos que viajar:
(Name)[Location] -> [Value at the Location]
---------------------
(Ref2Foo)[223] -> 47
(Foo)[47] -> 5
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 varios modos de invocación de método:
- 5 se copia en uno de los registros de la CPU (es decir, EAX).
- 5 obtiene PUSHd a la pila.
- 47 se copia en uno de los registros de la CPU
- 47 PUSHd a la pila.
- 223 se copia en uno de los registros de la CPU.
- 223 obtiene PUSHd a la pila.
En todos los casos por encima de un valor, se ha creado una copia de un valor existente, ahora depende del método de recepción para manejarlo. Cuando se escribe "Foo" dentro del método, se lee en EAX, o se elimina la referencia automáticamente o se hace doble referencia, el proceso depende de cómo funciona el idioma y / o de lo que dicta el tipo de Foo. Esto se oculta al desarrollador hasta que ella 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 idioma).
Ahora hemos pasado Foo al método:
- en el caso 1. y 2. si cambia Foo (
Foo = 9
), solo afecta el alcance local ya que tiene una copia del Valor. Desde dentro del método, ni siquiera podemos determinar dónde se ubicó el Foo original en la memoria. - en el caso 3. y 4. si usa construcciones de idioma predeterminadas y cambia Foo (
Foo = 11
), podría cambiar Foo globalmente (depende del idioma, es decir, Java o como laprocedure findMin(x, y, z: integer;
var m de Pascal: integer);
). Sin embargo, si el lenguaje le permite eludir el proceso de desreferencia, puede cambiar47
, digamos a49
. En ese punto, parece que Foo ha 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 ejecutables El programa y la escritura en él modificarán el código en 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. - en el caso 5. y 6. si modifica
223
dentro del método, crea el mismo caos que en 3. o 4. (un puntero, que apunta a un valor ahora malo, que se usa nuevamente como un puntero) pero esto sigue siendo un local Problema, ya que se copió el 223 . Sin embargo, si puede desreferenciarRef2Foo
(es decir223
), alcanzar y modificar el valor señalado47
, digamos,49
afectará a Foo globalmente , porque en este caso los métodos obtuvieron una copia223
pero la referencia47
solo existe una vez, y cambiarla que49
conducirá cadaRef2Foo
doble desreferencia a un valor incorrecto.
Al optar por detalles insignificantes, incluso los idiomas que pasan por referencia pasarán valores a las funciones, pero esas funciones saben que tienen que usarlo para fines de desreferenciación. Este paso-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 por valor estricto también es inútil, significaría que una matriz de 100 Mbyte debería copiarse cada vez que llamamos a un método con la matriz como argumento, por lo tanto, Java no puede ser estrictamente pasado por valor. Cada idioma pasaría una referencia a esta gran matriz (como un valor) y empleará un mecanismo de copia en escritura si esa matriz puede cambiarse localmente dentro del método o permite que el método (como lo hace Java) modifique la matriz globalmente (desde la vista de la persona que llama) y algunos idiomas permiten modificar el valor de la referencia en sí.
Entonces, en resumen y en la terminología propia de Java, Java es el valor por donde el valor puede ser: un valor real o un valor que es una representación de una referencia .
Básicamente, la reasignación de parámetros de Objeto no afecta el argumento, por ejemplo,
private void foo(Object bar) {
bar = null;
}
public static void main(String[] args) {
String baz = "Hah!";
foo(baz);
System.out.println(baz);
}
imprimirá "Hah!"
en lugar de null
La razón por la que esto funciona es porque bar
es una copia del valor de baz
, que es solo una referencia a "Hah!"
.Si se tratara de la referencia actual, foo
se habría redefinido baz
a null
.
Java pasa referencias a objetos por valor.
Java pasa referencias por valor.
Así que no puedes cambiar la referencia que se pasa.
Java siempre es pasar-por-valor . Desafortunadamente, decidieron llamar a la ubicación de un objeto una "referencia". Cuando pasamos el valor de un objeto, le estamos pasando la referencia . Esto es confuso para los principiantes.
Dice así:
public static void main(String[] args) {
Dog aDog = new Dog("Max");
// we pass the object to foo
foo(aDog);
// aDog variable is still pointing to the "Max" dog when foo(...) returns
aDog.getName().equals("Max"); // true
aDog.getName().equals("Fifi"); // false
}
public static void foo(Dog d) {
d.getName().equals("Max"); // true
// change d inside of foo() to point to a new Dog instance "Fifi"
d = new Dog("Fifi");
d.getName().equals("Fifi"); // true
}
En el ejemplo anterior, aDog.getName()
aún devolverá "Max"
. El valor aDog
dentro de main
no se cambia en la función foo
con Dog
"Fifi"
ya que la referencia del objeto se pasa por valor. Si se pasara por referencia, aDog.getName()
en main
devolvería "Fifi"
después de la llamada a foo
.
Igualmente:
public static void main(String[] args) {
Dog aDog = new Dog("Max");
foo(aDog);
// when foo(...) returns, the name of the dog has been changed to "Fifi"
aDog.getName().equals("Fifi"); // true
}
public static void foo(Dog d) {
d.getName().equals("Max"); // true
// this changes the name of d to be "Fifi"
d.setName("Fifi");
}
En el ejemplo anterior, Fifi
es el nombre del perro después de la llamada a foo(aDog)
porque el nombre del objeto se estableció dentro de foo(...)
. Todas las operaciones que foo
realiza en d
son tales que, para todos los propósitos prácticos, se realizan en aDog
(excepto cuando d
se cambia para apuntar a una instancia de Dog
diferente como d = new Dog("Boxer")
).
Java siempre pasa argumentos por valor NO por referencia.
Déjame explicarte esto a través de un example :
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");
}
}
Voy a explicar esto en pasos:
Declarar una referencia llamada
f
de tipoFoo
y asignarla a un nuevo objeto de tipoFoo
con un atributo"f"
.Foo f = new Foo("f");
Desde el lado del método, se declara una referencia de tipo
Foo
con un nombrea
y se asigna inicialmente anull
.public static void changeReference(Foo a)
Al llamar al método
changeReference
, la referenciaa
se asignará al objeto que se pasa como argumento.changeReference(f);
Declarar una referencia llamada
b
de tipoFoo
y asignarla a un nuevo objeto de tipoFoo
con un atributo"b"
.Foo b = new Foo("b");
a = b
está reasignando la referenciaa
NOTf
al objeto cuyo atributo es"b"
.Al llamar al
modifyReference(Foo c)
, se crea una referenciac
y se asigna al objeto con el atributo"f"
.c.setAttribute("c");
cambiará el atributo del objeto que la referenciac
apunta a él, y es el mismo objeto que la referenciaf
apunta a él.
Espero que entiendas ahora cómo funcionan los objetos pasados como argumentos en Java :)
Java siempre se pasa por valor, sin excepciones, nunca .
Entonces, ¿cómo es posible que alguien se confunda con esto y crea que Java se pasa por referencia, o cree que tiene un ejemplo de que Java actúa como pase por referencia? El punto clave es que Java nunca proporciona acceso directo a los valores de los objetos en cualquier 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 los campos y variables y los argumentos de los métodos como objetos , cuando de forma pedante son solo referencias a objetos . La confusión se deriva de este cambio (estrictamente hablando, incorrecto) en la nomenclatura.
Entonces, al llamar a un método.
- Para argumentos primitivos (
int
,long
, etc.), el paso por valor es el valor real de la primitiva (por ejemplo, 3). - Para los objetos, el paso por valor es el valor de la referencia al objeto .
Entonces, si tiene 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 se parece mucho (y en la práctica es indistinguible) pasar un objeto por referencia.
Me acabo de dar cuenta de que referenciaste mi artículo
La especificación de Java dice que todo en Java es pasado por valor. No hay tal cosa como "paso por referencia" en Java.
La clave para entender esto es que algo como
Dog myDog;
no es un perro; En realidad es un puntero a un perro.
Lo que eso significa, es cuando tienes
Dog myDog = new Dog("Rover");
foo(myDog);
Básicamente, estás pasando la dirección del objeto Dog
creado al método foo
.
(Digo esencialmente porque los punteros de Java no son direcciones directas, pero es más fácil pensarlos de esa manera)
Supongamos que el objeto Dog
reside en la dirección de memoria 42. Esto significa que pasamos 42 al método.
si el método fuera definido como
public void foo(Dog someDog) {
someDog.setName("Max"); // AAA
someDog = new Dog("Fifi"); // BBB
someDog.setName("Rowlf"); // CCC
}
echemos un vistazo a lo que está pasando.
- El parámetro
someDog
se establece en el valor 42 - en la línea "AAA"
-
someDog
le sigue elDog
que apunta (el objetoDog
en la dirección 42) - se le pide a ese
Dog
(el de la dirección 42) que cambie su nombre a Max
-
- en la línea "BBB"
- Se crea un nuevo
Dog
. Digamos que está en la dirección 74 - Le asignamos el parámetro
someDog
a 74
- Se crea un nuevo
- en la línea "CCC"
- A algún perro le sigue el
Dog
que apunta (el objetoDog
en la dirección 74) - a ese
Dog
(el que está en la dirección 74) se le pide que cambie su nombre a Rowlf
- A algún perro le sigue el
- luego volvemos
Ahora pensemos en lo que sucede fuera del método:
¿ myDog
?
Ahí está la clave.
Teniendo en cuenta que myDog
es un puntero , y no un Dog
real, la respuesta es NO. myDog
todavía tiene el valor 42; sigue apuntando al Dog
original (pero tenga en cuenta que debido a la línea "AAA", su nombre ahora es "Max", sigue siendo el mismo perro; el valor de myDog
no ha cambiado).
Es perfectamente válido seguir una dirección y cambiar lo que hay 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 a los que se apuntó. Sin embargo, no puede cambiar a donde apunta ese puntero.
En C ++, Ada, Pascal y otros idiomas que admiten paso por referencia, realmente puede cambiar la variable que se pasó.
Si Java tuviera una semántica de paso por referencia, el método foo
que definimos anteriormente habría cambiado donde myDog
estaba apuntando cuando asignó someDog
en la línea BBB.
Piense en los parámetros de referencia como si fueran alias para la variable pasada. Cuando se asigna ese alias, también lo es la variable que se pasó.
Siento que discutir sobre "paso por referencia vs paso por valor" no es muy útil.
Si dice: "Java es pasar por lo que sea (referencia / valor)", en cualquier caso, no proporciona una respuesta completa. Aquí hay información adicional que esperamos ayude a comprender lo que está sucediendo en la memoria.
Desplácese en la pila / montón antes de llegar a la implementación de Java: los valores se activan y desactivan de la pila de 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 aleatoria y desorganizada. La JVM solo encuentra espacio donde puede, y lo libera ya que las variables que lo utilizan ya no son necesarias.
Bueno. En primer lugar, los primitivos locales van a la pila. Así que este código:
int x = 3;
float y = 101.1f;
boolean amIAwesome = true;
resultados en esto:
Cuando declara y crea una instancia de un objeto. El objeto real va en el montón. ¿Qué pasa en la pila? La dirección del objeto en el montón. Los programadores de C ++ lo llamarían puntero, pero algunos desarrolladores de Java están en contra de la palabra "puntero". Lo que sea. Solo debes saber que la dirección del objeto va en la pila.
Al igual que:
int problems = 99;
String name = "Jay-Z";
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.
JButton[] marxBros = new JButton[3];
marxBros[0] = new JButton("Groucho");
marxBros[1] = new JButton("Zeppo");
marxBros[2] = new JButton("Harpo");
Entonces, ¿qué pasa cuando se llama 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 proponentes de "referencia" y "valor". Lo que llama no es tan importante como para comprender que lo que se pasa es la dirección del objeto.
private static void shout(String name){
System.out.println("There goes " + name + "!");
}
public static void main(String[] args){
String hisName = "John J. Jingleheimerschmitz";
String myName = hisName;
shout(myName);
}
One String se crea y se le asigna un espacio 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 ningún nuevo espacio de almacenamiento dinámico, sino que se crea un nuevo identificador en la pila. Luego llamamos shout()
: se crea un nuevo marco de pila y un nuevo identificador, se crea un name
y se asigna la dirección de la Cadena ya existente.
Entonces, valor, referencia? Usted dice "papa".
Solo para mostrar el contraste, compare los siguientes fragmentos de código C++ y Java :
En C ++: Nota: Código incorrecto - ¡Fugas de memoria! Pero demuestra el punto.
void cppMethod(int val, int &ref, Dog obj, Dog &objRef, Dog *objPtr, Dog *&objPtrRef)
{
val = 7; // Modifies the copy
ref = 7; // Modifies the original variable
obj.SetName("obj"); // Modifies the copy of Dog passed
objRef.SetName("objRef"); // Modifies the original Dog passed
objPtr->SetName("objPtr"); // Modifies the original Dog pointed to
// by the copy of the pointer passed.
objPtr = new Dog("newObjPtr"); // Modifies the copy of the pointer,
// leaving the original object alone.
objPtrRef->SetName("objRefPtr"); // Modifies the original Dog pointed to
// by the original pointer passed.
objPtrRef = new Dog("newObjPtrRef"); // Modifies the original pointer passed
}
int main()
{
int a = 0;
int b = 0;
Dog d0 = Dog("d0");
Dog d1 = Dog("d1");
Dog *d2 = new Dog("d2");
Dog *d3 = new Dog("d3");
cppMethod(a, b, d0, d1, d2, d3);
// a is still set to 0
// b is now set to 7
// d0 still have name "d0"
// d1 now has name "objRef"
// d2 now has name "objPtr"
// d3 now has name "newObjPtrRef"
}
En java
public static void javaMethod(int val, Dog objPtr)
{
val = 7; // Modifies the copy
objPtr.SetName("objPtr") // Modifies the original Dog pointed to
// by the copy of the pointer passed.
objPtr = new Dog("newObjPtr"); // Modifies the copy of the pointer,
// leaving the original object alone.
}
public static void main()
{
int a = 0;
Dog d0 = new Dog("d0");
javaMethod(a, d0);
// a is still set to 0
// d0 now has name "objPtr"
}
Java solo tiene los dos tipos de transferencia: por valor para los tipos incorporados y por valor del puntero para los tipos de objeto.
En Java solo se pasan las referencias y se pasan por valor:
Todos los argumentos de Java se pasan por valor (la referencia se copia cuando la utiliza 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 del Objeto son punteros (depósitos) que contienen solo la dirección del Objeto que se creó usando la palabra clave "nuevo", y se copian como tipos primitivos.
El comportamiento puede parecer diferente de los tipos primitivos: debido a que la variable de objeto copiada contiene la misma dirección (para el mismo Objeto), el contenido / 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.
"String" Los objetos parecen ser un ejemplo perfecto para la leyenda urbana que dice que "los objetos se pasan por referencia":
En efecto, dentro de un método nunca podrá actualizar el valor de un String pasado como argumento:
Un objeto de cadena, contiene caracteres por una matriz declarada final que no se puede modificar. Solo la dirección del Objeto podría ser reemplazada por otra utilizando "nuevo". El uso de "nuevo" para actualizar la variable no permitirá que se acceda al Objeto desde el exterior, ya que la variable se pasó inicialmente por valor y se copió.
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 de Java por referencia o por valor, simplemente sonreirá :-)
El primer paso borra de tu mente la 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.
El segundo paso recuerda que cuando pasas un objeto a un método, estás pasando la referencia del objeto y no el objeto en sí.
- Estudiante : Maestro, ¿significa esto que Java es paso por referencia?
- Maestro : Saltamontes, No.
Ahora piense en lo que hace / es la referencia / variable de un objeto:
- Una variable contiene los bits que le indican a la JVM cómo llegar al Objeto referenciado en la memoria (Heap).
- Al pasar argumentos a un método NO ESTÁ pasando la variable de referencia, sino una copia de los bits en la variable de referencia . Algo así: 3bad086a. 3bad086a representa una forma de llegar al objeto pasado.
- Así que solo estás pasando 3bad086a que es el valor de la referencia.
- Está pasando el valor de la referencia y no la referencia en sí (y no el objeto).
- Este valor es en realidad 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. }
¿Lo que pasa?
- La persona variable se crea en la línea # 1 y es nula al principio.
- Se crea un nuevo objeto de persona en la línea # 2, se almacena en la memoria, y la persona variable recibe la referencia al objeto de persona. Es decir, su dirección. Digamos 3bad086a.
- La persona variable 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.
- Compruebe el comentario en la línea # 5
- Se crea una variable local del método - anotherReferenceToTheSamePersonObject - y luego viene la magia en la línea # 6:
- La variable / persona de referencia se copia bit a bit y se pasa a otroReferenceToTheSamePersonObject dentro de la función.
- No se crean nuevas instancias de Person.
- Tanto " person " como " anotherReferenceToTheSamePersonObject " tienen el mismo valor de 3bad086a.
- No intente esto, pero la persona == anotherReferenceToTheSamePersonObject sería verdadera.
- Ambas variables tienen COPIAS IDENTICAS de la referencia y ambas se refieren al mismo Objeto de persona, el MISMO objeto en el montón y NO A UNA COPIA.
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, entonces confíe en mí y recuerde que es mejor decir que Java se pasa por valor . Bueno, pasar por valor de referencia . Oh, bueno, ¡aún mejor es pasar-por-la-copia-del-valor-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 se pasa una copia de los bits del valor de la referencia!
- Si se trata de un tipo de datos primitivos, estos bits contendrán el valor del tipo de datos primitivos en sí.
- Si se trata de un objeto, los bits contendrán el valor de la dirección que le indica a la JVM cómo llegar al objeto.
Java es pasado por valor porque dentro de un método puede modificar el Objeto referenciado tanto como quiera, pero no importa cuánto lo intente, nunca podrá modificar la variable pasada que seguirá haciendo referencia (no p _ _ _ _ _ _ _) ¡El mismo Objeto pase lo que pase!
La función de cambio de nombre anterior nunca podrá modificar el contenido real (los valores de bit) de la referencia pasada. En otras palabras, changeName no puede hacer que Person person se refiera a otro Objeto.
¡Por supuesto que puede abreviar y simplemente decir que Java se pasa de valor!
Java siempre se pasa por valor, no por referencia
En primer lugar, debemos entender qué pasa por valor y pasa 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 .
Paso por referencia (también llamado paso 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:
public class PassByValue {
public static void main(String[] args) {
Test t = new Test();
t.name = "initialvalue";
new PassByValue().changeValue(t);
System.out.println(t.name);
}
public void changeValue(Test f) {
f.name = "changevalue";
}
}
class Test {
String name;
}
La salida de este programa es:
changevalue
Vamos a entender paso a paso:
Test t = new Test();
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).
new PassByValue().changeValue(t);
Al pasar la referencia t a la función, no pasará directamente el valor de referencia real de la prueba del objeto, pero creará una copia de t y luego la pasará a la función. Dado que 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 tanto, apuntarán al mismo objeto.
Si cambia algo en la función usando la referencia f, modificará el contenido existente del objeto. Es por eso que obtuvimos la salida changevalue
, que se actualiza en la función.
Para entender esto más claramente, considere el siguiente ejemplo:
public class PassByValue {
public static void main(String[] args) {
Test t = new Test();
t.name = "initialvalue";
new PassByValue().changeRefence(t);
System.out.println(t.name);
}
public void changeRefence(Test f) {
f = null;
}
}
class Test {
String name;
}
¿Esto lanzará un NullPointerException
? No, porque solo pasa una copia de la referencia. En el caso de pasar por referencia, podría haber arrojado un NullPointerException
, como se ve a continuación:
Esperemos que esto ayude.
Algunas correcciones a algunos posts.
C no admite el paso por referencia. SIEMPRE se pasa por valor. C ++ admite el paso por referencia, pero no es el valor predeterminado y es bastante peligroso.
No importa cuál sea el valor en Java: primitivo o dirección (aproximadamente) del objeto, SIEMPRE se pasa por valor.
Si un objeto Java "se comporta" como si se pasara por referencia, eso es una propiedad de mutabilidad y no tiene absolutamente nada que ver con los mecanismos de paso.
No estoy seguro de por qué esto es tan confuso, quizás porque muchos "programadores" de Java no están formalmente entrenados y, por lo tanto, no entienden lo que realmente está pasando en la memoria
Java es una llamada por valor.
Cómo funciona
¡Siempre se 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 de 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 al archivo, supongamos que tenemos un archivo de texto abc.txt en C: / desktop 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 '''' y cierre el archivo y nuevamente abra el archivo desde el acceso directo. write ''es la comunidad en línea más grande para que aprendan los programadores'', luego el cambio total de archivos será '' es la comunidad en línea más grande para que aprendan los programadores''lo que significa que no importa desde donde abra el archivo, cada vez que accedemos al mismo archivo, aquí podemos asumir que Foo es un archivo y supongamos que foo está almacenado en 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). Así que para una mejor comprensión, haga un archivo de acceso directo y sienta ...
No, no es pasar por referencia.
Java se pasa por valor de acuerdo con la especificación del lenguaje Java:
Cuando se invoca el método o el constructor (§15.12), los valores de las expresiones de argumento reales inicializan las variables de parámetros recién creadas , cada una del tipo declarado, antes de la ejecución del cuerpo del método o constructor. El identificador que aparece en el DeclaratorId se puede usar como un nombre simple en el cuerpo del método o constructor para referirse al parámetro formal .
Pensé en contribuir con esta respuesta para agregar más detalles de las Especificaciones.
Primero, ¿cuál es la diferencia entre pasar por referencia y pasar por valor?
Pasar por referencia significa que el parámetro de las funciones llamadas será el mismo que el argumento pasado por los llamadores (no el valor, sino la identidad, la variable en sí).
Pasar por valor significa que el parámetro de las funciones llamadas será una copia del argumento pasado de los llamadores.
O de wikipedia, sobre el tema de paso por referencia.
En la evaluación llamada por referencia (también conocida como paso por referencia), una función recibe una referencia implícita a una variable utilizada como argumento, en lugar de una copia de su valor. Esto generalmente significa que la función puede modificar (es decir, asignar) a la variable utilizada como argumento, algo que será visto por su interlocutor.
Y sobre el tema del paso por valor.
En la llamada por valor, la expresión del argumento se evalúa, y el valor resultante está vinculado a la variable correspondiente en la función [...]. Si la función o el procedimiento puede asignar valores a sus parámetros, solo se asigna su copia local [...].
En segundo lugar, necesitamos saber qué utiliza Java en sus invocaciones de métodos. Los estados de especificación del lenguaje Java
Cuando se invoca el método o el constructor (§15.12), los valores de las expresiones de los argumentos reales inicializan las variables de parámetros recién creadas , cada una del tipo declarado, antes de la ejecución del cuerpo del método o del constructor.
Por lo tanto, asigna (o vincula) 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 especificación de la máquina virtual de Java
Hay tres tipos de tipos de referencia : tipos de clase, tipos de matriz y tipos de interfaz. Sus valores son referencias a instancias de clase, arrays o instancias de clase creadas dinámicamente que implementan interfaces, respectivamente.
La especificación del lenguaje Java también establece
Los valores de referencia (a menudo solo referencias) son punteros a estos objetos , y una referencia nula especial, que se refiere a ningún objeto.
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 retorno tipo de referencia y una expresión de creación de instancia ( new ...
) se resuelven en un valor de tipo de referencia.
Asi que
public void method (String param) {}
...
String var = new String("ref");
method(var);
method(var.toString());
method(new String("ref"));
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 puede seguir la referencia para invocar un método o acceder a un campo del objeto al que se hace referencia es completamente irrelevante para la conversación. La definición de paso por referencia era
Esto generalmente significa que la función puede modificar (es decir, asignar) a la variable utilizada como argumento, algo que será visto por su interlocutor.
En Java, modificar la variable significa reasignarla. En Java, si reasignó la variable dentro del método, pasaría desapercibida 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 la máquina virtual de Java, here . El valor del tipo es el valor de punto flotante o integral correspondiente, codificado apropiadamente (8, 16, 32, 64, etc. bits).
Como mucha gente lo mencionó antes, Java siempre es pasado por valor
Aquí hay otro ejemplo que le ayudará a comprender la diferencia ( el ejemplo de intercambio clásico ):
public class Test {
public static void main(String[] args) {
Integer a = new Integer(2);
Integer b = new Integer(3);
System.out.println("Before: a = " + a + ", b = " + b);
swap(a,b);
System.out.println("After: a = " + a + ", b = " + b);
}
public static swap(Integer iA, Integer iB) {
Integer tmp = iA;
iA = iB;
iB = tmp;
}
}
Huellas dactilares:
Antes: a = 2, b = 3
Después: a = 2, b = 3
Esto sucede porque iA y iB son nuevas variables de referencia local que tienen el mismo valor de las referencias pasadas (apuntan a y b respectivamente). Por lo tanto, intentar cambiar las referencias de iA o iB solo cambiará en el ámbito local y no fuera de este método.
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.
Por lo general, en referencia de Java significa una referencia a un objeto . Pero los términos técnicos que pasan por referencia / valor de la teoría del lenguaje de programación están hablando de una referencia a la celda de memoria que contiene la variable , que es algo completamente diferente.
En java, todo es referencia, así que cuando tienes algo como: Point pnt1 = new Point(0,0);
Java hace lo siguiente:
- Crea nuevo objeto Point
- Crea una nueva referencia de punto e inicializa esa referencia a punto (consulte) en el objeto de punto creado anteriormente.
- Desde aquí, a través de la vida del objeto Punto, accederá a ese objeto a través de la referencia pnt1. Entonces podemos decir que en Java manipulas el objeto a través de su referencia.
Java no pasa argumentos de método por referencia; Los pasa por valor. Usaré el ejemplo de este sitio :
public static void tricky(Point arg1, Point arg2) {
arg1.x = 100;
arg1.y = 100;
Point temp = arg1;
arg1 = arg2;
arg2 = temp;
}
public static void main(String [] args) {
Point pnt1 = new Point(0,0);
Point pnt2 = new Point(0,0);
System.out.println("X1: " + pnt1.x + " Y1: " +pnt1.y);
System.out.println("X2: " + pnt2.x + " Y2: " +pnt2.y);
System.out.println(" ");
tricky(pnt1,pnt2);
System.out.println("X1: " + pnt1.x + " Y1:" + pnt1.y);
System.out.println("X2: " + pnt2.x + " Y2: " +pnt2.y);
}
Flujo del programa:
Point pnt1 = new Point(0,0);
Point pnt2 = new Point(0,0);
Creando dos objetos Point diferentes con dos referencias diferentes asociadas.
System.out.println("X1: " + pnt1.x + " Y1: " +pnt1.y);
System.out.println("X2: " + pnt2.x + " Y2: " +pnt2.y);
System.out.println(" ");
Como se espera la salida será:
X1: 0 Y1: 0
X2: 0 Y2: 0
En esta línea ''pass-by-value'' entra en juego ...
tricky(pnt1,pnt2); public void tricky(Point arg1, Point arg2);
Las referencias pnt1
y pnt2
se pasan por valor al método complicado, lo que significa que ahora sus referencias pnt1
y pnt2
tienen su copies
nombre arg1
y arg2
.So pnt1
y arg1
apunta al mismo objeto. (Lo mismo para el pnt2
y arg2
)
En el tricky
método:
arg1.x = 100;
arg1.y = 100;
Siguiente en el tricky
método
Point temp = arg1;
arg1 = arg2;
arg2 = temp;
Aquí, primero debe crear la nueva temp
referencia del punto que apuntar en el mismo lugar como arg1
referencia. Luego mueves la referencia arg1
para apuntar al mismo lugar como arg2
referencia. Finalmente arg2
se apunte al mismo lugar como temp
.
Desde aquí ámbito del tricky
método se ha ido y que no tienen acceso a los más referencias: arg1
, arg2
, temp
. Pero la nota importante es que todo lo que haga con estas referencias cuando están ''en la vida'' afectará permanentemente al objeto al que apuntan .
Entonces, después de ejecutar el método tricky
, cuando regresas a main
, tienes esta situación:
Así que ahora, la ejecución completa del programa será:
X1: 0 Y1: 0
X2: 0 Y2: 0
X1: 100 Y1: 100
X2: 0 Y2: 0
He creado un hilo dedicado a este tipo de preguntas para cualquier lenguaje de programación here .
Java también se menciona . Aquí está el breve resumen:
- Java pasa los parámetros por valor
- "por valor" es la única forma en java de pasar un parámetro a un método
- el uso de métodos del objeto dado como parámetro alterará el objeto ya que las referencias apuntan a los objetos originales. (Si ese método sí altera algunos valores)
Java pasa los parámetros por VALOR, y por valor SOLAMENTE .
Para acortar el cuento largo:
Para aquellos que vienen de C #: NO HAY NINGÚN parámetro "out".
Para aquellos que vienen de PASCAL: NO HAY "var" parámetro .
Esto significa que no puede cambiar la referencia desde el objeto en sí, pero siempre puede cambiar las propiedades del objeto.
Una solución es utilizar el StringBuilder
parámetro en su lugar String
. ¡Y siempre puedes usar arreglos!
Java solo tiene pase por valor. Un ejemplo muy simple para validar esto.
public void test() {
MyClass obj = null;
init(obj);
//After calling init method, obj still points to null
//this is because obj is passed as value and not as reference.
}
private void init(MyClass objVar) {
objVar = new MyClass();
}
La distinción, o tal vez la forma en que recuerdo que solía tener la misma impresión que el póster original es la siguiente: Java siempre se pasa por valor. Todos los objetos (en Java, cualquier cosa excepto los primitivos) en Java son referencias. Estas referencias se pasan por valor.
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 compartiendo (también conocida como llamada por compartir objetos y llamada por objeto ) para este caso específico de "llamada por valor donde el valor una referencia".
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 de método. Considere el siguiente bit de código en C ++:
void getValues(int& arg1, int& arg2) {
arg1 = 1;
arg2 = 2;
}
void caller() {
int x;
int y;
getValues(x, y);
cout << "Result: " << x << " " << y << endl;
}
A veces quieres usar el mismo patrón en Java, pero no puedes; Al menos no directamente. En su lugar, podrías hacer algo como esto:
void getValues(int[] arg1, int[] arg2) {
arg1[0] = 1;
arg2[0] = 2;
}
void caller() {
int[] x = new int[1];
int[] y = new int[1];
getValues(x, y);
System.out.println("Result: " + x[0] + " " + y[0]);
}
Como se explicó en las respuestas anteriores, en Java está pasando un puntero a la matriz como un valor en getValues
. Eso es suficiente, porque el método modifica el elemento de la matriz y, por convención, se 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.
Para hacer una historia larga, los objetos Java tienen algunas propiedades muy peculiares.
En general, Java tiene tipos primitivos ( int
, bool
, char
, double
, etc) que se pasan directamente por valor. Entonces Java tiene objetos (todo lo que deriva de java.lang.Object
). En realidad, 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, ya que la referencia en sí misma se pasa por valor.
¿Esto suena extraño y confuso? Consideremos cómo C implementa pasar por referencia y pasar 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 una int 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áctica que oculta la parte del puntero de la ecuación: void foo(int &x)
es llamada por foo(a)
, donde el compilador mismo sabe que es una referencia y la dirección de la no referencia a
debe ser pasada. En Java, todas las variables que se refieren a objetos son en realidad de tipo de referencia, lo que en realidad obliga a llamar por referencia para la mayoría de los propósitos y propósitos sin el control detallado (y la complejidad) que ofrece, por ejemplo, C ++.
Permítame 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:
public class PassByValueString {
public static void main(String[] args) {
new PassByValueString().caller();
}
public void caller() {
String value = "Nikhil";
boolean valueflag = false;
String output = method(value, valueflag);
/*
* ''output'' is insignificant in this example. we are more interested in
* ''value'' and ''valueflag''
*/
System.out.println("output : " + output);
System.out.println("value : " + value);
System.out.println("valueflag : " + valueflag);
}
public String method(String value, boolean valueflag) {
value = "Anand";
valueflag = true;
return "output";
}
}
Resultado
output : output
value : Nikhil
valueflag : false
Ejemplo 2:
/ ** * * Pasar por valor * * /
public class PassByValueNewString {
public static void main(String[] args) {
new PassByValueNewString().caller();
}
public void caller() {
String value = new String("Nikhil");
boolean valueflag = false;
String output = method(value, valueflag);
/*
* ''output'' is insignificant in this example. we are more interested in
* ''value'' and ''valueflag''
*/
System.out.println("output : " + output);
System.out.println("value : " + value);
System.out.println("valueflag : " + valueflag);
}
public String method(String value, boolean valueflag) {
value = "Anand";
valueflag = true;
return "output";
}
}
Resultado
output : output
value : Nikhil
valueflag : false
Ejemplo 3:
/ ** Este ''Pasar por valor tiene la sensación de'' Pasar por referencia ''
Algunas personas dicen que los tipos primitivos y ''Cadena'' son ''pasar por valor'' y los objetos son ''pasar por referencia''.
Pero a partir de este ejemplo, podemos entender que se trata de pasar de hecho solo 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 son capaces de cambiar y aún se mantiene vigente 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.
* /
public class PassByValueObjectCase1 {
private class Student {
int id;
String name;
public Student() {
}
public Student(int id, String name) {
super();
this.id = id;
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Student [id=" + id + ", name=" + name + "]";
}
}
public static void main(String[] args) {
new PassByValueObjectCase1().caller();
}
public void caller() {
Student student = new Student(10, "Nikhil");
String output = method(student);
/*
* ''output'' is insignificant in this example. we are more interested in
* ''student''
*/
System.out.println("output : " + output);
System.out.println("student : " + student);
}
public String method(Student student) {
student.setName("Anand");
return "output";
}
}
Resultado
output : output
student : Student [id=10, name=Anand]
Ejemplo 4:
/ **
Además de lo que se mencionó en el Ejemplo 3 (PassByValueObjectCase1.java), no podemos cambiar la referencia real fuera del alcance original ".
Nota: No estoy pegando el código para private class Student
. La definición de clase para Student
es igual que en Ejemplo3.
* /
public class PassByValueObjectCase2 {
public static void main(String[] args) {
new PassByValueObjectCase2().caller();
}
public void caller() {
// student has the actual reference to a Student object created
// can we change this actual reference outside the local scope? Let''s see
Student student = new Student(10, "Nikhil");
String output = method(student);
/*
* ''output'' is insignificant in this example. we are more interested in
* ''student''
*/
System.out.println("output : " + output);
System.out.println("student : " + student); // Will it print Nikhil or Anand?
}
public String method(Student student) {
student = new Student(20, "Anand");
return "output";
}
}
Resultado
output : output
student : Student [id=10, name=Nikhil]
Por lo que sé, Java solo sabe 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 algunos escollos; por ejemplo, esto no funcionará:
public static void swap(StringBuffer s1, StringBuffer s2) {
StringBuffer temp = s1;
s1 = s2;
s2 = temp;
}
public static void main(String[] args) {
StringBuffer s1 = new StringBuffer("Hello");
StringBuffer s2 = new StringBuffer("World");
swap(s1, s2);
System.out.println(s1);
System.out.println(s2);
}
Esto llenará Hello World y no World Hello porque en la función de intercambio usas copys que no tienen impacto en las referencias en la página principal. Pero si tus objetos no son inmutables, puedes cambiarlos, por ejemplo:
public static void appendWorld(StringBuffer s1) {
s1.append(" World");
}
public static void main(String[] args) {
StringBuffer s = new StringBuffer("Hello");
appendWorld(s);
System.out.println(s);
}
Esto llenará Hello World en la línea de comando. Si cambias StringBuffer en String, solo se producirá Hello, porque String es inmutable. Por ejemplo:
public static void appendWorld(String s){
s = s+" World";
}
public static void main(String[] args) {
String s = new String("Hello");
appendWorld(s);
System.out.println(s);
}
Sin embargo, podrías hacer una envoltura para String como esta que lo haría capaz de usarlo con Strings:
class StringWrapper {
public String value;
public StringWrapper(String value) {
this.value = value;
}
}
public static void appendWorld(StringWrapper s){
s.value = s.value +" World";
}
public static void main(String[] args) {
StringWrapper s = new StringWrapper("Hello");
appendWorld(s);
System.out.println(s.value);
}
edit: creo que esta es también la razón para usar StringBuffer cuando se trata de "agregar" dos Strings porque puedes modificar el objeto original que no puedes con objetos inmutables como es String.
Siempre lo veo 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.
public class PassByCopy{
public static void changeName(Dog d){
d.name = "Fido";
}
public static void main(String[] args){
Dog d = new Dog("Maxx");
System.out.println("name= "+ d.name);
changeName(d);
System.out.println("name= "+ d.name);
}
}
class Dog{
public String name;
public Dog(String s){
this.name = s;
}
}
Salida de Java PassByCopy:
nombre = Maxx
nombre = Fido
Las clases de contenedor primitivo y las cadenas son inmutables, por lo que cualquier ejemplo que use esos tipos no funcionará igual que otros tipos / objetos.