האם ג'אווה עוברת לפי הפניה או עוברת לפי ערך?

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

קבל את קוד המקור

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

הפניות לאובייקטים מועברות לפי ערך

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

 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השיטה?

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

אם עדיין לא ברור לך איך זה עובד, הסתכל באיור שלמטה.

רפאל צ'ינלאטו דל נירו

האם טיפוסים פרימיטיביים עוברים לפי ערך?

כמו סוגי אובייקטים, גם סוגים פרימיטיביים עוברים לפי ערך. האם אתה יכול להסיק מה יקרה לסוגים הפרימיטיביים בדוגמת הקוד הבאה?

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

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

העברת הפניות לאובייקט בלתי משתנות

מה אם היינו עושים את אותו הבדיקה עם Stringאובייקט שאינו ניתן לשינוי ?

ה- JDK מכיל שיעורים רבים שאינם ניתנים לשינוי. דוגמאות כוללות את סוגי המעטפת Integer, Double, Float, Long, Boolean, BigDecimal, וכמובן ידוע היטב Stringבכיתה.

בדוגמה הבאה, שימו לב מה קורה כאשר אנו משנים את הערך של a 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הכיתה לבלתי משתנה נותנת לנו שליטה טובה יותר על אחד האובייקטים הנפוצים ביותר של Java. אם Stringניתן היה לשנות את הערך של a , זה היה יוצר הרבה באגים. שים לב גם שאנחנו לא משנים תכונה של Stringהכיתה; במקום זאת, אנו פשוט מקצים לו Stringערך חדש . במקרה זה, הערך "הומר" יועבר nameבתוך changeToHomerהשיטה. String"הומר" יהיה זכאי שתיגבה אשפה ברגע changeToHomerהשיטה משלימה ביצוע. למרות שלא ניתן לשנות את האובייקט, המשתנה המקומי יהיה.

מיתרים ועוד

למידע נוסף על Stringהכיתה של Java ועוד: ראה את כל הפוסטים של רפאל בסדרת Java Challengers.

העברת הפניות לאובייקט משתנות

שלא כמו 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"); } } 

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

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

קח את אתגר הפניות לאובייקטים!

בצ'לנג'ר ג'אווה זה נבדוק מה למדת אודות הפניות לאובייקטים. בדוגמת הקוד שלמטה, אתה רואה את המעמד הבלתי 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; } } 

להלן האפשרויות, בדוק בסוף מאמר זה למפתח התשובה.

ת : לוחם = אפס נשק = אפס

ב : לוחם = נשק הדרקון = דרקון

ג : לוחם = נשק אביר הדרקון = חרב הדרקון

D : לוחם = נשק אביר הדרקון = חרב

מה בדיוק קרה?

הפרמטר הראשון בדוגמה שלעיל הוא warriorProfessionהמשתנה, שהוא אובייקט משתנה. הפרמטר השני, הנשק, הוא בלתי משתנה String:

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

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

 warriorProfession.append("Knight"); 

בהוראה השנייה, Stringהמשתנה המקומי הבלתי משתנה ישתנה ל"חרב הדרקון ". האובייקט האמיתי לעולם לא ישתנה, עם זאת, מכיוון Stringשהוא בלתי משתנה ותכונותיו סופיות:

 weapon = "Dragon " + weapon; 

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

 weapon = null; warriorProfession = null; 

From all of this we can conclude that the final values from our mutable StringBuilder and immutable String will be:

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

The only value that changed in the changeWarriorClass method was warriorProfession, because it’s a mutable StringBuilder object. Note that warriorWeapon did not change because it’s an immutable String object.

The correct output from our Challenger code would be:

D: Warrior=Dragon Knight Weapon=Sword.

Video challenge! Debugging object references in Java

Debugging is one of the easiest ways to fully absorb programming concepts while also improving your code. In this video you can follow along while I debug and explain object references in Java.

Common mistakes with object references

  • Trying to change an immutable value by reference.
  • Trying to change a primitive variable by reference.
  • Expecting the real object won't change when you change a mutable object parameter in a method.

What to remember about object references

  • Java always passes parameter variables by value.
  • Object variables in Java always point to the real object in the memory heap.
  • A mutable object’s value can be changed when it is passed to a method.
  • An immutable object’s value cannot be changed, even if it is passed a new value.
  • “Passing by value” refers to passing a copy of the value.
  • “Passing by reference” refers to passing the real reference of the variable in memory.

Learn more about Java

  • Get more quick code tips: Read all of Rafael's posts in the JavaWorld Java Challengers series.
  • Learn more about mutable and immutable Java objects (such as String and StringBuffer) and how to use them in your code.
  • אתה עלול להיות מופתע לגלות שהסוגים הפרימיטיביים של Java שנויים במחלוקת. בתכונה זו, ג'ון הראשון מור מציג את המקרה לשמירתם ולמידה להשתמש בהם היטב.
  • המשך לבנות את כישורי התכנות שלך ב- Java במכון הכושר Java Dev.
  • אם אהבת לבצע ניפוי באגים בירושת ג'אווה, עיין בסרטונים נוספים בפלייליסט הווידיאו של Java מאתגר של רפאל (סרטונים בסדרה זו אינם קשורים ל- JavaWorld).
  • רוצים לעבוד על פרויקטים נטולי מתח ולכתוב קוד ללא באגים? עבור אל ה- NoBugs פרוייקט להעתקה שלך ללא באגים, ללא לחץ - צור תוכנה שמשנה את החיים מבלי להרוס את חייך .

סיפור זה, "האם ג'אווה עוברת לפי הפניה או עוברת לפי ערך?" פורסם במקור על ידי JavaWorld.