۳۰ آذر ۱۴۰۳

Techboy

اخبار و اطلاعات روز تکنولوژی

آیا جاوا با مرجع عبور می کند یا با مقدار عبور می کند؟

ممکن است بدانید که جاوا از ارزش عبور می کند، اما به درک دلیل کمک می کند. در اینجا آنچه اتفاق می افتد هنگامی که شما ارجاعات شیء قابل تغییر و تغییر ناپذیر را در جاوا ارسال می کنید، می باشد.

ممکن است بدانید که جاوا از ارزش عبور می کند، اما به درک دلیل کمک می کند. در اینجا آنچه اتفاق می افتد هنگامی که شما ارجاعات شیء قابل تغییر و تغییر ناپذیر را در جاوا ارسال می کنید، می باشد.

بسیاری از زبان‌های برنامه‌نویسی اجازه می‌دهند اشیاء را با مرجع یا مقدار ارسال کنند. در جاوا، ما فقط می‌توانیم پارامترهای شی را براساس مقدار ارسال کنیم. این محدودیت ها را تحمیل می کند و همچنین سؤالاتی را ایجاد می کند. به عنوان مثال، اگر مقدار پارامتر در متد تغییر کند، پس از اجرای متد چه اتفاقی برای مقدار می افتد؟ همچنین ممکن است تعجب کنید که جاوا چگونه مقادیر شی را در پشته حافظه مدیریت می کند. این مقاله به شما کمک می‌کند این سؤالات رایج و سایر سؤالات رایج در مورد ارجاعات شی در جاوا را حل کنید.

ارسال ارجاعات شی در جاوا

در این مقاله شما تفاوت بین عبور از طریق مرجع و عبور از مقدار در جاوا و نحوه ارسال ارجاعات شی جاوا را خواهید آموخت:

  • «Pass by reference» و «pass by value» تعریف شده است
  • ارجاعات شی جاوا با مقدار ارسال می شوند
  • گذراندن انواع اولیه در جاوا
  • ارسال ارجاعات شیء تغییرناپذیر در جاوا
  • ارسال ارجاعات شیء قابل تغییر در جاوا
  • هنگام ارسال ارجاعات اشیا از چه چیزهایی باید اجتناب کرد
  • چه چیزهایی را در مورد انتقال ارجاعات شیء به خاطر بسپارید

«گذر با مرجع» و «گذر بر اساس ارزش» تعریف شده است

Pass by مرجع به این معنی است که مکانی را در حافظه که مقدار متغیر در آن ذخیره می‌شود، منتقل می‌کنیم و pass by value به این معنی است که یک کپی از مقدار واقعی متغیر را ارسال می‌کنیم. البته کمی پیچیده‌تر از آن است، اما این تعریف نقطه شروع خوبی است.

  • گذر از مقدار به این معنی است که ما یک کپی از مقدار را ارسال می کنیم.
  • عبور از طریق مرجع به این معنی است که ما مرجع واقعی را به متغیر موجود در حافظه منتقل می‌کنیم.

ارجاعات شی جاوا با مقدار ارسال می شوند

همه ارجاعات شیء در جاوا با مقدار ارسال می شوند. این بدان معنی است که یک کپی از مقدار به یک متد ارسال می شود. بخش مشکل این است که ارسال یک کپی از مقدار، ارزش واقعی شی را تغییر می دهد. این مثال باید به شما کمک کند تا بفهمید چرا:


public class ObjectReferenceExample {

	public static void main(String... doYourBest) {
    	    Simpson simpson = new Simpson();
    	    transformIntoHomer(simpson);
    	    System.out.println(simpson.name);
	}

	static void transformIntoHomer(Simpson simpson) {
    	    simpson.name = "Homer";
	}

}

class Simpson {
	String name;
}

به نظر شما simpson.name پس از اجرای متد transformIntoHomer چه خواهد بود؟

در این صورت، هومر خواهد بود! دلیل آن این است که متغیرهای شی جاوا به سادگی ارجاعی هستند که به اشیاء واقعی در پشته حافظه اشاره می کنند. بنابراین، حتی اگر جاوا پارامترها را بر اساس مقدار به متدها ارسال می کند، اگر متغیر به یک مرجع شی اشاره کند، شی واقعی نیز تغییر می کند.

اگر هنوز مطمئن نیستید که چگونه کار می کند، نمودار زیر را در نظر بگیرید:

نمودار جریان مراجع اشیاء در جاوا

گذراندن انواع اولیه در جاوا

مانند انواع شی، انواع اولیه نیز با مقدار ارسال می شوند. آیا می توانید حدس بزنید چه اتفاقی برای انواع اولیه در مثال کد زیر می افتد؟


public class PrimitiveByValueExample {

	public static void main(String... primitiveByValue) {
    	    int homerAge = 30;
    	    changeHomerAge(homerAge);
    	    System.out.println(homerAge);
	}

	static void changeHomerAge(int homerAge) {
    	    homerAge = 35;
	}
}

اگر تشخیص داده اید که مقدار به ۳۰ تغییر می کند، درست می گویید. ۳۰ است زیرا (دوباره) جاوا پارامترهای شی را بر اساس مقدار ارسال می کند. عدد ۳۰ فقط یک کپی از مقدار است، نه مقدار واقعی. انواع اولیه در حافظه پشته تخصیص داده می شود، بنابراین فقط مقدار محلی تغییر خواهد کرد. در این مورد، هیچ مرجع شی وجود ندارد.

کد منبع را دریافت کنید

کد این مقاله را دریافت کنید. شما می توانید تست های خود را در حالی که از مثال ها پیروی می کنید، اجرا کنید.

ارسال ارجاعات شیء تغییرناپذیر در جاوا

اگر همان آزمایش را با یک شیء تغییرناپذیر String انجام دهیم چه؟ توجه کنید وقتی مقدار رشته:

را تغییر می‌دهیم چه اتفاقی می‌افتد


public class StringValueChange {

	public static void main(String... doYourBest) {
    	    String name = "";
    	    changeToHomer(name);
    	    System.out.println(name);
	}

	static void changeToHomer(String name) {
    	    name = "Homer";
	}
}

به نظر شما خروجی چه خواهد بود؟ اگر حدس زدید “” پس به شما تبریک می گویم! این به این دلیل است که یک شی String تغییرناپذیر است، به این معنی که فیلدهای داخل String نهایی هستند و قابل تغییر نیستند.

تغییرناپذیر کردن کلاس String به ما کنترل بهتری بر یکی از پرکاربردترین اشیاء جاوا می‌دهد. اگر مقدار رشته قابل تغییر باشد، باگ های زیادی ایجاد می کند. همچنین توجه داشته باشید که ما یک ویژگی از کلاس String را تغییر نمی دهیم. در عوض، ما به سادگی یک مقدار String جدید به آن اختصاص می دهیم. در این حالت، مقدار “Homer” به name در روش changeToHomer ارسال می شود. به محض اینکه روش changeToHomer اجرا شد، String “Homer” واجد شرایط جمع‌آوری زباله خواهد بود. حتی اگر شی را نمی توان تغییر داد، متغیر محلی خواهد بود.

ارسال ارجاعات شیء قابل تغییر در جاوا

برخلاف String، اکثر اشیاء در JDK مانند کلاس StringBuilder قابل تغییر هستند. مثال زیر شبیه نمونه قبلی است، اما ویژگی StringBuilder را به جای String دارد:


 static class MutableObjectReference {
    public static void main(String... mutableObjectExample) {
      StringBuilder name = new StringBuilder("Homer ");
      addSureName(name);
      System.out.println(name);
    }

    static void addSureName(StringBuilder name) {
      name.append("Simpson");
    }
  }
  

آیا می توانید خروجی این مثال را حدس بزنید؟ در این حالت، چون ما با یک شیء قابل تغییر کار می کنیم، خروجی «Homer Simpson» خواهد بود. می‌توانید از هر شیء قابل تغییر دیگری در جاوا انتظار رفتار مشابهی داشته باشید.

خلاصه آنچه آموخته اید

شما یاد گرفته اید که اشیاء جاوا توسط مقدار ارسال می شوند، به این معنی که یک کپی از مقدار ارسال می شود. فقط به یاد داشته باشید که مقدار کپی شده به یک شیء واقعی در پشته حافظه جاوا اشاره می کند. همچنین، به یاد داشته باشید که عبور از مقدار، مقدار شی واقعی را تغییر می دهد.

در هنگام ارسال ارجاعات شی جاوا از چه چیزهایی باید اجتناب کرد

  • سعی نکنید یک مقدار تغییرناپذیر را با مرجع تغییر دهید.
  • سعی نکنید یک متغیر اولیه را با مرجع تغییر دهید.
  • انتظار نداشته باشید که وقتی یک پارامتر شیء قابل تغییر را در یک متد تغییر می دهید، شی واقعی تغییر نکند (تغییر خواهد کرد).

چه چیزی را در مورد ارسال ارجاعات شی جاوا به خاطر بسپارید

  • جاوا همیشه متغیرهای پارامتر را بر اساس مقدار ارسال می کند.
  • متغیرهای شی در جاوا همیشه به شی واقعی در پشته حافظه اشاره می کنند.
  • مقدار یک شیء قابل تغییر زمانی که به یک متد ارسال می شود قابل تغییر است.
  • مقدار یک شیء تغییرناپذیر قابل تغییر نیست، حتی اگر مقدار جدیدی به آن ارسال شود.

آنچه را در مورد ارجاعات اشیا آموخته اید آزمایش کنید

اکنون، بیایید آنچه را که در مورد ارجاعات اشیا آموخته‌اید، آزمایش کنیم. در مثال کد زیر، کلاس String غیرقابل تغییر و StringBuilder قابل تغییر را مشاهده می کنید. هر کدام به عنوان یک پارامتر به یک متد ارسال می شود. با دانستن اینکه جاوا فقط از مقدار عبور می کند، فکر می کنید پس از اجرای متد اصلی از این کلاس، خروجی چه خواهد بود؟


public class DragonWarriorReferenceChallenger {

  public static void main(String... doYourBest) {
    StringBuilder warriorProfession = new StringBuilder("Dragon ");
    String warriorWeapon = "Sword ";
    changeWarriorClass(warriorProfession, warriorWeapon);

    System.out.println("Warrior=" + warriorProfession + " Weapon=" + warriorWeapon);
  }

  static void changeWarriorClass(StringBuilder warriorProfession, String weapon) {
    warriorProfession.append("Knight");
    weapon = "Dragon " + weapon;

    weapon = null;
    warriorProfession = null;
  }

}

در اینجا گزینه‌ها وجود دارد، برای پاسخ، انتهای مقاله را بررسی کنید.

A: Warrior=null Weapon=null
B: Warrior=Dragon Weapon=Dragon
ج: Warrior=Dragon Knight Weapon=Dragon Sword
D: Warrior=Dragon Knight Weapon=Sword

حل چالش

اولین پارامتر در مثال بالا، متغیر warriorProfession است که یک شیء قابل تغییر است. پارامتر دوم، سلاح، یک رشته تغییرناپذیر است:


 static void changeWarriorClass(StringBuilder warriorProfession, String weapon) {
    ...
  }

در خط اول این روش، مقدار Knight را به متغیر warriorProfession اضافه می‌کنیم. به یاد داشته باشید که warriorProfession یک شیء قابل تغییر است. بنابراین شی واقعی تغییر خواهد کرد و مقدار آن “Dragon Knight” خواهد بود.


warriorProfession.append("Knight");

در دستور دوم، متغیر محلی غیرقابل تغییر String به “شمشیر اژدها” تغییر خواهد کرد. با این حال، شی واقعی هرگز تغییر نخواهد کرد، زیرا رشته غیر قابل تغییر است و ویژگی های آن نهایی هستند:


weapon = "Dragon " + weapon;

در نهایت، null را به متغیرهای اینجا می‌دهیم، اما به آبجکت‌ها نمی‌دهیم. تا زمانی که اشیاء از خارج قابل دسترسی باشند – در این مورد از طریق روش اصلی – یکسان خواهند ماند. و اگرچه متغیرهای محلی تهی خواهند بود، اما هیچ اتفاقی برای اشیاء نخواهد افتاد:


weapon = null;
warriorProfession = null;

از این‌جا می‌توان نتیجه گرفت که مقادیر نهایی StringBuilder و String غیرقابل تغییر ما خواهد بود:


System.out.println("Warrior=" + warriorProfession + " Weapon=" + warriorWeapon);

تنها مقداری که در روش changeWarriorClass تغییر کرد warriorProfession بود، زیرا یک شیء StringBuilder قابل تغییر است. توجه داشته باشید که warriorWeapon تغییری نکرده است زیرا یک شیء String تغییرناپذیر است.

خروجی صحیح کد Challenger ما خواهد بود:

D: Warrior=Dragon Knight Weapon=Sword.

چالش ویدیویی! اشکال زدایی ارجاعات شی در جاوا

اشکال‌زدایی یکی از ساده‌ترین راه‌ها برای جذب کامل مفاهیم برنامه‌نویسی و در عین حال بهبود کد شماست. در این ویدیو، می‌توانید در حین اشکال‌زدایی و توضیح مراجع شی در جاوا، همراه باشید.

درباره جاوا بیشتر بیاموزید