بالنسبة لمبرمجي جافا، فإن القيمة null تمثل صداعًا. غالبًا ما تتعرض للمضايقات بسبب استثناءات Null Pointer (NPE). حتى مخترع جافا اعترف بأن هذا كان خطأً فادحًا من جانبه. لماذا تظل Java فارغة؟ لقد كانت القيمة الفارغة موجودة منذ فترة، وأعتقد أن مخترعي Java كانوا يعلمون أن القيمة الفارغة تسبب مشاكل أكثر من المشاكل التي حلتها، ولكن القيمة الفارغة لا تزال موجودة في Java.
أنا مندهش أكثر فأكثر، لأن مبدأ تصميم Java هو تبسيط الأشياء، وهذا هو السبب في عدم إضاعة الوقت في المؤشرات، والتحميل الزائد للمشغل، وتنفيذ الميراث المتعدد هو عكس ذلك تمامًا. حسنًا، لا أعرف حقًا إجابة هذا السؤال، ما أعرفه هو أنه بغض النظر عن مدى انتقاد مطوري Java ومجتمع المصادر المفتوحة للقيمة null، علينا التعايش مع القيمة null. بدلاً من الندم على وجود القيمة الفارغة، يجب أن نتعرف على القيمة الفارغة بشكل أفضل ونتأكد من استخدام القيمة الفارغة بشكل صحيح.
لماذا تحتاج إلى تعلم null في Java؟ لأنه إذا لم تنتبه إلى القيمة null، فإن Java ستجعلك تعاني من NullPointerException، وستتعلم درسًا مؤلمًا. البرمجة النشطة هي فن سيقدره فريقك وعملائك ومستخدميك أكثر. في تجربتي، أحد الأسباب الرئيسية لاستثناءات المؤشر الفارغ هو عدم كفاية المعرفة بالقيمة الفارغة في Java. الكثير منكم على دراية بالقيمة null، ولكن بالنسبة لأولئك الذين ليسوا كذلك، يمكنك تعلم شيء قديم وجديد عن null. دعونا نعيد تعلم بعض المعرفة المهمة حول القيمة الفارغة في Java.
ما هو Null في جافا؟
كما قلت، لاغية هو مفهوم مهم جدا في جافا. الهدف الأصلي من null هو تمثيل شيء مفقود، مثل مستخدم مفقود أو مورد أو أشياء أخرى. ومع ذلك، بعد مرور عام، تسبب استثناء المؤشر الفارغ المزعج في الكثير من المضايقات لمبرمجي Java. في هذه المادة، سوف نتعلم التفاصيل الأساسية للكلمة الأساسية الفارغة في Java، ونستكشف بعض التقنيات لتقليل عمليات التحقق من القيمة الفارغة وكيفية تجنب استثناءات المؤشر الفارغ السيئة.
1) أولاً وقبل كل شيء، null هي كلمة رئيسية في Java، مثل public وstatic وfinal. إنه حساس لحالة الأحرف، ولا يمكنك كتابة null كـ Null أو NULL، ولن يتعرف عليها المترجم ويبلغ عن خطأ.
انسخ رمز الكود كما يلي:
Object obj = NULL;
Object obj1 = null //Ok
قد يواجه المبرمجون الذين يستخدمون لغات أخرى هذه المشكلة، لكن استخدام IDE الآن جعل هذه المشكلة تافهة. الآن، عند كتابة التعليمات البرمجية، يمكن لـ IDEs مثل Eclipse وNetbeans تصحيح هذا الخطأ. ولكن باستخدام أدوات أخرى مثل المفكرة، Vim، و Emacs، ستضيع هذه المشكلة وقتك الثمين.
2) تمامًا كما أن كل نوع بدائي له قيمة افتراضية، على سبيل المثال، القيمة الافتراضية لـ int هي 0، والقيمة الافتراضية لـ boolean هي false، والقيمة null هي القيمة الافتراضية لأي نوع مرجعي، بالمعنى الدقيق للكلمة، هي القيمة الافتراضية لجميع أنواع الكائنات. تمامًا مثلما تقوم بإنشاء متغير منطقي له false كقيمة افتراضية، فإن أي متغير مرجعي في Java له قيمة فارغة كقيمة افتراضية. ينطبق هذا على جميع المتغيرات، مثل متغيرات الأعضاء، والمتغيرات المحلية، ومتغيرات الحالة، والمتغيرات الثابتة (ولكن عند استخدام متغير محلي غير مهيأ، سيحذرك المترجم). لتوضيح هذه الحقيقة، يمكنك ملاحظة هذا المتغير المرجعي عن طريق إنشاء متغير ثم طباعة قيمته، كما هو موضح في الكود التالي:
انسخ رمز الكود كما يلي:
كائن ثابت خاص myObj؛
الفراغ الثابت العام الرئيسي(String args[]){
System.out.println("ما هي قيمة myObjc:" + myObj);
}
ما هي قيمة myObjc: null
وينطبق هذا على كل من الكائنات الثابتة وغير الثابتة. كما ترون هنا، قمت بتعريف myObj كمرجع ثابت حتى أتمكن من استخدامه مباشرة في الطريقة الرئيسية. لاحظ أن الطريقة الرئيسية هي طريقة ثابتة ولا يمكنها استخدام متغيرات غير ثابتة.
3) نريد توضيح بعض حالات سوء الفهم. Null ليس كائنًا ولا نوعًا، بل هو مجرد قيمة خاصة. يمكنك تعيينه لأي نوع مرجعي.
انسخ رمز الكود كما يلي:
String str = null; // يمكن تعيين null إلى String
Integer itr = null; // يمكنك تعيين قيمة فارغة لعدد صحيح أيضًا
Double dbl = null; // يمكن أيضًا تعيين null لـ Double
String myStr = (String) null;
Integer myItr = (Integer) null; // يمكن أيضًا كتابته إلى Integer
Double myDbl = (Double) null;
يمكنك أن ترى أن تحويل القيمة الخالية إلى أي نوع مرجعي أمر ممكن في وقت الترجمة والتشغيل، ولن يؤدي إلى استثناء مؤشر فارغ في وقت التشغيل.
4) يمكن تعيين Null للمتغيرات المرجعية، لكن لا يمكنك تعيين Null لمتغيرات النوع الأساسية، مثل int وdouble وfloat وboolean. إذا قمت بذلك، فإن المترجم سوف يلقي خطأ مثل هذا:
انسخ رمز الكود كما يلي:
int i = null; // عدم تطابق النوع: لا يمكن التحويل من null إلى int
Short s = null; // عدم تطابق النوع: لا يمكن التحويل من null إلى short
byte b = null: // عدم تطابق النوع: لا يمكن التحويل من null إلى بايت
double d = null; // عدم تطابق النوع: لا يمكن التحويل من null إلى double
عدد صحيح itr = null;
int j = itr; // هذا جيد أيضًا، لكن NullPointerException في وقت التشغيل
كما ترون، عندما تقوم بتعيين قيمة خالية مباشرة إلى نوع بدائي، يحدث خطأ في الترجمة. ولكن إذا قمت بتعيين كائن فئة مجمّع فارغًا، ثم قمت بتعيين كائن للأنواع الأساسية المعنية، فلن يقوم المترجم بالإبلاغ عنه، ولكنك ستواجه استثناءً لمؤشر فارغ في وقت التشغيل. يحدث هذا بسبب إلغاء العلبة التلقائي في Java، وهو ما سنراه في النقطة التالية.
5) أي فئة مجمعة تحتوي على قيمة فارغة ستؤدي إلى استثناء مؤشر فارغ عندما تقوم Java بإلغاء تحديد المربع وإنشاء أنواع البيانات الأساسية. يخطئ بعض المبرمجين في الاعتقاد بأن التشغيل التلقائي سيحول القيمة الخالية إلى القيمة الافتراضية للنوع الأساسي المعني، مثل 0 لـ int وfalse للنوع المنطقي، ولكن هذا غير صحيح، كما هو موضح أدناه:
انسخ رمز الكود كما يلي:
عدد صحيح iAmNull = null;
int i = iAmNull; // تذكر - لا يوجد خطأ في الترجمة
ولكن عند تشغيل مقتطف التعليمات البرمجية أعلاه، سترى على وحدة التحكم أن مؤشر الترابط الرئيسي يطرح استثناءً لمؤشر فارغ. تحدث العديد من هذه الأخطاء عند استخدام قيم مفاتيح HashMap وInteger. سيظهر خطأ عند تشغيل الكود التالي.
انسخ رمز الكود كما يلي:
import java.util.HashMap;
import java.util.Map;
/**
* مثال على Autoboxing وNullPointerExcpetion
*
* @ المؤلف ويندوز 8
*/
اختبار الطبقة العامة {
public static void main(String args[]) يلقي InterruptedException {
Map numberAndCount = new HashMap<>();
أرقام int[] = {3, 5, 7,9, 11, 13, 17, 19, 2, 3, 5, 33, 12, 5};
ل(int i: أرقام){
int count = numberAndCount.get(i);
numberAndCount.put(i, count++); // NullPointerException هنا
}
}
}
الإخراج:
انسخ رمز الكود كما يلي:
استثناء في مؤشر الترابط "الرئيسي" java.lang.NullPointerException
في Test.main (Test.java:25)
يبدو هذا الرمز بسيطًا جدًا وخاليًا من الأخطاء. كل ما عليك فعله هو معرفة عدد المرات التي يظهر فيها رقم في مصفوفة، وهو الأسلوب النموذجي للعثور على التكرارات في مصفوفات Java. يحصل المطور أولاً على القيمة السابقة، ثم يضيف واحدة، وأخيرًا يعيد القيمة إلى الخريطة. قد يعتقد المبرمج أنه عند استدعاء طريقة الوضع، ستتعامل الملاكمة التلقائية مع الملاكمة في Interger، لكنه ينسى أنه عندما لا يحتوي الرقم على قيمة عددية، فإن طريقة get() الخاصة بـ HashMap سترجع فارغة وليس 0 لأن القيمة الافتراضية لعدد صحيح هي فارغة وليست 0. سوف يقوم Autoboxing بإرجاع NullPointerException عند تمرير قيمة فارغة إلى متغير int. تخيل لو كان هذا الرمز موجودًا داخل if Nest ولم يكن قيد التشغيل في بيئة ضمان الجودة، ولكن بمجرد وضعه في بيئة إنتاج، BOOM :-)
6) إذا تم استخدام متغير نوع مرجعي بقيمة فارغة، فسيعود مثيل العملية خطأ:
انسخ رمز الكود كما يلي:
عدد صحيح iAmNull = null;
إذا (مثيل iAmNull لعدد صحيح) {
System.out.println("iAmNull هو مثيل لعدد صحيح");
}آخر{
System.out.println("iAmNull ليس مثيلًا لعدد صحيح");
}
الإخراج:
انسخ رمز الكود كما يلي:
أنا
AmNull ليس مثالًا لـ Integer
هذه ميزة مهمة جدًا لمثيل العملية، مما يجعلها مفيدة لعمليات التحقق من النوع.
7) ربما تعلم أنه لا يمكنك استدعاء طريقة غير ثابتة لاستخدام متغير نوع مرجعي بقيمة فارغة. سيؤدي ذلك إلى استثناء مؤشر فارغ، لكنك قد لا تعلم أنه يمكنك استخدام أساليب ثابتة لاستخدام متغير نوع مرجعي بقيمة خالية. نظرًا لأن الأساليب الثابتة تستخدم الربط الثابت، فلن يتم طرح استثناءات المؤشر الفارغ. هنا مثال:
انسخ رمز الكود كما يلي:
اختبار الطبقة العامة {
الفراغ الثابت العام الرئيسي(String args[]){
اختبار myObject = null;
myObject.iAmStaticMethod();
myObject.iAmNonStaticMethod();
}
الفراغ الثابت الخاص iAmStaticMethod(){
System.out.println("أنا طريقة ثابتة، ويمكن استدعاؤها بواسطة مرجع فارغ");
}
الفراغ الخاص iAmNonStaticMethod(){
System.out.println("أنا غير ثابت، لا أتصل بي بالقيمة الخالية");
}
الإخراج:
انسخ رمز الكود كما يلي:
أنا طريقة ثابتة، ويمكن استدعاؤها بواسطة مرجع فارغ
استثناء في مؤشر الترابط "الرئيسي" java.lang.NullPointerException
في Testing.main(Testing.java:11)
8) يمكنك تمرير قيمة فارغة إلى الطريقة، ويمكن للطريقة تلقي أي نوع مرجع، على سبيل المثال، يمكن للطباعة الفارغة العامة (Object obj) استدعاء الطباعة (فارغة) مثل هذا. هذا أمر مقبول من منظور التجميع، لكن النتيجة تعتمد كليًا على الطريقة. لا تقوم الأساليب الخالية الآمنة، مثل طريقة الطباعة في هذا المثال، بطرح NullPointerException وتخرج ببساطة بأمان. إذا كان منطق العمل يسمح بذلك، فمن المستحسن استخدام أساليب خالية من الأمان.
9) يمكنك استخدام عمليات == أو != لمقارنة القيم الخالية، لكن لا يمكنك استخدام خوارزميات أو عمليات منطقية أخرى، مثل أقل من أو أكبر من. على عكس SQL، null==null سيرجع صحيحًا في Java، كما هو موضح أدناه:
انسخ رمز الكود كما يلي:
اختبار الطبقة العامة {
public static void main(String args[]) throws InterruptedException {
سلسلة اي بي سي = فارغة؛
سلسلة cde = فارغة؛
إذا (اي بي سي == كدي) {
System.out.println("null == null صحيح في Java");
}
إذا (فارغة!= فارغة){
System.out.println("null != null false in Java");
}
// التحقق من القيمة الفارغة الكلاسيكية
إذا (اي بي سي==فارغة){
// افعل شيئا
}
// ليس جيدًا، خطأ في وقت الترجمة
إذا (اي بي سي > خالية) {
}
}
}
الإخراج:
انسخ رمز الكود كما يلي:
null == null صحيح في Java
هذا كل شيء عن null في Java. مع بعض الخبرة في برمجة Java واستخدام حيل بسيطة لتجنب استثناءات المؤشر الفارغ، يمكنك جعل التعليمات البرمجية الخاصة بك خالية من الأخطاء. نظرًا لأن القيمة null غالبًا ما تستخدم كقيمة فارغة أو غير مهيأة، فهي مصدر للارتباك. بالنسبة للطرق، من المهم أيضًا تسجيل كيفية تصرف الطريقة عند استخدام القيمة null كمعلمة. بشكل عام، تذكر أن القيمة الخالية هي القيمة الافتراضية لأي متغير نوع مرجعي، ولا يمكنك استخدام المراجع الخالية لاستدعاء أي أساليب مثيل أو متغيرات مثيل في Java.