Java הוא עובר לפי ערך, לא עובר לפי ייחוס

הקדמה

רבים ממתכנתי Java שואלים אם Java היא מעבר לערך או מעבר לכתובת. מאמר זה מסכם למה Java היא תמיד מעבר לערך.

ראשית, מהו מעבר לערך ומעבר לכתובת?

  • מעבר לערך: ערכי הפרמטרים של השיטה מועתקים למשתנה אחר ואז העתק האובייקט מועבר לשיטה. השיטה משתמשת בהעתק.
  • מעבר לכתובת: פיענוח או כתובת לפרמטר המקורי מועבר לשיטה. השיטה גוששת לפרמטר המקורי.

לעיתים קרובות, הבלבול סביב המונחים הללו הוא תוצאה של המושג של הקישור לאובייקט ב-Java. מבחינה טכנית, Java היא תמיד מעבר לערך, מכיוון שאף על פי שמשתנה עשוי להחזיק קישור לאובייקט, קישור האובייקט הוא ערך שמייצג את מיקומו של האובייקט בזיכרון. קישורים לאובייקט מועברים לכן לפי ערך.

כל סוגי הנתונים המציינים כתובת וסוגי הנתונים הפרימיטיביים מועברים לפי ערך. למדו עוד על סוגי הנתונים ב-Java.

בנוסף להבנה של סוגי הנתונים, חשוב גם להבין הקצאת זיכרון ב-Java, מכיוון שסוגי הנתונים המציינים כתובת וסוגי הנתונים הפרימיטיביים מאוחסנים בדרכים שונות.

הדגמה של העברת ערך לפי ערך

הדוגמה הבאה מדגימה כיצד ערכים מועברים ב-Java.

התוכנית הדוגמאית משתמשת במחלקה הבאה:

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) { // בלון = 100
		balloon.setColor("Red"); // בלון = 100
		balloon = new Balloon("Green"); // בלון = 200
		balloon.setColor("Blue"); // בלון = 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() לא החליפה את ערכי הצבע של האובייקטים המקוריים. זה עוזר להראות ש-Java היא עוברת לפי ערך, מאחר ושיטת 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 הוחלפו, אך מאחר שהערכים הם העתקים של מיקומי זיכרון ה־red וה־blue, אין שינוי בערכי צבעי ה־red וה־blue.

מכיוון שהמשתנים מכילים את ההפניה לאובייקטים, זו טעות נפוצה להניח שאתה מעביר את ההפניה ושגם ג'אווה היא עוברת על ידי הפניה. אך, אתה מעביר ערך שהוא העתק של ההפניה ולכן זהו עבור על ידי ערך.

הדוגמה changeValue() נסברת

השיטה הבאה בדוגמה משנה את ערך הצבע של האובייקט שאליו מתייחסת המשתנה blue:

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

נכון להכניס לפרטים את השלבים בשיטה changeValue():

  • המחלקה קוראת לשיטת changeValue() על המשתנה blue שמתייחס למיקום זיכרון 100. השורה הראשונה יוצרת הפניה שגם מצביעה למיקום זיכרון 100. ערך הצבע של האובייקט במיקום זיכרון 100 משתנה ל-"Red".

  • השורה השנייה יוצרת אובייקט חדש (עם ערך צבע "Green"). האובייקט החדש נמצא במיקום זיכרון 200. כל השיטות הבאות שמתבצעות על המשתנה balloon פועלות על האובייקט במיקום זיכרון 200, ואין להן השפעה על האובייקט במיקום זיכרון 100. המשתנה החדש balloon מחליף את ההפניה שנוצרה בשורה 1 וההפניה ל־balloon מהשורה 1 לא נגישה יותר בתוך השיטה הזו.

  • השורה השלישית משנה את ערך הצבע של האובייקט החדש Balloon בכתובת הזיכרון 200 ל־"כחול", אך לא משפיעה על האובייקט המקורי שנמצא בהפניה של blue בכתובת הזיכרון 100. זה מסביר למה השורה הסופית של פלט תכנית הדוגמה מדפיסה ערך צבע blue = אדום, המשקף את השינוי משורה 1.

מסקנה

במאמר זה, למדת על כך למה Java היא שפת תכנות "עוברת לפי ערך". המשך ללמוד עם מדריכי Java נוספים.

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