التعرف على لغة الجافا

البرمجة الكائنية التوجه

ما هو الكائن؟

الكائنات (Objects) هي المفتاح لفهم البرمجة الكائنية التوجه. ألق نظرة حولك وستجد الكثير من الأمثلة الملموسة لكائنات (Objects) : كلبك، مكتبك، تلفازك، دراجتك...

الكائنات الموجودة في أرض الواقع تتشارك في خاصيتين : كلها لديها حالة و سلوك. الكلاب لها حالة (إسم، لون، ذرية، جائع) وسلوك (نباح، جلب، تحريك الذيل). الدراجة أيضا لديها حالة (الدولاب الحالي، السرعة الحالية) وسلوك (تغيير الدولاب، الفرملة). التعرف على حالة وسلوكات الكائنات الموجودة في أرض الواقع هي طريقة جيدة لفهم مبادئ البرمجة الكائنية التوجه.

خذ الآن دقيقة لملاحظة الكائنات التي حولك. لكل كائن تراه، اسأل نفسك سؤالين: "ما هي الحالات الممكنة التي يمكن أن يكون عليها؟" و "ما هي السلوكات التي ممكن أن يقوم بها؟". تأكد من كتابة ملاحظاتك. وأنت تقوم بذلك، ستلاحظ أن الكائنات المحيطة بنا تتفاوت من حيث التعقيد؛ فمصباحك المكتبي يمكن أن يتوفر على حالتين فقط (مفتوح، مقفل) وسلوكين (فتح، إقفال)، بينما جهاز الراديو يمكن أن يتوفر على حالات إضافية (مفتوح، مقفل، درجة الصوت، المحطة الحالية) وسلوكات (فتح، إقفال، زيادة الصوت، تخفيض الصوت، البحث عن محطة، تحديد المحطة). يمكن أن تلاحظ أيضا أن بعض الكائنات يمكن أن تحتوي على كائنات أخرى.
كل هذه الملاحظات تنطبق على البرمجة الكائنية التوجه.

تعتبر الكائنات البرمجية مشابهة للكائنات الحقيقية من حيث المبدأ: كلاهما يتجلى في حالة وسلوكات. الكائن يخزن حالته في حقول (fields) (تسمى متغيرات في لغات برمجية أخرى) ويعرض تصرفاته من خلال طرق (methods) (تسمى دوال (functions) في لغات برمجية أخرى). الطرق تعمل على الحالة الخاصة بالكائنات، وتشكل الآلية الأساسية للتواصل بين الكائنات. إخفاء الحالة واشتراط أن يكون كل تفاعل مع الكائن يمر عبر الطرق الخاصة به، يعرف ب تغليف البيانات (Data encapsulation)، وهو مبدأ أساسي في البرمجة الكائنية التوجه.

لنأخذ الدراجة كمثال:

عندما نحدد الحالة (السرعة الحالية، الإيقاع الحالي للدواسة، الدولاب الحالي) ونوفر الطرف التي تستطيع تغيير هذه الحالة، فإن الكائن يبقى متحكما في الطريقة المتاحة لاستعماله من أي عنصر خارجي. مثلا، إذا كانت الدراجة تتوفر فقط على 6 دواليب، فإن الطريقة التي تقوم بتغيير الدولاب يمكنها أن ترفض أي قيمة أصغر من 1 وأكبر من 6.

تجميع الكود في كائنات برمجية يوفر عدة مزايا، من بينها:

  1. ال Modularity : الكود المصدري لكائن ما يمكن أن تتم كتابته وصيانته بطريقة مستفلة عن الكائنات اﻷخرى. بعد إنشائه، يمكن تمرير الكائن بسهولة في أنحاء النظام.
  2. إخفاء المعلومات: بفضل التعامل مع الطرق الخاصة بالكائن فقط، يبقى التطبيق (implementation) مخفيا عن العالم الخارجي.
  3. إعادة استعمال الكود: إذا كان كائن ما موجود مسبقا (كتبه مبرمج آخر مثلا)، يمكنك استعمال هذا الكائن داخل برنامجك. هذا يسمح للمختصين بعمل implement/test/debug لكائنات معقدة وموجهة لغرض محدد، مما يجعلك تشتغل بها بكل ثقة داخل الكود.
  4. سهولة ال Pluggability and debugging: إذا اتضح أن كائنا يخلق مشاكل، يمكن إزالته وربط كائن آخر ليشغل مكانه. هذا الأمر مشابه لإصلاح المشاكل الميكانيكية في الواقع. إذا تعطل الصاعق، فإنك تغيره لوحده، ولا تغير كل المحرك.

ما هي الفئة؟

في أرض الواقع، ستجد الكثير من الكائنات من نفس النوع. يوجد هناك ربما المئات من الدراجات، كلها من نفس المصنع ونفس النموذج. كل دراجة تم بناؤها انطلاقا من نفس المخطط وبالتالي تحتوي على نفس المكونات. في إطار البرمجة الكائنية التوجه، نقول أن الدراجة عبارة عن instance لفئة الكائنات المسماة بالدراجات. الفئة (Class) هي المخطط الذي نبني انطلاقا منه الكائنات.

الفئة التالية Bicycle هي تطبيق ممكن لدراجة.

class Bicycle {

	int cadence = 0;
	int speed = 0;
	int gear = 1;

	void changeCadence(int newValue) {
		cadence = newValue;
	}

	void changeGear(int newValue) {
		gear = newValue;
	}

	void speedUp(int increment) {
		speed = speed + increment;   
	}
‏
	void applyBrakes(int decrement) {
		speed = speed - decrement;
	}

	void printStates() {
		System.out.println("cadence:"+cadence+" speed:"+speed+" gear:"+gear);
	}
}


طريقة الكتابة بالجافا ستبدو جديدة عليك، لكن تصميم هذه الفئة يعتمد على ما تكلمنا عنه سابقا بخصوص الدراجة ككائن. الحقول cadence ،speed و gear تمثل حالة الكائن ، والطرق changeCadence، changeGear ،speedUp ... تحدد طريقة تفاعله مع العالم الخارجي.

ربما لاحظت أن الفئة Bicycle لا تحتوي على الطريقة main، وذلك لأنها ليست برنامجا كاملا؛ بل فقط مخطط للدراجات التي يمكن أن تستعمل في برنامج ما. مسؤولية إنشاء واستعمال كائنات Bicycle جديدة تقع على عاتق فئات أخرى في برنامجك.

هذا مثال ينشئ كائنين Bicycle وينادي على الطرق الخاصة بها:

class BicycleDemo {
	public static void main(String[] args) {

		// إنشاء كائنين Bicycle مختلفين
		Bicycle bike1 = new Bicycle();
		Bicycle bike2 = new Bicycle();

		// المناداة على الطرق الخاصة بالكائنات المنشأة
		bike1.changeCadence(50);
		bike1.speedUp(10);
		bike1.changeGear(2);
		bike1.printStates();

		bike2.changeCadence(50);
		bike2.speedUp(10);
		bike2.changeGear(2);
		bike2.changeCadence(40);
		bike2.speedUp(10);
		bike2.changeGear(3);
		bike2.printStates();
	}
}


مخرجات هذا المثال تعطي القيم النهائية الخاصة ب: إيقاع الدواسة، السرعة، والدولاب بالنسبة للدراجتين:

cadence:50 speed:10 gear:2
cadence:40 speed:20 gear:3



ما هي الوراثة؟

نجد أحياتا أنواعا مختلفة من الكائنات لديها بعض القواسم المشتركة. مثلا، الدراجات الجبلية، دراجات الطريق، والدراجات الترادفية، كلها تتشارك خصائص الدراجات (السرعة الحالية، الإيقاع الحالي للدواسة، الدولاب الحالي)، بالإضافة إلى ذلك، فكل نوع لديه خصائص إضافية تجعله مختلفا عن الأنواع الأخرى: الدراجات الترادفية لديها مقعدين ومقودين؛ دراجات الطريق لديها مقود متجه للأسفل؛ بعض الدراجات الجبلية تمتلك سلسلة بحلقة إضافية، ما يعطيها a lower gear ratio.

البرمجة الكائنية التوجه تتيح للفئات وراثة الحالة والسلوكات المشتركة من فئات أخرى. في المثال التالي، الفئة Bicycle ستصبح الفئة الأم ل MountainBike، RoadBike، و TandemBike. في لغة البرمجة جافا، يسمح لكل فئة بامتلاك فئة أم واحدة فقط، وكل فئة أم يسمح لها بعدد لا منتهي من الفئات الفرعية:

طريقة إنشاء فئة فرعية بسيطة. عند تعريف الفئة، استعمل الكلمة المفتاحية extends، متبوعة بإسم الفئة الأم:

class MountainBike extends Bicycle {

     // نعرف هنا الحقول والطرق الجديدة الخاصة بالدراجات الجبلية 

}


هذا يعطي للفئة MountainBike نفس الطرق والخصائص الموجودة في Bicycle، مما يسمح بالتركيز على الميزات التي تنفرد بها الدراجات الجبلية، هذا يجعل شيفرة الفئات الفرعية سهلة القراءة.

ما هي الواجهة؟

كما رأينا سابقا، فالكائنات تتفاعل مع العالم الخارجي بواسطة الطرق (methods) التي تعرضها. إذن فالطرق تمثل واجهة (interface) الكائن مع العالم الخارجي؛ مثلا، الأزرار الموجودة في مقدمة التلفاز هي الواجهة بينك وبين الأسلاك الكهربائية الموجودة في الجهة الأخرى من الغطاء البلاستيكي. عندما تريد تشغيل أو إغلاق الجهاز، فإنك تضغط على زر التشغيل.

في أغلب الأحيان، تكون الواجهة عبارة عن مجموعة من الطرق المرتبطة، ذات جسم خالي. السلوك الخاص بالدراجة، إذا ما تم تعريفه على شكل واجهة، يمكن أن يكون بالشكل التالي:

interface Bicycle {

       void changeCadence(int newValue);

       void changeGear(int newValue);

       void speedUp(int increment);

       void applyBrakes(int decrement);
}


لتطبيق هذه الواجهة، يجب أن يكون إسم الفئة مغايرا (إضافة إسم الماركة مثلا، لنقل ACMEBicycle)، ويجب استعمال الكلمة المفتاحية implements عند تعريف الفئة:

class ACMEBicycle implements Bicycle {

   // نفس الشيفرة التي استعملنا في مثال الدراجة في الدروس السابقة

}


تطبيق واجهة يسمح للفئة بأن تصبح أكثر التزاما بالسلوك الذي تعد بتوفيره. الواجهات تمثل عقدا بين الفئة والعالم الخارجي، وهذا العقد يتم تأكيده عند عملية البناء من طرف المترجم (compiler). إذا كانت فئتك تسعى لتطبيق واجهة ما، فيجب أن تحتوي على كل الطرق المعرفة داخل الواجهة، حتى تتم عملية الترجمة (compilation) بدون أخطاء.

ملحوظة: لكي تتم ترجمة الفئة ACMEBicycle بنجاح، يجب إضافة الكلمة المفتاحية public قي بداية كل الطرق الخاصة بالواجهة والتي يتم تطبيقها. ستعلم أسباب ذلك لاحقا عندما نتحدث عن الفئات والكائنات و الواجهات والوراثة.



ما هي الحزمة؟

الحزمة (package) عبارة عن مجال إسم (namespace)، وهدفها تنظيم مجموعة من الفئات والواجهات ذات العلاقة. يمكن مقارنة فكرة الحزمة بمجلدات مختلفة موجودة في حاسوبك، مجلد خاص بالملفات HTML، مجلد آخر خاص بالصور، وآخر يحتوي على البرامج.
لأن البرامج المكتوبة بلغة الجافا يمكن أن تحتوي على المئات وربما الآلاف من الفئات، فإن المنطق يحتم علينا إبقاء كل شيء منظما، بوضع الفئات والواجهات ذات العلاقة داخل حزم.

منصة الجافا تقدم مكتبة فئات عملاقة (مجموعة من الحزم) مناسبة للإستعمال داخل برنامجك. هذه المكتبة تعرف ب "واجهة برمجة التطبيقات" (Application Programming Interface) أو "API" باختصار. هذه الحزم تمثل المهام الأكثر ارتباطا بالبرمجة الشاملة (general-purpose programming). على سبيل المثال، كائن String يحتوي على حالة وسلوك سلسلات الحروف؛ كائن File يتيح للمبرمج إنشاء، مسح، تفتيش، مقارنة، أو تعديل ملف ما بكل سهولة؛ كائن Socket يسمح بإنشاء واستعمال ال network sockets؛ كائنات عديدة خاصة بواجهة المستخدم الرسومية تتحكم بالأزرار وخانات التأشير (checkbox) وبكل ما يتعلق بواجهات المستخدم الرسومية.
هناك الآلاف من الفئات التي يمكننا أن نختار من بينها، هذا يسمح لك، المبرمج، بالتركيز على طريقة تصميم برنامجك، بدل التركيز على البنية التحتية اللازمة لتشغيله.

ال Java Platform API Specification تحتوي على لائحة بجميع الحزم، الواجهات، الفئات، الحقول والطرق التي توفرها منصة الجافا 6، النسخة المعيارية (Standard Edition).
إفتح الصفحة في المتصفح، وأضفها إلى المفضلة. كمبرمج، سيكون ذلك الموقع بالنسبة لك بمثابة أهم مرجع لتوثيق لغة الجافا.

أسئلة وتمارين

أسئلة

  1. الكائنات في العالم الحقيقي تتوفر على ــ و ــ .
  2. يتم تخزين حالة كائن برمجي في ــ .
  3. يتم عرض سلوك كائن برمجي عبر ــ .
  4. إخفاء البيانات الداخلية عن العالم الخارجي، والوصول إليها ققط باستعمال الطرق العلنية يعرف ب ــ .
  5. مخطط كائن برمجي يسمى ــ .
  6. السلوكات المشتركة يمكن تعريفها في ــ ويمكن وراثتها من طرف ــ باستعمال الكلمة المفتاحية ــ .
  7. مجموعة من الطرق بدون جسم تسمى ــ .
  8. مجال الإسم (namespace) التي تنظم الفئات والواجهات حسب وظيفتها يسمى ــ .
  9. كلمة API تعني ــ .

تمارين

  1. أكتب فئة جديدة لكل واحد من كائنات العالم الحقيقي التي لاحظت في بداية هذا الدرس. إرجع إلى الفئة Bicycle إذا نسيت طريقة الكتابة.
  2. لكل واحدة من الفئات التي كتبت أعلاه، أنشئ واجهة تحدد سلوكها، ثم أجبر فئتك على تطبيقها. أهمل طريقة أو طريقتين وحاول القيام بعملية التجميع. ما هو الخطأ الناتج؟

أساسيات اللغة

المتغيرات

تعلمت في الدروس السابقة أن الكائن يخزن حالته في حقول.

int cadence = 0;
int speed = 0;
int gear = 1; 

تعَرَّفْت على ماهية الحقول في درس ما هو الكائن؟، لكنك في الغالب مازالت لديك بعض التساؤلات: ما هي قواعد ومعايير تسمية الحقول؟ بالإضافة إلى int، ماهي أنواع البيانات الأخرى؟ هل يجب تحديد قيمة الحقول عند إعلانها؟ هل يتم إسناد قيمة افتراضية للحقل إذا لم يتم تحديد قيمته؟ سنَتَحرَّى الأجوبة على هذه الأسئلة في هذا الدرس، لكن قبل ذلك، هناك بعض الأمور التقنية التي يجب تمييزها.
في لغة البرمجة جافا، يتم استعمال كلا المصطلحين "حقل" و "متغير"؛ هذا الأمر يسبب ارتباكا للمبرمجين الجدد، لأن المصطلحين، وفي كثير من الأحيان، يشيران لنفس الشيء.

لغة البرمجة جافا تُحدد أنواع المتغيرات التالية:

  • متغيرات الكائن Instance Variables (Non-Static Fields) تخزن الكائنات حالتها في حقول non-static، أي حقول مُعَرَّفة بدون الكلمة المفتاحية static. الحقول non-static تُعْرف أيضا ب instance variables لأن قيمتها تكون فريدة في كل instance من الفئة (بتعبير آخر، في كل كائن)؛ قيمة currentSpeed الخاصة بدراجة ما مُسْتَقلة تماما عن قيمة الcurrentSpeed الخاصة بدراجة أخرى.
  • متغيرات الفئة (Static Fields) متغير الفئة عبارة عن حقل مُعْلن ياستعمال المُحَوِّل static؛ هذا يعني للمجمع أنه توجد نسخة واحدة فقط من هذا المتغير، بِغَضِّ النظر عن عدد الكائنات المُنْشَأة. مثلا، الحقل الذي يحدد عدد الدواسات الموجودة في نوع معين من الدراجات يمكن أن يتم إعلانه باستعمال static، بما أن نفس عدد الدواسات ينطبق على جميع الكائنات. يمكن إنشاء مثل هذا الحقل باستعمال الشيفرة التالية
    static int numGears = 6;

    بالإضافة إلى ذلك، يمكن استعمال الكلمة المفتاحية final للإشارة إلى أن عدد الدواسات لن يتغير أبدا.

  • المتغيرات المحلية مثل ما تقوم الكائنات بتخزين حالتها في الحقول، فإن الطرق تُخَزِّن أحيانا حالتها المؤقتة في متغيرات محلية. طريقة إعلان المتغيرات المحلية مشابهة لطريقة إعلان الحقول، مثلا
    int count = 0;

    لا توجد كلمة مفتاحية تُحَدِّد أن متغيرا ما هو متغير محلي؛ تحديد ذلك يعتمد أساسا على المكان الذي تم فيه إعلان المتغير -- والذي يوجد بين المعقوفات التي تحدد بداية ونهاية الطريقة. وبالتالي، فإن المتغيرات المحلية تكون مرئية فقط داخل الطرق التي أُعْلنت داخلها؛ ولا يمكن الوصول إليها من أي مكان آخر في الفئة.

  • المُعْطيات سبق ورأيت أمثلة لمعطيات، في الفئة Bicycle والطريقة main في البرنامج "Hello World!". تذكر أن توقيع الطريقة main هو
    public static void main(String[] args)

    هنا، المتغير args يُمَثل مُعْطى هذه الطريقة. الأمر المهم الذي يجب تذكره، هو أن المعطيات تُصَنَّف ك"متغيرات" وليس ك"حقول". هذا ينطبق أيضا على كل البنيات التي تقبل المعطيات (مثل الconstructors ومعالجات الإستثناءات) التي سنراها فيما يلي من الدروس.

فيما يلي، سنتبع القواعد التالية عندما نتكلم عن المتغيرات والحقول: إذا كنا نتحدث عن "الحقول بصفة عامة"، ( مع استثناء المتغيرات المحلية والمُعْطيات )، سنقول فقط "حقول". إذا كان الكلام ينطبق على "كل ما سبق"، سنستخدم فقط كلمة "متغيرات". إذا كان السياق يجبرنا على التمييز، سنستخدم عبارات محددة ( متغير مخلي، حقل static ...). يمكن أيضا في بعض الأحيان استعمال المصطلح "عضو" member. الحقول، الطرق والفئات الداخلية الخاصة بفئة ما، كلها تعتبر أعضاء.

التسمية

كل لغة برمجية لها قواعدها الخاصة فيما يخص أنواع الأسماء المسموح بها، والجافا ليست استثناء. يمكن تلخيص قواعد تسمية المتغيرات في ما يلي:

  • أسماء المتغيرات حساسة لحالة الحرف case-sensitive. إسم المتغير يمكن أن يكون أي معرف مسموح به -- سلسلة غير محدودة الطول من حروف ال unicode والأعداد، تبتدأ بحرف، رمز الدولار $ أو حرف التسطير السفلي "_". لكن الإجماع هو على عدم استعمال ال $ و "_" في بداية الإسم، والإكتفاء بالحروف. بالإضافة إلى ذلك، فإن هناك إجماعا على عدم استعمال الرمز $ بالمرة. يمكن أن تجد أحيانا بعض الأسماء المُوَلَّدة تلقائيا تختوي على الرمز $، لكن لا يجدر بك استعماله عند تسمية متغيراتك. هناك أيضا إجماع آخر يخص رمز التسطير السفلي، فبالرغم من كون استعماله في بداية الأسماء مسموحا به تقنيا، إلا أنه لا ينصح بذلك. بالنسبة للفراغات فغير مسموح بها.
  • الرموز التي تلي لحرف الأول يمكن أن تكون حروفا، أرقاما، الرمز $ أو رمز التسطير السفلي "_". الإجماع (والمنطق) ينطبقان على هذه القاعدة أيضا. عند اختيار إسم لمتغيراتك، إستعمل كلمات كاملة بدل اختصارات غير مفهومة. هذا يجعل الشيفرة سهلة القراءة والفهم، وفي كثير من لأحيان، ذلك يجعل شيفرتك ذاتية التوثيق؛ فمثلا، الحقول المسماة cadence, speed و gear تُعْتبر أكثر تعبيرا من الأسماء المختصرة، مثل c, s و g. يجب أيضا أن تأخد في الحسبان أن لا يكون الإسم أحد الكلمات المفتاحية أو الكلمات المحجوزة
  • .

  • إذا كان الإسم الذي اخترت يحتوي على كلمة واحدة فقط، إجعل كل الحروف صغيرة lowercase letters.
    إذا كان يختوي على أكثر من كلمة، إجعل الخرف الأول من كل كلمة بعد الأولى كبيرا Capital، الأسماء gearRatio و currentGear تعتبر أمثلة لهذه القاعدة.
    إذا كان المتغير يختوي على قيمة تابثة، مثلا
    static final int NUM_GEARS = 6

    فإن القاعدة تتغير. حيث يجب جعل كل الحروف كبيرة، والفصل بين كل كلمة باستعمال التسطير السفلي. هناك إجماع على عدم استعمال رمز التسطير السفلي في أي مكان آخر.

الأصناف البدائية للبيانات

لغة البرمجة جافا تُعتبر لغة ساكنة التصنيف statically-typed، ما يعني أنه يجب علينا تعريف كل المتغيرات قبل أن نتمكن من استعمالها. ما يعني تحديد نوع المتغير وإسمه، كما رأينا في الأمثلة السابقة:

int gear = 1;

بكتابتنا لهذا السطر فإننا نقول للبرنامج أن هناك حقلا إسمه "gear"، يخزن قيمة عددية، وقيمته الأولية هي "1". يتم تحديد نوع بيانات متغير ما انطلاقا من القيم التي يمكن أن يُخَزّنها، ومن العمليات التي يمكن تنفيذها عليه.

بالإضافة إلى int، لغة البرمجة جافا تدعم سبعة أصناف بيانات بدائية أخرى. الأصناف البدائية محددة مسبقا من طرف اللغة ومُسَمَّاة بكلمة محجوزة. القيم البدائية لا تتشارك حالتها مع القيم البدائية الأخرى. الأصناف البدائية للبيانات المدعومة من طرف لغة البرمجة جافا هي:

  • byte: صنف البيانات byte يمثل عددا صحيحا حجمه 8 بايت، signed ، ويستخدم نظام ال two's complement. قيمته الدُّنيا هي 128- وقيمته القصوى تصل إلى 127. الصنف byte يمكن أن يكون مفيدا للحفاظ على الذاكرة عند استعمال المصفوفات الكبيرة، حيث تكون هناك حاجة لاقتصاد الذاكرة. يمكن أيضا استعمال هذا الصنف مكان int حيث يمكن لحدود القيم الممكنة أن تساعد في توضيح الشيفرة؛ كون مجال المتغير محدودا يمكن أن يكون شكلا من أشكال التوثيق.
  • short: صنف البيانات short يمثل عددا صحيحا حجمه 16 بايت، signed ، ويستخدم نظام ال two's complement. قيمته الدُّنيا هي 32,768- وقيمته القصوى تصل إلى 32,767. كما هو الحال بالنسبة ل byte، يمكن استعمال short لاقتصاد الذاكرة في المصفوفات الكبيرة، في المواقف التي يكون فيها الحفاظ على الذاكرة ضروريا.
  • int: صنف البيانات int يمثل عددا صحيحا حجمه 32 بايت، signed ، ويستخدم نظام ال two's complement. قيمته الدُّنيا هي 2,147,483,648- وقيمته القصوى تصل إلى 2,147,483,647. بالنسبة للقيم الصحيحة غير الكسرية، هذا الصنف يكون عادة الإختيار الإفتراضي، إلا إذا كان هناك سبب ما (كما رأينا سابقا) لعدم استعماله. هذا الصنف سيكون غالبا كبيرا كفاية لاحتواء الأعداد التي سيستعملها برنامجك، لكن إذا احتجت مجالا أكبر من القيم، استعمل long.
  • long: صنف البيانات long يمثل عددا صحيحا حجمه 64 بايت، signed ، ويستخدم نظام ال two's complement. قيمته الدُّنيا هي 9,223,372,036,854,775,808- وقيمته القصوى تصل إلى 9,223,372,036,854,775,807. إستعمل هذا الصنف عندما تحتاج إلى قيم أكبر من تلك التي يوفرها الصنف int.
  • float: صنف البيانات float يمثل عددا حجمه 32 بايت، ذو فاصلة عائمة، أحادي الدقة حسب المعيار IEEE 754. مجال هذا الصنف يفوق نطاق هذا الدرس، لكنه مُحدد في الجزء 4.2.3 من مواصفات لغة الجافا. كما هو الحال بالنسبة ل byte و short، استعمل float (بدل double) عندما تحتاج الحفاظ على الذاكرة عند التعامل مع مصفوفات كبيرة من الأعداد عائمة الفاصلة. هذا الصنف لا يجب أبدا أن يُستعمل للقيم الدقيقة مثل المبالغ المالية، بل يجب استعمال الفئة BigDecimal. في الأعداد وسلسلات الحروف سنتكلم عن BigDecimal وفئات مفيدة أخرى متوفرة في منصة الجافا.
  • double: صنف البيانات double يمثل عددا حجمه 64 بايت، ذو فاصلة عائمة، أحادي الدقة حسب المعيار IEEE 754. مجال هذا الصنف يفوق نطاق هذا الدرس، لكنه مُحدد في الجزء 4.2.3 من مواصفات لغة الجافا. بالنسبة للقيم العشرية، هذا الصنف هو الإختيار الإفتراضي.
    لا يجب أبدا استعمال هذا الصنف للتعامل مع القيم الدقيقة مثل المبالغ المالية.
  • boolean: صنف البيانات boolean يقبل قيمتين فقط: true و false. إستعمل هذا الصنف لتتبع الشروط صحيح/خطأ. هذا الصنف يمثل معلومة مخزنة في bit واحد، لكن "حجمه" ليس بشيء يمكن تحديده بدقة.
  • char: صنف البيانات char هو حرف unicode بحجم 16 بايت. قيمته الدنيا هي 'u0000\' (أو 0) وقيمته القصوى هي 'uffff\' (أو 65,535).

بالإضافة إلى الأصناف البدائية الثماني المذكورة في الأعلى، لغة البرمجة جافا تقدم دعما لسلسلات الحروف عبر الفئة java.lang.String. وضع سلسلة حروف بين علامتي إقتباس مزدوجتين ينشئ كائن String جديد؛ مثلا

String s = "هذه سلسلة حروف";

الكائنات String غير قابلة للتعديل، ما يعني أن قيمتها لا يمكن أن تتغير بعد أن يتم إنشائها. الفئة String ليست تقنيا صنف بيانات بدائيا، لكن بالنظر للدعم الخاص الذي توفره لها اللغة، يمكن أن تتكون لديك هذه الفكرة. سنتكلم أكثر عن الفئة String في الكائنات البسيطة.

القيم الإفتراضية

ليس من الضروري أن نقوم دائما بتحديد قيمة حقل ما عند إعلانه. الحقول التي يتم إعلانها بدون تهيئتها سيتم إعطائها قيمة افتراضية من طرف المُجمع compiler. بصفة عامة، هذه القيمة ستكون صفر أو null، حسب الصنف. لكن الإعتماد على هذه القيم الإفتراضية يعتبر أسلوبا سيئا للبرمجة.

الجدول التالي يعطي القيم الإفتراضية لبعض الأصناف.

صنف البيانات القيمة الإفتراضية (بالنسبة للحقول)
byte 0
short 0
int 0
long 0L
float 0.0f
double 0.0d
char 'u0000\'
String (أو أي كائن)   null
boolean false

بالنسبة للمتغيرات المحلية فالأمر مختلف، فالمجمع لا يعطي أبدا قيمة افتراضية لمتغير محلي غير مُهَيَّء. إذا لم يكن بإمكانك تهيئة متغير محلي عند إعلانه، تأكد من تحديد قيمته قبل أي محاولة لاستعماله. إستعمال متغير محلي غير مهيئ ينتج عنه خطأ عند التجميع.

Literals

ربما لاحظت أننا لا نستعمل الكلمة المفتاحية new عندما نهيء متغيرا ذا صنف بدائي. الأصناف البدائية هي أصناف بيانات مبنية في داخل اللغة، وليست كائنات نُنشئها انطلاقا من فئة. literal هو كل قيمة تابثة تظهر في الشيفرة المصدرية؛ ال literals تُضاف مباشرة إلى الشيفرة دون القيام بأي حسابات. كما يظهر في الأسفل، يمكن إعطاء literal إلى متغير من صنف اساسي:

boolean result = true;

char capitalC = 'C';

byte b = 100; 

short s = 10000; 

int i = 100000; 

الأصناف الصحيحة غير الكسرية (byte, short, int, & long) يمكن التعبير عنها باستعمال نظام الأعداد العشري، الثماني أو الست عشري. النظام العشري هو النظام الذي تستعمله كل يوم؛ وهو يستعمل 10 أرقام، من 0 إلى 9. النظام الثماني أساسه 8، أي أننا نستطيع استعمال الأرقام من 0 إلى 7 فقط. النظام الست عشري أساسه 16، و الأرقام المتوفرة فيه هي الأرقام من 0 إلى 9 والأحرف من A إلى F. بالنسبة للبرمجة العامة التوجه، النظام العشري سيكون غالبا النظام الوحيد الذي ستستعمل، لكن إذا كنت بحاجة لاستعمال النظام الثماني أو الست عشري، الأمثلة التالية تبين الطريقة الصحيحة لاستعمالهما.

البادئة 0 تحدد النظام الثماني، بينما 0x تحدد النظام الست عشري.

int decVal = 26; 	// العدد 26 في النظام العشري

int octVal = 032; 	// العدد 26 في النظام الثماني

int hexVal = 0x1a;	// العدد 26 في النظام الست عشري

الأصناف ذات الفاصلة العائمة (float و double) يمكن أن تُكتب باستعمال E أو e (الكتابة العلمية)، F أو f أي (32-bit float literal) و D أو d التي تعني (64-bit double literal؛ هدا النصرف افتراضي، لدلك يتم تجاهل هده الكتابة).

double d1 = 123.4;

double d2 = 1.234e2; // نفس قيمة d1، لكن بالكتابة العلمية

float f1  = 123.4f;

ال literals من صنف char و String يمكن أن تحتوي على على أي حرف Unicode (UTF-16). إذا كان المحرر ونظام الملفات يسمحان بذلك، يمكنك كتابة هذه الحروف مباشرة داخل الشيفرة. إذا لم تكن كذلك، يمكنك استعمال "Unicode escape" مثل 'u0108\' (حرف C مع circumflex)، أو "S\u00ED se\u00F1or" وتعني (Sí Señor بالإسبانية). استعمل دائما علامة الإقتباس الأحادية (') بالنسبة لchar literals، وعلامة الإقتباس المزدوجة (") بالنسبة ل String literals. ال Unicode escape يمكن أن تُستعمل في أماكن أخرى من البرنامج (أسماء الحقول مثلا)، ليس فقط في ال char و String literals.

لغة الرمجة جافا تدعم أيضا بعض ال escape sequences الخاصة بالنسبة لل literals من صنف char و String:

\b (backspace),
\t (tab),
\n (line feed),
\f (form feed),
\r (carriage return),
\" (double quote),
\' (single quote),
\\ (backslash).

يوجد أيضا literal خاص: null والذي يمكن أن يُستعمل كقيمة لأي صنف reference. يمكن لأي متغير أن يأخد القيمة null، باستثناء المتغيرات من الأصناف الأساسية. ليس هناك الكثير مما يمكن فعله بقيمة null عدا التحقق من وجودها، لذلك فهي تستعمل كثيرا في البرامج كمُؤشر يبين أن كائنا ما غير متوفر.

أخيرا، هناك أيضا نوع خاص من ال literals يسمى class literal، نحصل عليه بضم إسم صنف مع "class."؛ مثلا: String.class. هذا يشير إلى الكائن (من صنف Class) الذي يمثل الصنف بذاته.

استعمال الرمز Underscore في ال Numeric Literals

في النسخة السابعة من الجافا، يمكن أن يظهر الرمز (_) في أكثر من مرة و في أي مكان بين الأرقام في اي literal number. هذه الخاصية تسمح لك مثلا بتفريق مجموعات من الأرقام، مما يزيد من وضوح الشيفرة.

مثلا، إذا كانت الشيفرة تحتوي على أعداد ذات أرقام كثيرة، يمكن استعمال ال underscore لجمع الأرقام في مجموعات من ثلاث، كما نفعل عادة عند استعمال علامة تنقيط كالفاصلة أو الفراغ كفاصل.

الأمثلة التالية تبين عدة طرق لاستعمال الرمز underscore في ال numeric literals:

long creditCardNumber = 1234_5678_9012_3456L;
long socialSecurityNumber = 999_99_9999L;
float pi = 	3.14_15F;
long hexBytes = 0xFF_EC_DE_5E;
long hexWords = 0xCAFE_BABE;
long maxLong = 0x7fff_ffff_ffff_ffffL;
byte nybbles = 0b0010_0101;
long bytes = 0b11010010_01101001_10010100_10010010;

يمكن وضع ال underscore بين الأرقام فقط؛ لا يمكن وضعها في الأماكن التالية:

  • بداية أو نهاية العدد.
  • بجانب النقطة العشرية في floating point literal.
  • قبل اللاحقة F أو L.
  • في أماكن حيث من المنتظر سلسلة من الأرقام.

الأمثلة التالية تبين الوضعيات الصحيحة والخاطئة للرمز underscore في ال numeric literals:

float pi1 = 3_.1415F;      // خاطئ، لا يمكن وضع الرمز بجانب النقطة العشرية 
float pi2 = 3._1415F;      // خاطئ، لا يمكن وضع الرمز بجانب النقطة العشرية
long socialSecurityNumber1 
  = 999_99_9999_L;         // خاطئ، لا يمكن وضع الرمز قبل اللاحقة L. 

int x1 = _52;              // هذا يعتبر معرف، وليس numeric literal
int x2 = 5_2;              // صحيح
int x3 = 52_;              // خاطئ، لا يمكن وضع الرمز في نهاية العدد
int x4 = 5_______2;        // صحيح

int x5 = 0_x52;            // Invalid; cannot put underscores in the 0x radix prefix
int x6 = 0x_52;            // خاطئ، لا يمكن وضع الرمز في بداية العدد
int x7 = 0x5_2;            // صحيح
int x8 = 0x52_;            // خاطئ، لا يمكن وضع الرمز في نهاية العدد

int x9 = 0_52;             // صحيح
int x10 = 05_2;            // صحيح
int x11 = 052_;            // خاطئ، لا يمكن وضع الرمز في نهاية العدد

المصفوفات

المصفوفة هي كائن حاوي، يحتوي على عدد محدد من العناصر ذات صنف واحد. يتم تحديد طول المصفوفة عند إنشائها. بعد الإنشاء، طول المصفوفة يبقى تابثا. لقد سبق ورأينا مثال عن المصفوفات، في الطريقة main لبرنامج "Hello World!". سنقوم الآن بالتعمق أكثر في المصفوفات.

كل قيمة في المصفوفة تسمى "عنصر"، ويتم الوصول لكل عنصر باستعمال مؤشره الرقمي. كما يظهر في الصورة، الترقيم يبدأ من الصفر 0. يتم الوصول للعنصر التاسع مثلا باستعمال المؤشر 8.

البرنامج التالي، ArrayDemo، يقوم بإنشاء مصفوفة من الأعداد الصحيحة، يملأها ببعض القيم، ويطبع قيمة كل عنصر.
class ArrayDemo {
     public static void main(String[] args) {
          int[] anArray;              // إعلان مصفوفة من الأعداد الصحيحة

          anArray = new int[10];      // حجز الذاكرة ل 10 أعداد صحيحة
            
          anArray[0] = 100; // تهيئة العنصر الأول
          anArray[1] = 200; // تهيئة العنصر الثاني
          anArray[2] = 300; // ...
          anArray[3] = 400;
          anArray[4] = 500;
          anArray[5] = 600;
          anArray[6] = 700;
          anArray[7] = 800;
          anArray[8] = 900;
          anArray[9] = 1000;

          System.out.println("Element at index 0: " + anArray[0]);
          System.out.println("Element at index 1: " + anArray[1]);
          System.out.println("Element at index 2: " + anArray[2]);
          System.out.println("Element at index 3: " + anArray[3]);
          System.out.println("Element at index 4: " + anArray[4]);
          System.out.println("Element at index 5: " + anArray[5]);
          System.out.println("Element at index 6: " + anArray[6]);
          System.out.println("Element at index 7: " + anArray[7]);
          System.out.println("Element at index 8: " + anArray[8]);
          System.out.println("Element at index 9: " + anArray[9]);
     }
} 
مخرج البرنامج هو كالتالي :

Element at index 0: 100
Element at index 1: 200
Element at index 2: 300
Element at index 3: 400
Element at index 4: 500
Element at index 5: 600
Element at index 6: 700
Element at index 7: 800
Element at index 8: 900
Element at index 9: 1000

في مواقف برمجية واقعية، سنستعمل في الغالب واحدة من أشكال الحلقات للمرور عبر عناصر المصفوفة، بدل كتابة كل سطر على حدة، كما في المثال السابق. لكن المثال يظهر بوضوح صياغة المصفوفات. سنتعرف على مختلف أشكال الحلقات ( for, while & do-while ) في درس التحكم في السير (سير تنفيذ الشيفرة) .

إعلان متغير يدل على مصفوفة

البرنامج السابق يعلن المصفوفة anArray باستعمال السطر التالي

int[] anArray;              // إعلان مصفوفة من الأعداد الصحيحة

كما الإعلان بالنسبة للمتغيرات من أنواع أخرى، الإعلان عن مصفوفة يحتوي على مكونين: نوع المصفوفة وإسمها. نوع المصفوفة يكتب كالتالي []type ،حيث type هو نوع العناصر داخل المصفوفة؛ الأقواس المربعة هى رمز خاص يبين أن المتغير يحتوي على مصفوفة. حجم المصفوفة ليس جزءا من نوعه (لهذا السبب الأقواس المربعة فارغة).
إسم المصفوفة يمكن أن يكون أي شيء نريد، مادام يحترم القواعد كما المبينة في قسم التسمية. كما المتغيرات من باقي الأنواع، إعلان المصفوفة لا يقوم بإنشائها -- يقوم فقط بالقول للمُجَمِّع أن المتغير سيحتوي على مصفوفة من النوع المحدد.

أيضا، يمكن إعلان مصفوفات من أنواع أخرى:

byte[] anArrayOfBytes;
short[] anArrayOfShorts;
long[] anArrayOfLongs;
float[] anArrayOfFloats;
double[] anArrayOfDoubles;
boolean[] anArrayOfBooleans;
char[] anArrayOfChars;
String[] anArrayOfStrings;

يمكن أيضا وضع الأقواس المربعة بعد إسم المصفوفة:

float anArrayOfFloats[]; // لا ينصح بهذه الطريقة
لكن لا ينصح باستعمال هذه الطريقة؛ الأقواس تميز نوع المصفوفة وبالتالي يفترض أن تظهر بجانب النوع.

إنشاء، تهيئة، والوصول للمصفوفة

إحدى طرق إنشاء مصفوفة استعمال العامل new. السطر التالي من برنامج ArrayDemo ينشئ مصفوفة لها ما يكفي من الذاكرة ل 10 أعداد صحيحة، ويخزن المصفوفة في المتغير anArray.

anArray = new int[10];  // إنشاء مصفوفة من الأعداد الصحيحة
إذا كان هذا السطر غير موجود، فإن المجمع سيطبع رسالة الخطأ التالية، وستفشل عملية التجميع.
	ArrayDemo.java:4: Variable anArray may not have been initialized.
السطور التالية تحدد قيمة كل عنصر من المصفوفة:
anArray[0] = 100; // تهيئة العنصر الأول
anArray[1] = 200; // تهيئة العنصر الثاني
anArray[2] = 300; // ...
يتم الوصول لكل عنصر من المصفوفة باستعمال مؤشره الرقمي:
System.out.println("Element 1 at index 0: " + anArray[0]);
System.out.println("Element 2 at index 1: " + anArray[1]);
System.out.println("Element 3 at index 2: " + anArray[2]);
توجد أيضا طريقة أخرى لإنشاء وتهيئة المصفوفة:
int[] anArray = {100, 200, 300, 400, 500, 600, 700, 800, 900, 1000};
في هذه الحالة، حجم المصفوفة هو عدد العناصر الموجودة بين } و {.

يمكننا أيضا إعلان مصفوفة مصفوفات (تعرف أيضا بمصفوفة متعددة الأبعاد) وذلك باستعمال قوسين مربعين أو أكثر، مثل

String[][] names
ويتم الوصول لكل عنصر باستعمال نفس العدد من المؤشرات.

في لغة البرمجة جافا، المصفوفة متعددة الأبعاد هي ببساطة مصفوفة يحتوي كل عنصر منها على مصفوفة. هذا مختلف عن المصفوفات في C أو Fortran. كنتيجة لذلك، يمكن للسطور أن تكون مختلفة في الحجم، كما هو مبين في البرنامج MultiDimArrayDemo:

class MultiDimArrayDemo {
    public static void main(String[] args) {
        String[][] names = {{"Mr. ", "Mrs. ", "Ms. "},
                            {"Smith", "Jones"}};
        System.out.println(names[0][0] + names[1][0]); //Mr. Smith
        System.out.println(names[0][2] + names[1][1]); //Ms. Jones
    }
}
خارج البرنامج هو:

Mr. Smith
Ms. Jones

وفي النهاية، يمكن استعمال الخاصية length لتحديد حجم مصفوفة ما. الشيفرة التالية:
System.out.println(anArray.length);
ستقوم بطباعة حجم المصفوفة.

نسخ المصفوفات

الفئة System تتوفر على الطريقة arraycopy التي تمكننا من نسخ مصفوفة إلى أخرى بطريقة فعالة.

public static void arraycopy(Object src,
                             int srcPos,
                             Object dest,
                             int destPos,
                             int length)
المعطيان من صنف Object يحددان المصفوفة التي سننسخ منها والمصفوفة التي سننسخ إليها. الثلاث معطيات من نوع int تحدد موقع البداية في المصفوفة الأصل، موقع البداية في المصفوفة الهدف، وعدد العناصر التي سيتم نسخها.

البرنامج التالي، ArrayCopyDemo، يعلن مصفوفة عناصر char، يحتوي على حروف الكلمة "decaffeinated". البرنامج يستعمل arraycopy لنسخ جزء من المصفوفة إلى مصفوفة ثانية:

class ArrayCopyDemo {
    public static void main(String[] args) {
        char[] copyFrom = { 'd', 'e', 'c', 'a', 'f', 'f', 'e',
			    'i', 'n', 'a', 't', 'e', 'd' };
        char[] copyTo = new char[7];

        System.arraycopy(copyFrom, 2, copyTo, 0, 7);
        System.out.println(new String(copyTo));
    }
}
خارج البرنامج هو:

caffein

الخلاصة حول المتغيرات

لعة البرمجة حافا تسنعمل الكلمتين "حقل" و "متغير" كجزء من مصطلحاتها. متغيرات النموذج "instance variables" (الحقول الغير تابثة) تكون خاصة بكل نموذج "instance" من قئة ما. متغيرات الفئة (الحقول التابثة) هي حقول مُعلنة مع المُحَوِّل static؛ توجد بالضبط نسخة واحدة من متغير الفئة، بغض النظر عن عدد المرات التي تم فيها إنشاء نماذج instance للفئة.
المتغيرات المحلية تخزن الحالة المؤقتة داخل الطريقة. المُعطيات هي متغيرات توفر معلومات إضافية للطريقة؛ المتغيرات المحلية والمُعطيات تصنف ك "متغيرات" (وليس "حقول").
عند تسمية المتغيرات أو الحقول، هناك قواعد وأعراف من المستحسن (او المفروض) اتباعها.

الأصناف البدائية الثمانية هي: byte, short, int, long, float, double, boolean, char. الفئة java.lang.String تمثل سلسة من الحروف. المُجمِّع سيحدد قيمة قياسية معقولة للحقول من الأصناف التي تم ذكرها؛ بالنسبة للمتغيرات المحلية، لا يتم تحديد أي قيمة قياسية.
ال literal هو تمثيل فيمة تابثة في الشيفرة المصدرية.
المصفوفة هي كائن حاوي، يحتوي على عدد محدد من العناصر ذات صنف واحد. يتم تحديد طول المصفوفة عند إنشائها. بعد الإنشاء، طول المصفوفة يبقى تابثا.