جافا هي تمرير حسب القيمة، وليس تمرير حسب المرجع

المقدمة

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

أولاً، ماذا تعني القيمة والإشارة؟

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

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

كل من أنواع البيانات المرجعية وأنواع البيانات الأساسية تتم نسخها بالقيمة. تعرف أكثر على أنواع البيانات في جافا.

بالإضافة إلى فهم أنواع البيانات، من المهم أيضًا فهم تخصيص الذاكرة في جافا، لأن أنواع البيانات المرجعية والأساسية تخزن بشكل مختلف.

عرض تمرير القيمة

المثال التالي يوضح كيفية تمرير القيم في لغة البرمجة جافا.

يستخدم البرنامج المثالي الصف التالي:

public class Balloon {

	private String color;

	public Balloon() {}
	
	public Balloon(String c) {
		this.color = c;
	}
	
	public String getColor() {
		return color;
	}

	public void setColor(String color) {
		this.color = color;
	}
}

البرنامج المثالي التالي يستخدم طريقة عامة، swap()، التي تقوم بتبديل قيمتين. وتحاول الطريقة الأخرى، changeValue()، تغيير قيم المتغيرات.

public class Test {

	public static void main(String[] args) {

		Balloon red = new Balloon("Red"); // مرجع الذاكرة = 50
		Balloon blue = new Balloon("Blue"); // مرجع الذاكرة = 100
		
		swap(red, blue);
		System.out.println("After the swap method executes:");
		System.out.println("`red` color value = " + red.getColor());
		System.out.println("`blue` color value = " + blue.getColor());
		
		changeValue(blue);
		System.out.println("After the changeValue method executes:");
		System.out.println("`blue` color value = " + blue.getColor());
		
	}

	// الطريقة العامة للتبديل
	public static void swap(Object o1, Object o2){
		Object temp = o1;
		o1 = o2;
		o2 = temp;
	}

	private static void changeValue(Balloon balloon) { // balloon = 100
		balloon.setColor("Red"); // balloon = 100
		balloon = new Balloon("Green"); // balloon = 200
		balloon.setColor("Blue"); // balloon = 200
	}

}

عند تنفيذ البرنامج المثالي، تحصل على الناتج التالي:

Output
After the swap method executes: 'red' color value = Red 'blue' color value = Blue After the changeValue method executes: 'blue' color value = Red

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

يمكن استخدام اختبار طريقة swap() هذا مع أي لغة برمجة للتحقق مما إذا كانت تمرر القيمة أم تمرر المرجع.

تم شرح مثال الطريقة swap()

عند استخدام مشغل new لإنشاء مثيل من فئة، يتم إنشاء الكائن والمتغير يحتوي على الموقع في الذاكرة حيث يتم حفظ الكائن.

Balloon red = new Balloon("Red");
Balloon blue = new Balloon("Blue");

وفيما يلي عرض تفصيلي خطوة بخطوة لما يحدث عند تنفيذ الطريقة swap():

  • لنفترض أن red يشير إلى موقع الذاكرة 50 و blue يشير إلى موقع الذاكرة 100، وأن هذه هي مواقع الذاكرة لكائني Balloon كلاهما.

  • عندما يقوم الفصيلة باستدعاء الطريقة swap() مع المتغيرات red و blue كمعلمات، يتم إنشاء متغيرات كائن جديدة، o1 و o2. o1 و o2 أيضًا يشيران إلى مواقع الذاكرة 50 و 100 على التوالي.

  • توضح مقاطعة الشفرة التالية ما يحدث داخل طريقة swap():

    public static void swap(Object o1, Object o2) { // o1 = 50, o2 = 100
    	Object temp = o1; // تعيين قيمة مرجع الكائن لـ o1 إلى temp: temp = 50، o1 = 50، o2 = 100
    	o1 = o2; // تعيين قيمة مرجع الكائن لـ o2 إلى o1: temp = 50، o1 = 100، o2 = 100
    	o2 = temp; // تعيين قيمة مرجع temp إلى o2: temp = 50، o1 = 100، o2 = 50
    } // انتهت الطريقة
    
  • قيم o1 و o2 تم استبدالها، ولكن نظرًا لأن القيم هي نسخ من مواقع الذاكرة للأحمر والأزرق، فليس هناك تغيير في قيم الأحمر والأزرق.

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

شرح مثال طريقة changeValue()

private static void changeValue(Balloon balloon) { // البالون = 100
	balloon.setColor("Red"); // البالون = 100
	balloon = new Balloon("Green"); // البالون = 200
	balloon.setColor("Blue"); // البالون = 200
}

الطريقة التالية في البرنامج المثال تغير قيمة اللون للكائن المشار إليه بواسطة متغير blue:

  • تقوم الفئة باستدعاء طريقة changeValue() على المتغير blue الذي يشير إلى موقع الذاكرة 100. السطر الأول ينشئ مرجعًا يشير أيضًا إلى موقع الذاكرة 100. يتم تغيير قيمة اللون للكائن في موقع الذاكرة 100 إلى "أحمر".

  • السطر الثاني ينشئ كائنًا جديدًا (بقيمة للون "أخضر"). الكائن الجديد في موقع الذاكرة 200. أي طرق تُنفذ على متغير balloon بعد ذلك تؤثر على الكائن في موقع الذاكرة 200، ولا تؤثر على الكائن في موقع الذاكرة 100. يعوض المتغير الجديد balloon المرجع الذي تم إنشاؤه في السطر 1، والمرجع balloon من السطر 1 لم يعد قابلًا للوصول في هذه الطريقة.

  • السطر الثالث يغير قيمة اللون للكائن الجديد Balloon في موقع الذاكرة 200 إلى "Blue"، ولكن لا يؤثر على الكائن الأصلي الذي يشير إليه blue في موقع الذاكرة 100. وهذا يفسر لماذا تطبع السطر النهائي من إخراج برنامج المثال blue color value = Red، مما يعكس التغيير من السطر 1.

الختام

في هذا المقال تعلمت عن سبب تمرير Java بالقيمة. استمر في تعلمك مع المزيد من دروس Java.

Source:
https://www.digitalocean.com/community/tutorials/java-is-pass-by-value-and-not-pass-by-reference