objektidentität Ist Java „Referenzübergabe“ oder „Wertübergabe“?




15 Answers

Ich habe gerade bemerkt, dass Sie meinen Artikel referenziert haben.

Die Java-Spezifikation besagt, dass alles in Java pass-by-value ist. In Java gibt es keine "Pass-by-Reference".

Der Schlüssel zum Verständnis ist das so etwas

Dog myDog;

ist kein Hund; Es ist eigentlich ein Hinweis auf einen Hund.

Was das bedeutet, ist, wenn Sie haben

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

Sie übergeben im Wesentlichen die Adresse des erstellten Dog Objekts an die Methode foo .

(Ich sage im Wesentlichen, weil Java-Zeiger keine direkten Adressen sind, aber es ist am einfachsten, sie so zu sehen.)

Angenommen, das Dog Objekt befindet sich an der Speicheradresse 42. Das heißt, wir übergeben 42 an die Methode.

wenn die Methode definiert wurde als

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

Schauen wir uns an, was passiert.

  • Der Parameter someDog wird auf den Wert 42 gesetzt
  • in der Zeile "AAA"
    • someDog folgt dem Dog er zeigt (das someDog an Adresse 42)
    • Dieser Dog (der an Adresse 42) wird gebeten, seinen Namen in Max zu ändern
  • in der Zeile "BBB"
    • Ein neuer Dog wird erstellt. Nehmen wir an, er ist bei Adresse 74
    • Wir weisen 74 den Parameter someDog zu
  • in der Zeile "CCC"
    • someDog folgt dem Dog er verweist (das Hundegegenstand an Adresse 74)
    • Dieser Dog (der an Adresse 74) wird gebeten, seinen Namen in Rowlf zu ändern
  • dann kehren wir zurück

Jetzt überlegen wir uns, was außerhalb der Methode passiert:

Hat sich myDog geändert?

Da ist der Schlüssel.

Wenn man bedenkt, dass myDog ein Zeiger und kein myDog Dog , lautet die Antwort NEIN. myDog immer noch den Wert 42; Es zeigt immer noch auf den ursprünglichen Dog (beachte jedoch, dass der Name in der Zeile "AAA" nun "Max" lautet - immer noch derselbe Hund; der Wert von myDog hat sich nicht geändert.)

Es ist absolut gültig, einer Adresse zu folgen und das Ende der Adresse zu ändern. das ändert jedoch nicht die Variable.

Java funktioniert genauso wie C. Sie können einen Zeiger zuweisen, den Zeiger an eine Methode übergeben, dem Zeiger in der Methode folgen und die Daten ändern, auf die verwiesen wurde. Sie können jedoch nicht ändern, wo der Zeiger zeigt.

In C ++, Ada, Pascal und anderen Sprachen, die Pass-by-Reference unterstützen, können Sie tatsächlich die übergebene Variable ändern.

Wenn Java über Pass-by-Reference-Semantik verfügte, hätte sich die oben definierte foo Methode geändert, wohin myDog zeigte, als sie someDog in Zeile BBB zugewiesen someDog .

Stellen Sie sich Referenzparameter als Aliasnamen für die übergebene Variable vor. Wenn dieser Alias ​​zugewiesen wird, ist dies auch die übergebene Variable.

java identität

Ich dachte immer, Java sei Referenz-Referenz .

Ich habe jedoch einige Blogbeiträge gesehen (zum Beispiel dieses Blog ), die behaupten, dass dies nicht der Fall ist.

Ich glaube nicht, dass ich den Unterschied verstehe, den sie machen.

Was ist die Erklärung?




Hier erhalten Sie einige Einblicke in die Wirkungsweise von Java bis zu dem Punkt, dass Sie in Ihrer nächsten Diskussion über die Weitergabe von Java als Referenz oder Wertübergabe nur lächeln werden :-)

Schritt eins, bitte löschen Sie das Wort, das mit "p" "_ _ _ _ _ _" beginnt, aus Ihrem Kopf, besonders wenn Sie aus anderen Programmiersprachen stammen. Java und 'p' können nicht in dasselbe Buch, Forum oder sogar in TXT geschrieben werden.

Schritt 2 Denken Sie daran, dass Sie beim Übergeben eines Objekts an eine Methode die Objektreferenz und nicht das Objekt selbst übergeben.

  • Student : Master, bedeutet das, dass Java Referenzreferenz ist?
  • Meister : Heuschrecke, Nr.

Überlegen Sie sich nun, was die Referenz / Variable eines Objekts macht:

  1. Eine Variable enthält die Bits, die der JVM mitteilen, wie sie zu dem referenzierten Objekt im Speicher (Heap) gelangen.
  2. Bei der Übergabe von Argumenten an eine Methode übergeben Sie NICHT die Referenzvariable, sondern eine Kopie der Bits in der Referenzvariablen . So etwas wie dieses: 3bad086a. 3bad086a stellt eine Möglichkeit dar, zum übergebenen Objekt zu gelangen.
  3. Sie übergeben also nur 3bad086a, dass es der Wert der Referenz ist.
  4. Sie übergeben den Wert der Referenz und nicht die Referenz selbst (und nicht das Objekt).
  5. Dieser Wert ist tatsächlich COPIED und wird der Methode übergeben .

Im Folgenden (bitte nicht versuchen, das zu kompilieren / auszuführen ...):

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

Was geschieht?

  • Die Variable person wird in Zeile # 1 erstellt und ist am Anfang null.
  • In Zeile 2 wird ein neues Personenobjekt erstellt, das im Speicher abgelegt wird, und die Variable Person erhält den Verweis auf das Personenobjekt. Das ist seine Adresse. Sagen wir 3bad086a.
  • Die Variable Person mit der Adresse des Objekts wird an die Funktion in Zeile 3 übergeben.
  • In Zeile 4 können Sie den Klang der Stille hören
  • Überprüfen Sie den Kommentar in Zeile 5
  • Eine lokale Variable der Methode - anotherReferenceToTheSamePersonObject - wird erstellt und dann kommt die Magie in Zeile 6:
    • Die Variable / Referenzperson wird bitweise kopiert und an ein anderesReferenceToTheSamePersonObject innerhalb der Funktion übergeben.
    • Es werden keine neuen Instanzen von Person erstellt.
    • " Person " und " anotherReferenceToTheSamePersonObject " enthalten den gleichen Wert von 3bad086a.
    • Versuchen Sie es nicht, aber Person == ein anderesReferenceToTheSamePersonObject wäre wahr.
    • Beide Variablen haben IDENTICAL COPIES der Referenz und beide beziehen sich auf dasselbe Personenobjekt, das gleiche Objekt auf dem Heap und NICHT eine Kopie.

Ein Bild sagt mehr als tausend Worte:

Beachten Sie, dass die Pfeile anotherReferenceToTheSamePersonObject auf das Objekt und nicht auf die variable Person gerichtet sind!

Wenn Sie es nicht verstanden haben, vertrauen Sie mir einfach und denken Sie daran, dass es besser ist, zu sagen, dass Java Wert ist . Nun, übergeben Sie den Referenzwert . Naja, noch besser ist das Kopieren der Variablen. ;)

Hassen Sie mich jetzt ungern, beachten Sie jedoch, dass es bei der Diskussion über Methodenargumente keinen Unterschied zwischen der Übergabe primitiver Datentypen und Objekten gibt .

Sie übergeben immer eine Kopie der Bits des Referenzwerts!

  • Wenn es sich um einen primitiven Datentyp handelt, enthalten diese Bits den Wert des primitiven Datentyps selbst.
  • Wenn es sich um ein Objekt handelt, enthalten die Bits den Wert der Adresse, über die der JVM mitgeteilt wird, wie sie zum Objekt gelangen.

Java ist Pass-by-Value, da Sie innerhalb einer Methode das referenzierte Objekt beliebig ändern können. Unabhängig davon, wie sehr Sie sich bemühen, können Sie niemals die übergebene Variable ändern, die weiter referenziert (nicht p _ _ _ _ _ _ _) dasselbe Objekt, egal was!

Die changeName-Funktion oben kann niemals den tatsächlichen Inhalt (die Bitwerte) der übergebenen Referenz ändern. Mit anderen Worten, changeName kann nicht dazu führen, dass Person auf ein anderes Objekt verweist.

Natürlich können Sie es abkürzen und einfach sagen, dass Java Wert für Wert ist!




Java übergibt Referenzen nach Wert.

Sie können also nicht die Referenz ändern, die übergeben wird.




Vergleichen Sie die folgenden C++ und Java Snippets, um den Kontrast zu zeigen:

In C ++: Hinweis: Falscher Code - Speicherlecks! Aber es zeigt den Punkt.

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

In Java gibt es nur zwei Arten der Übergabe: nach Wert für integrierte Typen und nach Wert des Zeigers für Objekttypen.




Grundsätzlich wirkt sich die Neuzuweisung von Objektparametern nicht auf das Argument aus, z.

private void foo(Object bar) {
    bar = null;
}

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

wird ausgedruckt "Hah!"statt null. Der Grund, warum dies funktioniert, ist, weil bares eine Kopie des Wertes von ist baz, auf die nur Bezug genommen wird"Hah!" .Wenn es die tatsächliche Referenz selbst wäre, dann foohätte neu definiert bazzu null.




Der entscheidende Punkt ist, dass die Wortreferenz in dem Ausdruck "Pass by Reference" etwas völlig anderes bedeutet als die übliche Bedeutung der Wortreferenz in Java.

Normalerweise bedeutet in Java- Referenz eine Referenz auf ein Objekt . Die Fachbegriffe, die als Referenz / Wert aus der Theorie der Programmiersprache gelten, sprechen jedoch von einem Verweis auf die Speicherzelle, die die Variable enthält .




Eine Referenz ist immer ein Wert, wenn dargestellt, unabhängig von der verwendeten Sprache.

Um eine Ansicht außerhalb der Box zu erhalten, schauen wir uns Assembly oder eine einfache Speicherverwaltung an. Auf CPU-Ebene wird ein Hinweis auf etwas sofort zu einem Wert, wenn es in den Speicher oder in eines der CPU-Register geschrieben wird. (Deshalb ist der Zeiger eine gute Definition. Es ist ein Wert, der gleichzeitig einen Zweck erfüllt.)

Daten im Speicher haben einen Ort und an diesem Ort gibt es einen Wert (Byte, Wort, was auch immer). In Assembly haben wir eine bequeme Lösung, um einem bestimmten Ort (auch als Variable bezeichnet) einen Namen zu geben. Beim Erstellen des Codes ersetzt der Assembler jedoch einfach Name durch den angegebenen Ort, genau wie Ihr Browser Domänennamen durch IP-Adressen ersetzt.

Bis auf den Kern ist es technisch unmöglich, einen Verweis auf irgendetwas in einer beliebigen Sprache zu übergeben, ohne es darzustellen (wenn es sofort zu einem Wert wird).

Nehmen wir an, wir haben eine Variable Foo, die Position befindet sich am 47. Byte im Speicher und der Wert ist 5. Wir haben eine weitere Variable Ref2Foo, die sich bei 223. Byte befindet, und ihr Wert wird 47 sein. Dieses Ref2Foo könnte eine technische Variable sein , nicht explizit vom Programm erstellt. Wenn Sie nur 5 und 47 ohne weitere Informationen betrachten, werden nur zwei Werte angezeigt . Wenn Sie sie als Referenzen verwenden, müssen 5wir reisen , um zu erreichen :

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

So funktionieren Sprungtabellen.

Wenn Sie eine Methode / Funktion / Prozedur mit dem Wert von Foo aufrufen möchten, gibt es mehrere Möglichkeiten, die Variable an die Methode zu übergeben, je nach Sprache und den verschiedenen Methodenaufrufmodi:

  1. 5 wird in eines der CPU-Register (dh EAX) kopiert.
  2. 5 bringt PUSHd zum Stack.
  3. 47 wird in eines der CPU-Register kopiert
  4. 47 DRÜCKEN zum Stapel.
  5. 223 wird in eines der CPU-Register kopiert.
  6. 223 bringt PUSHd zum Stack.

In allen Fällen wurde ein Wert - eine Kopie eines vorhandenen Werts - erstellt, der nun von der empfangenden Methode übernommen wird. Wenn Sie "Foo" in die Methode schreiben, wird sie entweder aus EAX ausgelesen oder automatisch dereferenziert oder doppelt dereferenziert. Der Vorgang hängt davon ab, wie die Sprache arbeitet und / oder welche Art von Foo es vorschreibt. Dies ist vor dem Entwickler verborgen, bis sie den Dereferenzierungsprozess umgeht. Eine Referenz ist also ein Wert, wenn sie dargestellt wird, da eine Referenz ein Wert ist, der verarbeitet werden muss (auf Sprachebene).

Nun haben wir Foo an die Methode übergeben:

  • Wenn Sie Foo ( Foo = 9) ändern, wirkt sich dies nur auf den lokalen Bereich aus, da Sie eine Kopie des Werts haben. Innerhalb der Methode können wir nicht einmal feststellen, wo sich der ursprüngliche Foo im Speicher befand.
  • In den Fällen 3. und 4., wenn Sie Standardsprachkonstrukte verwenden und Foo ( Foo = 11) ändern, kann Foo global geändert werden (abhängig von der Sprache, dh Java oder wie Pascals procedure findMin(x, y, z: integer; var m : integer); ). Wenn jedoch die Sprache können Sie den dereferenzieren Prozess umgehen, die Sie ändern können 47, sagen 49. An diesem Punkt scheint Foo geändert worden zu sein, wenn Sie es lesen, weil Sie den lokalen Zeiger darauf geändert haben . Und wenn Sie dieses Foo innerhalb der Methode ( Foo = 12) modifizieren würden, werden Sie wahrscheinlich die Ausführung des Programms (aka. Segfault) FUBAR ausführen, da Sie in einen anderen Speicher schreiben werden als erwartet. Sie können sogar einen Bereich ändern, in dem die ausführbare Datei gespeichert werden soll Das Programm und das Schreiben darauf ändern den laufenden Code (Foo ist jetzt nicht bei 47). ABER Foo Wert von47hat sich nicht global geändert, nur die innerhalb der Methode, weil 47auch eine Kopie der Methode vorhanden war.
  • In den Fällen 5. und 6. Wenn Sie 223innerhalb der Methode ändern , wird dasselbe Chaos wie in 3. oder 4. erzeugt (ein Zeiger, der auf einen jetzt schlechten Wert zeigt, der wiederum als Zeiger verwendet wird), aber dies ist immer noch ein lokaler Problem, da 223 kopiert wurde . Wenn Sie jedoch dereferenzieren können Ref2Foo(d. H. 223), Den angegebenen Wert erreichen und ändern können 47, z. B. bis 49, wirkt sich dies auf Foo global aus , da in diesem Fall die Methoden eine Kopie erhalten haben 223, die referenzierten jedoch 47nur einmal vorhanden sind und dies ändert to 49führt jedes Ref2Foodoppelte Dereferenzieren zu einem falschen Wert.

Bei der Auswahl unbedeutender Details werden sogar Sprachen, die Referenzreferenzen übergeben, Werte an Funktionen übergeben, aber diese Funktionen wissen, dass sie es für Dereferenzierungszwecke verwenden müssen. Dieser Pass-the-Reference-as-value ist dem Programmierer nur verborgen, da er praktisch unbrauchbar ist und die Terminologie nur Pass-by-Reference ist .

Ein strikter Pass-by-Wert ist auch unbrauchbar. Dies bedeutet, dass ein 100-MB-Array jedes Mal kopiert werden muss, wenn wir eine Methode mit dem Array als Argument aufrufen. Daher kann Java kein strikter Pass-by-Wert sein. Jede Sprache würde einen Verweis auf dieses riesige Array (als Wert) übergeben und entweder den Copy-On-Write-Mechanismus verwenden, wenn dieses Array innerhalb der Methode lokal geändert werden kann, oder die Methode (wie Java) das Array global ändern kann (von die Ansicht des Aufrufers) und einige Sprachen erlauben es, den Wert der Referenz selbst zu ändern.

Kurz gesagt und in Javas eigener Terminologie ist Java ein Wert-für-Wert, wobei value Folgendes sein kann: entweder ein reeller Wert oder ein Wert , der eine Darstellung einer Referenz darstellt .




Soweit ich weiß, kennt Java den Aufruf nur nach Wert. Das bedeutet für primitive Datentypen, dass Sie mit einer Kopie arbeiten und für Objekte mit einer Kopie der Referenz auf die Objekte. Ich denke jedoch, dass es einige Fallstricke gibt. Zum Beispiel wird dies nicht funktionieren:

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

Dies wird Hello World und nicht World Hello bevölkern, da Sie in der Swap-Funktion Kopien verwenden, die sich nicht auf die Referenzen im Hauptbereich auswirken. Wenn Ihre Objekte nicht unveränderlich sind, können Sie sie beispielsweise ändern:

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

Dadurch wird Hello World in der Befehlszeile angezeigt. Wenn Sie StringBuffer in String ändern, wird nur Hello erzeugt, da String unveränderlich ist. Zum Beispiel:

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

Sie können jedoch einen Wrapper für String wie diesen erstellen, der ihn mit Strings verwenden könnte:

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: Ich glaube, das ist auch der Grund, StringBuffer zu verwenden, wenn es darum geht, zwei Strings "hinzuzufügen", weil Sie das ursprüngliche Objekt ändern können, das Sie nicht mit unveränderlichen Objekten wie String verwenden können.




Ich möchte versuchen, mein Verständnis anhand von vier Beispielen zu erklären. Java ist Pass-by-Value und nicht Pass-by-Reference

/ **

Wert übergeben

In Java werden alle Parameter als Wert übergeben, dh das Zuweisen eines Methodenarguments ist für den Aufrufer nicht sichtbar.

* /

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

Ergebnis

output : output
value : Nikhil
valueflag : false

Beispiel 2

/ ** * * Wert übergeben * * /

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

Ergebnis

output : output
value : Nikhil
valueflag : false

Beispiel 3:

/ ** Dieses 'Pass By Value' hat ein Gefühl von 'Pass By Reference'

Einige Leute sagen, dass primitive Typen und 'String' 'Wertübergabe' und Objekte 'Referenzübergabe' sind.

Aber aus diesem Beispiel können wir verstehen, dass es sich nur um einen rein wertmäßigen Wert handelt, wobei zu beachten ist, dass hier der Bezug als Wert übergeben wird. dh: Referenz wird per Wert übergeben. Deshalb können sie sich ändern und es gilt auch nach dem lokalen Geltungsbereich. Die tatsächliche Referenz kann jedoch nicht außerhalb des ursprünglichen Bereichs geändert werden. Was dies bedeutet, zeigt das nächste Beispiel von 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";
    }
}

Ergebnis

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

Beispiel 4:

/ **

Zusätzlich zu dem, was in Beispiel 3 (PassByValueObjectCase1.java) erwähnt wurde, können wir die tatsächliche Referenz nicht außerhalb des ursprünglichen Bereichs ändern. "

Hinweis: Ich füge den Code nicht für ein private class Student. Die Klassendefinition für Studentist dieselbe wie in Beispiel3.

* /

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

}

Ergebnis

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



Java ist ein Aufruf nach Wert.

Wie es funktioniert

  • Sie übergeben immer eine Kopie der Bits des Referenzwerts!

  • Wenn es sich um einen primitiven Datentyp handelt, enthalten diese Bits den Wert des primitiven Datentyps selbst. Wenn wir also den Wert des Headers innerhalb der Methode ändern, werden die Änderungen außerhalb der Methode nicht berücksichtigt.

  • Wenn es sich um einen Objektdatentyp wie Foo foo = new Foo () handelt, wird in diesem Fall eine Kopie der Adresse des Objekts wie eine Dateiverknüpfung übergeben. Angenommen, wir haben eine Textdatei abc.txt unter C: \ desktop und nehmen an, dass eine Verknüpfung von erstellt wird Dieselbe Datei und füge diese in C: \ desktop \ abc-shortcut ein , wenn du also von C: \ desktop \ abc.txt auf die Datei zugreifst und '' schreibst und die Datei schließt und die Datei dann erneut von der Verknüpfung aus öffnest Schreiben 'ist die größte Online-Community für Programmierer zum Lernen' Dann wird die gesamte Dateiänderung ' ist die größte Online-Community für Programmierer zum Lernen'Das heißt, es spielt keine Rolle, von wo aus Sie die Datei öffnen. Jedes Mal, wenn wir auf dieselbe Datei zugreifen, können wir Foo als Datei annehmen und annehmen, dass Foo bei 123hd7h (ursprüngliche Adresse wie C: \ desktop \ abc.txt ) gespeichert ist. address and 234jdid (kopierte Adresse wie C: \ desktop \ abc-shortcut, die tatsächlich die ursprüngliche Adresse der Datei enthält) .. Also für ein besseres Verständnis machen Sie die Shortcut-Datei und ...




Der Unterschied oder vielleicht genau die Art und Weise, an die ich mich erinnere, als ich früher unter dem gleichen Eindruck wie das Originalplakat stand, ist Folgendes: Java ist immer wertorientiert. Alle Objekte (in Java, mit Ausnahme von Primitiven) in Java sind Verweise. Diese Referenzen werden nach Wert übergeben.




Java hat nur Wertübergabe. Ein sehr einfaches Beispiel, um dies zu überprüfen.

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



Ich halte es immer für "pass by copy". Es ist eine Kopie des Werts, sei es primitiv oder referenziert. Wenn es sich um ein Grundelement handelt, handelt es sich um eine Kopie der Bits, die den Wert darstellen. Wenn es sich um ein Objekt handelt, handelt es sich um eine Kopie der Referenz.

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

Ausgabe von Java PassByCopy:

name = Maxx
name = Fido

Primitive Wrapper-Klassen und Strings sind unveränderlich, sodass jedes Beispiel, das diese Typen verwendet, nicht wie andere Typen / Objekte funktioniert.




Einige Korrekturen an einigen Stellen.

C unterstützt KEINE Referenzübergabe. Es ist IMMER nach Wert zu übergeben. C ++ unterstützt Pass-by-Reference, ist jedoch nicht die Standardeinstellung und ziemlich gefährlich.

Es ist egal, was der Wert in Java ist: primitiv oder Adresse (ungefähr) des Objekts, es wird IMMER über den Wert übergeben.

Wenn sich ein Java-Objekt so verhält, als würde es durch Verweis übergeben, ist dies eine Eigenschaft der Veränderlichkeit und hat absolut nichts mit Übergabemechanismen zu tun.

Ich bin nicht sicher, warum das so verwirrend ist, vielleicht weil so viele Java-Programmierer nicht formal ausgebildet sind und daher nicht verstehen, was wirklich im Speicher vorgeht.




Java übergibt Parameter nach VALUE und NUR nach Wert .

Um es kurz zu machen:

Für diejenigen, die von C # kommen: Es gibt keinen "out" -Parameter.

Für diejenigen, die von PASCAL kommen: Es gibt keinen "var" -Parameter .

Dies bedeutet, dass Sie den Verweis nicht vom Objekt selbst aus ändern können, aber Sie können jederzeit die Eigenschaften des Objekts ändern.

Eine Problemumgehung besteht darin, StringBuilderstattdessen Parameter zu verwenden String. Und Sie können immer Arrays verwenden!




Related