שייכות למחלקה, לא לאובייקט
רוב השדות והמתודות שכתבתם עד עכשיו שייכים לאובייקטים: בכל פעם שקוראים ל-new, כל מופע מקבל עותק משלו של השדות, והמתודות רצות מול המצב של האובייקט המסוים הזה. מילת המפתח static הופכת את זה. איבר static שייך למחלקה עצמה: יש עותק אחד, שכל המופעים חולקים, והוא קיים בין אם יוצרים אובייקט ובין אם לא.
ההבחנה האחת הזו מסבירה שדות סטטיים, מתודות סטטיות, קבועים, ואפילו למה main היא תמיד static.
שדה static משותף
שדה לא סטטי נותן לכל אובייקט משבצת משלו. שדה static נותן למחלקה משבצת אחת שכולם חולקים. הדוגמה הקלאסית היא מונה שעוקב אחרי כמה אובייקטים נוצרו:
שימו לב שקוראים את המונה כ-User.count, דרך שם המחלקה, כי הערך לא שייך לאף User מסוים. כל name, לעומת זאת, חי בתוך האובייקט שלו. שנו את count דרך מופע כלשהו, וכל המופעים יראו את הערך החדש, כי יש רק אחד כזה.
מתודות static
גם מתודה static שייכת למחלקה, ולכן קוראים לה על המחלקה בלי אובייקט:
זה בדיוק הדפוס שמאחורי הספרייה הסטנדרטית: Math.max, Integer.parseInt, Arrays.sort ו-List.of הן כולן static, התנהגות עזר שלא צריכה אובייקט כדי לפעול. השתמשו במתודה static כשהעבודה תלויה רק בארגומנטים שלה, ולא במצב של אובייקט כלשהו.
המלכודת הגדולה: static לא רואה איברי מופע
מתודה static רצה בלי שום אובייקט, ולכן אין this. המשמעות היא שהיא לא יכולה לגעת בשדות מופע או לקרוא למתודות מופע ישירות: אין אובייקט מסוים לקרוא אותם ממנו. זו השגיאה הנפוצה ביותר של מתחילים עם static:
class Account {
int balance = 100; // שדה מופע
static int show() {
return balance; // שגיאת קומפילציה: השדה הלא סטטי 'balance'
} // לא ניתן להפניה מהקשר סטטי
}
התיקון הוא להפוך את המתודה למתודת מופע (להסיר את static, כדי שיהיה לה this), או להעביר את האובייקט במפורש:
הכיוון ההפוך תקין: מתודת מופע יכולה לקרוא בחופשיות שדות static ולקרוא למתודות static, כי הנתונים המשותפים ברמת המחלקה תמיד קיימים.
קבועים עם static final
משלבים static עם final ומקבלים קבוע: ערך משותף אחד שלעולם לא משתנה. לפי המוסכמה, השמות שלהם נכתבים ב-UPPER_SNAKE_CASE:
static final היא הדרך המקובלת לבטא "ערך קבוע ששייך לטיפוס", כמו Integer.MAX_VALUE או Math.PI בספרייה הסטנדרטית. static אומר שלא מבזבזים עותק לכל אובייקט; final אומר שאף אחד לא יכול לשייך אותו מחדש.
בלוקי אתחול static
שדה static פשוט אפשר לאתחל באותה שורה. כשההכנה דורשת לוגיקה אמיתית, כמו בניית טבלת חיפוש עם HashMap או קריאת הגדרות, משתמשים בבלוק static. הוא רץ פעם אחת, כשהמחלקה נטענת לראשונה, לפני שקיים אובייקט כלשהו:
הבלוק רץ פעם אחת בלבד, לא משנה כמה פעמים משתמשים במחלקה, וזה הופך אותו למקום הנכון להכנה חד פעמית ברמת המחלקה כולה.
מתי להשתמש ב-static
כלל אצבע מהיר:
- השתמשו בשדה static רק לנתונים שבאמת משותפים לכל המופעים: מונה, מטמון, קבוע. אם סביר ששני אובייקטים יחזיקו ערכים שונים, זה צריך להיות שדה מופע.
- השתמשו במתודה static כשהתוצאה תלויה רק בארגומנטים, ולא במצב של אובייקט כלשהו (פונקציות עזר טהורות).
- ברירת המחדל היא איברי מופע. שימוש מופרז ב-
staticהופך בשקט את התוכנית לערימה של מצב גלובלי, שקשה לבדוק ולהבין.staticהוא החריג, לא נקודת ההתחלה.
הבא: Enums
קבוע static final מתאים לערך קבוע אחד, אבל כשיש קבוצה קטנה וקבועה של ערכים קשורים, כמו כיוונים, ימי השבוע או סטטוסים של הזמנה, ל-Java יש טיפוס ייעודי שבטוח ומבטא יותר מקבועים מפוזרים. זה ה-enum, וזה העמוד הבא.
שאלות נפוצות
מה המשמעות של static ב-Java?
static פירושו ששדה או מתודה שייכים למחלקה עצמה, ולא לאובייקט מסוים. יש בדיוק עותק אחד של שדה static שכל המופעים חולקים, ומתודה static נקראת על המחלקה (Math.max(...)) בלי צורך באובייקט. איברים לא סטטיים (איברי מופע), לעומת זאת, מקבלים עותק חדש לכל אובייקט.
מה ההבדל בין משתנה סטטי למשתנה מופע ב-Java?
למשתנה מופע יש ערך אחד לכל אובייקט: שני אובייקטים יכולים להחזיק ערכים שונים. למשתנה static יש ערך יחיד שמשותף לכל האובייקטים של המחלקה, כך ששינוי שלו דרך אובייקט אחד (או דרך שם המחלקה) נראה לכל האחרים. השתמשו בשדות מופע למצב של כל אובייקט, ובשדות static לנתונים שבאמת גלובליים למחלקה, כמו מונה או קבוע.
למה המתודה main היא static ב-Java?
ה-JVM צריכה לקרוא ל-main לפני שקיים אובייקט כלשהו של המחלקה שלכם. מכיוון שמתודה static שייכת למחלקה ולא למופע, סביבת הריצה יכולה להפעיל את Main.main(args) ישירות בלי לבנות קודם Main. לכן החתימה היא תמיד public static void main(String[] args).