Java NullPointerException – الكشف عن الخطأ وإصلاحه وأفضل الممارسات

java.lang.NullPointerException هو واحد من أشهر الاستثناءات في برمجة جافا. يجب على أي شخص يعمل في جافا أن يكون قد رأى هذا المشكلة تظهر فجأة في البرنامج المستقل في جافا وكذلك في تطبيق الويب جافا.

java.lang.NullPointerException

NullPointerException هو استثناء تشغيلي، لذا لا يلزم أن نلتقطه في البرنامج. يتم رفع NullPointerException في التطبيق عندما نحاول القيام بعملية ما على null حيث يتطلب وجود كائن. بعض الأسباب الشائعة لحدوث NullPointerException في برامج جافا هي:

  1. استدعاء طريقة على مثيل كائن ولكن في وقت التشغيل يكون الكائن هو null.
  2. الوصول إلى متغيرات مثيل كائن تكون قيمته null في وقت التشغيل.
  3. رمي القيمة null في البرنامج
  4. الوصول إلى الفهرس أو تعديل قيمة الفهرس لمصفوفة تكون قيمتها null
  5. فحص طول مصفوفة تكون قيمتها null في وقت التشغيل.

أمثلة على Java NullPointerException

دعونا نلقي نظرة على بعض أمثلة على NullPointerException في برامج جافا.

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” هو قيمة فارغة هنا.

2. NullPointerException في جافا عند الوصول/تعديل حقل لكائن فارغ

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

}

البرنامج أعلاه يطرح سلسلة تعقب NullPointerException التالية.

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

يتم طرح NullPointerException في البيان int i = t.x; لأن “t” هو قيمة فارغة هنا.

3. NullPointerException في جافا عند تمرير قيمة فارغة كوسيط في الطريقة

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 عندما يتم إلقاء القيمة الفارغة

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 عند المزامنة على كائن فارغ

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” هو فارغ.

8. حالة HTTP 500 java.lang.NullPointerException

في بعض الأحيان نحصل على صفحة خطأ من تطبيق ويب جافا مع رسالة خطأ تظهر كـ “حالة HTTP 500 – خطأ في الخادم الداخلي” وسبب الجذر هو 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() يعيد قيمة فارغة.

كيفية الكشف عن 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 إذا تم تمرير الوسيط كقيمة فارغة. يمكن كتابة نفس الدالة كما يلي لتجنب استثناء NullPointerException.

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

2. يمكننا أيضًا إضافة فحص للقيمة الفارغة للوسيط ورمي IllegalArgumentException إذا لزم الأمر.

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

3. يمكن استخدام المشغل الثلاثي كما هو موضح في مثال الكود أدناه.

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

4. استخدم String.valueOf() بدلاً من الطريقة toString(). على سبيل المثال، تحقق من كود طريقة PrintStream println() المعرفة كما هو موضح أدناه.

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

المقطع البرمجي أدناه يظهر مثالًا على استخدام طريقة valueOf() بدلاً من toString().

Object mutex = null;

// يطبع قيمة فارغة
System.out.println(String.valueOf(mutex));

// سيُرمي java.lang.NullPointerException
System.out.println(mutex.toString());

5. اكتب الطرق التي تعيد كائنات فارغة بدلاً من القيمة الفارغة في كل مكان إذا كان ذلك ممكنًا، على سبيل المثال، قائمة فارغة، سلسلة فارغة، وما إلى ذلك.

6. هناك بعض الطرق المعرفة في فصول الأدوات لتجنب استثناء NullPointerException، يجب استخدامها. على سبيل المثال contains()، containsKey()، و containsValue().

المرجع: وثائق الواجهة البرمجية للتطبيقات

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