java - هل جافا "تمرير بواسطة مرجع" أو "تمرير حسب قيمة"؟


ظننت دائما أن جافا كانت تمر بالرجوع إليها . ومع ذلك رأيت اثنين من بلوق وظيفة (على سبيل المثال، هذه المدونة ) التي تدعي أنها ليست. لا أعتقد أنني أفهم التمييز الذي يصنعونه.

ما هو التفسير؟




Answers


جافا دائما تمرير تلو القيمة . لسوء الحظ، قرروا استدعاء موقع كائن "مرجع". عندما نقوم بتمرير قيمة كائن، نحن تمرير الإشارة إليه. هذا أمر مربك للمبتدئين.

وغني عن هذا النحو:

public static void main( String[] args ) {
    Dog aDog = new Dog("Max");
    // we pass the object to foo
    foo(aDog);
    // aDog variable is still pointing to the "Max" dog when foo(...) returns
    aDog.getName().equals("Max"); // true, java passes by value
    aDog.getName().equals("Fifi"); // false 
}

public static void foo(Dog d) {
    d.getName().equals("Max"); // true
    // change d inside of foo() to point to a new Dog instance "Fifi"
    d = new Dog("Fifi");
    d.getName().equals("Fifi"); // true
}

في هذا المثال سوف aDog.getName() لا تزال تعود "Max" . لا يتم تغيير قيمة aDog داخل main في وظيفة foo مع Dog "Fifi" كما يتم تمرير مرجع الكائن القيمة. إذا تم تمريرها بالرجوع، ثم aDog.getName() في main سيعود "Fifi" بعد الدعوة إلى foo .

بطريقة مماثلة:

public static void main( String[] args ) {
    Dog aDog = new Dog("Max");
    foo(aDog);
    // when foo(...) returns, the name of the dog has been changed to "Fifi"
    aDog.getName().equals("Fifi"); // true
}

public static void foo(Dog d) {
    d.getName().equals("Max"); // true
    // this changes the name of d to be "Fifi"
    d.setName("Fifi");
}

في المثال أعلاه، FiFi هو اسم الكلب بعد استدعاء foo(aDog) لأنه تم تعيين اسم الكائن داخل foo(...) . أي العمليات التي foo يؤدى على d هي بحيث، لجميع الأغراض العملية، يتم تنفيذها على aDog نفسها (إلا عندما يتم تغيير d للإشارة إلى مثيل Dog مختلف مثل d = new Dog("Boxer") ).




أنا فقط لاحظت كنت المشار إليها مقالتي .

يقول جافا سبيك أن كل شيء في جافا هو تمرير حسب القيمة. لا يوجد شيء مثل "تمرير من قبل المرجعية" في جافا.

المفتاح لفهم هذا هو أن شيئا من هذا القبيل

Dog myDog;

ليس كلب. انها في الواقع مؤشر على الكلب.

ما يعنيه ذلك، هو عندما يكون لديك

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

كنت تمرير أساسا عنوان الكائن Dog إنشاؤها على طريقة foo .

(أقول أساسا لأن مؤشرات جافا ليست عناوين مباشرة، ولكن من الأسهل أن نفكر بها بهذه الطريقة)

لنفترض أن الكائن Dog يقيم في عنوان الذاكرة 42. وهذا يعني أننا تمرير 42 إلى الأسلوب.

إذا تم تعريف الطريقة بأنها

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

دعونا ننظر إلى ما يحدث.

  • يتم تعيين المعلمة someDog إلى القيمة 42
  • في خط "آا"
    • someDog إلى Dog تشير إلى (الكائن Dog في العنوان 42)
    • أن Dog (واحد في العنوان 42) يطلب تغيير اسمه إلى ماكس
  • في خط "بب"
    • يتم إنشاء Dog جديد. لنفترض أنه في العنوان 74
    • ونحن تعيين المعلمة someDog إلى 74
  • في خط "سك"
    • ويتبع سوميدوغ إلى Dog تشير إلى (الكائن Dog في العنوان 74)
    • أن Dog (واحد في العنوان 74) يطلب تغيير اسمه إلى رولف
  • ثم، نعود

الآن دعونا نفكر في ما يحدث خارج الطريقة:

هل تغير myDog ؟

هناك المفتاح.

مع الأخذ في الاعتبار أن myDog هو مؤشر ، وليس Dog الفعلي، والجواب هو لا. myDog لا يزال لديه قيمة 42؛ فإنه لا يزال يشير إلى Dog الأصلي (ولكن لاحظ أنه بسبب خط "آا"، اسمها الآن "ماكس" - لا يزال نفس الكلب؛ لم تتغير قيمة myDog ل.)

انها صالحة تماما لمتابعة عنوان وتغيير ما هو في نهاية ذلك؛ التي لا تغير المتغير، ومع ذلك.

يعمل جافا تماما مثل C. يمكنك تعيين مؤشر، تمرير المؤشر إلى أسلوب، اتبع المؤشر في الأسلوب وتغيير البيانات التي تم الإشارة إليها. ومع ذلك، لا يمكنك تغيير مكان نقاط المؤشر.

في C ++، أدا، باسكال وغيرها من اللغات التي تدعم تمرير من قبل المرجعية، يمكنك فعلا تغيير المتغير الذي تم تمريره.

إذا كان جافا قد دلالات مرجعية تمرير مرجعية، فإن طريقة foo myDog أعلاه قد تغيرت حيث كان myDog يشير عند تعيين بعض someDog على خط بب.

فكر في المعلمات المرجعية على أنها أسماء مستعارة للمتغير الذي تم تمريره. عند تعيين الاسم المستعار، فذلك هو المتغير الذي تم تمريره فيه.




جافا دائما يمرر الوسيطات القيمة ليس بالرجوع إليها.

اسمحوا لي أن أشرح ذلك من خلال مثال :

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

وسأوضح ذلك في الخطوات التالية:

  1. إعلان مرجع اسمه f من نوع Foo وتعيينه إلى كائن جديد من نوع Foo مع سمة "f" .

    Foo f = new Foo("f");

  2. من جانب الطريقة، يتم الإعلان عن مرجع من نوع Foo مع اسم a وتم تعيينه في البداية إلى null .

    public static void changeReference(Foo a)

  3. أثناء استدعاء الأسلوب changeReference ، سيتم تعيين المرجع a إلى الكائن الذي تم تمريره كوسيطة.

    changeReference(f);

  4. إعلان مرجع اسمه b من نوع Foo وتعيينه إلى كائن جديد من نوع Foo مع سمة "b" .

    Foo b = new Foo("b");

  5. a = b يعيد تعيين المرجع a f إلى الكائن الذي الخاصية المميزة هي "b" .

  6. عند استدعاء modifyReference(Foo c) ، يتم إنشاء مرجع c وتعيين إلى الكائن مع السمة "f" .

  7. c.setAttribute("c"); سيغير سمة الكائن الذي يشير إلى نقاط c إليه، وهو نفس الكائن الذي يشير إلى نقاط f إليه.

آمل أن نفهم الآن كيف يمر الأشياء كما الحجج يعمل في جافا :)




وهذا سوف تعطيك بعض الأفكار حول كيفية جافا يعمل حقا إلى النقطة التي في المناقشة القادمة حول جافا يمر بالرجوع أو يمر بقيمة سوف تبتسم فقط :-)

الخطوة الأولى يرجى مسح من عقلك أن الكلمة التي تبدأ مع 'p' "_ _ _ _ _ _ _"، وخاصة إذا كنت تأتي من لغات البرمجة الأخرى. لا يمكن كتابة جافا و 'p' في نفس الكتاب أو المنتدى أو حتى النص.

الخطوة الثانية تذكر أنه عند تمرير كائن إلى أسلوب كنت تمرير المرجع كائن وليس الكائن نفسه.

  • الطالب : سيد، هل هذا يعني أن جافا تمرير بواسطة مرجع؟
  • سيد : جندب، رقم

تفكر الآن في ما هو المرجع / المتغير /

  1. متغير يحمل البتات التي تخبر جفم كيفية الوصول إلى الكائن المشار إليه في الذاكرة (كومة الذاكرة المؤقتة).
  2. عند تمرير الحجج لطريقة لا تمرر المتغير المرجعي، ولكن نسخة من البتات في المتغير المرجعي . شيء من هذا القبيل: 3bad086a. يمثل 3bad086a طريقة للوصول إلى الكائن الذي تم تمريره.
  3. لذلك كنت مجرد تمرير 3bad086a أنه من قيمة المرجع.
  4. أنت تمرر قيمة المرجع وليس المرجع نفسه (وليس الكائن).
  5. هذه القيمة في الواقع كوبيد وتعطى للطريقة .

في ما يلي (من فضلك لا تحاول تجميع / تنفيذ هذا ...):

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

ما يحدث؟

  • يتم إنشاء الشخص المتغير في السطر رقم 1 وانه فارغ في البداية.
  • يتم إنشاء كائن شخص جديد في السطر رقم 2، المخزنة في الذاكرة، ويتم إعطاء الشخص المتغير المرجع إلى كائن الشخص. وهذا هو عنوانها. دعنا نقول 3bad086a.
  • يتم تمرير الشخص المتغير الذي يحمل عنوان الكائن إلى الدالة في السطر رقم 3.
  • في الخط رقم 4 يمكنك الاستماع إلى صوت الصمت
  • تحقق من التعليق على السطر رقم 5
  • يتم إنشاء متغير محلي الأسلوب - آخر ريفيرنستوثسامبيرسونوبجيكت ثم يأتي السحر في السطر رقم 6:
    • يتم نسخ الشخص المتغير / مرجع بت بتة وتمريرها إلى ريفيرنستوثساميبرسون أوبجيكت داخل الدالة.
    • لم يتم إنشاء أي مثيلات جديدة من الأشخاص.
    • كل من " شخص " و "آخر ريفيرنستوثيساميبرسونوبجيكت " يحمل نفس القيمة من 3bad086a.
    • لا تحاول هذا ولكن الشخص == آخر ريفيرنستوثيساميبرسونوبجيكت سيكون صحيحا.
    • يحتوي كلا المتغيرات على نسخ إيدنتيكال للمرجع وكلاهما يشير إلى كائن الشخص نفسه، كائن سيم في كومة وليس نسخة.

صورة تساوي ألف كلمة:

لاحظ أن أسهم ريليفيرانستوثزاميبرسونوبجيكت أخرى موجهة نحو الكائن وليس نحو المتغير!

إذا لم تحصل عليه ثم فقط ثق بي وتذكر أنه من الأفضل أن نقول أن جافا تمرير القيمة . حسنا، تمر بالقيمة المرجعية . حسنا، حتى أفضل هو تمرير من قبل نسخة من المتغير قيمة! ؛)

الآن لا تتردد في الكراهية لي ولكن لاحظ أنه نظرا لهذا لا يوجد فرق بين تمرير أنواع البيانات البدائية والكائنات عند الحديث عن الحجج الأسلوب.

كنت دائما تمرير نسخة من بت من قيمة المرجع!

  • إذا كان نوع البيانات البدائية، فإن هذه البتات تحتوي على قيمة نوع البيانات البدائية نفسها.
  • إذا كان كائن سوف تحتوي البتات على قيمة العنوان الذي يخبر جفم كيفية الوصول إلى الكائن.

جافا تمرير بواسطة قيمة لأنه داخل طريقة يمكنك تعديل كائن المشار إليه بقدر ما تريد ولكن مهما كانت صعبة حاولت لن تكون قادرا على تعديل متغير مرت التي سوف تبقي المرجعية (لا p _ _ _ _ _ _ _) نفس الكائن بغض النظر عن ما!

لن تكون الدالة تشانجنام أعلاه قادرة على تعديل المحتوى الفعلي (قيم بت) للمرجع الذي تم تمريره. في كلمة أخرى تارجينام لا يمكن أن تجعل الشخص الشخص الرجوع إلى كائن آخر.

بالطبع يمكنك قصها قصيرة وتقول فقط أن جافا هو تمرير من قبل قيمة!




جافا تمر دائما بالقيمة، مع عدم وجود استثناءات، من أي وقت مضى .

فكيف هو أن أي شخص يمكن أن يكون على الإطلاق الخلط من قبل هذا، ونعتقد أن جافا تمرير بالمرجعية، أو يعتقدون أن لديهم مثالا على جافا بمثابة تمرير مرجعا؟ النقطة الرئيسية هي أن جافا لا توفر الوصول المباشر إلى قيم الكائنات نفسها ، في أي ظرف من الظروف. الوصول الوحيد إلى الكائنات هو من خلال إشارة إلى هذا الكائن. لأن كائنات جافا يتم الوصول إليها دائما من خلال مرجع، بدلا من مباشرة، فمن الشائع الحديث عن الحقول والمتغيرات وسيطات الأسلوب ككائنات ، عندما يكون دنيا مراجع فقط الكائنات . وينبع الارتباك من هذا (بالمعنى الدقيق للكلمة، غير صحيح) تغيير في التسميات.

لذلك، عند استدعاء طريقة

  • بالنسبة للحجج البدائية ( int ، long ، etc.)، فإن قيمة التمرير هي القيمة الفعلية للبدائية (على سبيل المثال، 3).
  • بالنسبة إلى الكائنات، يكون التمرير حسب القيمة هو قيمة المرجع إلى الكائن .

حتى إذا كان لديك doSomething(foo) public void doSomething(Foo foo) { .. } و فوس اثنين نسخ الإشارات التي تشير إلى نفس الكائنات.

وبطبيعة الحال، يمر من خلال قيمة إشارة إلى كائن يبدو كثيرا مثل (وغير تمييز في الممارسة العملية من) تمرير كائن بالرجوع.




جافا يمر المراجع حسب القيمة.

لذلك لا يمكنك تغيير المرجع الذي يتم تمريره.




فقط لإظهار التباين، مقارنة C ++ التالية وجافا مقتطفات:

في C ++: ملاحظة: رمز سيئة - تسرب الذاكرة! لكنه يدل على هذه النقطة.

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

في جافا،

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

جافا فقط نوعين من تمرير: بواسطة قيمة لأنواع المضمنة، وقيمة المؤشر لأنواع الكائنات.




أشعر بأن الجدل حول "تمرير حسب المرجع مقابل تمرير حسب القيمة" ليس مفيدا للغاية.

إذا قلت، "جافا تمرير بأي شيء (المرجع / القيمة)"، ولكن في كلتا الحالتين لا تقدم إجابة كاملة. إليك بعض المعلومات الإضافية التي نأمل أن تساعد في فهم ما يحدث في الذاكرة.

دورة تحطم على كومة / كومة قبل أن نصل إلى تنفيذ جافا: القيم تذهب وإيقاف المكدس بطريقة منظمة لطيفة، مثل كومة من لوحات في الكافتيريا. الذاكرة في كومة الذاكرة المؤقتة (المعروفة أيضا باسم الذاكرة الديناميكية) هي عشوائية وغير منظمة. جفم فقط يجد الفضاء أينما كان ذلك، وتحريره حتى المتغيرات التي تستخدم لم تعد هناك حاجة.

حسنا. أولا، الأوليات المحلية تذهب على كومة. لذلك هذا الرمز:

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

النتائج في هذا:

عند الإعلان عن كائن. الكائن الفعلي يذهب على كومة الذاكرة المؤقتة. ما الذي يحدث على المكدس؟ عنوان الكائن على كومة الذاكرة المؤقتة. C ++ المبرمجين استدعاء هذا المؤشر، ولكن بعض المطورين جافا عنصرية ضد كلمة "مؤشر". ايا كان. أعرف فقط أن عنوان الكائن يذهب في كومة.

مثل ذلك:

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

صفيف هو كائن، لذلك يذهب في كومة أيضا. وماذا عن الكائنات في المصفوفة؟ يحصلون على مساحة كومة الخاصة بهم، ويذهب عنوان كل كائن داخل المصفوفة.

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

إذن، ما الذي يتم تمريره عند استدعاء طريقة؟ إذا كنت تمر في كائن، ما كنت تمر فعلا في هو عنوان الكائن. قد يقول البعض "قيمة" العنوان، والبعض يقول انها مجرد اشارة الى الكائن. هذا هو نشأة الحرب المقدسة بين مؤيدي "المرجع" و "القيمة". ما تسمونه لا يقل أهمية عن أن تفهم أن ما يتم تمريره هو عنوان الكائن.

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

يتم إنشاء سلسلة واحدة ويتم تخصيص مساحة لذلك في كومة الذاكرة المؤقتة، ويتم تخزين العنوان إلى السلسلة على المكدس ومعرف hisName ، لأن عنوان السلسلة الثانية هو نفسه الأول، لا يتم إنشاء سلسلة جديدة ولا يتم تخصيص مساحة كومة جديدة، ولكن يتم إنشاء معرف جديد على المكدس. ثم نطلق shout() : يتم إنشاء إطار كومة جديد ومعرف جديد، يتم إنشاء name وتعيين عنوان السلسلة الموجودة بالفعل.

لذلك، قيمة، مرجع؟ أنت تقول "البطاطس".




جافا يمر مراجع الكائنات من حيث القيمة.




لا أستطيع أن أصدق أن أحدا ذكر باربارا لسكوف حتى الان. عندما صمم CLU في عام 1974، وقالت انها واجهت هذه المشكلة المصطلحات نفسها، وأنها اخترع مصطلح دعوة تقاسم (المعروف أيضا باسم الدعوة عن طريق تبادل كائن و دعوة وجوه ) لهذه الحالة المحددة من "الدعوة من حيث القيمة حيث القيمة مرجع".




في الأساس، وإعادة تعيين المعلمات كائن لا تؤثر على حجة، على سبيل المثال،

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

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

سوف تطبع "Hah!"بدلا من NULL. السبب يعمل هذا لأن barهو نسخة من قيمة baz، الذي هو مجرد إشارة إلى"Hah!" .إذا كانت الإشارة الفعلية نفسها، ثم fooقد أعاد bazل null.




وجوهر المسألة هو أن كلمة المرجعية في التعبير "تمرير حسب المرجع" تعني شيئا مختلفا تماما عن mening المعتاد للكلمة المرجعية في جاوا.

عادة في جاوة إشارة تعني أأ إشارة إلى كائن . ولكن المصطلحات الفنية تمر بالرجوع / قيمة من الناحية النظرية لغة البرمجة يتحدث عن إشارة إلى خلية ذاكرة عقد متغير ، وهو سوميتينج مختلف تماما.




في جافا كل ما هو المرجعية، لذلك عندما يكون لديك شيء من هذا القبيل: Point pnt1 = new Point(0,0);جافا يفعل التالية:

  1. يخلق كائن نقطة جديدة
  2. يخلق إشارة نقطة جديدة وتهيئة أن الإشارة إلى نقطة (راجع) على الكائن نقطة تم إنشاؤها مسبقا.
  3. من هنا، من خلال حياة الكائن نقطة، ويمكنك الوصول إلى هذا الكائن من خلال الإشارة pnt1. لذلك يمكننا القول إن في جاوة تقوم بمعالجة الكائن من خلال إشارته.

جافا لا تمرير الوسائط أسلوب حسب المرجع. يمر بهم من حيث القيمة. وسوف تستخدم المثال من هذا الموقع :

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

تدفق من البرنامج:

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

كما سيكون الناتج المتوقع:

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

على هذا الخط »القبض على حدة قيمة 'يذهب الى اللعب ...

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

المراجع pnt1و pnt2يتم تمريرها من حيث القيمة لطريقة صعبة، وهو ما يعني أن لك المراجع الآن pnt1و pnt2لها من copiesاسمها arg1و arg2لذا pnt1و arg1 نقاط إلى نفس الكائن. (نفس ل pnt2و arg2)

في trickyالأسلوب:

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

التالي في trickyطريقة

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

هنا، عليك أولا خلق فرص عمل جديدة tempاشارة نقطة والتي سوف نشير في نفس المكان مثل arg1المرجعية. ثم قمت بنقل الإشارة arg1إلى نقطة إلى نفس المكان مثل arg2المرجعية. وأخيرا arg2سوف نشير إلى نفس المكان مثل temp.

من هنا نطاق trickyرحل طريقة ولم يكن لديك وصول أي أكثر إلى المراجع: arg1، arg2، temp. ولكن ملاحظة هامة هي أن كل ما تفعله مع هذه المراجع عندما يكونون في الحياة "سوف تؤثر على الكائن الذي هم دائم نقطة ل.

حتى بعد تنفيذ الطريقة tricky، عند العودة إلى main، لديك هذه الحالة:

وحتى الآن، فإن التنفيذ تماما من البرنامج على النحو التالي:

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



إشارة هي دائما قيمة عندما ممثلة، بغض النظر عن اللغة التي تستخدمها.

الحصول على خارج ترى مربع، دعونا ننظر في الجمعية أو بعض إدارة الذاكرة مستوى منخفض. على مستوى وحدة المعالجة المركزية في إشارة إلى أي شيء يصبح على الفور قيمة إذا كان يحصل كتابة إلى الذاكرة أو إلى أحد سجلات وحدة المعالجة المركزية. (وهذا هو السبب في مؤشر هو تعريف جيد، وهو القيمة، التي لديها هدف في نفس الوقت).

البيانات في الذاكرة لديه المكان وفي هذا المكان هناك قيمة (البايت، كلمة، أيا كان). في الجمعية لدينا حل مناسب لإعطاء اسم لبعض الموقع (ويعرف أيضا باسم متغير)، ولكن عندما ترجمة التعليمات البرمجية، المجمع ببساطة استبدال اسم مع مكان معين تماما مثل متصفحك يستبدل أسماء النطاقات مع عناوين IP.

وصولا الى جوهر فإنه من المستحيل من الناحية الفنية لتمرير إشارة إلى أي شيء في أي لغة بدون يمثل ذلك (عندما يصبح على الفور قيمة).

دعونا نقول لدينا فو متغير، على الموقع هو في البايت 47th في الذاكرة ولها قيمة هو 5. لدينا متغير آخر Ref2Foo الذي هو في البايت 223rd في الذاكرة، وسوف تكون قيمته 47. هذا Ref2Foo قد يكون متغير الفني ، لم يخلق صراحة من قبل البرنامج. إذا كنت مجرد إلقاء نظرة على 5 و 47 دون أي معلومات أخرى، سترى اثنين فقط من القيم . اذا كنت استخدامها كمراجع ثم للوصول إلى 5دينا في السفر:

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

هذه هي الطريقة التي عمل قفزة الجداول.

إذا كنا نريد أن استدعاء أسلوب / وظيفة / الإجراء مع قيمة فو، وهناك عدد قليل من الطرق الممكنة لتمرير المتغير إلى الأسلوب، اعتمادا على اللغة والعديد من وسائط طريقة الاحتجاج بها:

  1. يحصل على نسخ 5 إلى أحد سجلات وحدة المعالجة المركزية (أي EAX).
  2. يحصل أوامر pushd 5 إلى المكدس.
  3. 47 يحصل على نسخ لأحد سجلات وحدة المعالجة المركزية
  4. 47 أوامر pushd إلى المكدس.
  5. 223 يحصل على نسخ لأحد سجلات وحدة المعالجة المركزية.
  6. 223 يحصل على أوامر pushd إلى المكدس.

في كل الحالات المذكورة أعلاه قيمة - وهو نسخة من قيمة موجودة - تم إنشاء، هو الآن تصل طريقة الاستقبال للتعامل مع ذلك. عند كتابة "فو" داخل الأسلوب، فإنه إما تلا من EAX، أو تلقائيا ألغى الإشارة القيمة ، أو ألغى الإشارة القيمة مزدوج، عملية يعتمد على كيفية عمل اللغة و / أو ما نوع فو يملي. يتم إخفاء هذا من المطور حتى انها تلتف عملية dereferencing. لذلك المرجع هو قيمة عندما ممثلة، لأن المرجعية هي القيمة التي لابد من معالجتها (على مستوى اللغة).

الآن لقد مرت علينا فو إلى الأسلوب:

  • في حالة 1. و2. إذا قمت بتغيير فو ( Foo = 9) أنه يؤثر فقط نطاق المحلي لديك نسخة من القيمة. من داخل الطريقة لا نستطيع حتى تحديد مكان في الذاكرة ويقع فو الأصلي.
  • في حالة 3. و4. إذا كنت تستخدم بنيات اللغة الافتراضية وتغيير فو ( Foo = 11)، فإنه يمكن تغيير فو عالميا (يعتمد على اللغة، أي. جاوة أو مثل باسكال procedure findMin(x, y, z: integer; فار م : integer); ). ومع ذلك إذا كانت اللغة يسمح لك للالتفاف على عملية dereference، يمكنك تغيير 47، ويقول ل 49. عند هذه النقطة يبدو فو قد تتغير إذا كنت تقرأ ذلك، لكنت قد قمت بتغيير المؤشر المحلي لها. وإذا كنت لتعديل هذه فو داخل طريقة ( Foo = 12) وربما كنت FUBAR تنفيذ برنامج (ويعرف أيضا باسم. segfault) لأنك سوف الكتابة إلى ذاكرة مختلفة مما كان متوقعا، حتى يمكنك تعديل المنطقة التي كانت متجهة إلى عقد تنفيذ سوف برنامج والكتابة إليها تعديل تشغيل التعليمات البرمجية (فو هو الآن ليس في 47). ولكن قيمة فو لل47لم يتغير على الصعيد العالمي، إلا أن واحدة داخل الأسلوب، لأنه 47كان أيضا نسخة إلى الأسلوب.
  • في حالة 5. و6. إذا قمت بتعديل 223داخل الأسلوب يخلق نفس الفوضى كما هو الحال في 3. أو 4. (مؤشر، لافتا إلى قيمة الآن سيئة، وهذا هو استخدامها مرة أخرى كمؤشر) ولكن هذا لا يزال المحلية المشكلة، وكما 223 نسخ . ولكن إذا كنت قادرا على dereference Ref2Foo(أي 223)، والوصول إلى وتعديل قيمة مدببة 47، مثلا، إلى 49، وسوف تؤثر فو على مستوى العالم ، لأنه في هذه الحالة الأساليب حصلت على نسخة من 223ولكن الإشارة 47موجود مرة واحدة فقط، وتغيير ذلك ل 49سيؤدي كل Ref2Fooالمزدوج dereferencing إلى قيمة خاطئة.

نيتبيكينج على تفاصيل تافهة، وحتى اللغات التي لا تمر من قبل المرجعية تمرير القيم إلى وظائف، ولكن هذه المهام يعرفون أن لديهم لاستخدامها لأغراض dereferencing. يتم إخفاء هذه القيمة تمرير الإسناد كما هو وفقط من مبرمج لأنها غير مجدية عمليا والمصطلحات ليست سوى تمرير من قبل المرجعية .

الصارم تمرير كل قيمة هو أيضا لا طائل منه، فإن ذلك يعني أن يجب أن يكون مجموعة 100 ميجابايت ليتم نسخها في كل مرة نطلق عليه الأسلوب مع مجموعة كوسيطة، وبالتالي جافا لا يمكن أن يكون stricly تمرير كل قيمة. فإن كل لغة تمرير إشارة إلى هذه المجموعة الضخمة (كقيمة) وإما يستخدم آلية النسخ عند الكتابة إذا يمكن تغيير هذا الصفيف محليا داخل الأسلوب أو يسمح الطريقة (كما يفعل جافا) لتعديل مجموعة على مستوى العالم (من عرض المتصل) وبعض اللغات يسمح لتعديل قيمة المرجع نفسه.

هكذا وباختصار وفي المصطلحات جافا نفسه، جافا تمرير كل قيمة حيث قيمة يمكن أن يكون: إما القيمة الحقيقية أو القيمة التي هي تمثيل ل مرجعية .




لا، انها ليست تمرير حسب المرجع.

جافا تمريرة من قيمة وفقا لمواصفات لغة جافا:

عندما يتم استدعاء الأسلوب أو منشئ (§15.12)، القيم من التعبيرات حجة الفعلية تهيئة خلقت حديثا المتغيرات المعلمة ، كل نوع أعلن، قبل التنفيذ من الجسم من طريقة أو منشئ. المعرف الذي يظهر في DeclaratorId يمكن أن تستخدم كاسم بسيط في الجسم من طريقة أو منشئ للإشارة إلى معلمة رسمية .




وبقدر ما أعرف، جافا وحده يعلم الدعوة من حيث القيمة. وهذا يعني لأنواع البيانات البدائية سوف تعمل مع نسخة والكائنات التي سوف تعمل مع نسخة من الإشارة إلى الكائنات. ومع ذلك أعتقد أن هناك بعض العثرات. على سبيل المثال، هذا لن يعمل:

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

هذا وسوف ملء مرحبا الدولي وليس مرحبا العالم لأنه في وظيفة تبديل استخدام copys التي ليس لها تأثير على المراجع بشكل رئيسي. ولكن إذا الأشياء الخاصة بك ليست ثابتة يمكنك تغييره على سبيل المثال:

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

هذا وسوف ملء مرحبا العالم على سطر الأوامر. إذا قمت بتغيير StringBuffer في سلسلة انها ستنتج فقط مرحبا بسبب سلسلة غير ثابتة. فمثلا:

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

ولكن هل يمكن جعل مجمع لسلسلة مثل هذا الأمر الذي يجعلها قادرة على استخدامه مع الجمل:

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

تحرير: وأعتقد أن هذا هو أيضا سبب لاستخدام StringBuffer عندما يتعلق الأمر "، مضيفا" اثنين من سلاسل لأنه يمكنك modifie الكائن الأصلي الذي ش لا يمكن أن بأجسام ثابتة مثل سلسلة هي.




اسمحوا لي أن أحاول أن أشرح فهمي بمساعدة أربعة أمثلة. جافا تمرير كل قيمة، وليس تمرير كل إشارة

/ **

تمرير من حيث القيمة

في جاوة، يتم تمرير كافة المعلمات من حيث القيمة، أي تعيين الحجة طريقة غير مرئية إلى الطالب.

* /

مثال 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";
    }
}

نتيجة

output : output
value : Nikhil
valueflag : false

مثال 2:

/ ** * * يمر بها القيمة * * /

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

نتيجة

output : output
value : Nikhil
valueflag : false

مثال 3:

/ ** هذا "ممر بواسطة القيمة لديه شعور من" ممر حسب المرجع "

يقول بعض الناس أنواع بدائية و'سلسلة' هي 'تمرير من حيث القيمة "والكائنات" تمر بالرجوع.

ولكن من هذا المثال، يمكننا أن نفهم أنه هي في الواقع تمريرة من قيمة فقط، مع الأخذ في الاعتبار أن هنا نحن تمرير إشارة كقيمة. يتم تمرير الإشارة من حيث القيمة: أي. لهذا السبب تكون قادرة على تغيير وانه لا يزال يصدق بعد نطاق محلي. ولكن لا يمكننا تغيير المرجع الفعلية خارج نطاق الأصلي. ماذا يعني ذلك يتضح من خلال المثال التالي من 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";
    }
}

نتيجة

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

مثال 4:

/ **

بالإضافة إلى ما ذكر في Example3 (PassByValueObjectCase1.java)، ونحن لا يمكن تغيير المرجع الفعلية خارج نطاق الأصلي ".

ملاحظة: أنا لا لصق رمز ل private class Student. تعريف الفئة ل Studentهو نفس Example3.

* /

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

}

نتيجة

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



أنت لا يمكن أبدا تمرير بالرجوع بلغة جافا، واحدة من الطرق التي هو واضح هو عندما تريد العودة أكثر من قيمة واحدة من استدعاء الأسلوب. النظر في بت التالية من التعليمات البرمجية في 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;
}

في بعض الأحيان كنت تريد استخدام نفس النمط في جاوة، ولكن لا أستطيع؛ على الأقل ليس مباشرة. وبدلا من ذلك يمكنك أن تفعل شيئا من هذا القبيل:

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

كما أوضح في الأجوبة السابقة، في جاوة كنت تمرير مؤشر إلى مجموعة كقيمة في getValues. هذا يكفي، لأن أسلوب ثم تعديل عنصر المصفوفة، واتفاقية كنت تتوقع العنصر 0 لاحتواء قيمة الإرجاع. ومن الواضح أن يمكنك القيام بذلك بطرق أخرى، مثل هيكلة التعليمات البرمجية بحيث هذا ليس ضروريا، أو بناء على الفئة التي يمكن أن تحتوي على قيمة الإرجاع أو يسمح لها وضعها. ولكن نمط بسيط المتاحة لك في C ++ فوق غير متوفر في جاوة.




اعتقدت أن تسهم هذه الإجابة لإضافة مزيد من التفاصيل من المواصفات.

أولا، ما هو الفرق بين المارة بالإشارة مقابل تمرير من حيث القيمة؟

يمر بالرجوع يعني دعا وظائف "والمعلمة يكون نفس المتصلين حجة مرت (وليس قيمة، ولكن الهوية - المتغير نفسه).

تمريرة من قيمة يعني يسمى وظائف "والمعلمة يكون نسخة من المتصلين حجة تمريرها.

أو من ويكيبيديا، حول موضوع تمرير كل إشارة

في تقييم الدعوة من قبل المرجعية (كما يشار إلى تمرير من قبل المرجعية) يتلقى ظيفة إشارة ضمنية إلى متغير تستخدم حجة، وليس نسخة من قيمته. وهذا يعني عادة أن وظيفة يمكن تعديل (تعيين أي ل) المتغير تستخدم حجة شيء من شأنها أن ينظر إليها من قبل المتصل به.

و حول موضوع تمرير كل قيمة

في استدعاء كل قيمة، يتم تقييم التعبير الحجة، ولا بد القيمة الناتجة إلى متغير المقابلة في وظيفة [...]. إذا كانت وظيفة أو إجراء غير قادرة على تعيين قيم لمعالمها، فقط يتم تعيين النسخة المحلية [...].

ثانيا، نحن بحاجة إلى معرفة ما يستخدم جافا في الدعاء منهجه. و لغة جافا مواصفات الدول

عندما يتم استدعاء الأسلوب أو منشئ (§15.12)، القيم من التعبيرات حجة الفعلية تهيئة خلقت حديثا المتغيرات المعلمة ، كل نوع أعلن، قبل التنفيذ من الجسم من طريقة أو منشئ.

لذلك يعين (أو ضم) قيمة الوسيطة إلى متغير المعلمة المقابلة.

ما هي قيمة الوسيطة؟

دعونا النظر في أنواع المراجع، و آلة جافا الافتراضية مواصفات الدول

هناك ثلاثة أنواع من أنواع المراجع : أنواع الطبقة، وأنواع مجموعة، وأنواع واجهة. قيمهم هي إشارات إلى إنشاؤه بشكل حيوي الحالات الطبقة، المصفوفات، أو مثيلات فئة أو صفائف أن تنفيذ واجهات، على التوالي.

و لغة جافا المواصفات تنص أيضا

القيم المرجعية (غالبا ما يشير فقط) هي مؤشرات إلى هذه الكائنات ، ومرجع فارغة الخاصة، والتي تشير إلى أي كائن.

قيمة حجة (من نوع المرجعية) هو مؤشر إلى كائن. لاحظ أن متغير، استدعاء من طريقة مع نوع مرجع نوع الإرجاع، وتعبيرا عن إنشاء المثيل ( new ...) عن حل لقيمة نوع مرجع.

وبالتالي

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

كل ربط قيمة إشارة إلى Stringالمثال إلى المعلمة الأسلوب الذي تم إنشاؤه حديثا، param. هذا هو بالضبط ما يصف تعريف تمرير كل قيمة. على هذا النحو، جافا تمرير كل قيمة .

والحقيقة التي يمكنك اتباعها للإشارة إلى استدعاء أسلوب أو الوصول إلى مجال الكائن المشار إليه غير ذي صلة تماما إلى المحادثة. تعريف تمرير من قبل المرجعية كان

وهذا يعني عادة أن وظيفة يمكن تعديل (تعيين أي ل) المتغير تستخدم حجة شيء من شأنها أن ينظر إليها من قبل المتصل به.

في جاوة، تعديل متغير يعني إعادة تعيين ذلك. في جاوة، إذا كنت إعادة تعيين المتغير داخل الأسلوب، فإنه لن تمر مرور الكرام على المتصل. تعديل الكائن المشار إليه بواسطة متغير هو مفهوم مختلف تماما.

يتم تحديد القيم البدائية أيضا في آلة جافا الافتراضية مواصفات، هنا . قيمة نوع هي القيمة المقابلة لا يتجزأ أو النقطة العائمة، المشفرة بشكل مناسب (8، 16، 32، 64، الخ بت).




التمييز، أو ربما مجرد وسيلة أتذكر عندما اعتدت أن أكون تحت نفس الانطباع كما الملصق الأصلي هو: جافا تمرير دائما من حيث القيمة. كل الاشياء (في جاوة، أي شيء باستثناء البدائيون) في جاوة والمراجع. يتم تمرير هذه المراجع من حيث القيمة.




جافا تمرير دائما بالقيم NOT تمرير بالرجوع

أولا من أننا نفهم ما هو تمرير من حيث القيمة وتمرير بالرجوع

تمريرة من قيمة يعني كنت ترغب بجعل نسخة في الذاكرة من قيمة المعلمة الفعلية التي يتم تمريرها في، نسخة من محتويات المعلمة الفعلية

تمرير بالرجوع (وتسمى أيضا تمريرة من العنوان)، يتم تخزين نسخة من عنوان المعلمة الفعلية

بعض الوقت أنه يعطي تمريرة وهم من reference.lets نرى كيف يعمل بالقدوة

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

إخراج هذا البرنامج

تغيير القيمة

يتيح فهم خطوة بخطوة

اختبار ر = اختبار جديد ()؛

كما نعلم جميعا أنه سيتم إنشاء الكائن في كومة والعودة القيمة المرجعية العودة إلى ر. لنفترض على سبيل المثال قيمة t غير 0x100234 (القيمة الداخلية JVM ونحن لا حول هذا الموضوع لقد تنظر فقط لأنه على سبيل المثال)

Passbyvalue الجديدة () changeValue (ر)؛

عند تمرير ر إشارة إلى العمل لن يمر مباشرة القيمة المرجعية الفعلية للاختبار الكائن ولكن فإنه سيتم إنشاء نسخة من ر وبعد ذلك تمر على وظيفة (كما تمر من حيث القيمة يمر نسخة من متغير لا إشارة الفعلية منه). ونحن ننظر قيمة ر سوف be0x100234. حتى في هذه الطريقة سوف كلا ر و و لديها نفس القيمة، وبالتالي فإنها سوف نشير إلى نفس الكائن

حتى إذا قمت بتغيير أي شيء في وظيفة باستخدام الإشارة و أنها سوف تعديل احتواء الموجودة من الكائن الذي لماذا كنا نحصل الانتاج "changevalue" والتي يتم تحديثها في وظيفة

لفهم هذا المثال أكثر وضوحا النظر التالية

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

وسوف تعطي مؤشر فارغة لا لأنه يمر النسخة الوحيدة من إشارة. وفي حالة بالرجوع أنه يمكن أن يعطي استثناء nullpointer

نأمل أن هذا سيساعد




جافا مكالمة من حيث القيمة.

كيف تعمل.

  • كنت دائما تمرير نسخة من البتات من قيمة الإشارة!

  • إذا كان نوع البيانات البدائية هذه القطع تحتوي على قيمة من نوع البيانات البدائية نفسها، وهذا هو السبب إذا قمنا بتغيير قيمة رأس داخل الأسلوب فإنه لا تعكس التغيرات في الخارج.

  • اذا كان نوع بيانات كائن مثل فو فو فو = جديد () ثم في هذه النسخة حالة عنوان الكائن يمر مثل اختصار ملف، لنفترض أن لدينا ملف نصي abc.txt في C: \ سطح المكتب ونفترض أننا جعل اختصار نفس الملف ووضع هذا داخل C: \ سطح المكتب \ حروف الاختصار وذلك عند الوصول إلى الملف من C: \ سطح المكتب \ abc.txt والكتابة "تجاوز المكدس" وأغلق الملف ومرة أخرى ها أنت تفتح الملف من الاختصار ثم ل الكتابة "هو أكبر مجتمع على الإنترنت للمبرمجين لتعلم" ثم الكلي تغيير الملف سيكون "تجاوز المكدس هو أكبر مجتمع على الانترنت للمبرمجين لتعلم"مما يعني أنه لا يهم من أين قمت بفتح الملف، في كل مرة كنا الوصول إلى نفس الملف، وهنا يمكننا أن نفترض فو كملف وفو نفترض المخزنة في 123hd7h (العنوان الأصلي مثل C: \ سطح المكتب \ abc.txt ) عنوان و 234jdid (نسخ عنوان مثل C: \ سطح المكتب \ حروف الاختصار الذي يحتوي في الواقع على العنوان الأصلي للملف داخل) .. لذلك فهم أفضل جعل ملف الاختصار ويشعر ..




كما ذكر كثير من الناس من قبل، جافا وتمرير كل قيمة دائما

هنا هو مثال آخر من شأنها أن تساعدك على فهم الفرق ( المبادلة المثال الكلاسيكي ):

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

مطبوعات:

قبل: أ = 2، ب = 3
بعد: أ = 2، ب = 3

يحدث هذا لأن الضبط التلقائي الذكي iA وباء متغيرات المرجعية المحلية الجديدة التي لها نفس القيمة من المراجع مرت (يشيرون إلى أ و ب على التوالي). لذلك، في محاولة لتغيير المراجع من الضبط التلقائي الذكي iA أو البكالوريا الدولية سوف تتغير إلا في نطاق محلي وليس خارج هذا الأسلوب.




كنت دائما أعتقد أنها "تمر نسخة". وهو نسخة من القيمة سواء كانت بدائية أو إشارة. إذا كانت بدائية هو نسخة من البتات التي هي قيمة وإذا كان هذا هو كائن هو نسخة من المرجع.

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

إخراج PassByCopy جافا:

اسم = ماكس
اسم = فيدو

فئات المجمع البدائية وسلاسل هي ثابتة لذلك أي سبيل المثال استخدام تلك الأنواع لا تعمل نفس أنواع / غيرها من الأشياء.




لقد خلق موضوع المخصصة لهذا النوع من الأسئلة عن أي لغات البرمجة هنا .

كما ذكر جافا . هنا هو موجز قصير:

  • جافا يمررها المعلمات من حيث القيمة
  • "من حيث القيمة" هو السبيل الوحيد في جافا لتمرير معلمة إلى الأسلوب
  • وذلك باستخدام أساليب من كائن معطى كمعلمة يغير الكائن كما تشير المراجع إلى الكائنات الأصلية. (إذا كانت هذه الطريقة نفسها تغير بعض القيم)



لجعل قصة قصيرة طويلة، جافا كائنات لها بعض خصائص غريبة جدا.

بشكل عام، جافا أنواع البدائية ( int، bool، char، double، الخ) التي تم تمريرها مباشرة من حيث القيمة. ثم جافا الكائنات (كل ما يستمد من java.lang.Object). في الواقع التعامل مع الأشياء دائما من خلال إشارة (إشارة كونها المؤشر أنه لا يمكن لمس). وهذا يعني أنه في الواقع، يتم تمرير الكائنات بالرجوع، والمراجع عادة يست مثيرة للاهتمام. ومع ذلك فإنه لا يعني أنه لا يمكنك تغيير أي كائن يشار إلى أنه يتم تمرير الإشارة نفسها من حيث القيمة.

هل هذا يبدو غريبا ومربكا؟ دعونا النظر في كيفية C تنفذ تمر بالرجوع وتمر من حيث القيمة. في C، واتفاقية الافتراضية هي تمرير من حيث القيمة. void foo(int x)يمر عدد صحيح من حيث القيمة. void foo(int *x)هي وظيفة لا تريد int a، ولكن مؤشر إلى int: foo(&a). يمكن للمرء استخدام هذا مع &المشغل لتمرير عنوان متغير.

أغتنم هذه إلى C ++، ولدينا المراجع. المراجع هي في الأساس (في هذا السياق) نحوي السكر التي تخفي الجزء المؤشر من المعادلة: void foo(int &x)يسمى من قبل foo(a)، حيث المترجم نفسه يعرف أنه هو المرجعية وعنوان غير مرجعية aيجب تمريرها. في جاوة، كل المتغيرات في اشارة الى الكائنات هي في الواقع من نوع مرجع، في الواقع مما اضطر دعوة المرجعية لمعظم تعتزم والأغراض دون مراقبة دقيقة الحبيبات (والتعقيد) التي توفرها، على سبيل المثال، C ++.




هناك عدد قليل من التصحيحات لبعض الوظائف.

C لا تدعم تمرير حسب المرجع. ومن تمرير ALWAYS من حيث القيمة. C ++ لا تمرير الدعم بالرجوع، ولكن ليس الافتراضي وهذا خطير جدا.

لا يهم ما هي القيمة في جاوة: بدائية أو عنوان (تقريبا) من وجوه، يتم تمرير ALWAYS من حيث القيمة.

إذا كائن جافا "يتصرف" مثل يتم تمريرها من قبل المرجع، وهذا هو خاصية التحولية، ولها على الاطلاق للقيام مع آليات تمرير أي شيء.

ولست متأكدا لماذا هذا الخلط بين ذلك، ربما لأن الكثير من "المبرمجين" جافا وغير مدربين بشكل رسمي، وبالتالي لا أفهم ما يحدث في الواقع في الذاكرة؟




في جاوة يتم تمرير إشارات فقط، ويتم تمرير حسب القيمة:

والحجج جافا تمرير كافة من حيث القيمة (يتم نسخ المرجع عند استخدامها من قبل طريقة):

في حالة أنواع بدائية، والسلوك جافا بسيط: يتم نسخ القيمة في مثيل آخر من نوع بدائي.

في حالة الأجسام، وهذا هو نفسه: المتغيرات كائن هي مؤشرات (دلاء) عقد كائن الوحيد في عنوان التي تم إنشاؤها باستخدام الكلمة "الجديدة"، ويتم نسخ مثل أنواع بدائية.

يمكن أن تظهر السلوك مختلفة من أنواع البدائية: لأن الكائن المتغير نسخ يحتوي على نفس العنوان (لنفس كائن) الكائن في المحتوى / أعضاء قد لا يزال يمكن تعديل في طريقة ووقت لاحق وصول خارج، وإعطاء الوهم بأن (الحاوي) كائن صدر نفسها حسب المرجع.

ويبدو أن "سلسلة" كائنات أن يكون مثاليا لمكافحة سبيل المثال إلى أسطورة الحضرية قائلا ان "يتم تمرير كائنات حسب المرجع":

في الواقع، داخل أسلوب وأنك لن تكون قادرا، لتحديث قيمة السلسلة التي تم تمريرها كوسيطة:

كائن سلسلة، يحمل حرفا من قبل مجموعة أعلنت النهائية التي لا يمكن تعديلها. عنوان كائن يمكن استبدال فقط من جانب آخر باستخدام "الجديدة". عن طريق "جديدة" لتحديث متغير، لن تسمح يمكن الوصول إلى كائن من الخارج، منذ أن تم تمرير المتغير في البداية من حيث القيمة ونسخها.




جاوا تمر فقط من حيث القيمة. وهناك مثال بسيط جدا للتحقق من صحة هذا.

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



انها حقا جدا، بسيط جدا:

لمتغير من نوع بدائي (على سبيل المثال int، boolean، char، الخ ...)، عند استخدام اسمها لحجة الطريقة، يتم تمرير القيمة الموجودة فيه ( 5، trueأو 'c'). يحصل هذه القيمة "نسخ"، ويحتفظ المتغير قيمته حتى بعد استدعاء الأسلوب.

لمتغير من نوع مرجع (على سبيل المثال String، Object، الخ ...)، عند استخدام اسمها لحجة الطريقة، يتم تمرير القيمة الموجودة فيه ( و القيمة المرجعية أن "نقطة" إلى كائن ). هذه القيمة المرجعية يحصل "نسخ"، ويحتفظ المتغير قيمته حتى بعد استدعاء الأسلوب. المتغير إشارة يحتفظ "لافتا" إلى نفس الكائن.

وفي كلتا الحالتين، كنت تمر دائما الاشياء من حيث القيمة.

قارن هذا القول C ++ حيث يمكن أن يكون وسيلة لاتخاذ int&، أو في C # أين أنت يمكن أن تأخذ ref int(على الرغم من أنه في هذه الحالة، يجب عليك أيضا استخدام refمعدل عند تمرير اسم متغير إلى الأسلوب.)