Java NullPointerException – איתור, תיקון ושיטות מומלצות

java.lang.NullPointerException הוא אחד החריגים הפופולריים ביותר בתכנות בשפת java. כל מי שעובד ב-java כנראה פגש בזה שצצה משום מקום בתוכנית java עצמאית כמו גם באפליקציית אינטרנט של java.

java.lang.NullPointerException

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

  1. קריאה לשיטה על מופע של אובייקט אך בזמן ריצה האובייקט הוא null.
  2. גישה למשתנים של מופע של אובייקט שהוא null בזמן ריצה.
  3. מטיל null בתוכנית
  4. גישה לאינדקס או שינוי של ערך של אינדקס של מערך שהוא null
  5. בדיקת אורך של מערך שהוא null בזמן ריצה.

דוגמאות ל-NullPointerException ב-Java

נבדק כמה דוגמאות של NullPointerException בתוכניות Java.

1. NullPointerException בזמן קריאה לשיטת מופע

public class Temp {

	public static void main(String[] args) {

		Temp t = initT();
		
		t.foo("Hi");
		
	}

	private static Temp initT() {
		return null;
	}

	public void foo(String s) {
		System.out.println(s.toLowerCase());
	}
}

כאשר אנו מריצים את התוכנית לעיל, היא מפילה את הודעת השגיאה הבאה של NullPointerException.

Exception in thread "main" java.lang.NullPointerException
	at Temp.main(Temp.java:7)

אנו מקבלים NullPointerException בהצהרת t.foo("Hi"); משום ש-"t" הוא null כאן.

2. NullPointerException ב-Java בעת גישה/שינוי שדה של אובייקט שהוא null

public class Temp {

	public int x = 10;
	
	public static void main(String[] args) {

		Temp t = initT();
		
		int i = t.x;
		
	}

	private static Temp initT() {
		return null;
	}

}

התוכנית לעיל משפילה את מערך העקומה (stack trace) של NullPointerException הבא.

Exception in thread "main" java.lang.NullPointerException
	at Temp.main(Temp.java:9)

NullPointerException מושלכת בהצהרת int i = t.x; משום ש-"t" הוא null כאן.

3. NullPointerException ב-Java כאשר null מועבר כארגומנט לשיטה

public class Temp {

	public static void main(String[] args) {

		foo(null);

	}

	public static void foo(String s) {
		System.out.println(s.toLowerCase());
	}
}

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

התמונה למטה מציגה את חריגת הנקודת האפס הנאל כאשר התוכנית לעיל מופעלת ב- Eclipse IDE.

4. java.lang.NullPointerException כאשר מסור Null

public class Temp {

	public static void main(String[] args) {

		throw null;
	}

}

למטה נמצא דיווח על מצור החריגה של התוכנית לעיל, המציין NullPointerException בשל צו השליפה של throw null;.

Exception in thread "main" java.lang.NullPointerException
	at Temp.main(Temp.java:5)

5. NullPointerException כאשר מנסים לקבל את האורך של מערך נאל

public class Temp {

	public static void main(String[] args) {

		int[] data = null;
		
		int len = data.length;
	}

}
Exception in thread "main" java.lang.NullPointerException
	at Temp.main(Temp.java:7)

6. NullPointerException כאשר ניתן לגישה לערך האינדקס של מערך נאל

public class Temp {

	public static void main(String[] args) {

		int[] data = null;
		
		int len = data[2];
	}

}
Exception in thread "main" java.lang.NullPointerException
	at Temp.main(Temp.java:7)

7. NullPointerException בזמן שסונכרן על אובייקט שהוא null

public class Temp {

	public static String mutex = null;
	
	public static void main(String[] args) {

		synchronized(mutex) {
			System.out.println("synchronized block");
		}		
	}
}

הקוד synchronized(mutex) יזרוק NullPointerException מכיוון שהאובייקט "mutex" הוא null.

8. HTTP Status 500 java.lang.NullPointerException

לפעמים אנו מקבלים תגובת דף שגיאה מיישום אינטרנט ב-Java עם הודעת שגיאה "HTTP Status 500 – Internal Server Error" ושורש הבעיה הוא java.lang.NullPointerException.

בשביל זה, ערכתי את הפרויקט Spring MVC Example ושיניתי את שיטת ה-HomeController כך:

@RequestMapping(value = "/user", method = RequestMethod.POST)
	public String user(@Validated User user, Model model) {
		System.out.println("User Page Requested");
		System.out.println("User Name: "+user.getUserName().toLowerCase());
		System.out.println("User ID: "+user.getUserId().toLowerCase());
		model.addAttribute("userName", user.getUserName());
		return "user";
	}

התמונה למטה מראה את הודעת השגיאה שנזרקה על ידי תגובת היישום אינטרנט.

כאן ההדפסת תיק חריגה:

HTTP Status 500Internal Server Error

Type Exception Report

Message Request processing failed; nested exception is java.lang.NullPointerException

Description The server encountered an unexpected condition that prevented it from fulfilling the request.

Exception

org.springframework.web.util.NestedServletException: Request processing failed; nested exception is java.lang.NullPointerException
	org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:982)
	org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:872)
	javax.servlet.http.HttpServlet.service(HttpServlet.java:661)
	org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846)
	javax.servlet.http.HttpServlet.service(HttpServlet.java:742)
	org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
Root Cause

java.lang.NullPointerException
	com.journaldev.spring.controller.HomeController.user(HomeController.java:38)
	sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	java.lang.reflect.Method.invoke(Method.java:498)

השורש לבעיה הוא NullPointerException בהצהרת המשתמש user.getUserId().toLowerCase() מכיוון ש-user.getUserId() מחזיר null.

איך לזהות java.lang.NullPointerException

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

איך לתקן את NullPointerException

java.lang.NullPointerException הוא חריג בלתי מבודד, לכן אין צורך לתפוס אותו. חריגות נקודה הריקה ניתן למנוע באמצעות בדיקות נקודה ריקה וטכניקות קידוד מונעות. ראו בדוגמאות הקוד למטה איך למנוע את java.lang.NullPointerException.

if(mutex ==null) mutex =""; //קידוד מונע
		
synchronized(mutex) {
	System.out.println("synchronized block");
}
//שימוש בבדיקות נקודה ריקה
if(user!=null && user.getUserName() !=null) {
System.out.println("User Name: "+user.getUserName().toLowerCase());
}
if(user!=null && user.getUserName() !=null) {
	System.out.println("User ID: "+user.getUserId().toLowerCase());
}

שיטות הקידוד הטובות ביותר למניעת NullPointerException

1. נחשוב על הפונקציה למטה ונחפש תרחיש הגורם לחריגה במצב של הפניה לא-קיימת (

public void foo(String s) {
    if(s.equals("Test")) {
	System.out.println("test");
    }
}

NullPointerException יכולה להתרחש אם הארגומנט נמסר כ-

public void foo(String s) {
	if ("Test".equals(s)) {
		System.out.println("test");
	}
}

null. ניתן לכתוב את אותה השיטה כך שלא תתרחש החריגה באמצעות:

public int getArrayLength(Object[] array) {
	
	if(array == null) throw new IllegalArgumentException("array is null");
	
	return array.length;
}

IllegalArgumentException ניתן גם להוסיף בדיקה של אי-קיום עבור הארגומנט ולזרוק אם נדרש.

String msg = (str == null) ? "" : str.substring(0, str.length()-1);

2. ניתן להשתמש באופרטור טרנרי כפי שמוצג בקוד הדוגמא למטה.

public void println(Object x) {
        String s = String.valueOf(x);
        synchronized (this) {
            print(s);
            newLine();
        }
    }

3. השתמש ב למעשה, ניתן להוסיף בדיקה של אי-קיום עבור הארגומנט ולזרוקIllegalArgumentException ניתן גם להוסיף בדיקת

Object mutex = null;

אם נדרש.4. השתמש ב־String.valueOf() במקום ב־toString() כמו שמוצג בדוגמא למטה.
System.out.println(String.valueOf(mutex));

5. כתוב שיטות שמחזירות אובייקטים ריקים במקום null בכל דרך אפשרית, לדוג, רשימה ריקה, מחרוזת ריקה וכו'
System.out.println(mutex.toString());

6. ישנם שיטות מוגדרות במחלקות אוסף למניעת

NullPointerException, עליך להשתמש בהן, לדוג, contains(), containsKey(), ו־containsValue().

הפנייה: מסמך ה-API

Source:
https://www.digitalocean.com/community/tutorials/java-lang-nullpointerexception