Is Java “pass-by-reference” of “pass-by-value”?

Ik dacht altijd dat Java pass-by-reference gebruikt.

Ik heb echter een aantal blogposts gezien (bijvoorbeeld deze blog) die beweren van niet (in de blogpost staat dat Java pass-by-value gebruikt).

Ik denk niet dat ik het onderscheid begrijp dat ze maken.

Wat is de uitleg?


Antwoord 1, autoriteit 100%

Java is altijd doorgeefwaarde. Helaas hebben we, als we met objecten te maken hebben, te maken met object-handles, referenties genaamd, die ook per waarde worden doorgegeven. Deze terminologie en semantiek brengen veel beginners gemakkelijk in verwarring.

Het gaat als volgt:

public static void main(String[] args) {
    Dog aDog = new Dog("Max");
    Dog oldDog = aDog;
    // 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
    aDog == oldDog; // true
}
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
}

In het bovenstaande voorbeeld zal aDog.getName() nog steeds "Max" retourneren. De waarde aDog binnen main wordt niet gewijzigd in de functie foo met de Dog "Fifi" als de objectreferentie wordt doorgegeven door waarde. Als het door verwijzing zou zijn doorgegeven, dan zou de aDog.getName() in main "Fifi" retourneren na de aanroep van foo.

Evenzo:

public static void main(String[] args) {
    Dog aDog = new Dog("Max");
    Dog oldDog = aDog;
    foo(aDog);
    // when foo(...) returns, the name of the dog has been changed to "Fifi"
    aDog.getName().equals("Fifi"); // true
    // but it is still the same dog:
    aDog == oldDog; // true
}
public static void foo(Dog d) {
    d.getName().equals("Max"); // true
    // this changes the name of d to be "Fifi"
    d.setName("Fifi");
}

In het bovenstaande voorbeeld is Fifi de naam van de hond na aanroep van foo(aDog) omdat de naam van het object binnen foo(...). Alle bewerkingen die foo uitvoert op d zijn zodanig dat ze, voor alle praktische doeleinden, worden uitgevoerd op aDog, maar het is niet mogelijk om de waarde van de variabele aDog zelf te wijzigen.


Antwoord 2, autoriteit 52%

Ik zie net dat je verwijst naar mijn artikel.

De Java-specificatie zegt dat alles in Java een pass-by-waarde is. Er bestaat niet zoiets als “pass-by-reference” in Java.

De sleutel om dit te begrijpen is dat zoiets als

Dog myDog;

is geen een hond; het is eigenlijk een wijzer naar een hond. Het gebruik van de term “verwijzing” in Java is erg misleidend en veroorzaakt hier de meeste verwarring. Wat ze “referenties” noemen handelen/voelen meer als wat we zouden noemen "aanwijzers" in de meeste andere talen.

Wat dat betekent, is wanneer je

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

je geeft in feite het adres van het gemaakte Dog-object door aan de foo-methode.

(Ik zeg in wezen omdat Java-aanwijzers/verwijzingen geen directe adressen zijn, maar het is het gemakkelijkst om ze op die manier te zien.)

Stel dat het Dog object zich op geheugenadres 42 bevindt. Dit betekent dat we 42 doorgeven aan de methode.

als de methode was gedefinieerd als

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

laten we eens kijken wat er aan de hand is.

  • de parameter someDog is ingesteld op de waarde 42
  • op regel "AAA"
    • someDog wordt gevolgd naar de Dog waarnaar het verwijst (het Dog-object op adres 42)
    • die Dog (die op adres 42) wordt gevraagd zijn naam te veranderen in Max
  • bij regel "BBB"
    • een nieuwe Dog is gemaakt. Laten we zeggen dat hij op adres 74 is
    • we wijzen de parameter someDog toe aan 74
  • op regel “CCC”
    • someDog wordt gevolgd naar de Dog waarnaar hij verwijst (het object Dog op adres 74)
    • die Dog (die op adres 74) wordt gevraagd zijn naam te veranderen in Rowlf
  • dan keren we terug

Laten we nu eens nadenken over wat er buiten de methode gebeurt:

Is myDog gewijzigd?

Daar is de sleutel.

Rekening houdend met het feit dat myDog een pointer is, en geen echte Dog, is het antwoord NEE. myDog heeft nog steeds de waarde 42; het wijst nog steeds naar de originele Dog (maar merk op dat vanwege de regel “AAA”, de naam nu “Max” is – nog steeds dezelfde hond; myDog‘s waarde is niet veranderd.)

Het is volkomen geldig om een ​​adres te volgen en te wijzigen wat er aan het einde staat; dat verandert echter niets aan de variabele.

Java werkt precies zoals C. U kunt een aanwijzer toewijzen, de aanwijzer doorgeven aan een methode, de aanwijzer in de methode volgen en de gegevens wijzigen waarnaar wordt verwezen. De beller ziet echter geen wijzigingen die u aanbrengt in waar die aanwijzer wijst. (In een taal met pass-by-reference-semantiek, kan de methodefunctie kan de aanwijzer veranderen en de beller zal die verandering zien.)

In C++, Ada, Pascal en andere talen die pass-by-reference ondersteunen, kun je de doorgegeven variabele daadwerkelijk wijzigen.

Als Java pass-by-reference-semantiek had, zou de foo-methode die we hierboven hebben gedefinieerd, zijn gewijzigd waar myDog naar wees toen het someDog online BBB.

Beschouw referentieparameters als aliassen voor de doorgegeven variabele. Wanneer die alias is toegewezen, wordt de variabele die is doorgegeven dat ook.


Antwoord 3, autoriteit 31%

Java geeft argumenten altijd door op waarde, NIET op referentie.


Laat me dit uitleggen aan de hand van een voorbeeld:

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");
     }
}

Ik zal dit in stappen uitleggen:

  1. Een referentie met de naam f van het type foo declareren en er een nieuw object van het type foo aan toewijzen met een attribuut "f".

    Foo f = new Foo("f");
    

    voer hier de afbeeldingsbeschrijving in

  2. Van de kant van de methode wordt een referentie van het type foo met de naam a gedeclareerd en wordt in eerste instantie null toegewezen.

    public static void changeReference(Foo a)
    

    voer hier de afbeeldingsbeschrijving in

  3. Als je de methode changeReference aanroept, wordt aan de referentie a het object toegewezen dat als argument wordt doorgegeven.

    changeReference(f);
    

    voer hier de afbeeldingsbeschrijving in

  4. Een referentie met de naam b van het type foo declareren en er een nieuw object van het type foo aan toewijzen met een attribuut "b".

    Foo b = new Foo("b");
    

    voer hier de afbeeldingsbeschrijving in

  5. a = b maakt een nieuwe toewijzing aan de referentie a, niet f, van het object waarvan het attribuut "b" is.

    voer hier de afbeeldingsbeschrijving in

  6. Terwijl u de methode modifyReference(Foo c) aanroept, wordt een referentie c gemaakt en wordt het object toegewezen met het kenmerk "f".

    voer hier de afbeeldingsbeschrijving in

  7. c.setAttribute("c"); verandert het attribuut van het object waarnaar c verwijst, en het is hetzelfde object dat referentie f verwijst ernaar.

    voer hier de afbeeldingsbeschrijving in

Ik hoop dat je nu begrijpt hoe het doorgeven van objecten als argumenten in Java werkt 🙂


Antwoord 4, autoriteit 12%

Java is altijd een waarde die wordt doorgegeven, zonder uitzonderingen, ooit.

Dus hoe kan iemand hierdoor in de war raken en geloven dat Java pass-by-referentie is, of denken dat ze een voorbeeld hebben van Java als pass-by-referentie? Het belangrijkste punt is dat Java nooit directe toegang biedt tot de waarden van objecten zelf, in alle omstandigheden. De enige toegang tot objecten is via een verwijzing naar dat object. Omdat Java-objecten altijd toegankelijk zijn via een verwijzing, in plaats van rechtstreeks, is het gebruikelijk om over velden en variabelen en methodeargumenten te praten als zijnde objecten , als ze pedant zijn, zijn het slechts verwijzingen naar objecten. De verwarring komt voort uit deze (strikt genomen onjuiste) wijziging in de nomenclatuur.

Dus bij het aanroepen van een methode

  • Voor primitieve argumenten (int, long, enz.), is de waarde die voorbijgaat de werkelijke waarde van de primitief (bijvoorbeeld , 3)).
  • Voor objecten is de pass-by-waarde de waarde van de verwijzing naar het object.

Dus als je doSomething(foo) en public void doSomething(Foo foo) { .. } hebt, hebben de twee Foos referenties gekopieerd die naar dezelfde objecten verwijzen.

Natuurlijk lijkt het doorgeven van een waarde aan een verwijzing naar een object erg veel op (en is het in de praktijk niet te onderscheiden van) het doorgeven van een object door middel van verwijzing.


Antwoord 5, autoriteit 12%

Dit geeft je enig inzicht in hoe Java echt werkt, tot op het punt dat je in je volgende discussie over Java als referentie of het doorgeven van waarde gewoon zult glimlachen 🙂

Stap één, wis dat woord dat begint met ‘p’ “_ _ _ _ _ _ _” uit uw hoofd, vooral als u uit andere programmeertalen komt. Java en ‘p’ kunnen niet in hetzelfde boek, forum of zelfs txt worden geschreven.

Stap twee onthoud dat wanneer u een object doorgeeft aan een methode, u de objectreferentie doorgeeft en niet het object zelf.

  • Student: Meester, betekent dit dat Java pass-by-reference is?
  • Master: Sprinkhaan, nee.

Bedenk nu wat de referentie/variabele van een object doet/is:

  1. Een variabele bevat de bits die de JVM vertellen hoe hij bij het object waarnaar wordt verwezen in het geheugen (Heap) moet gaan.
  2. Als je argumenten doorgeeft aan een methode geef je NIET de referentievariabele door, maar een kopie van de bits in de referentievariabele. Zoiets als dit: 3bad086a. 3bad086a staat voor een manier om bij het doorgegeven object te komen.
  3. Dus je geeft gewoon 3bad086a door dat het de waarde van de referentie is.
  4. Je geeft de waarde van de referentie door en niet de referentie zelf (en niet het object).
  5. Deze waarde wordt feitelijk GEKOPIEERD en aan de methode gegeven.

In het volgende (probeer dit niet te compileren/uit te voeren…):

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

Wat gebeurt er?

  • De variabele person is gemaakt in regel #1 en is null aan het begin.
  • Er wordt een nieuw Persoonsobject gemaakt in regel #2, opgeslagen in het geheugen, en de variabele persoon krijgt de verwijzing naar het Persoonsobject. Dat wil zeggen, het adres. Laten we zeggen 3bad086a.
  • De variabele persoon met het adres van het object wordt doorgegeven aan de functie in regel #3.
  • In regel #4 kun je luisteren naar het geluid van stilte
  • Controleer de opmerking op regel #5
  • Er wordt een lokale methodevariabele –anotherReferenceToTheSamePersonObject– gemaakt en dan komt de magie in regel #6:
    • De variabele/referentie persoon wordt stukje bij beetje gekopieerd en doorgegeven aan een anderReferenceToTheSamePersonObject binnen de functie.
    • Er worden geen nieuwe exemplaren van Persoon gemaakt.
    • Zowel “person” als “anotherReferenceToTheSamePersonObject” hebben dezelfde waarde van 3bad086a.
    • Probeer dit niet, maar person==anotherReferenceToTheSamePersonObject zou waar zijn.
    • Beide variabelen hebben IDENTIEKE KOPIEN van de referentie en ze verwijzen beide naar hetzelfde Persoonsobject, HETZELFDE Object op de Heap en GEEN KOPIE.

Een foto zegt meer dan duizend woorden:

Pass by Value

Houd er rekening mee dat de pijlen van een andereReferentieToTheSamePersonObject naar het object zijn gericht en niet naar de variabele persoon!

Als je het niet hebt begrepen, vertrouw me dan en onthoud dat het beter is om te zeggen dat Java waardeloos is. Nou, geef de referentiewaarde door. Ach ja, nog beter is doorgeef-kopie-van-de-variabele-waarde! 😉

Voel je nu vrij om me te haten, maar houd er rekening mee dat gezien dit er geen verschil is tussen het doorgeven van primitieve gegevenstypen en objecten als we het hebben over methodeargumenten.

Je geeft altijd een kopie door van de bits van de waarde van de referentie!

  • Als het een primitief datatype is, bevatten deze bits de waarde van het primitieve datatype zelf.
  • Als het een object is, bevatten de bits de waarde van het adres dat de JVM vertelt hoe hij bij het object kan komen.

Java is een pass-by-waarde omdat je binnen een methode het object waarnaar wordt verwezen zo veel kunt wijzigen als je wilt, maar hoe hard je ook probeert, je zult nooit in staat zijn om de doorgegeven variabele te wijzigen die blijft verwijzen (niet p _ _ _ _ _ _ _) hetzelfde object, wat er ook gebeurt!


De functie changeName hierboven zal nooit de feitelijke inhoud (de bitwaarden) van de doorgegeven referentie kunnen wijzigen. Met andere woorden changeName kan Persoon niet naar een ander Object laten verwijzen.


Natuurlijk kun je het kort houden en gewoon zeggen dat Java een pass-by-waarde is!


Antwoord 6, autoriteit 6%

Java geeft referenties door op waarde.

Je kunt de referentie die wordt doorgegeven dus niet wijzigen.


Antwoord 7, autoriteit 4%

Ik heb het gevoel dat ruzie maken over “pass-by-reference vs pass-by-waarde” niet erg nuttig is.

Als je zegt: “Java is pass-by-whatever (referentie/waarde)”, geef je in beide gevallen geen volledig antwoord. Hier is wat aanvullende informatie die hopelijk helpt om te begrijpen wat er in het geheugen gebeurt.

Crashcursus op stack/heap voordat we bij de Java-implementatie komen:
Waarden gaan op een mooie ordelijke manier de stapel op en af, zoals een stapel borden in een cafetaria.
Geheugen in de hoop (ook bekend als dynamisch geheugen) is lukraak en ongeorganiseerd. De JVM vindt gewoon ruimte waar hij kan en maakt deze vrij omdat de variabelen die hem gebruiken niet langer nodig zijn.

Ok. Allereerst gaan lokale primitieven op de stapel. Dus deze code:

int x = 3;
float y = 101.1f;
boolean amIAwesome = true;

resulteert hierin:

primitieven op de stapel

Wanneer u een object declareert en instantiëert. Het eigenlijke object gaat op de hoop. Wat komt er op de stapel? Het adres van het object op de heap. C++-programmeurs zouden dit een pointer noemen, maar sommige Java-ontwikkelaars zijn tegen het woord “pointer”. Wat dan ook. Weet gewoon dat het adres van het object op de stapel gaat.

Vind ik leuk:

int problems = 99;
String name = "Jay-Z";

een b*7ch is niet één!

Een array is een object, dus het komt ook op de heap. En hoe zit het met de objecten in de array? Ze krijgen hun eigen heapruimte en het adres van elk object komt binnen de array.

JButton[] marxBros = new JButton[3];
marxBros[0] = new JButton("Groucho");
marxBros[1] = new JButton("Zeppo");
marxBros[2] = new JButton("Harpo");

marx broers

Dus, wat wordt er doorgegeven als je een methode aanroept? Als u een object doorgeeft, geeft u feitelijk het adres van het object door. Sommigen zeggen misschien de “waarde” van het adres, en sommigen zeggen dat het slechts een verwijzing naar het object is. Dit is het ontstaan ​​van de heilige oorlog tussen voorstanders van “referentie” en “waarde”. Hoe je het noemt is niet zo belangrijk als dat je begrijpt dat wat wordt doorgegeven het adres van het object is.

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);
}

Er wordt één string gemaakt en er wordt ruimte voor toegewezen in de heap, en het adres van de string wordt op de stapel opgeslagen en krijgt de identifier hisName, aangezien het adres van de tweede string de hetzelfde als de eerste, er wordt geen nieuwe string gemaakt en er wordt geen nieuwe heapruimte toegewezen, maar er wordt een nieuwe identifier op de stapel gemaakt. Dan noemen we shout(): er wordt een nieuw stackframe gemaakt en een nieuwe identifier, name wordt aangemaakt en het adres van de reeds bestaande String wordt toegewezen.

la da da da da

Dus, waarde, referentie? Je zegt “aardappel”.


Antwoord 8, autoriteit 3%

Om het contrast te laten zien, vergelijk de volgende C++ en Java-fragmenten:

In C++: Opmerking: slechte code – geheugenlekken! Maar het toont het punt aan.

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"
}

In 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 heeft alleen de twee soorten doorgeven: op waarde voor ingebouwde typen en op waarde van de aanwijzer voor objecttypen.


Antwoord 9, autoriteit 3%

Java geeft verwijzingen naar objecten op waarde door.


Antwoord 10, autoriteit 3%

In principe heeft het opnieuw toewijzen van objectparameters geen invloed op het argument, bijvoorbeeld

private void foo(Object bar) {
    bar = null;
}
public static void main(String[] args) {
    String baz = "Hah!";
    foo(baz);
    System.out.println(baz);
}

drukt "Hah!" af in plaats van null. De reden dat dit werkt is omdat bar een kopie is van de waarde van baz, wat slechts een verwijzing is naar "Hah!". Als het de feitelijke referentie zelf was, dan zou foo baz opnieuw hebben gedefinieerd in null.


Antwoord 11, autoriteit 3%

Ik kan niet geloven dat nog niemand Barbara Liskov heeft genoemd. Toen ze in 1974 CLU ontwierp, stuitte ze op hetzelfde terminologieprobleem en bedacht ze de term call by sharing (ook bekend als call by object-sharing en call by object) voor dit specifieke geval van “call by value waarbij de waarde een referentie is”.


Antwoord 12, autoriteit 2%

De kern van de zaak is dat het woord verwijzing in de uitdrukking “doorverwijzen” iets heel anders betekent dan de gebruikelijke betekenis van het woord verwijzing in Java.

Meestal betekent in Java verwijzing een verwijzing naar een object. Maar de technische termen doorverwijzing/waarde uit de programmeertaaltheorie hebben het over een verwijzing naar de geheugencel die de variabele vasthoudt, wat iets heel anders is.


Antwoord 13

In Java is alles referentie, dus als je zoiets hebt als:
Point pnt1 = new Point(0,0); Java doet het volgende:

  1. Maakt een nieuw puntobject
  2. Maakt een nieuwe puntreferentie aan en initialiseert die referentie naar punt (zie) op eerder gemaakt puntobject.
  3. Vanaf hier, via Point object life, krijg je toegang tot dat object via pnt1
    referentie. We kunnen dus zeggen dat je in Java een object manipuleert door middel van zijn referentie.

voer hier de afbeeldingsbeschrijving in

Java geeft geen methodeargumenten door als referentie; het geeft ze door op waarde. Ik zal een voorbeeld gebruiken van deze site:

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);  
}

Verloop van het programma:

Point pnt1 = new Point(0,0);
Point pnt2 = new Point(0,0);

Twee verschillende Point-objecten maken met twee verschillende gekoppelde referenties.
voer hier de afbeeldingsbeschrijving in

System.out.println("X1: " + pnt1.x + " Y1: " +pnt1.y); 
System.out.println("X2: " + pnt2.x + " Y2: " +pnt2.y);
System.out.println(" ");

Zoals de verwachte output zal zijn:

X1: 0     Y1: 0
X2: 0     Y2: 0

Op deze regel komt ‘pass-by-value’ in het spel…

tricky(pnt1,pnt2);           public void tricky(Point arg1, Point arg2);

Referenties pnt1 en pnt2 worden door waarde doorgegeven aan de lastige methode, wat betekent dat de jouwe referenties pnt1 en pnt2 hebben hun copies genaamd arg1 en arg2.Dus pnt1 en arg1 wijst naar hetzelfde object. (Hetzelfde voor de pnt2 en arg2)
voer hier de afbeeldingsbeschrijving in

In de tricky methode:

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

voer hier de afbeeldingsbeschrijving in

Volgende in de tricky methode

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

Hier maakt u eerst een nieuwe temp Puntreferentie aan die wijst naar dezelfde plaats als een arg1 referentie. Vervolgens verplaats je referentie arg1 naar point naar dezelfde plaats als arg2 referentie.
Ten slotte zal arg2 wijzen naar dezelfde plaats als temp.

voer hier de afbeeldingsbeschrijving in

Vanaf hier is het bereik van de tricky methode verdwenen en heb je geen toegang meer tot de referenties: arg1, arg2, temp. Maar belangrijke opmerking is dat alles wat je doet met deze verwijzingen wanneer ze ‘in het leven’ zijn, permanent van invloed zal zijn op het object waarnaar ze verwijzen naar.

Dus na het uitvoeren van de methode tricky, wanneer je terugkeert naar main, heb je deze situatie:
voer hier de afbeeldingsbeschrijving in

Dus nu zal de volledige uitvoering van het programma zijn:

X1: 0         Y1: 0
X2: 0         Y2: 0
X1: 100       Y1: 100
X2: 0         Y2: 0

Antwoord 14

Java wordt altijd doorgegeven op basis van waarde, niet op basis van referentie

Allereerst moeten we begrijpen wat pass-by-waarde en pass-by-referentie zijn.

Waarde doorgeven betekent dat u een kopie maakt in het geheugen van de daadwerkelijke parameterwaarde die is doorgegeven. Dit is een kopie van de inhoud van de daadwerkelijke parameter.

Pass-by-referentie (ook wel pass-by-adres genoemd) betekent dat een kopie van het adres van de daadwerkelijke parameter wordt opgeslagen.

Soms kan Java de illusie geven van pass by reference. Laten we eens kijken hoe het werkt aan de hand van het onderstaande voorbeeld:

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;
}

De uitvoer van dit programma is:

changevalue

Laten we het stap voor stap begrijpen:

Test t = new Test();

Zoals we allemaal weten, zal het een object in de heap creëren en de referentiewaarde teruggeven aan t. Stel bijvoorbeeld dat de waarde van t 0x100234 is (we kennen de werkelijke interne JVM-waarde niet, dit is slechts een voorbeeld).

eerste illustratie

new PassByValue().changeValue(t);

Bij het doorgeven van referentie t aan de functie, zal het niet direct de werkelijke referentiewaarde van objecttest doorgeven, maar het zal een kopie van t maken en deze vervolgens doorgeven aan de functie. Omdat het waarde doorgeeft, geeft het een kopie van de variabele door in plaats van de werkelijke referentie ervan. Aangezien we zeiden dat de waarde van t 0x100234 was, zullen zowel t als f dezelfde waarde hebben en daarom zullen ze naar hetzelfde object verwijzen.

tweede afbeelding

Als u iets in de functie wijzigt met verwijzing f, wordt de bestaande inhoud van het object gewijzigd. Daarom hebben we de output changevalue, die is bijgewerkt in de functie.

Beschouw het volgende voorbeeld om dit beter te begrijpen:

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;
}

Gooit dit een NullPointerException? Nee, omdat het alleen een kopie van de referentie doorgeeft.
In het geval van doorgeven via verwijzing, zou het een NullPointerException kunnen hebben gegenereerd, zoals hieronder te zien is:

derde afbeelding

Hopelijk helpt dit.


Antwoord 15

Er zijn al geweldige antwoorden die dit dekken. Ik wilde een kleine bijdrage leveren door een heel eenvoudig voorbeeld te delen (dat zal compileren) waarin het gedrag tussen Pass-by-reference in c++ en Pass-by-value in Java wordt vergeleken.

Een paar punten:

  1. De term ‘referentie’ is een overladen met twee afzonderlijke betekenissen. In Java betekent het gewoon een aanwijzer, maar in de context van “Pass-by-reference” betekent het een handvat naar de oorspronkelijke variabele die is doorgegeven.
  2. Java is pass-by-waarde. Java is een afstammeling van C (onder andere talen). Vóór C ondersteunden verschillende (maar niet alle) eerdere talen zoals FORTRAN en COBOL PBR, maar C niet. PBR stond deze andere talen toe om wijzigingen aan te brengen in de doorgegeven variabelen binnen subroutines. Om hetzelfde te bereiken (d.w.z. de waarden van variabelen binnen functies te veranderen), hebben C-programmeurs verwijzingen naar variabelen doorgegeven aan functies. Talen die door C zijn geïnspireerd, zoals Java, hebben dit idee overgenomen en gaan door met het doorgeven van pointers aan methoden zoals C deed, behalve dat Java zijn pointers References noemt. Nogmaals, dit is een ander gebruik van het woord “Referentie” dan in “Pass-By-Referentie”.
  3. C++ staat Pass-by-reference toe door een referentieparameter te declareren met de “&” teken (wat toevallig hetzelfde teken is dat wordt gebruikt om “het adres van een variabele” aan te geven in zowel C als C++). Als we bijvoorbeeld een aanwijzer door verwijzing doorgeven, verwijzen de parameter en het argument niet alleen naar hetzelfde object. Het zijn eerder dezelfde variabelen. Als de ene wordt ingesteld op een ander adres of op null, doet de andere dat ook.
  4. In het onderstaande C++-voorbeeld geef ik een pointer door naar een null-beëindigde string door verwijzing. En in het onderstaande Java-voorbeeld geef ik een Java-verwijzing naar een tekenreeks (nogmaals hetzelfde als een aanwijzer naar een tekenreeks) op waarde. Let op de uitvoer in de opmerkingen.

C++ pass-by-referentievoorbeeld:

using namespace std;
#include <iostream>
void change (char *&str){   // the '&' makes this a reference parameter
    str = NULL;
}
int main()
{
    char *str = "not Null";
    change(str);
    cout<<"str is " << str;      // ==>str is <null>
}

Java geeft “een Java-referentie” door met een waardevoorbeeld

public class ValueDemo{
    public void change (String str){
        str = null;
    }
     public static void main(String []args){
        ValueDemo vd = new ValueDemo();
        String str = "not null";
        vd.change(str);
        System.out.println("str is " + str);    // ==> str is not null!!
                                                // Note that if "str" was
                                                // passed-by-reference, it
                                                // WOULD BE NULL after the
                                                // call to change().
     }
}

BEWERKEN

Verschillende mensen hebben opmerkingen geschreven die erop lijken te wijzen dat ze ofwel niet naar mijn voorbeelden kijken, ofwel het c++-voorbeeld niet krijgen. Ik weet niet zeker waar de verbinding is verbroken, maar gissen naar het voorbeeld van c++ is niet duidelijk. Ik plaats hetzelfde voorbeeld in pascal omdat ik denk dat pass-by-reference er in pascal schoner uitziet, maar ik kan het mis hebben. Ik zou mensen misschien meer in verwarring kunnen brengen; Ik hoop het niet.

In pascal worden door verwijzing doorgegeven parameters “var-parameters” genoemd. Noteer in de onderstaande procedure setToNil het trefwoord ‘var’ dat voorafgaat aan de parameter ‘ptr’. Wanneer een verwijzing naar deze procedure wordt doorgegeven, wordt deze door verwijzing doorgegeven. Let op het gedrag: wanneer deze procedure ptr instelt op nul (dat is pascal spreekt voor NULL), zal het argument op nul worden gezet – dat kan niet in Java.

program passByRefDemo;
type 
   iptr = ^integer;
var
   ptr: iptr;
   procedure setToNil(var ptr : iptr);
   begin
       ptr := nil;
   end;
begin
   new(ptr);
   ptr^ := 10;
   setToNil(ptr);
   if (ptr = nil) then
       writeln('ptr seems to be nil');     { ptr should be nil, so this line will run. }
end.

BEWERK 2

Enkele fragmenten uit “THE Java Programming Language” door Ken Arnold, James Gosling (de man die Java uitvond) en David Holmes, hoofdstuk 2, sectie 2.6. 5

Alle parameters aan methoden worden “op waarde” doorgegeven. Met andere woorden,
waarden van parametervariabelen in een methode zijn kopieën van de aanroeper
opgegeven als argumenten.

Hij gaat verder met het maken van hetzelfde punt over objecten. . .

Houd er rekening mee dat wanneer de parameter een objectreferentie is, het is
de objectreferentie – niet het object zelf – die doorgegeven is “door waarde”.

En tegen het einde van dezelfde sectie doet hij een bredere uitspraak dat Java alleen een waarde heeft en nooit een referentie.

De Java-programmeertaal geeft geen objecten door als referentie; het
geeft objectverwijzingen door op waarde. Omdat twee exemplaren van hetzelfde
verwijzing verwijzen naar hetzelfde werkelijke object, wijzigingen aangebracht via een
referentievariabele zijn zichtbaar door de andere. Er is er precies één
parameter doorgeven modus-waarde doorgeven-en dat helpt dingen te behouden
eenvoudig.

Dit gedeelte van het boek bevat een goede uitleg over het doorgeven van parameters in Java en over het onderscheid tussen pass-by-reference en pass-by-waarde en is van de hand van de maker van Java. Ik zou iedereen willen aanmoedigen om het te lezen, vooral als je nog steeds niet overtuigd bent.

Ik denk dat het verschil tussen de twee modellen heel subtiel is en tenzij je hebt geprogrammeerd waar je pass-by-reference hebt gebruikt, kun je gemakkelijk over het hoofd zien waar twee modellen verschillen.

Ik hoop dat dit het debat beslecht, maar waarschijnlijk niet.

BEWERK 3

Ik ben misschien een beetje geobsedeerd door dit bericht. Waarschijnlijk omdat ik vind dat de makers van Java onbedoeld verkeerde informatie verspreiden. Als ze in plaats van het woord “referentie” voor verwijzingen iets anders hadden gebruikt, zeg:
dingleberry, dan was er geen probleem geweest. Je zou kunnen zeggen: “Java geeft dingleberries door op waarde en niet op referentie”, en niemand zou in de war raken.

Dat is de reden waarom alleen Java-ontwikkelaars hier problemen mee hebben. Ze kijken naar het woord ‘referentie’ en denken dat ze precies weten wat dat betekent, dus nemen ze niet eens de moeite om het tegenargument in overweging te nemen.

Hoe dan ook, ik zag een opmerking in een ouder bericht, die een ballon-analogie maakte die ik erg leuk vond. Zozeer zelfs dat ik besloot wat clipart aan elkaar te lijmen om een ​​reeks cartoons te maken om het punt te illustreren.

Een verwijzing doorgeven op waarde–Wijzigingen aan de verwijzing worden niet weerspiegeld in het bereik van de beller, maar de wijzigingen aan het object wel. Dit komt doordat de referentie wordt gekopieerd, maar zowel het origineel als de kopie verwijzen naar hetzelfde object.
Objectreferenties doorgeven op waarde

Geef een referentie door–Er is geen kopie van de referentie. Enkele referentie wordt gedeeld door zowel de beller als de functie die wordt aangeroepen. Alle wijzigingen in de referentie of de gegevens van het object worden weergegeven in het bereik van de beller.
Gegeven door referentie

BEWERK 4

Ik heb berichten over dit onderwerp gezien die de implementatie op laag niveau van het doorgeven van parameters in Java beschrijven, wat volgens mij geweldig en zeer nuttig is omdat het een abstract idee concreet maakt. Voor mij gaat de vraag echter meer over het gedrag beschreven in de taalspecificatie dan over de technische implementatie van het gedrag. Dit is een uittreksel uit de Java Taalspecificatie, sectie 8.4.1 :

Als de methode of constructor wordt aangeroepen ( 15.12), de waarden van de
werkelijke argumentuitdrukkingen initialiseren nieuw gemaakte parameter
variabelen, elk van het gedeclareerde type, vóór uitvoering van de body of
de methode of constructor.
De Identifier die verschijnt in de
DeclaratorId kan worden gebruikt als een eenvoudige naam in de hoofdtekst van de methode of
constructor om naar de formele parameter te verwijzen.

Dat betekent dat java een kopie maakt van de doorgegeven parameters voordat een methode wordt uitgevoerd. Zoals de meeste mensen die compilers op de universiteit hebben gestudeerd, gebruikte ik “The Dragon Book “ wat HET boek voor compilers is. Het heeft een goede beschrijving van “Call-by-value” en “Call-by-Reference” in hoofdstuk 1. De Call-by-value beschrijving komt exact overeen met de Java-specificaties.

Toen ik compilers studeerde – in de jaren ’90, gebruikte ik de eerste editie van het boek uit 1986, dat ongeveer 9 of 10 jaar ouder was dan Java. Ik kwam echter net een kopie tegen van de 2e editie uit 2007 waarin eigenlijk Java wordt genoemd! Paragraaf 1.6.6 met het label “Parameter Passing Mechanisms” beschrijft het doorgeven van parameters vrij aardig. Hier is een fragment onder de kop “Call-by-value” waarin Java wordt genoemd:

In call-by-value wordt de werkelijke parameter geëvalueerd (als het een
uitdrukking) of gekopieerd (als het een variabele is). De waarde wordt geplaatst in
de locatie die hoort bij de corresponderende formele parameter van de
procedure genoemd. Deze methode wordt gebruikt in C en Java, en is een veelgebruikte
optie in C++ , evenals in de meeste andere talen.


Antwoord 16

Java is een pass-by-waarde (stapelgeheugen)

Hoe het werkt

  • Laten we eerst begrijpen waar Java het primitieve gegevenstype en het objectgegevenstype opslaat.

  • Primitieve gegevenstypen zelf en objectreferenties worden in de stapel opgeslagen.
    Objecten zelf worden in de hoop opgeslagen.

  • Het betekent dat het stapelgeheugen primitieve gegevenstypen opslaat en ook de
    adressen van objecten.

  • En je geeft altijd een kopie door van de bits van de waarde van de referentie.

  • Als het een primitief datatype is, dan bevatten deze gekopieerde bits de waarde van het primitieve datatype zelf. Daarom, wanneer we de waarde van het argument binnen de methode veranderen, weerspiegelt het niet de veranderingen daarbuiten.

  • Als het een objectgegevenstype is zoals Foo foo=new Foo(), dan gaat in dit geval een kopie van het adres van het object door zoals file shortcut , stel dat we een tekstbestand hebben abc.txt op C:\desktop en stel dat we een snelkoppeling maken van hetzelfde bestand en dit in C:\desktop\abc-shortcut plaatsen, zodat wanneer u het bestand opent vanuit C:\desktop\abc.txt en ‘Stack Overflow’ schrijft en het bestand sluit en opnieuw opent u het bestand vanuit de snelkoppeling, dan schrijft u ‘ is de grootste online community voor programmeurs om te leren’ dan is de totale bestandswijziging ‘Stack Overflow is de grootste online community voor programmeurs om te leren’ wat betekent dat het er niet toe doet van waar je het bestand opent, elke keer dat we hetzelfde bestand openen, kunnen we hier Foo als een bestand aannemen en veronderstellen dat foo is opgeslagen op 123hd7h(oorspronkelijk adres zoals C:\desktop\abc.txt ) adres en 234jdid(gekopieerd adres zoals C:\desktop\abc-shortcut dat feitelijk het originele adres van het bestand bevat) ..
    Dus maak voor een beter begrip een snelkoppelingsbestand en voel.


Antwoord 17

Een referentie is altijd een waarde wanneer deze wordt weergegeven, ongeacht welke taal je gebruikt.

Om een ​​out-of-the-box weergave te krijgen, laten we eens kijken naar Assembly of wat low-level geheugenbeheer. Op CPU-niveau wordt een verwijzing naar iets onmiddellijk een waarde als het naar het geheugen of naar een van de CPU-registers wordt geschreven. (Daarom is pointer een goede definitie. Het is een waarde, die tegelijkertijd een doel heeft).

Gegevens in het geheugen hebben een Locatie en op die locatie is er een waarde (byte,woord, wat dan ook). In Assembly hebben we een handige oplossing om een ​​Naam te geven aan een bepaalde Locatie (ook wel variabele genoemd), maar bij het compileren van de code vervangt de assembler gewoon Naam met de aangewezen locatie, net zoals uw browser domeinnamen vervangt door IP-adressen.

Het is technisch gezien onmogelijk om een ​​verwijzing naar iets in welke taal dan ook door te geven zonder het te representeren (wanneer het onmiddellijk een waarde wordt).

Laten we zeggen dat we een variabele Foo hebben, de Locatie bevindt zich op de 47e byte in het geheugen en de Waarde is 5. We hebben een andere variabele Ref2Foo wat zich op 223e byte in het geheugen bevindt, en de waarde zal 47 zijn. Deze Ref2Foo kan een technische variabele zijn, niet expliciet gemaakt door het programma. Als u alleen naar 5 en 47 kijkt zonder enige andere informatie, ziet u slechts twee Waarden.
Als u ze als referentie gebruikt, moeten we reizen om 5 te bereiken:

(Name)[Location] -> [Value at the Location]
---------------------
(Ref2Foo)[223]  -> 47
(Foo)[47]       -> 5

Dit is hoe jump-tabellen werken.

Als we een methode/functie/procedure willen aanroepen met de waarde van Foo, zijn er een paar mogelijke manieren om de variabele aan de methode door te geven, afhankelijk van de taal en de verschillende methode-aanroepmodi:

  1. 5 wordt gekopieerd naar een van de CPU-registers (bijv. EAX).
  2. 5 krijgt PUSHd op de stapel.
  3. 47 wordt gekopieerd naar een van de CPU-registers
  4. 47 PUSHd naar de stapel.
  5. 223 wordt gekopieerd naar een van de CPU-registers.
  6. 223 krijgt PUSHd naar de stapel.

In alle gevallen boven een waarde – een kopie van een bestaande waarde – is gemaakt, is het nu aan de ontvangstmethode om deze af te handelen. Wanneer u “Foo” in de methode schrijft, wordt het ofwel voorgelezen uit EAX, of automatisch dereferenced, of dubbele dereferenced, het proces hangt af van hoe de taal werkt en/of wat het type Foo dicteert . Dit is verborgen voor de ontwikkelaar totdat ze het dereferentieproces omzeilt. Dus een referentie is een waarde indien weergegeven, omdat een referentie een waarde is die moet worden verwerkt (op taalniveau).

Nu hebben we Foo doorgegeven aan de methode:

  • in geval 1. en 2. als u Foo wijzigt (Foo = 9), heeft dit alleen invloed op het lokale bereik, aangezien u een kopie van de waarde hebt. Vanuit de methode kunnen we niet eens bepalen waar in het geheugen de originele Foo zich bevond.
  • in geval 3. en 4. als u standaardtaalconstructies gebruikt en Foo wijzigt (Foo = 11), kan dit Foo globaal wijzigen (afhankelijk van de taal, bijv. Java of zoals Pascal’s procedure findMin(x, y, z: integer;var m: integer);). Als de taal u echter in staat stelt het dereferentieproces te omzeilen, kunt u 47 wijzigen, bijvoorbeeld 49. Op dat moment lijkt Foo veranderd te zijn als je het leest, omdat je de lokale aanwijzer ernaar hebt veranderd. En als u deze Foo binnen de methode zou wijzigen (Foo = 12), zult u waarschijnlijk de uitvoering van het programma (ook bekend als segfault) FUBAR doen omdat u naar een ander geheugen schrijft dan verwacht, u kunt zelfs een gebied wijzigen dat bestemd is om een ​​uitvoerbaar programma te bevatten en ernaar te schrijven zal de actieve code wijzigen (Foo bevindt zich nu niet op 47). MAAR Foo’s waarde van 47 veranderde niet globaal, alleen die binnen de methode, omdat 47 ook een kopie was van de methode.
  • in geval 5. en 6. als je 223 binnen de methode aanpast, ontstaat er dezelfde chaos als in 3. of 4. (een aanwijzer die wijst naar een nu slechte waarde, dat wil zeggen gebruikt als aanwijzer) maar dit is nog steeds een lokaal probleem, aangezien 223 gekopieerd is. Als u echter in staat bent om Ref2Foo te derefereren (dat is 223), bereik dan de puntige waarde 47 en wijzig deze, zeg, naar 49, heeft dit invloed op Foo wereldwijd, omdat in dit geval de methoden een kopie van 223 hebben gekregen, maar de 47 waarnaar wordt verwezen alleen bestaat eenmaal, en als u dat verandert in 49, leidt elke dubbele verwijzing van Ref2Foo naar een verkeerde waarde.

Nitpicking op onbeduidende details, zelfs talen die pass-by-reference doen, zullen waarden doorgeven aan functies, maar die functies weten dat ze het moeten gebruiken voor dereferentiedoeleinden. Deze pass-the-reference-as-waarde is gewoon verborgen voor de programmeur omdat het praktisch nutteloos is en de terminologie slechts pass-by-reference is.

Strikte pass-by-value is ook nutteloos, het zou betekenen dat een array van 100 Mbyte moet worden gekopieerd elke keer dat we een methode aanroepen met de array als argument, daarom kan Java niet strikt worden pass-by-waarde. Elke taal zou een verwijzing naar deze enorme array doorgeven (als een waarde) en ofwel een copy-on-write-mechanisme gebruiken als die array lokaal binnen de methode kan worden gewijzigd of de methode (zoals Java doet) de array globaal kan wijzigen (van de weergave van de beller) en in een paar talen kunt u de waarde van de verwijzing zelf wijzigen.

Dus in het kort en in Java’s eigen terminologie, Java is pass-by-value waarbij waarde kan zijn: ofwel een echte waarde of een waarde die een weergave is van een referentie.


Antwoord 18

Voor zover ik weet, kent Java alleen call by value. Dit betekent dat je voor primitieve datatypes met een kopie werkt en voor objecten met een kopie van de verwijzing naar de objecten. Ik denk echter dat er enkele valkuilen zijn; dit werkt bijvoorbeeld niet:

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);
}

Hiermee wordt Hello World ingevuld en niet World Hello omdat je in de swap-functie kopieën gebruikt die geen invloed hebben op de referenties in het hoofdgedeelte. Maar als uw objecten niet onveranderlijk zijn, kunt u deze bijvoorbeeld wijzigen:

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);
}

Hiermee wordt Hello World op de opdrachtregel ingevuld. Als je StringBuffer in String verandert, zal het alleen Hallo produceren omdat String onveranderlijk is. Bijvoorbeeld:

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);
}

Je zou echter een wrapper voor String kunnen maken zoals deze, waardoor het in staat zou zijn om het met Strings te gebruiken:

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: ik geloof dat dit ook de reden is om StringBuffer te gebruiken als het gaat om het “toevoegen” van twee strings, omdat je het originele object kunt wijzigen, wat niet kan met onveranderlijke objecten zoals String.


Antwoord 19

Nee, het is geen doorverwijzing.

Java wordt doorgegeven volgens de Java-taalspecificatie:

Wanneer de methode of constructor wordt aangeroepen ( 15.12), de waarden van de feitelijke argumentexpressies initialiseren nieuw gecreëerde parametervariabelen, elk van het gedeclareerde type, voordat de hoofdtekst van de methode of aannemer. De identifier die in de DeclaratorId verschijnt, kan worden gebruikt als een eenvoudige naam in de hoofdtekst van de methode of constructor om te verwijzen naar de formele parameter.


Antwoord 20

Laat me proberen mijn begrip uit te leggen aan de hand van vier voorbeelden. Java is pass-by-value, en niet pass-by-referentie

/**

Waarde doorgeven

In Java worden alle parameters op waarde doorgegeven, d.w.z. het toewijzen van een methodeargument is niet zichtbaar voor de beller.

*/

Voorbeeld 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";
    }
}

Resultaat

output : output
value : Nikhil
valueflag : false

Voorbeeld 2:

/**
*
* Pass Door Waarde
*
*/

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";
    }
}

Resultaat

output : output
value : Nikhil
valueflag : false

Voorbeeld 3:

/**
Deze ‘Pass By Value’ heeft een gevoel van ‘Pass By Reference’

Sommige mensen zeggen dat primitieve typen en ‘String’ een ‘doorgeefwaarde’ zijn
en objecten zijn ‘pass by reference’.

Maar uit dit voorbeeld kunnen we begrijpen dat het in feite alleen een waarde is,
Houd er rekening mee dat we hier de referentie doorgeven als de waarde.
dat wil zeggen: referentie wordt doorgegeven door waarde.
Dat is de reden waarom kunnen veranderen en nog steeds geldt het na de lokale reikwijdte.
Maar we kunnen de werkelijke referentie niet wijzigen buiten het oorspronkelijke bereik.
wat dat betekent wordt aangetoond door het volgende voorbeeld van 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";
    }
}

Resultaat

output : output
student : Student [id=10, name=Anand]

Voorbeeld 4:

/**

In aanvulling op wat werd vermeld in Voorbeeld3 (PassByValueObjectCase1.java), kunnen we de werkelijke verwijzing niet wijzigen buiten het oorspronkelijke bereik.”

Opmerking: ik plak de code voor private class Student niet. De klasdefinitie voor Student is hetzelfde als Voorbeeld3.

*/

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";
    }
}

Resultaat

output : output
student : Student [id=10, name=Nikhil]

Antwoord 21

Je kunt in Java nooit een referentie doorgeven, en een van de manieren die voor de hand ligt, is wanneer je meer dan één waarde van een methodeaanroep wilt retourneren. Overweeg het volgende stukje code in 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;
}

Soms wil je hetzelfde patroon in Java gebruiken, maar dat kan niet; althans niet direct. In plaats daarvan zou je zoiets als dit kunnen doen:

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]);
}

Zoals in eerdere antwoorden is uitgelegd, geeft u in Java een pointer naar de array door als waarde in getValues. Dat is genoeg, omdat de methode vervolgens het array-element wijzigt, en volgens afspraak verwacht je dat element 0 de geretourneerde waarde bevat. Uiteraard kunt u dit op andere manieren doen, zoals het structureren van uw code zodat dit niet nodig is, of het construeren van een klasse die de geretourneerde waarde kan bevatten of kan worden ingesteld. Maar het eenvoudige patroon dat voor u beschikbaar is in C++ hierboven is niet beschikbaar in Java.


Antwoord 22

Ik dacht dat ik dit antwoord zou bijdragen om meer details uit de Specificaties toe te voegen.

Ten eerste, Wat is het verschil tussen slagen op referentie vs. op waarde doorgeven?

Passeren door middel van verwijzing betekent dat de parameter van de aangeroepen functies de . is
hetzelfde als het doorgegeven argument van de beller (niet de waarde, maar de identiteit)
– de variabele zelf).

Waarde doorgeven betekent dat de parameter van de aangeroepen functies een kopie zal zijn van
het door de bellers doorgegeven argument.

Of van wikipedia, over het onderwerp pass-by-reference

In call-by-reference evaluatie (ook wel aangeduid als
pass-by-referentie), krijgt een functie een impliciete verwijzing naar a
variabele gebruikt als argument, in plaats van een kopie van zijn waarde. Deze
betekent meestal dat de functie de kan wijzigen (d.w.z. toewijzen aan)
variabele gebruikt als argument iets dat door de beller zal worden gezien.

En over het onderwerp pass-by-value

In call-by-value wordt de argumentuitdrukking geëvalueerd en de
resulterende waarde is gebonden aan de corresponderende variabele in de functie […].
Als de functie of procedure in staat is om waarden toe te kennen aan zijn
parameters, wordt alleen de lokale kopie toegewezen […].

Ten tweede moeten we weten wat Java gebruikt in zijn methode-aanroepen. De Java-taalspecificatie staten

Als de methode of constructor wordt aangeroepen ( 15.12), de waarden van de
werkelijke argumentuitdrukkingen initialiseren nieuw gemaakte parameter
variabelen
, elk van het gedeclareerde type, vóór uitvoering van de body of
de methode of constructor.

Dus het wijst de waarde van het argument toe aan (of bindt) aan de corresponderende parametervariabele.

Wat is de waarde van het argument?

Laten we eens kijken naar de referentietypen, de Java Virtual Machine-specificatie staten

Er zijn drie soorten referentietypen: klassentypen, arraytypen,
en interfacetypes. Hun waarden zijn verwijzingen naar dynamisch
klasse-instanties, arrays of klasse-instanties of arrays gemaakt die:
respectievelijk interfaces implementeren.

De Java-taalspecificatie vermeldt ook

De referentiewaarden (vaak alleen verwijzingen) zijn verwijzingen naar deze objecten en een speciale null-referentie, die naar geen object verwijst.

De waarde van een argument (van een bepaald type referentie) is een verwijzing naar een object. Merk op dat een variabele, een aanroep van een methode met een retourtype van het referentietype en een expressie voor het maken van een instantie (new ...) allemaal worden omgezet in een waarde van het referentietype.

Dus

public void method (String param) {}
...
String var = new String("ref");
method(var);
method(var.toString());
method(new String("ref"));

allemaal binden de waarde van een verwijzing naar een String-instantie aan de nieuw gemaakte parameter van de methode, param. Dit is precies wat de definitie van pass-by-waarde beschrijft. Als zodanig is Java een pass-by-waarde.

Het feit dat je de verwijzing kunt volgen om een ​​methode aan te roepen of toegang te krijgen tot een veld van het object waarnaar wordt verwezen, is volledig irrelevant voor de conversatie. De definitie van pass-by-reference was

Dit betekent meestal dat de functie de kan wijzigen (d.w.z. toewijzen aan)
variabele gebruikt als argument iets dat door de beller zal worden gezien.

In Java betekent het wijzigen van de variabele dat deze opnieuw wordt toegewezen. Als u in Java de variabele binnen de methode opnieuw zou toewijzen, zou deze onopgemerkt blijven voor de beller. Het wijzigen van het object waarnaar wordt verwezen door de variabele is een heel ander concept.


Primitieve waarden worden ook gedefinieerd in de Java Virtual Machine Specification, hier. De waarde van het type is de corresponderende integrale of drijvende-kommawaarde, op de juiste manier gecodeerd (8, 16, 32, 64, etc. bits).


Antwoord 23

In Java worden alleen referenties doorgegeven en doorgegeven door waarde:

Java-argumenten worden allemaal doorgegeven door waarde (de referentie wordt gekopieerd wanneer deze door de methode wordt gebruikt):

In het geval van primitieve typen is het Java-gedrag eenvoudig:
De waarde wordt gekopieerd in een ander exemplaar van het primitieve type.

In het geval van objecten is dit hetzelfde:
Objectvariabelen zijn pointers (buckets) die alleen het adres van Object bevatten dat is gemaakt met het trefwoord “new” en worden gekopieerd als primitieve typen.

Het gedrag kan er anders uitzien dan bij primitieve typen: omdat de gekopieerde objectvariabele hetzelfde adres bevat (naar hetzelfde object).
De inhoud/leden van het object kunnen nog steeds worden gewijzigd binnen een methode en later buiten toegang, waardoor de illusie wordt gewekt dat het (bevattende) object zelf door verwijzing is doorgegeven.

‘String’-objecten lijken een goed tegenvoorbeeld te zijn voor de stadslegende die zegt dat ‘Objects by reference worden doorgegeven’:

In feite zult u met behulp van een methode nooit in staat zijn om de waarde van een String die als argument is doorgegeven, bij te werken:

Een String Object, bevat tekens door een array die als final is verklaard en die niet kan worden gewijzigd.
Alleen het adres van het Object kan worden vervangen door een ander adres met “nieuw”.
Als u “new” gebruikt om de variabele bij te werken, wordt het object niet van buitenaf benaderd, aangezien de variabele aanvankelijk door waarde werd doorgegeven en gekopieerd.


Antwoord 24

Het onderscheid, of misschien gewoon de manier waarop ik me herinner, omdat ik vroeger dezelfde indruk had als de originele poster, is dit: Java is altijd waardeloos. Alle objecten (in Java, alles behalve primitieven) in Java zijn referenties. Deze referenties worden op waarde doorgegeven.


Antwoord 25

Zoals veel mensen al eerder zeiden, Java is altijd pass-by-value

Hier is nog een voorbeeld dat u zal helpen het verschil te begrijpen (het klassieke voorbeeld van swap ):

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;
  }
}

Afdrukken:

Vroeger: a = 2, b = 3
Na: a = 2, b = 3

Dit gebeurt omdat iA en iB nieuwe lokale referentievariabelen zijn die dezelfde waarde hebben als de doorgegeven referenties (ze verwijzen respectievelijk naar a en b). Dus het proberen om de referenties van iA of iB te veranderen zal alleen veranderen in de lokale scope en niet buiten deze methode.


Antwoord 26

Ik beschouw het altijd als ‘doorgaan met kopiëren’. Het is een kopie van de waarde, of het nu primitief of referentie is. Als het een primitief is, is het een kopie van de bits die de waarde zijn en als het een object is, is het een kopie van de referentie.

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;
    }
}

uitvoer van Java PassByCopy:

naam= Maxx
naam= Fido

Primitieve wrapper-klassen en strings zijn onveranderlijk, dus elk voorbeeld dat deze typen gebruikt, werkt niet hetzelfde als andere typen/objecten.


Antwoord 27

In tegenstelling tot sommige andere talen, staat Java u niet toe om te kiezen tussen pass-by-value en pass-by-reference. Alle argumenten worden doorgegeven op waarde. Een methodeaanroep kan twee soorten waarden doorgeven aan een methode: kopieën van primitieve waarden (bijv. waarden van int en double) en kopieën van verwijzingen naar objecten.

Als een methode een parameter van het primitieve type wijzigt, hebben wijzigingen aan de parameter geen effect op de oorspronkelijke argumentwaarde in de aanroepende methode.

Als het om objecten gaat, kunnen objecten zelf niet worden doorgegeven aan methoden. Dus we geven de referentie (adres) van het object door. We kunnen het originele object manipuleren met behulp van deze referentie.

Hoe Java objecten maakt en opslaat: Wanneer we een object maken, slaan we het adres van het object op in een referentievariabele. Laten we de volgende verklaring analyseren.

Account account1 = new Account();

Account account1 is het type en de naam van de referentievariabele, = is de toewijzingsoperator, new vraagt ​​om de benodigde hoeveelheid ruimte van het systeem. De constructor rechts van het trefwoord new die het object maakt, wordt impliciet aangeroepen door het trefwoord new. Het adres van het gemaakte object (resultaat van de rechterwaarde, een uitdrukking genaamd “expressie voor het maken van klasseninstanties”) wordt toegewezen aan de linkerwaarde (die een referentievariabele is met een naam en een opgegeven type) met behulp van de toewijzingsoperator.

Hoewel de referentie van een object op waarde wordt doorgegeven, kan een methode nog steeds interageren met het object waarnaar wordt verwezen door zijn openbare methoden aan te roepen met behulp van de kopie van de referentie van het object. Aangezien de referentie die is opgeslagen in de parameter een kopie is van de referentie die als argument is doorgegeven, verwijzen de parameter in de aangeroepen methode en het argument in de aanroepende methode naar hetzelfde object in het geheugen.

Het doorgeven van verwijzingen naar arrays, in plaats van de array-objecten zelf, is om prestatieredenen logisch. Omdat alles in Java door waarde wordt doorgegeven, als array-objecten werden doorgegeven,
een kopie van elk element zou worden doorgegeven. Voor grote arrays zou dit tijd verspillen en veel tijd kosten
aanzienlijke opslagruimte voor de kopieën van de elementen.

In de onderstaande afbeelding kun je zien dat we twee referentievariabelen hebben (dit worden pointers genoemd in C/C++, en ik denk dat die term het gemakkelijker maakt om deze functie te begrijpen.) in de hoofdmethode. Primitieve en referentievariabelen worden in het stapelgeheugen bewaard (linkerkant in onderstaande afbeeldingen). array1- en array2-referentievariabelen “point” (zoals C/C++-programmeurs het noemen) of verwijzing naar respectievelijk a- en b-arrays, die objecten zijn (waarden die deze referentievariabelen bevatten zijn adressen van objecten) in heapgeheugen (rechterkant in onderstaande afbeeldingen) .

Waarde doorgeven voorbeeld 1

Als we de waarde van de referentievariabele array1 als argument doorgeven aan de methode reverseArray, wordt er een referentievariabele gemaakt in de methode en begint die referentievariabele naar dezelfde array te verwijzen (a).

public class Test
{
    public static void reverseArray(int[] array1)
    {
        // ...
    }
    public static void main(String[] args)
    {
        int[] array1 = { 1, 10, -7 };
        int[] array2 = { 5, -190, 0 };
        reverseArray(array1);
    }
}

Waarde doorgeven voorbeeld 2

Dus, als we zeggen

array1[0] = 5;

in de reverseArray-methode zal het een wijziging aanbrengen in array a.

We hebben nog een referentievariabele in de methode reverseArray (array2) die verwijst naar een array c. Als we zouden zeggen

array1 = array2;

in de methode reverseArray, zou de referentievariabele array1 in de methode reverseArray niet meer naar array a wijzen en beginnen naar array c te wijzen (stippellijn in tweede afbeelding).

Als we de waarde van de referentievariabele array2 retourneren als de retourwaarde van de methode reverseArray en deze waarde toewijzen aan de referentievariabele array1 in de hoofdmethode, zal array1 in de main beginnen te verwijzen naar array c.

Dus laten we alle dingen die we hebben gedaan nu in één keer opschrijven.

public class Test
{
    public static int[] reverseArray(int[] array1)
    {
        int[] array2 = { -7, 0, -1 };
        array1[0] = 5; // array a becomes 5, 10, -7
        array1 = array2; /* array1 of reverseArray starts
          pointing to c instead of a (not shown in image below) */
        return array2;
    }
    public static void main(String[] args)
    {
        int[] array1 = { 1, 10, -7 };
        int[] array2 = { 5, -190, 0 };
        array1 = reverseArray(array1); /* array1 of 
         main starts pointing to c instead of a */
    }
}

voer hier de afbeeldingsbeschrijving in

En nu die reverseArray-methode voorbij is, zijn de referentievariabelen (array1 en array2) verdwenen. Wat betekent dat we nu alleen de twee referentievariabelen hebben in de hoofdmethode array1 en array2 die respectievelijk verwijzen naar c- en b-arrays. Geen enkele referentievariabele wijst naar object (array) a. Het komt dus in aanmerking voor het ophalen van huisvuil.

U kunt ook de waarde van array2 in main toewijzen aan array1. array1 zou beginnen te wijzen naar b.


Antwoord 28

Java heeft alleen een pass-by-waarde. Een heel eenvoudig voorbeeld om dit te valideren.

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

Antwoord 29

Ik heb een thread gemaakt gewijd aan dit soort vragen voor alle programmeertalen hier.

Java wordt ook genoemd. Hier is de korte samenvatting:

  • Java geeft de parameters per waarde door
  • “op waarde” is de enige manier in Java om een ​​parameter door te geven aan een methode
  • het gebruik van methoden van het object dat als parameter is opgegeven, verandert de
    object als de referenties verwijzen naar
    de originele voorwerpen. (als dat
    methode zelf verandert sommige waarden)

Antwoord 30

Om een ​​lang verhaal kort te maken: Java-objecten hebben enkele zeer eigenaardige eigenschappen .

In het algemeen heeft Java primitieve typen (int, bool, char, double, enz.) worden direct door waarde doorgegeven. Dan heeft Java objecten (alles dat is afgeleid van java.lang.Object). Objecten worden eigenlijk altijd behandeld via een referentie (een referentie is een aanwijzer die je niet kunt aanraken). Dat betekent dat objecten in feite door middel van verwijzing worden doorgegeven, aangezien de verwijzingen normaal gesproken niet interessant zijn. Het betekent echter dat u niet kunt wijzigen naar welk object wordt verwezen, aangezien de referentie zelf door waarde wordt doorgegeven.

Klinkt dit vreemd en verwarrend? Laten we eens kijken hoe C pass by reference en pass by value implementeert. In C is de standaardconventie pass-by-waarde. void foo(int x) geeft een int door op waarde. void foo(int *x) is een functie die geen int a wil, maar een pointer naar een int: foo(&a). Men zou dit gebruiken met de operator & om een ​​variabel adres door te geven.

Breng dit naar C++ en we hebben referenties. Verwijzingen zijn in feite (in deze context) syntactische suikers die het aanwijzergedeelte van de vergelijking verbergen: void foo(int &x) wordt aangeroepen door foo(a), waarbij de compiler weet zelf dat het een referentie is en het adres van de niet-referentie a moet worden doorgegeven. In Java zijn alle variabelen die naar objecten verwijzen eigenlijk van het referentietype, waardoor call by reference voor de meeste doeleinden en doeleinden wordt geforceerd zonder de fijnmazige controle (en complexiteit) die bijvoorbeeld wordt geboden door C++.

LEAVE A REPLY

Please enter your comment!
Please enter your name here

9 + thirteen =

Other episodes