parametros - ¿Es Java "paso por referencia" o "paso por valor"?




paso por referencia java (20)

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?

https://code.i-harness.com


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 5tenemos 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:

  1. 5 se copia en uno de los registros de la CPU (es decir, EAX).
  2. 5 obtiene PUSHd a la pila.
  3. 47 se copia en uno de los registros de la CPU
  4. 47 PUSHd a la pila.
  5. 223 se copia en uno de los registros de la CPU.
  6. 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 la procedure findMin(x, y, z: integer; var m de Pascal : integer);). Sin embargo, si el lenguaje le permite eludir el proceso de desreferencia, puede cambiar 47, digamos a 49. 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á en 47). PERO el valor de Foo de47No cambió globalmente, solo el que estaba dentro del método, porque 47también era una copia del método.
  • en el caso 5. y 6. si modifica 223dentro 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 desreferenciar Ref2Foo(es decir 223), alcanzar y modificar el valor señalado 47, digamos, 49afectará a Foo globalmente , porque en este caso los métodos obtuvieron una copia 223pero la referencia 47solo existe una vez, y cambiarla que 49conducirá cada Ref2Foodoble 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, foose habría redefinido baza null.


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 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 el Dog que apunta (el objeto Dog 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
  • en la línea "CCC"
    • A algún perro le sigue el Dog que apunta (el objeto Dog 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
  • 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ó.


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ó.


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


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 Stringejemplo 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).


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:

  1. Crea nuevo objeto Point
  2. Crea una nueva referencia de punto e inicializa esa referencia a punto (consulte) en el objeto de punto creado anteriormente.
  3. 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 pnt1y pnt2se pasan por valor al método complicado, lo que significa que ahora sus referencias pnt1y pnt2tienen su copiesnombre arg1y arg2.So pnt1y arg1 apunta al mismo objeto. (Lo mismo para el pnt2y arg2)

En el trickymétodo:

 arg1.x = 100;
 arg1.y = 100;

Siguiente en el trickymétodo

Point temp = arg1;
arg1 = arg2;
arg2 = temp;

Aquí, primero debe crear la nueva tempreferencia del punto que apuntar en el mismo lugar como arg1referencia. Luego mueves la referencia arg1para apuntar al mismo lugar como arg2referencia. Finalmente arg2se apunte al mismo lugar como temp.

Desde aquí ámbito del trickymé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

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 StringBuilderpará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();
}

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.


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 Studentes 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.





pass-by-value