القائمة الرئيسية

الصفحات

تعلم البرمجه تعلم لغه البرمجه Javaالدرس الرابع 4


تعلم البرمجه من الصفر حتى الختراف


تعلم البرمجه  تعلم لغه البرمجه الجافا الدرس الرابع 4



Java
التاريخ و الوقت في جافا



معرفة التاريخ و الوقت الحالي
معرفة التاريخ و الوقت الحالي سهلة جداً في جافا. كل ما عليك فعله هو إنشاء كائن من الكلاس Date, ثم عرض قيمة هذا الكائن.

مثال
package date;

import java.util.Date;

public class Test {

    public static void main(String[] args) {

        // date هنا قمنا بإنشاء تاريخ و وضعناه في الكائن
        Date date = new Date();

        // date هنا قمنا بعرض التاريخ الذي تم تخزينه في الكائن
        System.out.println( date );

    }

}
•سنحصل على نتيجة تشبه النتيجة التالية عند التشغيل.

Tue Dec 22 11:20:42 EET 2015
•هذه النتيجة تتغير حسب الوقت الحالي لجهازك.

الكلاس Date
الكلاس Date هو كلاس جاهز في جافا يستخدم لمعرفة التاريخ و الوقت. و يحتوي على عدة دوال للتعامل مع الوقت سواء لمقارنة وقت مع وقت أو لمعرفة وقت معين إلخ..

الكلاس Date يحتوي على إثنين كونستركتور ذكرناهما في الجدول التالي.

الكونستركتور تعريفه
Date() هذا الكونستركتور يمهد الكائن الذي قام باستدعائه, بالوقت الحالي.
Date(long millisec) هذا الكونستركتور يستخدم لتمهيد الكائن الذي يمثل الوقت بعدد بالـ milliseconds ( أي كل ثانية واحدة تساوي 1000 ).
العدد الذي نضعه يمثل عدد الـ milliseconds الذي سيضاف على هذا التاريخ 1/1/1970 00:00:00 GMT
أي إذا وضعت 0 ستحصل على هذا التاريخ كما هو.
و إذا وضعت 1000 ستضيف ثانية على هذا التاريخ.
و إذا وضعت 2000 ستضيف ثانيتين على هذا التاريخ و هكذا.

شاهد المثال »

دوال الكلاس Date
إسم الدالة تعريفها
after() ترجع true إذا كان كائن الـ Date الذي قام باستدعائها يحتوي على تاريخ أحدث من تاريخ الـ Date الذي نضعه كـ argument.
تابع القراءة »
before() ترجع true إذا كان كائن الـ Date الذي قام باستدعائها يحتوي على تاريخ أقدم من تاريخ الـ Date الذي نضعه كـ argument.
تابع القراءة »
clone() تنشئ نسخة ثانية من كائن الـ Date الذي قام باستدعائها.
تابع القراءة »
compareTo() تقارن كائن الـ Date الذي قام باستدعائها مع كائن الـ Date الذي نضعه كـ argument.
تابع القراءة »
equals() ترجع true إذا كان كائن الـ Date الذي قام باستدعائها يحتوي على نفس قيمة الكائن الذي نضعه كـ argument.
تابع القراءة »
getTime() ترجع عدد الـ milliseconds من تاريخ 1/1/1970 إلى تاريخ الوقت الذي تم به استدعاء هذه الدالة.
تابع القراءة »
setTime() تستخدم لتحديد الوقت إعتباراً من تاريخ 1/1/1970, العدد الذي نضعه يمثل عدد الثواني بالـ milliseconds.
تابع القراءة »
hashCode() ترجع hash code لكائن الـ Date الذي قام باستدعائها.
تابع القراءة »
toString() تحول قيمة الـ Date لقيمة نوعها String و ترجعها.
تابع القراءة »
الكلاس SimpleDateFormat
الكلاس SimpleDateFormat يستخدم للحصول على التاريخ و الوقت بالشكل الذي تريده, بمعنى أنه يسمح لك بتخصيص طريقة ظهور التاريخ و الوقت.
يتم تحديد شكل التاريخ و الوقت في كونستركتور الـ SimpleDateFormat بواسطة الأحرف الأبجدية. هنا يقال لهذه الأحرف user-defind patterns.

عند تعاملك مع الكلاس SimpleDateFormat يمكنك إستخدام جميع الأحرف من a ← z و من A ← Z.

عندما تقوم بتحديد طريقة ظهور التاريخ و الوقت فأنت بذلك تقوم بتحديد الـ format للتاريخ و الوقت.
بمعنى آخر الأحرف التي تضعها في كونستركتور الـ SimpleDateFormat مع بعضها تشكل الـ format.


هذا الجدول يحتوي على جميع الأحرف المستخدمة لتحديد الـ format في الكلاس SimpleDateFormat.

الحرف إستخدامه مثال
G لإظهار عصر التسمية AD
Y لإظهار السنة, وتكون مؤلفة من أربع أرقام 2005
M لإظهار رقم الشهر من السنة 07
d لإظهار رقم اليوم من الشهر 9
h لإظهار الساعة, بنظام 12 ساعة (1~12) A.M./P.M. 12
H لإظهار الساعة في اليوم بنظام 24 ساعة (0~23) 23
m لإظهار الدقائق في الساعة 45
s لإظهار الثواني في الدقيقة 36
S لإظهار الأجزاء في الثانية Millisecond 458
E لإظهار إسم اليوم في الأسبوع Saturday
D لإظهار رقم اليوم في السنة 360
F لإظهار عدد تكرار اليوم في الشهر 3 (third Mon. in May)
w لإظهار رقم الأسبوع في السنة 34
W لإظهار رقم الأسبوع في الشهر 1
a لإظهار إذا كان الوقت الآن A.M/P.M AM
k لإظهار الساعة في اليوم بنظام 24 ساعة (1~24) 23
K لإظهار الساعة في اليوم بنظام 12 ساعة (0~11) 9
z لإظهار المنطقة الزمنية EET
' لإظهار أي نص Current Date

مثال
•سنقوم الآن بإنشاء format جديدة لعرض الوقت.

package date;

import java.text.SimpleDateFormat;
import java.util.Date;

public class Test {

    public static void main(String[] args) {

        // date هنا قمنا بإنشاء تاريخ و وضعناه في الكائن
        Date date = new Date();

        // جديدة لإظهار الوقت format هنا قمنا بإنشاء
        SimpleDateFormat sdf = new SimpleDateFormat("'Current Date:' dd/MM/yy     'Current Time:' hh:mm:ss a");

        // ثم قمنا بعرض النتيجة format من خلال الدالة date على الـ format هنا قمنا بتطبيق الـ
        System.out.println( sdf.format(date) );

    }

}
•سنحصل على نتيجة تشبه النتيجة التالية عند التشغيل.

Current Date: 22/12/15     Current Time: 07:46:05 PM
•هذه النتيجة تتغير حسب الوقت الحالي لجهازك.



من خلال الجدول السابق يمكنك معرفة أن الكلاس Date يستخدم إفتراضياً الـ format التالية لعرض التاريخ و الوقت: E MMM d hh:mm:ss zzz Y


مثال
•سنقوم الآن بعرض التاريخ مرتين,الأولى بدون هذه الـ format, و الثانية مع هذه الـ format و لاحظ كيف سنحصل على نفس النتيجة.

package date;

import java.text.SimpleDateFormat;
import java.util.Date;

public class Test {

    public static void main(String[] args) {

        // date هنا قمنا بإنشاء تاريخ و وضعناه في الكائن
        Date date = new Date();

        // إفتراضياً Date التي يستخدمها الكلاس format جديدة لإظهار الوقت و لكننا إستخدمنا الـ format هنا قمنا بإنشاء
        SimpleDateFormat sdf = new SimpleDateFormat("E MMM d hh:mm:ss zzz Y");

        System.out.println( date );               // عليه format بدون أن نطبق أي Date هنا قمنا بعرض الـ
        System.out.println( sdf.format(date) );   // عليه format مع تطبيق الـ Date هنا قمنا بعرض الـ

    }

}
•سنحصل على نتيجة تشبه النتيجة التالية عند التشغيل.

Wed Dec 23 07:36:44 EET 2015
Wed Dec 23 07:36:44 EET 2015
•هذه النتيجة تتغير حسب الوقت الحالي لجهازك, لكن التاريخ الأول سيكون نفس التاريخ الثاني دائماً.

تحديد الـ format بواسطة الدالة printf()
يمكنك أيضاً إستخدام الدالة printf() لتعرض التاريخ و الوقت بالشكل الذي تريده.
كل ما عليك فعله هو وضع %t ثم وضع حرف من الأحرف الموجودة في الجدول التالي.

الحرف إستخدامه مثال
c لإظهار التاريخ و الوقت بشكل كامل Thu Dec 24 13:39:33 EET 2015
F لإظهار التاريخ بنظام ISO 8601 2015-12-24
D لإظهار التاريخ بنظام U.S. أي month/day/year 12/24/15
T لإظهار الوقت بنظام 24 ساعة 13:40:43
r لإظهار الوقت بنظام 12 ساعة 01:40:43 PM
R لإظهار الوقت بنظام 24 ساعة, و بدون إظهار الثواني 13:40
Y لإظهار السنة, وتكون مؤلفة من أربع أرقام ( مع إبقاء الأصفار الموجودة على اليسار ) 2015
y لإظهار آخر رقمين من السنة ( مع إبقاء الأصفار الموجودة على اليسار ) 15
C لإظهار أول رقمين من السنة ( مع إبقاء الأصفار الموجودة على اليسار ) 20
B لإظهار إسم الشهر كاملاَ December
b لإظهار إسم الشهر بشكل مختصر Dec
m لإظهار الشهر بشكل رقمين ( مع إبقاء الأصفار الموجودة على اليسار ) 02
d لإظهار اليوم بشكل رقمين ( مع إبقاء الأصفار الموجودة على اليسار ) 12
e لإظهار اليوم بشكل رقمين ( مع إزالة الأصفار الموجودة على اليسار ) 24
A لإظهار إسم اليوم بشكل كامل Thursday
a لإظهار إسم اليوم بشكل مختصر Thu
j لإظهار رقم اليوم في السنة ( مع إبقاء الأصفار الموجودة على اليسار ) 358
H لإظهار الساعة بنظام 24 ساعة ( مع إبقاء الأصفار الموجودة على اليسار ), الرقم يكون بين 00 و 23 13
k لإظهار الساعة بنظام 24 ساعة ( مع إزالة الأصفار الموجودة على اليسار ), الرقم يكون بين 0 و 23 13
I لإظهار الساعة بنظام 12 ساعة ( مع إبقاء الأصفار الموجودة على اليسار ), الرقم يكون بين 01 و 12 01
l لإظهار الساعة بنظام 12 ساعة ( مع إزالة الأصفار الموجودة على اليسار ), الرقم يكون بين 1 و 12 1
M لإظهار الدقائق ( مع إبقاء الأصفار الموجودة على اليسار ) 02
S لإظهار الثواني ( مع إبقاء الأصفار الموجودة على اليسار ) 09
L لإظهار الثواني بشكل ثلاث أرقام بالـ milliseconds ( مع إبقاء الأصفار الموجودة على اليسار ) 959
N لإظهار الثواني بشكل تسعة أرقام بالـ nanoseconds ( مع إبقاء الأصفار الموجودة على اليسار ) 959000000
p لإظهار إذا كان الوقت قبل الظهر أو بعد الظهر بأحرف صغيرة pm
z لإظهار الفارق بين وقت الجهاز و الوقت GMT +0200
Z لإظهار المنطقة الزمنية EET
s لإظهار عدد الثواني منذ 1970/01/01 00:00:00 GMT 1450958247
Q لإظهار عدد الثواني بالـ milliseconds منذ 1970/01/01 00:00:00 GMT 1450958247680


يمكنك أن تعرض التاريخ و الوقت بطرق كثيرة جداً و بعدة أساليب.

المثال الأول
•سنقوم الآن بعرض التاريخ و الوقت بعدة أشكال بـ format نحددها بالدالة printf()

package date;

import java.util.Date;

public class Test {

    public static void main(String[] args) {

        // date هنا قمنا بإنشاء تاريخ و وضعناه في الكائن
        Date date = new Date();

        // هنا قمنا بعرض التاريخ والوقت
        System.out.printf( "Current Date/time: %tc", date );

        // هنا قمنا بعرض التاريخ فقط
        System.out.printf( "\nCurrent Date: %tF", date );

        // هنا قمنا بعرض الوقت فقط
        System.out.printf( "\nCurrent time: %tr", date );

    }

}
•سنحصل على نتيجة تشبه النتيجة التالية عند التشغيل.

Current Date/time: Thu Dec 24 19:03:58 EET 2015 
Current Date: 2015-12-24 
Current time: 07:03:58 PM
•هذه النتيجة تتغير حسب الوقت الحالي لجهازك.



المثال الثاني
•سنقوم الآن بعرض الوقت بـ format تتألف من ثلاث أحرف H و M و S

•أولاً سنقوم بوضع الأحرف الثلاثة ( الأول يشير للساعة, الثاني يشير للدقائق, و الثالث يشير للثواني ).
•ثانياً سنقوم بوضع كائن الـ Date لكل حرف منهم.
•يجب وضع فاصلة قبل كل كائن, و هكذا سيفهم المترجم أن الحرف الأول سيظهر لنا الساعة الموجودة في الكائن الأول, و الحرف الثاني سيظهر لنا الدقائق الموجودة في الكائن الثاني, و الحرف الثالث سيظهر لنا الثواني الموجودة في الكائن الثالث.

package date;

import java.util.Date;

public class Test {

    public static void main(String[] args) {

        // date هنا قمنا بإنشاء تاريخ و وضعناه في الكائن
        Date date = new Date();

        // هنا قمنا بعرض الوقت فقط
        System.out.printf( "Current time: %tH:%tM:%tS", date, date, date );

    }

}
•سنحصل على نتيجة تشبه النتيجة التالية عند التشغيل.

Current time: 19:03:58
•هذه النتيجة تتغير حسب الوقت الحالي لجهازك.

شرح الكود



لست مضطراً أن تضع الكائن Date لكل حرف كما فعلنا في المثال السابق.
يمكنك وضع أرقام index للأحرف التي ستستخدمها لإنشاء format معينة و هكذا سيصبح حجم الـ format أصغر أيضاً.
يجب وضع الأحرف المستخدمة في الـ format بترتيب معين بحيث تشير للـ Date و الـ String و أي شيء آخر تضعه في هذه الدالة كـ arguments.

رقم الـ index يتم إضافته مباشرةً بعد الرمز % و نضع بعده الرمز $, ثم الحرف t, ثم الحرف الذي نريده من الأحرف الموجودة في الجدول السابق.

أمثلة :

إذا كنا نضع index لـ Date نعتمد هذا الشكل: % index $ t format
    مثال: %2$tA
إذا كنا نضع index لـ String نعتمد هذا الشكل: % index $ s
    مثال: %1$s

المثال الثالث
•سنقوم الآن بعرض التاريخ الحالي.

•سنقوم بوضع قيمة String في الـ index رقم 1.
•سنقوم بوضع كائن الـ Date في الـ index رقم 2.
•يجب وضع فاصلة قبل كل argument, و هكذا سيتم إعطاء كل argument منهم رقم index.

package date;

import java.util.Date;

public class Test {

    public static void main(String[] args) {

        // date هنا قمنا بإنشاء تاريخ و وضعناه في الكائن
        Date date = new Date();

        // هنا قمنا بعرض التاريخ فقط
        System.out.printf( "%1$s %2$tA %2$te-%2$tm-%2$tY", "Today is:", date );

    }

}
•سنحصل على نتيجة تشبه النتيجة التالية عند التشغيل.

Today is: Friday 25-12-2015
•هذه النتيجة تتغير حسب الوقت الحالي لجهازك.

شرح الكود



يمكنك أيضاً إستخدام المؤشر < بدل إستخدام الـ index.
المؤشر < يعني أن الـ argument الذي تم استخدامه سابقاً سيعاد استخدامه من جديد.


المثال الرابع
•سنقوم الآن بتنفيذ نفس المثال السابق و لكننا سنستخدم المؤشر < بدل إستخدام الـ index.

package date;

import java.util.Date;

public class Test {

    public static void main(String[] args) {

        // date هنا قمنا بإنشاء تاريخ و وضعناه في الكائن
        Date date = new Date();

        // هنا قمنا بعرض التاريخ فقط
        System.out.printf( "%s %tA %<te-%<tm-%<tY", "Today is:", date );

    }

}
•سنحصل على نتيجة تشبه النتيجة التالية عند التشغيل.

Today is: Friday 25-12-2015
•هذه النتيجة تتغير حسب الوقت الحالي لجهازك.

شرح الكود


تذكر أن هناك طرق كثيرة يمكنك إعتمادها لطباعة التاريخ و الوقت بالشكل الذي تريده, و هناك العديد من الكلاسات الجاهزة في جافا تمكنك من التعامل مع التاريخ و الوقت.


Java
التعابير النمطية في جافا



مفهوم التعابير النمطية
تعابير نمطية: تعني Regular Expressions في البرمجة و تختصر بـ regex أو regexp, و هي عبارة عن نص له معنى محدد.
للدقة أكثر, كل حرف أو رمز نضعه في هذا النص يعني شيء معين.
الـ regex هم أحرف و رموز جاهزة تستخدم للبحث في النصوص بطريقة سهلة جداً, دون الحاجة لكتابة خوارزميات معقدة جداً.

خلاصة: نستخدم الـ regex لوضع شروط أثناء البحث في نص معين. و يمكن إستخدامهم في البحث, التعديل, أو لمعالجة النصوص و البيانات.


طريقة التعامل مع التعابير النمطية
لإنشاء regex و إستخدامه, إتبع الخطوات التالية:

قم بإنشاء نص يمثل الـ regex.
قم بترجمة هذا النص باستخدام الدالة compile(), ثم قم بتخزين النص المترجم في كائن نوعه Pattern.
قم باستدعاء الدالة matcher() من كائن الـ Pattern على أي نص تريد البحث فيه عن تطابق. ثم قم بتخزين النتيجة التي سترجعها الدالة في كائن نوعه Matcher.

لمعرفة إذا كان النص يحتوي على أحرف هي نفسها لأحرف المقصودة من الـ regex يمكنك إستدعاء الدالة matches() على كائن الـ Matcher و سترجع true في حال كان هناك تطابق.


مثال
package regex;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Test {

    public static void main(String[] args) {

        // b و ينتهي بالحرف a و هو يعني نص يبدأ بالحرف a*b هنا هو النص regex الـ
        // Pattern ثم قمنا بتخزينه في كائن نوعه compile() قمنا بتحويله باستخدام الدالة
        Pattern p = Pattern.compile("a*b");

        // Matcher ثم قمنا بتخزينه في كائن نوعه aaaaaaaab على النص matcher() قمنا باستدعاء الدالة
        Matcher m = p.matcher("aaaaaaaab");

        // regex لمعرفة إذا تم إيجاد نص يطابق الـ Matcher على كائن الـ matches() قمنا باستدعاء الدالة
        System.out.println( "Result: " + m.matches() );

    }

}
•سنحصل على النتيجة التالية عند التشغيل.

Result: true


الكلاسات التي تستخدم للتعامل مع التعابير النمطية
هناك إثنين كلاس يستخدمان للتعامل مع الـ regex و هما : Pattern - Matcher
الكلاس Pattern
هذا الكلاس يستخدم لتحويل كائن نوعه String ( عبارة عن regex ) إلى كائن نوعه Pattern.
إذاً كائن الـ Pattern يمثل الحرف أو الأحرف المقصودة من الـ regex.
هذا الكلاس لا يحتوي على كونستركتور.
لإنشاء Pattern, عليك استدعاء واحدة من دواله الثابتة و هي الدالة compile(), و تمرير regex لها كـ argument, و هكذا سترجع كائن نوعه Pattern يمثل regex يفهمها المترجم و جاهزة للإستخدام.


الكلاس Matcher
هذا الكلاس يستخدم لمقارنة الأحرف المقصودة من كائن الـ Pattern مع نص معين.
هذا الكلاس لا يحتوي على كونستركتور.
لإنشاء كائن من الكلاس Matcher, عليك استدعاء الدالة matcher(), على كائن من الكلاس Pattern و تمرير String لها كـ argument, هذه الـ String تمثل النص الذي سيتم البحث فيه عن أحرف تتطابق مع أحرف كائن الـ Pattern.
إذاً كائن الـ Matcher هو الذي ينفذ عمليات المقارنة بين الـ regex و النص.

الأحرف المستخدمة في التعابير النمطية
Regex إستخدامه
"" تعني نص عادي
مثال: "this is just a text"

معناه: إبحث عن نفس هذا النص.
^ تعني بداية السطر
$ تعني نهاية السطر
. تعني أي حرف موجود ما عدا الحرف الذي يشير لسطر جديد.
ملاحظة: يمكن استخدام الحرف m حتى يتم تجاهل الذي يشير لسطر جديد.
[...] تعني أي حرف من الأحرف الموجودة بين المربعين

مثال: "[abc]"
معناه: إبحث عن الحرف a أو b أو c.
[^...] تعني أي حرف غير الأحرف الموجودة بين المربعين

مثال: "[^abc]"
معناه: إبحث عن أي حرف غير الأحرف a أو b أو c.
\A تعني بداية النص
\z تعني نهاية النص
\Z تعني نهاية النص, و إذا كان النص يحتوي على أكثر من سطر, فإنها توقف عملية البحث عن تطابق في نهاية أول سطر.
\b تعني مجموعة أحرف تمثل كلمة أو رقم لا يوجد بينها مسافة أو رمز, في حال لم يتم وضعها بين [ ]
\B تعني ليس كلمة, أي حرف أو رقم واحد. و هي عكس الـ \b
\w تعني مجموعة أحرف تمثل كلمة أو رقم لا يوجد بينها مسافة أو رمز. و هي نفسها [a-zA-Z_0-9]
\W تعني ليس كلمة, أي حرف أو رقم واحد. و هي نفسها [^\W]
\h تعني أن الحرف يمثل مسافة فارغة بشكل أفقي, أي مثل مسافة فارغة بين الكلمات. و هي نفسها [\t\xA0\u1680\u180e\u2000-\u200a\u202f\u205f\u3000]
\H تعني أن الحرف ليس مسافة فارغة. و هي نفسها [^\h]
\s تعني أن الحرف يمثل مسافة فارغة. و هي نفسها [\t\n\x0B\f\r]
\S تعني أن الحرف ليس مسافة فارغة. و هي نفسها [^\S]
\v تعني أن الحرف يمثل مسافة فارغة بشكل عامود, أي يجعل النص ينزل على سطر جديد. و هي نفسها [\n\x0B\f\r\x85\u2028\u2029]
\V تعني أن الحرف لا يمثل مسافة فارغة بشكل عامود. و هي نفسها [^\v]
\d تعني أي رقم. و هي نفسها [0-9]
\D تعني ليس رقم. و هي نفسها [^0-9]
\G تعني نهاية التطابق السابق
\R تعني أي حرف يفصل سلسلة الأحرف عن بعضها. و هي نفسها [\u000D\u000A|[\u000A\u000B\u000C\u000D\u0085\u2028\u2029]
\n تعني حرف يجعل النص ينزل على سطر جديد. و هي نفسها ('\u000A')
\r تعني جعل النص يبدأ من بداية السطر. و هذا الحرف يمثل \r\n. و هي نفسها ('\u000D')
* لتكرار الشيء الذي قبلها من صفر ( أي حتى لو لم يكن هناك شيء أصلاً ) إلى ما لا نهاية.
و هي تستخدم للبحث عن تطابق يبدأ بحرف معين و ينتهي بحرف معين دون الإهتمام إذا كان يحتوي على شيء بين هذين الحرفين أم لا.
+ لتكرار الشيء الذي قبلها مرة أو أكثر
? لتكرار الشيء مرة واحدة أو لا مرة.
| تعني ( أو ) و هي تستخدم لوضع إحتمالات.

مثال: "a|b"
معناه: إبحث عن الحرف a أو b.
() تعني مجموعة, و تستخدم لإنشاء regex ترجع محتوى النص المتطابق مع الـ Pattern الذي تم إيجاده كمجموعة
{n,} لتكرار الشيء الذي قبلها بقيمة العدد الذي نضعه مكان الحرف n

مثال: "\d{4}"
معناه: إبحث عن عدد يتألف من 4 أرقام.
{n, m} لتكرار الشيء الذي قبلها بقيمة بقيمة محصورة بين m و n

مثال: "\d{2,4}"
معناه: إبحث عن عدد يتألف من 2 إلى 4 أرقام كحد أقصى.

إنتبه
الرمز \ يسمى backslash, و هو escape character أي حرف له معنى خاص في جافا.
يجب أن تضع \\ لتعريف \. فمثلاً لتعريف \w نكتب \\w
في الـ regex يجب أن تضع \\\\ لتعريف \ يفهم على أنه escape character.

دوال الكلاس Matcher
الكلاس Matcher هو كلاس جاهز في جافا, يحتوي على دوال كثيرة للتعامل مع محتوى النصوص, سواء للبحث عن أحرف أو كلمات أو جمل, و تقسيمها ضمن مجموعات إلخ..


سنقسم دوال الكلاس Matcher إلى 3 فئات أساسية:

دوال تستخدم لمعرفة إذا تم إيجاد تطابق.
دوال تستخدم لمعرفة الـ index الذي تم فيه إيجاد تطابق.
دوال تستخدم للتبديل.
دوال تستخدم لمعرفة إذا تم إيجاد تطابق
إسم الدالة تعريفها
lookingAt() ترجع true إذا تم إيجاد تطابق بين كائن الـ Pattern و النص الذي نريد البحث فيه.
هذه الدالة تتوقف مباشرةً عن البحث عندما تجد أول تطابق.
find() ترجع true إذا تم إيجاد أكثر من تطابق بين كائن الـ Pattern و النص الذي نريد البحث فيه.
هذه الدالة تبحث في النص كله من الحرف الأول إلى الحرف الأخير.
أي حتى لو تم إيجاد النص الذي تبحث عنه ستظل تحاول إيجاد تطابق حتى تمر على جميع أحرف النص.
matches() ترجع true إذا تطابقت جميع أحرف النص مع أحرف كائن الـ Pattern.
أي في حال كان الـ Pattern يطابق كل النص الذي نريد البحث فيه.
وضعنا هنا أمثلة شاملة تعلمك استخدام الدوال المذكورة في الجدول.

شاهد الأمثلة »



إذاً إستخدم الدالة lookingAt() في حال كنت تريد معرفة إذا كان النص يحتوي على حرف, رقم, رمز, كلمة, جملة معينة مرة واحدة.

و استخدم الدالة find() في حال كنت تريد معرفة كم مرة في النص يوجد حرف, رقم, رمز, كلمة, جملة معينة, بالإضافة أنه يمكنك الإستفادة من الدوال start() و end() لمعرفة مكان إيجاد جميع التطابقات في النص.

و استخدم الدالة matches() في حال كنت تريد معرفة إذا كان النص يحتوي على نفس ما يحتويه الـ Pattern.

دوال تستخدم لمعرفة الـ index الذي تم فيه إيجاد تطابق
إسم الدالة تعريفها
start() ترجع رقم الـ index للحرف الذي بدأ التطابق من عنده.
end() ترجع رقم الـ index للحرف الذي إنتهى التطابق عنده.
وضعنا هنا مثال شامل يعلمك استخدام الدوال المذكورة في الجدول.

شاهد المثال »

دوال تستخدم للتبديل
إسم الدالة تعريفها
replaceAll() تقوم بتبديل كل محتوى موجود في النص يتطابق مع محتوى الـ Pattern بمحتوى جديد.
replaceFirst() تقوم بتبديل أول محتوى موجود في النص يتطابق مع محتوى الـ Pattern بمحتوى جديد.
وضعنا هنا أمثلة شاملة تعلمك استخدام الدوال المذكورة في الجدول.

شاهد الأمثلة »



الجدول التالي يحتوي على دالتين عليك إستدعائهما مع بعض لتنفيذ عملية التبديل.
إسم الدالة تعريفها
appendReplacement() هذه الدالة تبدأ عملية تبديل محتوى محدد في النص كلما وجدته بمحتوى جديد.
فهي تبدل المحتوى الذي تم إيجاده في النص بمحتوى جديد و ترجع النص كله مع التبديل ككائن نوعه StringBuffer.
كما أنها تعتمد على الدوال find() و start() و end() عندما تبحث عن المحتوى الذي سيتم تبديله.
و هي تستفيد من الدوال start() و end() لمعرفة أين يبدأ و أين ينتهي المحتوى الذي سيتم تبديله في النص.
الدالة find() نستخدمها للبحث عن كل تطابق موجود النص.

ملاحظة: الدالة appendReplacement() لا تضيف بقية النص الموجودة بعد آخر مرة تم فيها إيجاد تطابق.
appendTail() هذه الدالة تكمل عملية تبديل محتوى النص بمحتوى جديد.
هي تقوم فقط بإضافة النص العادي الموجود بعد آخر مرة تم فيها تبديل القيمة.
هذه الدالة تُستدعى بعد إستدعاء الدالة appendReplacement() بالترتيب حتى تضيف باقي المحتوى على الـ StringBuffer.
وضعنا هنا مثال شامل يعلمك استخدام الدوال المذكورة في الجدول.

شاهد المثال »

تقسيم الـ regex إلى مجموعات
تقسيم regex إلى مجموعات هي طريقة لمعالجة عدة أحرف كمجموعة واحدة يتم إنشائهم من خلال وضع الأحرف التي نريد تجميعها بين عدة أقواس.

مثال: الـ regex "dog" هو مجموعة واحدة تتألف من الأحرف "g" , "o" , "d".

المجموعات الموجودة يتم ترقيمها من خلال حساب عدد الأقواس المفتوحة من اليسار إلى اليمين.

مثال: في الـ regex "((A)(B(C)))" يوجد أربع مجموعات كما في الصورة التالية:



في مادة الرياضيات يتم رسم هذه المجموعات كما في الصورة التالية:



لمعرفة عدد المجموعات الموجودة في الـ regex, قم باستدعاء الدالة groupCount() على كائن نوعه Matcher.
لاحظ أن group(0) يمثل كل الـ regex الموجود. لذلك الدالة groupCount() لا تضيف الـ group(0) على عدد المجموعات الذي ترجعه.
إذاً هنا إذا قمت بإستدعاء الدالة groupCount() سترجع 3.


مثال
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Test {

    public static void main(String[] args) {

        String line = "This order was placed for QT3000! OK?";;   // line: هو النص الذي سنبحث فيه
        String REGEX = "(.*?)(\\d+)(.*)";                         // REGEX: هو النص الذي سنبحث عنه و سيتم تقسيمه إلى 3 مجموعات

        Pattern p = Pattern.compile(REGEX);                       // Pattern ثم قمنا بتخزينه في كائن نوعه compile() باستخدام الدالة Pattern إلى REGEX قمنا بتحويل الـ

        Matcher m = p.matcher(line);                              // Matcher ثم قمنا بتخزينه في كائن نوعه INPUT على النص matcher() قمنا باستدعاء الدالة

        if ( m.find() )                                           // سيبحث عن التطابق التالي INPUT موجود في الـ Pattern هنا طالما أن الـ
        {
            System.out.println( "group 0: " + m.group(0) );       // هنا قمنا بعرض محتوى المجموعة رقم 0 و التي تحتوي على جميع المجموعات
            System.out.println( "group 1: " + m.group(1) );       // هنا قمنا بعرض محتوى المجموعة رقم 1
            System.out.println( "group 2: " + m.group(2) );       // هنا قمنا بعرض محتوى المجموعة رقم 2
            System.out.println( "group 3: " + m.group(3) );       // هنا قمنا بعرض محتوى المجموعة رقم 3
        }
        else
        {
            System.out.println( "NO MATCH" );                     // سيطبع هذا النص في حال لم يجد أي تطابق
        }

    }

}
•سنحصل على النتيجة التالية عند التشغيل.

group 0: This order was placed for QT3000! OK?
group 1: This order was placed for QT
group 2: 3000
group 3: ! OK?
شرح الكود


Java
الإستثناءات في جافا




        int[] a = { 1, 2, 3, 4, 5 };
        System.out.println( a[10] );

    }

}
•سيظهر الخطأ التالي إذا قمنا بتشغيل البرنامج.

Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 10
•هذا الخطأ يعني أن العنصر ليس موجود في المصفوفة.
ثم أخبرنا بسبب المشكلة و هي أنه لم يجد عنصر يحمل الـ index رقم 10.



Error
Error تعني خطأ يحدث بسبب الجهاز الذي نحاول تشغيل البرنامج عليه, لا علاقة أبداً للبرنامج بهذا الخطأ.

فمثلاً إذا إمتلأت ذاكرة الجهاز الذي يعمل عليه البرنامج سيحدث خطأ, و هو أن نظام التشغيل لا يقدر أن يشغل هذا البرنامج لأن ذاكرة الجهاز ممتلئة. و عندها سيظهر الرسالة التالية لتوضيح الخطأ JVM is out of Memory

لذلك تجد بعض البرامج تحفظ الأشياء التي يفعلها المستخدم كل مدة معينة, و هكذا لن يقلق المستخدم إذا توقف البرنامج الذي يعمل عليه فجأةً, لأن هذا البرنامج في نظره قوي فهو يحفظ له بياناته كل مدة و يتيح له استردادها إذا حدث خطأ ما متى شاء.

بناء الإستثناءات
تم تقسيم الإستثناءات في جافا إلى عدة أنواع و كل نوع تم تمثيله في كلاس منعزل.
جميع هذه الكلاسات ترث من كلاس أساسي إسمه Exception.
الكلاس Exception بدوره يرث من كلاس إسمه Throwable.

إذاً, أي كلاس يرث من الكلاس Exception هو كلاس يمثل إستثناء معين.

الأخطاء التي سببها الأجهزة و التي تسمى Errors, تم إنشاء كلاس خاص لهم إسمه Error و هو يرث مباشرةً من الكلاس Throwable.


ملاحظة
لا تقلق بتاتاً إذا لم تفهم أي شيء الآن ستفهم الوراثة في دروس لاحقة, لكننا ذكرنا لك كلمة "وراثة" لأنها ستمر معك لاحقاً في هذا الدرس.


فكرة عامة عن الوراثة
إذا قلنا الكلاس B يرث من الكلاس A. فهذا يعني أن الكلاس B أصبح يملك نسخة من الأشياء الموجودة في الكلاس A.


تم بناء الإستثناءات بشكل منظم كما في الصورة التالية.



لمشاهدة أغلب الإستثناءات الـ checked و unchecked التي يواجهها المبرمجين

أدخل هنا »

إلتقاط الإستثناء
إلتقاط الإستثناء يسمى Catching Exception, و هو عبارة عن طريقة تسمح لك بحماية البرنامج من أي كود تشك بأنه قد يسبب أي خطأ باستخدام الجملتين try و catch.

أي كود مشكوك فيه يجب وضعه بداخل حدود الجملة try.
الجملة catch عبارة عن دالة يمكنك من خلالها معرفة كل شيء عن الخطأ الذي حدث.


طريقة تعريف الجمل try/catch
try {
    // Protected Code
    // هنا نكتب الأوامر التي قد تولد إستثناء
}
catch(ExceptionType e) {
    // Error Handling Code
    // برمي إستثناء try هنا نكتب أوامر تحدد للبرنامج ماذا يفعل إذا قامت الـ
}

الكود الذي نضعه بداخل الجملة try يسمى Protected Code و هذا يعني أن البرنامج محمي من أي خطأ قد يحدث بسبب هذا الكود.
الكود الذي نضعه بداخل الجملة catch يسمى Error Handling Code و يقصد منها الكود الذي سيعالج الإستثناء الذي قد يتم إلتقاطه.

عندما تضع الكود بداخل حدود الجملة try فأنت بذلك تقوم بتجربة هذا الكود, و ذلك يعني أن جميع الأوامر الموضوعة فيها ستنفذ بشكل عادي جداً, و في حال حدثت أي مشكلة في الكود, ستقوم الجملة try بتمرير الإستثناء الذي يمثل هذه المشكلة كـ argument إلى الدالة catch ليتم معالجته بدل أن تظهر أمام المستخدم.

الإستثناء الذي تقوم الجملة try برميه عبارة عن كائن من إحدى الكلاسات التي ترث من الكلاس Exception.


ملاحظة
عندما تستخدم الجملة try حتى لو لم تضع بداخلها أي كود, فأنت مجبر على وضع الجملة catch أو finally بعدها.
كما أنه بإمكانك وضع العدد الذي تريده من الجملة catch و سترى ذلك و ستتعرف على الجملة finally لاحقاً في هذا الدرس.


مثال
package exceptions;

public class Test {

    public static void main(String[] args) {

        try {                              // try هنا قمنا بتجربة الكود الموجود بداخل الجملة
            int[] a = new int [5];         // هنا قمنا بإنشاء مصفوفة تتألف من 5 عناصر
            System.out.println( a[10] );   // catch هنا حاولنا عرض قيمة عنصر غير موجود في المصفوفة, لذلك سيحدث خطأ, مما سيؤدي رمي إستثناء إلى الدالة
        }
        catch( Exception e ) {                                // e هنا سيتم إلتقاط الإستثناء, ثم تخزينه في الكائن
            System.out.println( "Exception thrown: " + e );   // لنعرف طبيعة الخطأ الذي حدث e هنا قمنا بعرض محتوى الكائن
        }

        // بعد الإنتهاء من تجربة الكود سيتم تنفيذ باقي الأوامر الموجودة في البرنامج
        System.out.println( "The program still work properly" );

    }

}
•سنحصل على النتيجة التالية عند التشغيل.

Exception thrown: java.lang.ArrayIndexOutOfBoundsException: 10
The program still work properly

إذا وضعنا الكلاس Exception كباراميتر للدالة catch, فهذا يعني أن أي إستثناء سترميه الدالة try ستقوم الدالة catch بالتقاطه.

في المثال السابق كان بإمكانك وضع الكلاس ArrayIndexOutOfBoundsException بدل الكلاس Exception, لأننا بعد تجربة الكود عرفنا أن نوع الخطأ الذي قد يرمى بسبب هذا الكود نوعه ArrayIndexOutOfBoundsException.

إذاً إذا كنت لا تعرف طبيعة الخطأ الذي قد يحدث ضع الكلاس Exception في الدالة catch لأن الكلاس Exception يعتبر الكلاس الأساسي و الإفتراضي لجميع الأخطاء التي قد تحدث.

وضع عدة جمل catch
أحياناً الكود الواحد قد يسبب عدة أخطاء, ففي هذه الحالة ستضطر إلى معالجة كل خطأ قد يحدث على حدى ليستمر البرنامج في العمل بشكل طبيعي.

إذاً يمكنك وضع العدد الذي تريده من الجملة catch بعد الجملة try.


طريقة وضع أكثر من catch
try {
    // Protected Code
    // هنا نكتب الأوامر التي قد تولد إستثناء
}
catch(ExceptionType1 e1) {
    // Error Handling Code For ExceptionType1
    // ExceptionType1 برمي إستثناء من النوع try هنا نكتب أوامر تحدد للبرنامج ماذا يفعل إذا قامت الـ
}
catch(ExceptionType2 e2) {
    // Error Handling Code For ExceptionType2
    // ExceptionType2 برمي إستثناء من النوع try هنا نكتب أوامر تحدد للبرنامج ماذا يفعل إذا قامت الـ
}
catch(ExceptionType3 e3) {
    // Error Handling Code For ExceptionType3
    // ExceptionType3 برمي إستثناء من النوع try هنا نكتب أوامر تحدد للبرنامج ماذا يفعل إذا قامت الـ
}

هنا قمنا بوضع ثلاث جمل catch و طبعاً كان بإمكاننا وضع العدد الذي نريده.


طريقة تعامل البرنامج مع الجمل catch
إذا قامت الجملة try برمي إستثناء, سيتم إرساله إلى جملة الـ catch الأولى, بعدها سيتم مقارنة نوع الإستثناء المرمي مع نوع الإستثناء الموضوع كباراميتر في الدالة, إذا كان نوع الإستثناء المرمي هو نفسه نوع الإستثناء الموضوع كباراميتر, سيتم معالجة الخطأ الذي حدث بداخل هذه الـ catch.
إذا كان نوع الإستثناء المرمي غير النوع الموضوع كباراميتر, سيتم الإنتقال إلى جملة الـ catch التالية و تنفيذ نفس الخطوات السابقة.

في حال تم إيجاد إستثناء يطابق الإستثناء المرمي, سيتم تنفيذ جميع الأوامر الموجودة في جملة الـ catch و بعدها سيتخطى البرنامج جميع جمل الـ catch الموجودة بعدها و يكمل باقي الأوامر الموجودة في البرنامج.

و في حال لم يتم إيجاد أي إستثناء يطابق الإستثناء المرمي, سيتوقف البرنامج و يظهر الخطأ الذي حدث.

مثال
package exceptions;

public class Test {

    public static void main(String[] args) {

        String s = "1234567891011121314151617181920212223";
        int a;

        try {
            System.out.println( "s.charAt(28): " + s.charAt(28) );   // s رقم 28 في النص index هنا حاولنا عرض الحرف الموجود على الـ
            a = Integer.parseInt(s);                                 // int لقيمة s هنا حاولنا تحويل قيمة
        }
        catch( StringIndexOutOfBoundsException e1 ) {                // e1 بعدها تقوم بتخزينه في الكائن StringIndexOutOfBoundsException تلتقط الإستثناء الذي نوعه catch هذه الـ
            System.out.println( "Index is not exist in the string!" );
        }
        catch( NumberFormatException e2 ) {                          // e2 بعدها تقوم بتخزينه في الكائن NumberFormatException تلتقط الإستثناء الذي نوعه catch هذه الـ
            System.out.println( "Can't convert 's' to a number because is to long!" );
        }
        catch( Exception e3 ) {                                      // e3 تلتقط أي إستثناء آخر قد يحدث نسينا ذكره, بعدها تقوم بتخزينه في الكائن catch هذه الـ
            System.out.println( "Exception thrown: " + e3 );
        }

        // بعد الإنتهاء من تجربة الكود سيتم تنفيذ باقي الأوامر الموجودة في البرنامج
        System.out.println( "The program still work properly" );

    }

}
•سنحصل على النتيجة التالية عند التشغيل.

s.charAt(28): 9
Can't convert 's' to a number because is to long!
The program still work properly

يمكنك أيضأ وضع أكثر من إستثناء بداخل نفس الجملة catch باستخدام العامل | و الذي يعني " أو "
عندها إذا رمت الجملة try أي إستثناء من الإستثناءات الموجودة في الجملة catch سيتم معالجة الإستثناء فيها.

مثال
package exceptions;

public class Test {

    public static void main(String[] args) {

        String s = "1234567891011121314151617181920212223";
        int a;

        try {
            System.out.println( "s.charAt(28): " + s.charAt(28) );            // s رقم 28 في النص index هنا حاولنا عرض الحرف الموجود على الـ
            a = Integer.parseInt(s);                                          // int لقيمة s هنا حاولنا تحويل قيمة
        }
        catch( StringIndexOutOfBoundsException|NumberFormatException e1 ) {   // e1 بعدها تقوم بتخزينه في الكائن ,NumberFormatException أو StringIndexOutOfBoundsException تلتقط الإستثناء إذا كان نوعه catch هذه الـ
            System.out.println( "The string 's' throw: " +e1 );
        }
        catch( Exception e2 ) {                                               // e2 تلتقط أي إستثناء آخر قد يحدث نسينا ذكره, بعدها تقوم بتخزينه في الكائن catch هذه الـ
            System.out.println( "Exception thrown: " + e2 );
        }

        // بعد الإنتهاء من تجربة الكود سيتم تنفيذ باقي الأوامر الموجودة في البرنامج
        System.out.println( "The program still work properly" );

    }

}
•سنحصل على النتيجة التالية عند التشغيل.

s.charAt(28): 9
The string 's' throw: java.lang.NumberFormatException: For input string: "1234567891011121314151617181920212223"
The program still work properly
الجملة finally
الجملة finally تأتي بعد الجملتين try و catch.
الكود الموضوع في الجملة finally يتنفذ دائماً, أي في حال حدث إستثناء أو لم يحدث فإنه سيتنفذ.

الجملة finally تستخدم لتنظيف الكود, بمعنى أنها تستخدم للتخلص من أي كود لم يعد البرنامج يحتاج إليه.


مكان وضع الجملة finally
try {
    // Protected Code
}
catch(ExceptionType1 e1) {
    // Error Handling Code For ExceptionType1
}
catch(ExceptionType2 e2) {
    // Error Handling Code For ExceptionType2
}
catch(ExceptionType3 e3) {
    // Error Handling Code For ExceptionType3
}
finally {
    // Optional Cleanup Code
    // هنا نقوم بكتابة أوامر للتخلي عن أي شيء لم يعد البرنامج بحاجة له
}

مثال
package exceptions;

public class Test {

    public static void main(String[] args) {

        String s = "1234567891011121314151617181920212223";
        int a;

        try {
            System.out.println( "s.charAt(28): " + s.charAt(28) );   // s رقم 28 في النص index هنا حاولنا عرض الحرف الموجود على الـ
            a = Integer.parseInt(s);                                 // int لقيمة s هنا حاولنا تحويل قيمة
        }
        catch( StringIndexOutOfBoundsException e1 ) {                // e1 بعدها تقوم بتخزينه في الكائن StringIndexOutOfBoundsException تلتقط الإستثناء الذي نوعه catch هذه الـ
            System.out.println( "Index is not exist in the string!" );
        }
        catch( NumberFormatException e2 ) {                          // e2 بعدها تقوم بتخزينه في الكائن NumberFormatException تلتقط الإستثناء الذي نوعه catch هذه الـ
            System.out.println( "Can't convert 's' to a number because is to long!" );
        }
        catch( Exception e3 ) {                                      // e3 تلتقط أي إستثناء آخر قد يحدث نسينا ذكره, بعدها تقوم بتخزينه في الكائن catch هذه الـ
            System.out.println( "Exception thrown: " + e3 );
        }
        finally {                                                    // تتنفذ في حال حدث إستثناء أو لم يحدث finally الجملة
            System.out.println( "finally codes always execute" );
        }

        // بعد الإنتهاء من تجربة الكود سيتم تنفيذ باقي الأوامر الموجودة في البرنامج
        System.out.println( "The program still work properly" );

    }

}
•سنحصل على النتيجة التالية عند التشغيل.

s.charAt(28): 9
Can't convert 's' to a number because is to long!
finally codes always execute
The program still work properly

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


ملاحظات مهمة
لا يمكنك إستخدام الجملة try إذا لم تضع بعدها الجملة catch أو الجملة finally.
لا يمكنك وضع الجملة catch إذا لم تستخدم قبلها الجملة try.
الجملة finally توضع إختيارياً.
لا يمكنك وضع أي كود بين الجمل try و catch و finally.
دوال الكلاس Exception
هنا وضعنا بعض الدوال الموجودة في الكلاس Exception و التي يمكن إستخدامها لمعرفة الأخطاء التي حدثت في البرنامج.

الدالة مع تعريفها
1
public String getMessage()
ترجع رسالة بسيطة تظهر لنا الخطأ الذي حدث.
2
public String toString()
ترجع إسم كلاس الإستثناء الذي حدث مضافاً إليه نتيجة الخطأ التي ترجعها الدالة getMessage()
3
public void printStackTrace()
يستخدمها المبرمج لمعرفة الأخطاء التي قد تحدث أثناء تشغيل البرنامج, و هي مفيدة جداً لأنها تظهر الخطأ الذي حدث كما هو بكامل تفاصيله. إذاً هذه الدالة تستخدم للـ Debugging.


المثال الأول
•في هذا المثال سنستخدم الدالة getMessage() لمعرفة سبب الخطأ الذي حدث فقط.

package exceptions;

public class Test {

    public static void main(String[] args) {

        String s = "abcd 12345";
        int a;

        try {
            a = Integer.parseInt(s);                 // int لقيمة s هنا حاولنا تحويل قيمة
        }
        catch( Exception e ) {                       // e تلتقط أي إستثناء قد يحدث, بعدها تقوم بتخزينه في الكائن catch هذه الـ
            System.out.println( e.getMessage() );    // لمعرفة الخطأ الذي حدث getMessage() هنا قمنا باستخدام الدالة
        }

    }

}
•سنحصل على النتيجة التالية عند التشغيل.

For input string: "abcd 12345"


المثال الثاني
•في هذا المثال سنستخدم الدالة toString() لمعرفة سبب الخطأ الذي حدث و إسم الكلاس الذي يمثل الإستثناء الذي حدث.

package exceptions;

public class Test {

    public static void main(String[] args) {

        String s = "abcd 12345";
        int a;

        try {
            a = Integer.parseInt(s);                // int لقيمة s هنا حاولنا تحويل قيمة
        }
        catch( Exception e ) {                      // e تلتقط أي إستثناء قد يحدث, بعدها تقوم بتخزينه في الكائن catch هذه الـ
            System.out.println( e.toString() );     // لمعرفة الخطأ الذي حدث toString() هنا قمنا باستخدام الدالة
        }

    }

}
•سنحصل على النتيجة التالية عند التشغيل.

java.lang.NumberFormatException: For input string: "abcd 12345"


المثال الثالث
•في هذا المثال سنستخدم الدالة printStackTrace() لمعرفة تفاصيل الخطأ الذي حدث بتفصيل.

package exceptions;

public class Test {

    public static void main(String[] args) {

        String s = "abcd 12345";
        int a;

        try {
            a = Integer.parseInt(s);    // int لقيمة s هنا حاولنا تحويل قيمة
        }
        catch( Exception e ) {          // e تلتقط أي إستثناء قد يحدث, بعدها تقوم بتخزينه في الكائن catch هذه الـ
            e.printStackTrace();        // لمعرفة الخطأ الذي حدث printStackTrace() هنا قمنا باستخدام الدالة
        }

    }

}
•سنحصل على النتيجة التالية عند التشغيل.

java.lang.NumberFormatException: For input string: "abcd 12345"
at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
at java.lang.Integer.parseInt(Integer.java:580)
at java.lang.Integer.parseInt(Integer.java:615)
at testexceptions.Test.main
الكلمتين throws و throw
إذا قمت بتعريف دالة و أردت لهذه الدالة أن ترمي إستثناء إذا حدث شيء معين فعليك وضع الكلمة throws بعد أقواس الباراميترات ثم تحديد نوع الإستثناء الذي قد ترميه الدالة, و إذا قمت مسبقاً بتعريف إستثناء يمكنك جعل الدالة تقوم برميه.

الكلمة throw تستخدم لتحديد نوع الإستثناء الذي سيرسل إلى الجملة catch.
الكلمة throws تستخدم لتحديد نوع الإستثناء الذي قد تقوم الدالة برميه في حال كنت تريد تجربة الكود و معالجته في مكان إستدعاء الدالة.


المثال الأول
•الأسلوب المتبع هنا يسمى Exception Thrower/Exception Catcher.

•في هذا المثال قمنا ببناء دالة ترمي إستثناء و تعالجه بداخلها في حال قمنا بإعطائها عمر أكبر من 63.

package exceptions;

public class Test {

    public static void main(String[] args) {
        // مما يعني أنها ستقوم برمي إستثناء argument هنا قمنا باستدعاء الدالة و أعطيناها القيمة 70 كـ
        checkAge(70);
    }


    public static void checkAge (int age) {
        // و النص الذي وضعناه بين القوسين عبارة عن شرح للمشكلة التي قد تحدث و هو الذي سيتم عرضه catch أكبر من 63 سيتم رمي إستثناء إلى الدالة age هنا الدالة تقوم بتجربة الكود و في حال كانت قيمة
        try {
            if(age > 63) {
                throw new Exception("you are too old!");
            }
        }
        // ثم تقوم بعرض نوع الخطأ e تلتقط أي إستثناء قد يحدث, بعدها تقوم بتخزينه في الكائن catch هذه الـ
        catch( Exception e ) {
            System.out.println( e.getMessage() );
        }
    }

}
•سنحصل على النتيجة التالية عند التشغيل.

you are too old!

إذاً لا نحتاج إلى إستخدام الكلمة throws إلا إذا أردنا وضع الدالة بداخل try/catch عند إستدعائها.



المثال الثاني
•الأسلوب المتبع هنا يسمى Exception Thrower/Exception Propagator.

•في هذا المثال قمنا ببناء دالة ترمي إستثناء يعالج في مكان الإستدعاء في حال قمنا بإعطائها عمر أكبر من 63.

package exceptions;

public class Test {

    public static void main(String[] args) {
        // لأنها قد ترمي إستثناء try و وضعناها بداخل الجملة argument هنا قمنا باستدعاء الدالة و أعطيناها القيمة 70 كـ
        try {
            checkAge(70);
        }
        // ثم تقوم بعرض نوع الخطأ e تلتقط أي إستثناء قد يحدث, بعدها تقوم بتخزينه في الكائن catch هذه الـ
        catch( Exception e ) {
            System.out.println( e.getMessage() );
        }
    }


    // و النص الذي وضعناه بين القوسين عبارة عن شرح للمشكلة التي قد تحدث و هو الذي سيتم عرضه catch أكبر من 63 سيتم رمي إستثناء إلى الدالة age هنا الدالة تقوم بتجربة الكود عند إستدعئها و في حال كانت قيمة
    public static void checkAge (int age) throws Exception{
        if(age > 63) {
            throw new Exception("you are too old!");
        }
    }

}
•سنحصل على النتيجة التالية عند التشغيل.

you are too old!

إذا أردت أن كتابة دالة ترمي أكثر من إستثناء, عليك فقط وضع فاصلة بين كل نوعين, و في حال لم يتم التعرف على الإستثناء الذي قمت باستخدامه, يجب أن تفعل له import فقط.


المثال الثالث
•الأسلوب المتبع هنا يسمى Exception Thrower/Exception Propagator.

•في هذا المثال قمنا ببناء دالة قد ترمي إستثناءين يتم معالجتهما في مكان الإستدعاء.

package exceptions;

public class Test {

    public static void main(String[] args) {

        // لأنها قد ترمي إستثناء try و وضعناها بداخل الجملة argument و أعطيناها القيمة 0 كـ checkAge() هنا قمنا باستدعاء الدالة
        try {
            checkAge(0);
        }
        // و بعدها تقوم بعرض الخطأ e1 ثم تقوم بتخزينه في الكائن ArithmeticException تلتقط أي إستثناء من النوع catch هذه الـ
        catch( ArithmeticException e1 ) {
            System.out.println( e1.getMessage() );
        }
        // ثم تقوم بعرض نوع الخطأ e2 تلتقط أي إستثناء آخر قد يحدث ثم تقوم بتخزينه في الكائن catch هذه الـ
        catch( Exception e2 ) {
            System.out.println( e3.getMessage() );
        }

    }


    // Exception أو من النوع ArithmeticException هنا قلنا أن الدالة قد ترمي إستثناء من النوع
    // ArithmeticException أصغر أو تساوي صفر سيتم رمي إستثناء من النوع argument هنا الدالة تقوم بتجربة الكود عند إستدعائها و في حال كانت القيمة التي نمررها لها كـ
    // Exception و إذا حدث أي إستثناء آخر سيتم رمي إستثناء من النوع
    public static void checkAge (int age) throws ArithmeticException, Exception{
        if(age <= 0) {
            throw new ArithmeticException("This is ArithmeticException");
        }
        else{
            System.out.println( 100/age );
        }
    }

}
•سنحصل على النتيجة التالية عند التشغيل.

This is ArithmeticException
إنشاء Exception جديد و إستخدامه
في البداية ننصح بالعودة لهذه الفقرة بعد دراسة الوارثة, لكي يتم فهم الكود بشكل كامل.


خطوات إنشاء إستثناء جديد
يجب إنشاء كلاس جديد يرث من الكلاس Exception.
يجب إستدعاء الكونستركتور الـ Superclass (أي الخاص بالكلاس Exception) في كونستركتور الكلاس الجديد الذي نقوم بإنشائه.
يجب تمرير الرسالة التي سيرميها الإستثناء في الدالة super().

مثال
•هنا قمنا بتعريف كلاس جديد يمثل Exception و قمنا بتسميته MyException

package exceptions;

public class MyException extends Exception {

    public MyException(String msg){
        super(msg);
    }

}

•هنا قمنا بإنشاء الكلاس Test لتجربة الكلاس MyException

package exceptions;

public class Test {

    public static void main(String[] args) {

        // لأنها قد ترمي إستثناء try و وضعناها بداخل الجملة argument و أعطيناها القيمة 0 كـ checkAge() هنا قمنا باستدعاء الدالة
        try {
            checkAge(20);
            checkAge(18);
            checkAge(10);
        }
        // ثم تقوم بعرض رسالة الخطأ e ثم تقوم بتخزينه في الكائن MyException تلتقط إستثناء نوعه catch هذه الـ
        catch( MyException e ) {
            System.out.println( e.getMessage() );
        }

    }


    // أصغر من 13 argument إذا كان العمر الموضوع فيها كـ MyException هذه الدالة تقوم برمي إستثناء نوعه
    public static void checkAge(int age) throws MyException {
        if(age < 13) {
            throw new MyException("you can't watch horror movies");
        }
        else {
            System.out.println( "you can watch the movie" );
        }
    }

}

•سنحصل على النتيجة التالية عند التشغيل.

you can watch the movie
you can watch the movie
you can't watch horror movies


Java
الكلاس و الكائن في جافا



مفهوم الـ Class
Class: نكتبها كلاس في العربية. و الكلاس عبارة عن حاوية كبيرة تستطيع أن نحتوي على كل الكود من متغيرات و دوال و كائنات إلخ..

لتعريف كلاس جديد يكفي فقط كتابة الكلمة class, ثم وضع إسم له, ثم فتح أقواس تحدد بدايته و نهايته. مثال:

مثال
class ClassName {

}

الآن سنقوم بتعريف كلاس جديد يحتوي على 4 متغيرات, بالإضافة إلى دالة تعرض قيم هذه المتغيرات عندما يتم إستدعاءها.

مثال
class Person {

    String name;
    String sex;
    String job;
    int age;

    void printInfo() {
        System.out.println("Name: " +name);
        System.out.println("Sex: " +sex);
        System.out.println("Job: " +job);
        System.out.println("Age: " +age);
    }

}
هنا قمنا بتعريف كلاس إسمه Person يحتوي على 4 متغيرات بالإضافة إلى دالة تعرض قيم هذه المتغيرات عندما يتم إستدعاءها.


مفهوم الخصائص
أي متغيرات يتم تعريفها بداخل كلاس و خارج أي دالة تسمى خصائص (Attributes), و هذا يعني أن أي كائن من هذا الكلاس سيكون عنده هذه الخصائص.
تستطيع التعامل مع هذه الخصائص من الكائن مباشرةً, بينما المتغيرات العادية لا يمكنك التعامل معها من الكائن.
المتغيرات التي يتم وضعها كباراميترات أو التي يتم تعريفها بداخل الدوال تسمى متغيرات عادية.


ملاحظة
برنامج الـ Netbeans يلون أسماء الخصائص باللون الأخضر, لكي يساعدك في التفريق بين المتغيرات العادية و المتغيرات التي يتم إعتبارها خصائص.
و تذكر أن المتغيرات تسمى خصائص, لأن أي كائن من هذا الكلاس سيملك نسخته الخاصة منها.

مفهوم الـ Object
object: تعني كائن في اللغة العربية. و الكائن عبارة عن نسخة مطابقة لكلاس معين.

بما أن الكائن عبارة عن نسخة من الكلاس, يمكننا القول أنه لا يمكن إنشاء كائن إذا لم يكن هناك كلاس.
إذاً في مفهوم برمجة الكائنات نقوم بإنشاء كلاس معين يسمونه blue print أي (النسخة الخام أو النسخة الأصلية) , و بعدها ننشئ نسخة أو أكثر من هذا الكلاس و نفعل بها ما نريد بدون أن نغير محتويات الكلاس الأساسي و هكذا نكون حافظنا على كودات الكلاس الأساسي لأننا نعدل على النسخ و ليس عليه مباشرةً.


بما أن الكائن عبارة عن نسخة من الكلاس. لتعريف كائن من كلاس معين يجب وضع إسم الكلاس ثم وضع إسم للكائن.

مثال
Person ahmad = new Person();
هنا قمنا بتعريف كائن من الكلاس Person إسمه ahmad.
إذاً الكائن ahmad سيكون عنده نسخة خاصة فيه من خصائص الكلاس Person.

ملاحظة: الكود new Person() هو الذي يقوم فعلياً بتوليد كائن من الكلاس. و هو يعطي قيم أولية للخصائص الموجودة فيه و ستفهم ذلك لاحقاً.


سنقوم الآن بكتابة نفس الكود السابق على مرحلتين لتحصل على كائن من الكلاس Person.

مثال
Person ahmad;             // Person سيمثل كائن من الكلاس ahmad هنا قلنا أن
ahmad = new Person();     // Person يمثل كائن من الكلاس ahmad هنا أصبح ,ahmad و بعدها قمنا بتخزينه في Person هنا قمنا بتوليد كائن من الكلاس


طريقة التعامل مع الكائنات
نقوم بإنشاء كائن من الكلاس.
بعدها نقوم بإدخال قيم لخصائصه, إستدعاء دواله إلخ..


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


نصائح عليك إتباعها
يفضل إنشاء كل كلاس في ملف جافا خاص.
إبدأ إسم الكلاس دائماً بحرف كبير.
إبدأ إسم الكائن دائماً بحرف صغير.
علاقة الـ Object بالـ Class
الكائنات تساعد المبرمج كثيراً, فمثلاً إذا كنت تنوي إنشاء برنامج بسيط لحفظ معلومات أشخاص, هل ستنشئ كلاس لكل شخص ؟!
طبعاً لا, بل تنشئ كلاس واحد فقط يمثل شخص, و تضع فيه الأشياء الأساسية التي تريدها أن تكون موجودة عند كل شخص. ثم تنشئ منه كائنات قدر ما شئت, و عندها يصبح كل كائن من هذا الكلاس عبارة عن شخص له معلوماته الخاصة.



كما تلاحظ قمنا بإنشاء كلاس يحتوي على المعلومات الأساسية التي نريد تعبئتها لكل شخص.
بعدها قمنا بإنشاء 4 كائنات ( أي 4 أشخاص ), ثم قمنا بإدخال معلومات خاصة لكل كائن فيهم.

الآن في حال قمت بإضافة أي متغير أو دالة جديدة في الكلاس Person, فإن أي كائن من هذا الكلاس سيملك نسخة من الشيء الجديد الذي أضفته.
و في حال قمت بتعديل كود معين في الكلاس Person, فأيضاً سيتم تعديل هذا الكود عند جميع الكائنات من هذا الكلاس.



مثال
الآن سنقوم بإنشاء الكلاس Person و إنشاء كائنات منه في الكلاس الذي يحتوي على الدالة main().
إنتبه: يجب إنشاء الكلاس Person و الكلاس Main في نفس الـ package حتى يعمل الكود بشكل صحيح. ستفهم السبب في الدرس التالي.

مثال
Person.java
package person;

public class Person {

    // هنا قمنا بتعريف 4 خصائص
    String name;
    String sex;
    String job;
    int age;

    // هنا قمنا بتعريف دالة تطبع محتوى كل خاصية عندما يتم استدعاءها
    void printInfo() {
        System.out.println("Name: " +name);
        System.out.println("Sex: " +sex);
        System.out.println("Job: " +job);
        System.out.println("Age: " +age);
        System.out.println();
    }

}

Main.java
package person;

public class Main {

    public static void main(String[] args) {

        // Person هنا قمنا بإنشاء كائنات من الكلاس
        Person p1 = new Person();     // سيمثل محمد p1 الكائن
        Person p2 = new Person();     //  سيمثل روز p2 الكائن
        Person p3 = new Person();     // سيمثل أحمد p3 الكائن
        Person p4 = new Person();     // سيمثل ربيع p4 الكائن

        // p1 هنا قمنا بتحديد خصائص الكائن
        p1.name = "Mhamad";
        p1.sex  = "Male";
        p1.job  = "Programmer";
        p1.age  = 21;

        // p2 هنا قمنا بتحديد خصائص الكائن
        p2.name = "Rose";
        p2.sex  = "Female";
        p2.job  = "Secretary";
        p2.age  = 22;

        // p3 هنا قمنا بتحديد خصائص الكائن
        p3.name = "Ahmad";
        p3.sex  = "Male";
        p3.job  = "Doctor";
        p3.age  = 34;

        // p4 هنا قمنا بتحديد خصائص الكائن
        p4.name = "Rabih";
        p4.sex  = "Male";
        p4.job  = "Engineer";
        p4.age  = 27;

        // هنا قمنا بعرض خصائص كل كائن
        p1.printInfo();
        p2.printInfo();
        p3.printInfo();
        p4.printInfo();

    }

}

•سنحصل على النتيجة التالية عند التشغيل.

Name: Mhamad
Sex: Male
Job: Programmer
Age: 21

Name: Rose
Sex: Female
Job: Secretary
Age: 22

Name: Ahmad
Sex: Male
Job: Doctor
Age: 34

Name: Rabih
Sex: Male
Job: Engineer
Age: 27
مفهوم الـ Constructor
Constructor: تكتب كونستركتور بالعربية.
من أهم الأشياء التي عليك التفكير بها بعد إنشاء كلاس جديد, هي تسهيل طريقة خلق كائنات من هذا الكلاس.
من هنا جائت فكرة الكونستركتور و الذي هو عبارة عن دالة لها نوع خاص, يتم إستدعائها أثناء إنشاء كائن لتوليد قيم أولية للخصائص الموجودة فيه.

بما أنه لا يمكن إنشاء كائن من كلاس إلا من خلال كونستركتور, سيقوم مترجم جافا بتوليد كونستركتور إفتراضي فارغ عنك إذا وجد أن الكلاس الذي قمت بتعريفه لا يحتوي على أي كونستركتور.


مثال
•إذا قمنا بتعريف كلاس إسمه Person و لم نقم بتعريف كونستركتور له كما في الكلاس التالي.

class Person {

}
•سيقوم المترجم بإنشاء كونستركتور فارغ بشكل تلقائي عننا كالتالي.

class Person {

    public Person() {

    }

}


نقاط مهمة حول الكونستركتور
كل كلاس يتم إنشاءه, يحتوي على كونستركتور واحد على الأقل. و حتى إن لم تقم بتعريف أي كونستركتور, سيقوم المترجم بإنشاء واحد إفتراضي عنك.
في كل مرة يتم إنشاء كائن جديد, يجب استدعاء كونستركتور حتى يتم إنشاء هذا الكائن.
القاعدة الأساسية عند تعريف كونستركتور هي أنه يجب أن يحمل نفس إسم الكلاس و يكون نوعه public.
في حال قمت بتعريف كونستركتور, لن يقوم المترجم بإنشاء واحد إفتراضي, أي لن يعود هناك كونستركتور إفتراضي.
يمكنك تعريف أكثر من كونستركتور. و يمكنك دائماً إنشاء كونستركتور فارغ, حتى تستخدمه إن كنت لا تريد إعطاء قيم أولية محددة للخصائص عند إنشاء كائن.

الآن سنرجع إلى الكلاس Person, و سنضيف فيه 2 كونستركتور, واحد فارغ ( أي مثل الإفتراضي ), و آخر يمكننا من خلاله إدخال قيم مباشرةً في الخصائص الموجودة في الكائن بدل إستدعاء كل خاصية موجودة فيه.

مثال
•ملاحظة: الأشياء الموضوعة باللون الرمادي هي التي قمنا بإضافتها في الكلاس.

Person.java
package person;

public class Person {

    // هنا قمنا بتعريف 4 خصائص
    String name;
    String sex;
    String job;
    int age;

    // إفتراضي constructor فارغ, أي كأننا قمنا بتعريف constructor هنا قمنا بتعريف
    public Person() {

    }

    // ثاني, الهدف منه إعطاء قيم لجميع الخصائص الموجودة في الكائن عند إنشاءه مباشرةً constructor هنا قمنا بتعريف
    // عليك إدخال 4 قيم من نفس النوع و بالترتيب الموضوع constructor عند استدعاء هذا الـ
    public Person(String n, String s, String j, int a) {
        name = n;    // name سيتم وضعه كقيمة للخاصية n الذي سيتم تخزينه في String الـ
        sex = s;     // sex سيتم وضعه كقيمة للخاصية s الذي سيتم تخزينه في String الـ
        job = j;     // job سيتم وضعه كقيمة للخاصية j الذي سيتم تخزينه في String الـ
        age = a;     // age سيتم وضعه كقيمة للخاصية a الذي سيتم تخزينه في int الـ
    }

    // هنا قمنا بتعريف دالة تطبع محتوى كل خاصية عندما يتم استدعاءها
    void printInfo() {
        System.out.println("Name: " +name);
        System.out.println("Sex: " +sex);
        System.out.println("Job: " +job);
        System.out.println("Age: " +age);
        System.out.println();
    }

}

Main.java
package person;

public class Main {

    public static void main(String[] args) {

        // Person هنا قمنا بإنشاء كائنات من الكلاس
        Person p1 = new Person("Mhamad", "Male", "Programmer", 21);  // يمثل الشخص محمد مع تحديد كامل خصائصه p1 الكائن
        Person p2 = new Person("Rose", "Female", "Secretary", 22);   // يمثل الشخص روز  مع تحديد كامل خصائصه p2 الكائن
        Person p3 = new Person("Ahmad", "Male", "Doctor", 34);       // يمثل الشخص أحمد مع تحديد كامل خصائصه p3 الكائن
        Person p4 = new Person("Rabih", "Male", "Engineer", 27);     // يمثل الشخص ربيع مع تحديد كامل خصائصه p4 الكائن

        // الفارغ, فإضطررنا إلى إدخال قيمة لكل خاصية موجودة فيه constructor هنا قمنا بإنشاء كائن جديد باستخدام الـ
        Person p5 = new Person();

        // p5 هنا قمنا بتحديد خصائص الكائن
        p5.name = "Lina";
        p5.sex  = "Female";
        p5.job  = "Graphic Designer";
        p5.age  = 24;

        // هنا قمنا بعرض خصائص كل كائن
        p1.printInfo();
        p2.printInfo();
        p3.printInfo();
        p4.printInfo();
        p5.printInfo();

    }

}

•سنحصل على النتيجة التالية عند التشغيل.

Name: Mhamad
Sex: Male
Job: Programmer
Age: 21

Name: Rose
Sex: Female
Job: Secretary
Age: 22

Name: Ahmad
Sex: Male
Job: Doctor
Age: 34

Name: Rabih
Sex: Male
Job: Engineer
Age: 27

Name: Lina
Sex: Female
Job: Graphic Designer
Age: 24


المتغيرات التي يتم وضعها في الكلاس تقسم إلى ثلاث فئات أساسية ذكراناها في الجدول التالي.

بالإنجليزية معناها
Local Variables هي المتغيرات التي يتم تعريفها بداخل أي دالة, أو constructor, أو بداخل block ( مثل الحلقات, الجملة switch إلخ.. ).
Instance Variables هي المتغيرات التي يتم تعريفها بداخل الكلاس و خارج حدود أي دالة أو constructor, أو block.
تسمى أيضاً Global Variables.
Class Variables هي المتغيرات التي يتم تعريفها كـ static بداخل الكلاس و خارج حدود أي دالة أو constructor, أو block.

مثال
class VariablesTypes {

    // block لأنه تم تعريفهم بداخل الكلاس و خارج أي دالة أو Instance Variables تعتبر ( a, b, c, d ) المتغيرات
    // ستفهم معناها في الدرس التالي, لكننا وضعناها فقط لتفهم الأسماء المستخدمة ( public, protected, private ) الكلمات
    int a;
    public int b;
    protected int c;
    private int d;

    // static لأن نوعه Class Variable يعتبر e المتغير
    static int e;

    // لأنه تم تعريفها بداخل الدالة Local Variables تعتبر ( x, y, z ) المتغيرات
    public int sum(int x, int y) {
        int z = x + y;
        return z;
    }

}
الكلمة this
الكلمة this هي كلمة محجوزة في لغة جافا, و هي تستخدم للإشارة إلى الـ Global Variables, و تستخدم أيضاً للإشارة إلى الكائن الحالي. و يمكن استخدامها في أماكن عديدة ستتعرف عليها في دروس مقبلة.

في هذا الدرس سنستخدمها للتفرقة بين المتغيرات التي تم تعريفها بداخل الدوال Local Variables و بين المتغيرات التي تم تعريفها بداخل الكلاس و خارج الدوال Global Variables.

سنرجع إلى الكلاس Person و سنقوم باستخدام الكلمة this عدة مرات لمعرفة تأثيرها على الكود.


في هذا المثال لم نغير أي كود كان موجود, لكننا أضفنا كلمة this في كل مكان كنا نقصد فيه أننا نريد الوصول للخصائص.

المثال الأول
Person.java
package person;

public class Person {

    String name;
    String sex;
    String job;
    int age;

    public Person() {

    }

    // لأن أسماء الباراميترات الموضوعة ليست نفسها أسماء الخصائص this هنا لا يوجد داعي لاستخدام الكلمة
    public Person(String n, String s, String j, int a) {
        this.name = n;     // الموجودة في الكلاس name الموجود في الدالة, سيتم وضعها في الخاصية n القيمة التي سيتم إدخالها في المتغير
        this.sex  = s;     // الموجودة في الكلاس sex الموجود في الدالة,  سيتم وضعها في الخاصية s القيمة التي سيتم إدخالها في المتغير
        this.job  = j;     // الموجودة في الكلاس job الموجود في الدالة,  سيتم وضعها في الخاصية j القيمة التي سيتم إدخالها في المتغير
        this.age  = a;     // الموجودة في الكلاس age الموجود في الدالة,  سيتم وضعها في الخاصية a القيمة التي سيتم إدخالها في المتغير
    }

    // لأن الدالة لا تحتوي على باراميترات و بالتالي سيفهم المترجم أنك تقصد عرض قيم الخصائص الموجودة في الكائن حتى لو لم تستخدمها this هنا لا يوجد داعي لاستخدام الكلمة
    void printInfo() {
        System.out.println("Name: " +this.name);
        System.out.println("Sex: "  +this.sex);
        System.out.println("Job: "  +this.job);
        System.out.println("Age: "  +this.age);
        System.out.println();
    }

}

•سنحصل على نفس النتيجة السابقة عند التشغيل.

Name: Mhamad
Sex: Male
Job: Programmer
Age: 21

Name: Rose
Sex: Female
Job: Secretary
Age: 22

Name: Ahmad
Sex: Male
Job: Doctor
Age: 34

Name: Rabih
Sex: Male
Job: Engineer
Age: 27

Name: Lina
Sex: Female
Job: Graphic Designer
Age: 24


في هذا المثال سنستخدم الكلمة this عند الحاجة لها فقط.

المثال الثاني
•هنا قمنا بوضع أسماء الباراميترات مثل أسماء الخصائص.
إذاً هنا يجب وضع الكلمة this إذا كنا نريد الوصول للخصائص و إلا ستحدث أخطاء منطقية في البرنامج.

Person.java
package person;

public class Person {

    String name;
    String sex;
    String job;
    int age;

    public Person() {

    }

    // للتفرقة بين الباراميترات و الخصائص الموجودة في الكائن this هنا يجب إستخدام الكلمة
    public Person(String name, String sex, String job, int age) {
        this.name = name;    // الموجودة في الكلاس name الموجود في الدالة, سيتم وضعها في الخاصية name القيمة التي سيتم إدخالها في المتغير
        this.sex  = sex;     // الموجودة في الكلاس sex الموجود في الدالة,  سيتم وضعها في الخاصية sex القيمة التي سيتم إدخالها في المتغير
        this.job  = job;     // الموجودة في الكلاس job الموجود في الدالة,  سيتم وضعها في الخاصية job القيمة التي سيتم إدخالها في المتغير
        this.age  = age;     // الموجودة في الكلاس age الموجود في الدالة,  سيتم وضعها في الخاصية age القيمة التي سيتم إدخالها في المتغير
    }

    // لأن الدالة لا تحتوي على باراميترات و بالتالي سيفهم المترجم أنك تقصد عرض قيم الخصائص الموجودة في الكائن حتى لو لم تستخدمها this هنا لا يوجد داعي لاستخدام الكلمة
    void printInfo() {
        System.out.println("Name: " +name);
        System.out.println("Sex: "  +sex);
        System.out.println("Job: "  +job);
        System.out.println("Age: "  +age);
        System.out.println();
    }

}

•سنحصل على نفس النتيجة السابقة عند التشغيل.

Name: Mhamad
Sex: Male
Job: Programmer
Age: 21

Name: Rose
Sex: Female
Job: Secretary
Age: 22

Name: Ahmad
Sex: Male
Job: Doctor
Age: 34

Name: Rabih
Sex: Male
Job: Engineer
Age: 27

Name: Lina
Sex: Female
Job: Graphic Designer
Age: 24


في هذا المثال وضعنا أسماء الباراميترات في كونستركتور نفس أسماء الخصائص و لم نستخدم الكلمة this للتفريق بينهم.
إذاً هنا لن تحدث أخطاء تؤدي إلى إيقاف البرنامج بل ستحدث أخطاء منطقية.

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

Person.java
package person;

public class Person {

    String name;
    String sex;
    String job;
    int age;

    public Person() {

    }

    // سيسبب مشكلة لأن المترجم لن يستطيع التفرقة بين الباراميترات و الخصائص الموجودة في الكائن this هنا عدم إستخدام الكلمة
    // غير مفيد و يحتوي على أخطاء منطقية constructor إذاً هذا الـ
    public Person(String name, String sex, String job, int age) {
        name = name;    // من جديد name سيتم وضعها في الباراميتر name القيمة التي سيتم إدخالها في المتغير
        sex  = sex;     // من جديد sex  سيتم وضعها في الباراميتر sex  القيمة التي سيتم إدخالها في المتغير
        job  = job;     // من جديد job  سيتم وضعها في الباراميتر job  القيمة التي سيتم إدخالها في المتغير
        age  = age;     // من جديد age  سيتم وضعها في الباراميتر age  القيمة التي سيتم إدخالها في المتغير
    }

    // لأن الدالة لا تحتوي على باراميترات و بالتالي سيفهم المترجم أنك تقصد عرض قيم الخصائص الموجودة في الكائن حتى لو لم تستخدمها this هنا لا يوجد داعي لاستخدام الكلمة
    void printInfo() {
        System.out.println("Name: " +name);
        System.out.println("Sex: "  +sex);
        System.out.println("Job: "  +job);
        System.out.println("Age: "  +age);
        System.out.println();
    }

}

•سنحصل على النتيجة التالية عند التشغيل.

•لاحظ أن الكائنات التي استخدمت الكونستركتور الأول فقط هي التي حدث فيها أخطاء منطقية.

Name: null
Sex: null
Job: null
Age: 0

Name: null
Sex: null
Job: null
Age: 0

Name: null
Sex: null
Job: null
Age: 0

Name: null
Sex: null
Job: null
Age: 0

Name: Lina
Sex: Female
Job: Graphic Designer
Age: 24


Java
الـ Modifiers في جافا


مفهوم الـ Modifiers
الـ Modifiers هم كلمات يمكنك إضافتهم عند تعريف أشياء جديدة ( سواء كلاس, متغير, دالة إلخ.. ) لتحديد طريقة الوصول إليها.
ستحتاجهم في الغالب إن كنت تعمل في برنامج كبير ضمن فريق من المبرمجين.

فإذا كنت تعمل على إنشاء برنامج معين ضمن فريق من المبرمجين, و تريد ضمان عدم إساءة إستخدام الأشياء التي قمت بتعريفها من قبل مبرمج آخر.
الـ Modifiers سيساعدوك في ذلك, فباستخدامهم يمكنك تحديد الأشياء التي يمكن لباقي المبرمجين الوصول إليها و الأشياء التي تريد التأكد من عدم التعديل عليها إلخ..


الـ Modifiers ينقسمون إلى قسمين أساسيين:

Access Modifiers
Non Access Modifiers

في المثال التالي قمنا بتعليم الكلمات التي تعتبر من الـ Access Modifiers باللون الأزرق و الكلمات التي تعتبر من الـ Non Access Modifiers باللون الأخضر.

مثال
public class Student {

    private String  firstName;
    private String  lastName;
    private String  specialization;
    private int     id;
    private boolean isWork;
    final   String  theAvgerageForSuccess = "50%";
    static  String  CollegeName = "MIT";

    public static void printFullName() {
        System.out.println("Name: " +firstName+ " " +lastName);
    }

}

ملاحظة
في هذا الدرس سنعطيك معلومات عامة عن الـ Modifiers و في الدروس المقبلة سنستخدمهم في معظم الدروس لذلك عليك التركيز عليهم جيداً.
كما أننا سنتطرق قليلاً إلى الـ Encapsulation و الـ Inheritance, أي التغليف و الوراثة. لا تقلق إن لم تفهم أي شيء منهم الآن لأنك ستراهم في دروس لاحقة.

Access Modifiers
الجدول التالي يحتوي على الكلمات التي تنتمي للـ Access Modifiers.

Modifier تعريفه
public الكلاس أو الدالة أو المتغير الذي يتم تعريفه كـ public يمكن الوصول إليه مباشرةً.
protected الدالة أو المتغير الذي يتم تعريفه كـ protected يمكن الوصول إليه فقط من الكلاسات الموجودة في نفس الـ package أو من الكلاسات التي ترث منه.

ملاحظة:
لا يمكنك تعريف كلاس كـ protected.
إذا لم تضع أي كلمة من الـ Access Modifiers عند تعريف كلاس أو دالة أو متغير سيتم وضع Modifier إفتراضي عنك يسمى package private. و هذا يعني أنه يمكن الوصول إليه فقط من الكلاسات الموجودة في نفس الـ package.
private الـ private هو أعلا مستوى من حيث الحماية. المتغيرات و الدوال التي يتم تعريفها كـ private يمكن الوصول لها فقط من داخل الكلاس الذي تم تعريفها فيه.

ملاحظات
لا يمكنك تعريف كلاس كـ private.
لا تقم بتعريف دالة كـ private إذا كان نوعها أيضاً abstract لأنك لن تستطيع أن تفعل لها override.

الـ Access Modifiers هم الأشياء الأساسية التي تسمح لك بتطبيق مبدأ الـ Encapsulation الذي يمكنك من إخفاء البيانات الأساسية في الكود التي لا تريد لأحد من المبرمجين الآخرين أن يعرف بتفاصيلهم. ستتعلم مبدأ الـ Encapsulation في الدرس التالي.



كتابة الكود بشكل مثالي
بشكل عام, الـ Modifier الإفتراضي لا يستخدم في الغالب. و الـ public يستخدم مع الدوال التي تريد للجميع أن يصل إليها. الـ private هو للمتغيرات التي لا تريد للكائنات و الكلاسات التي ترث من الكلاس أن تصل إليها. الـ protected يستخدم من أجل الكلاسات المرتبطة بالكلاس الذي تعمل عليه ( فعلياً التي ترث منه ) فمن خلاله ستكون البيانات متاحة أمام الكلاسات المرتبطة بالكلاس و لكنها غير متاحة أمام أي كلاس آخر.


الخطوات التي عليك اتباعها للتحكم بالكود و لحمايته من المبرمجين الآخرين
ضع Modifier ملائم لكل عنصر تقوم بتعريفه, لحماية البيانات قدر المستطاع.
المتغيرات التي تمثل الخصائص يجب أن لا تكون أبداً public. يجب وضعهم private أو protected لتمنع الكلاسات الأخرى من الوصول المباشر إليهم.
يجب تجهيز دوال نوعها public للتعامل مع هذه الخصائص. الدوال التي نوعها public تسمح للمبرمجين ( أو الكلاسات الأخرى ) بالوصول إلى الخصائص. هذه الدوال تسمح لك بإخفاء المتغيرات بالإضافة إلى التحكم بالخصائص كما تريد.


القواعد التالية تم فرضها بالنسبة للدوال التي يرثها كلاس من كلاس آخر
الدوال التي يتم تعريفها كـ public في الـ Superclass تعتبر public في جميع الـ Subclasses.
الدوال التي يتم تعريفها كـ protected في الـ Superclass تعتبر protected أو public في جميع الـ Subclasses.
الدوال التي يتم تعريفها كـ private, لا يتم توريثها إلى أي كلاس كان, لذلك لا يوجد قواعد من أجلهم.


مثال
في الصورة التالية قمنا بإنشاء كلاس إسمه C1 يحتوي على المتغيرات a, b, c, d و وضعنا Access Modifier لكل متغير.
ثم قمنا بإنشاء الكلاسات C2 و C3 في نفس الـ package و التي إسمها p1.
ثم قمنا بإنشاء الكلاسات C4 و C5 في package ثانية إسمها p2.
و في كل كلاس حاولنا الوصول لجميع العناصر الموجودة في الكلاس C1.



Non Access Modifiers
جميع الكلمات التي تنتمي إلى الـ Non Access Modifiers هي التالية:

static
final
abstract
synchronized
native
transient
volatile
strictfp

الجدول التالي يحتوي على الـ Non Access Modifiers الأكثر إستخداماً, مع العلم أننا سنشرح باقي الكلمات في دروس متقدمة.

Modifier تعريفه
static يستخدم لتعريف كلاس أو متغير أو دالة مشتركة بين جميع الكائنات من كلاس معين.
final يستخدم لمنع الوراثة من الكلاس, أو لمنع كتابة محتوى الدالة ( أو تعديلها ) في الكلاس الذي يرثها, أو لجعل قيمة المتغير غير قابلة للتغير بعد تحديدها.
abstract يستخدم لإنشاء كلاس أو دالة مجردة ( أي دالة لا تحتوي على كود ), الهدف من هذا الـ Modifier تجهيز كلاس معين و جعل الكلاسات التي ترث من هذا الكلاس هي من تقوم بتعريف الأشياء الموجودة بداخله.
الكلمة static
ما الحاجة إلى تعريف شيء كـ static؟

إن أردت تعريف شيء ثابت لجميع الكائنات, قم بتعريفه كـ static.
إن أردت تعريف شيء بداخل كلاس معين, و تريد الوصول إليه مباشرةً من الكلاس بدل إنشاء كائن من الكلاس ثم استدعاء الشيء منه, قم بتعريفه كـ static.

المتغيرات التي يتم تعريفها كـ static
المتغير الذي يتم تعريفه كـ static يعتبر مشترك بين جميع الكائنات من نفس الكلاس. بمعنى أن كل كائن يتم إنشاءه من نفس الكلاس سيملك نفس هذا المتغير.
فعلياً المتغير هنا سيتم تعريفه مرة واحدة في الذاكرة و جميع الكائنات من نفس الكلاس ستشير إليه بدل أن تملك نسخة خاصة منه. إذاً static تعني نسخة واحدة من المتغير لجميع الكائنات.

المتغير الذي يتم تعريفه كـ static يسمى أيضاً Class Variable. لا يمكن تعريف الـ Local Variables كـ static.

يمكن الوصول للمتغير الذي تم تعريفه كـ static بذكر إسم الكلاس الذي تم تعريفه فيه ثم وضع إسمه, أو من أي كائن من الكلاس.


الدوال التي يتم تعريفها كـ static
الدالة دائماً يتم تعريفها مرة واحدة في الذاكرة و جميع الكائنات من نفس الكلاس ستشير إليها. لكن الكلمة static تمكنك من الوصول إليها مباشرةً من الكلاس دون الحاجة لخلق كائن و استدعائها من خلاله.

الدالة التي نوعها static يمكنها الوصول للمتغيرات المعرفة في الكلاس بشرط أن تكون هذه المتغيرات أيضاً static. لكن بشكل عام الدوال التي نوعها static لا تستخدم المتغيرات الموجودة في الكلاس, بل تستخدم المتغيرات التي يتم تعريفها كباراميترات لها أو المتغيرات التي يتم تعريفها بداخلها.

يمكن الوصول للدالة التي تم تعريفها كـ static بذكر إسم الكلاس الذي تم تعريفها فيه ثم وضع إسمه, أو من أي كائن من الكلاس.


أمثلة
المثال التالي يوضح تأثير الكلمة static على المتغيرات و الدوال شاهد المثال »
المثال الثاني يعطيك أفكار حول فائدة استعمال الكلمة static شاهد المثال »
الكلمة final
ما الحاجة إلى تعريف شيء كـ final؟

في حال أردت إنشاء متغير يمكن تحديد قيمته مرة واحدة فقط.
في حال أردت إنشاء دالة لا يمكن تعريفها من جديد في الكلاس الذي يرثها ( أي لمنع الـ override ).
في حال أردت إنشاء كلاس لا يمكن الوراثة منه.


المتغيرات التي يتم تعريفها كـ final
المتغير الذي يتم تعريفه كـ final يعني أنه بمجرد إعطاءه قيمة, لا يمكن تغييرها من جديد.
عند إنشاء متغير نوعه final يجب تحديد قيمته مرة واحدة فقط إما عند تعريفه أو في الكونستركتور.



المتغيرات التي يتم تعريفها كـ final static
يمكن تعريف المتغير كـ final و static مع بعض, و عندها يمكن الوصول للمتغير من الكلاس مباشرةً أو من أي كائن من الكلاس, مع عدم إمكانية تغيير قيمته بعد تحديدها.
الـ Math.PI و الـ Math.E هم من المتغيرات المعرفة كـ final static في جافا, يمكنك استخدامهم كما هم لكن لا يمكنك تغيير قيمهم.



الدوال التي يتم تعريفها كـ final
الدالة التي يتم تعريفها كـ final يعني أنه لا يمكن أن يتم تعريف محتواها في أي كلاس آخر. أي الكلاس الذي يرثها لا يسمح له بأن يفعل لها override.



الكلاسات التي يتم تعريفها كـ final
الكلاس الذي يتم تعريفه كـ final يعني أنه لا يمكن الوراثة منه.
فمثلاً تم تعريف الكلاس Math في جافا كـ final static حتى يكون متاح للإستخدام من أي مكان, مع عدم القدرة على تعديل الأشياء التي تم تعريفها بداخله.


أمثلة
هنا وضعنا أمثلة شاملة حول أماكن وضع الكلمة final شاهد الأمثلة »
هنا وضعنا مثال مهم يعطيك أفكار حول فائدة استعمال الكلمة final 

Java
التغليف في جافا


أنه يجب تعريف جميع الخصائص ( أي المتغيرات التي ستحفظ البيانات ) الموجودة في الكلاس كـ private. لأن تعريف الخصائص كـ private يعني أنه يمكن الوصول إليهم فقط من داخل الكلاس الموجودين فيه.

ثاني شيىء عليك التفكير فيه هو إيجاد طريقة للوصول إلى هذه الخصائص من الخارج. لذلك عليك تجهيز دوال نوعها public للتعامل مع هذه الخصائص, لأن الدوال التي نوعها public يمكن الوصول إليهم من أي مكان.

إذاً لتحقيق مبدأ التغليف, عليك تعريف الخصائص كـ private و تعريف الدوال التي تستخدم للوصول إليهم كـ public.


مفهوم دوال الـ setter و الـ getter
عند التعامل مع أي متغير ( أو خاصية ) فعندك خيارين و هما إما إعطاءه قيمة جديدة و إما الحصول على القيمة الموجودة فيه. و بما أنه يجب بناء دوال للتعامل مع كل خاصية من الخصائص الموجودة في الكلاس, ينصح بإعتماد أسماء متعارف عليها كالتالي:

إبدأ إسم كل دالة الهدف منها إعطاء قيمة للخاصية بالكلمة set ثم إسم الخاصية.
إبدأ إسم كل دالة الهدف منها الحصول على قيمة الخاصية بالكلمة get ثم إسم الخاصية.


أمثلة شاملة
الآن سنقوم بإنشاء كلاس إسمه Employee و فكرته تخزين معلومات الموظفين مثل الإسم name, الراتب salary, العمر age.
بعدها سنقوم بتجربة الكلاس Employee في الكلاس Main.

المثال الأول
•هنا لم نطبق مبدأ التغليف.

Employee.java
package employee;

public class Employee {

                        // يملك 3 خصائص Employee الكلاس
    String name;        // لأنه عبارة عن نص String الإسم نوعه
    int    age;         // لأنه عبارة عن رقم int العمر نوعه
    double salary;      // لأنه عبارة عن رقم كبير يمكن أن يحتوي على فاصلة double الراتب نوعه

}

Main.java
package employee;

public class Main {

    public static void main(String[] args) {

        // Employee من الكلاس e هنا قمنا بإنشاء الكائن
        Employee e = new Employee();

        // e هنا قمنا بوضع قيم لخصائص الكائن
        e.name   = "Mhamad";
        e.age    = 21;
        e.salary = 1500000;

        // e هنا قمنا بعرض قيم خصائص الكائن
        System.out.println("Name: "   +e.name);
        System.out.println("Age: "    +e.age);
        System.out.println("Salary: " +e.salary);

    }

}

•سنحصل على النتيجة التالية عند التشغيل.

Name: Mhamad
Age: 21
Salary: 1500000.0


الآن سنقوم بتعريف الخصائص كـ private و سنقوم بتعريف دوال نوعها public للتعامل مع هذه الخصائص.

المثال الثاني
•هنا قمنا بتطبيق مبدأ التغليف.

Employee.java
package employee;

public class Employee {

    // private الآن قمنا بتعريف الخصائص كـ
    private String name;
    private int    age;
    private double salary;


    public String getName() {            // name هذه الدالة ترجع قيمة المخزنة الخاصية
        return name;
    }

    public int getAge() {                // age هذه الدالة ترجع قيمة المخزنة الخاصية
        return age;
    }

    public double getSalary() {          // salary هذه الدالة ترجع قيمة المخزنة الخاصية
        return salary;
    }


    public void setName(String n) {      // name هذه الدالة نعطيها إسم فتقوم بوضعه للخاصية
        name = n;
    }

    public void setAge(int a) {          // age هذه الدالة نعطيها رقم فتقوم بوضعه للخاصية
        age = a;
    }

    public void setSalary(double s) {    // salary هذه الدالة نعطيها رقم فتقوم بوضعه للخاصية
        salary = s;
    }

}

Main.java
package employee;

public class Main {

    public static void main(String[] args) {

        // Employee من الكلاس e هنا قمنا بإنشاء الكائن
        Employee e = new Employee();

        // Setter من خلال دوال الـ e هنا قمنا بوضع قيم لخصائص الكائن
        e.setName("Mhamad");
        e.setAge(21);
        e.setSalary(1500000);

        // Getter من خلال دوال الـ e هنا قمنا بعرض قيم خصائص الكائن
        System.out.println("Name: "   +e.getName());
        System.out.println("Age: "    +e.getAge());
        System.out.println("Salary: " +e.getSalary());

    }

}

•سنحصل على النتيجة التالية عند التشغيل.

Name: Mhamad
Age: 21
Salary: 1500000.0


من فوائد التغليف أيضاً أنه يتيح لك وضع شروط لتخزين البيانات, كما أنه يتيح لك الحصول على البيانات بالطريقة التي تريدها.

الآن سنقوم بإضافة بعض التعديلات على دوال الـ Setter و الـ Getter.

المثال الثالث
•هنا قمنا بتطبيق مبدأ التغليف مع وضع بعض القيود عند إدخال البيانات, و إضافة بعض التعديلات عند جلب البيانات.

•قمنا بتعليم الشروط و التعديلات الجديدة باللون الرمادي.

Employee.java
package employee;

public class Employee {

    private String name;
    private int    age;
    private double salary;


    public String getName() {            // مع إظهار جملة صغيرة قبلها name هذه الدالة ترجع قيمة الخاصية
        return "Name: " +name;
    }

    public int getAge() {
        return age;
    }

    public double getSalary() {
        return salary;
    }


    public void setName(String n) {      // بشرط أن يكون الإسم أكبر من 3 أحرف name هذه الدالة نعطيها رقم فتقوم بوضعه للخاصية
        if (n.length() < 3) {
            System.out.println("Name is too short, name can't be less then 3 characters!");
        }
        else {
            name = n;
        }
    }

    public void setAge(int a) {          // age هذه الدالة نعطيها رقم فتقوم بوضعه للخاصية
        age = a;
    }

    public void setSalary(double s) {    // salary هذه الدالة نعطيها رقم فتقوم بوضعه للخاصية
        salary = s;
    }

}

Main.java
package employee;

public class Main {

    public static void main(String[] args) {

        Employee e = new Employee();

        e.setName("dj");     // لن يقبله لأنه أصغر من حرفين
        e.setAge(21);
        e.setSalary(1500000);

        System.out.println(e.getName());
        System.out.println("Age: "    +e.getAge());
        System.out.println("Salary: " +e.getSalary());

    }

}

•سنحصل على النتيجة التالية عند التشغيل.

Name is too short, name can't be less then 3 characters!
Name: null
Age: 21
Salary: 1500000.0
•لاحظ أنه لم يقبل الإسم الذي أدخلناه له لأنه أصغر من ثلاثة أحرف, لذلك طبع الرسالة التي قمنا بتجهيزها في حال تم إدخال قيمة أصغر من ثلاثة أحرف, كما أنه لم يضع الإسم الذي قمنا بإدخاله في الخاصية name لأنه كما نلاحظ أن الدالة getName() قامت بإرجاع القيمة null



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

Java
الوراثة في جافا


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

فكرة الوراثة بسيطة, لكن فائدتها قوية جداً. فمثلاً إذا كنت تريد إنشاء كلاس جديد و لاحظت أنه يوجد كلاس جاهز يحتوي على كودات قد تفيدك يمكنك استغلالها بدل كتابتها من الصفر, أي يمكنك جعل الكلاس الذي قمت بتعريفه يرث هذا الكلاس, و بعدها يمكنك إستخدام جميع المتغيرات و الدوال التي ورثها الكلاس الجديد من الكلاس الجاهز.


مفهوم Superclass و Subclass
الكلاس الذي يرث من كلاس آخر يسمى Subclass و يسمى أيضاً ( derived class, extended class أو child class ).
الكلاس الذي يورث محتوياته لكلاس آخر يسمى Superclass و يسمى أيضاً ( base class أو parent class ).

مثال
الآن لنفترض أننا قمنا بتعريف كلاس إسمه A يحتوي على متغير إسمه x و دالة إسمها printA().
بعدها قمنا بإنشاء كلاس جديد فارغ إسمه B و قلنا أنه يرث من الكلاس A. إذاً هذا يعني أن الكلاس B أصبح يملك نسخة من جميع المتغيرات و الدوال الموجودة في الكلاس A.




إنتبه
الـ Subclass يرث كل شيء موجود في الـ Superclass بشكل تلقائي ما عدا الـكونستركتور.
مع العلم أنه يمكن استدعاء كونستركتور الـ Superclass من الـ Subclass بواسطة الكلمة super التي سنشرحها لاحقاً في هذا الدرس.

الكلمة extends
الكلمة extends تستخدم لجعل الكلاس يرث من كلاس آخر.


مكان وضع الكلمة extends
نضع الكلمة extends بعد إسم الكلاس, ثم نضع بعدها إسم الكلاس الذي نريد الوراثة منه.
الكود التالي يعني أن الكلاس B يرث من الكلاس A.

مثال
class A {

}


class B extends A {

}

إنتبه: في حال كنت تحاول الوراثة من كلاس غير موجود سيظهر لك الخطأ التالي: java.lang.ExceptionInInitializerError



مثال
الآن لنفترض أننا قمنا بتعريف كلاس إسمه A يحتوي على متغير إسمه x و دالة إسمها printA(). بعدها قمنا بإنشاء كلاس جديد فارغ إسمه B و قلنا أنه يرث من الكلاس A. إذاً هذا يعني أن الكلاس B أصبح يملك نسخة من جميع المتغيرات و الدوال الموجودة في الكلاس A.

بعد إنشاء هذا الكلاس, سنقوم بإنشاء الكلاس Test لتجربته.

مثال
A.java
package inheritance;

public class A {

    public int x;

    public void printA() {
        System.out.println("I am from class A");
    }

}

B.java
package inheritance;

// A يرث المتغيرات و الدوال الموجودة في الكلاس B هنا قلنا أن الكلاس
public class B extends A {

    // A سيحتوي المتغيرات و الدوال الموجودة في الكلاس B إذاً أي كائن من الكلاس

}

Test.java
package inheritance;

public class Test {

    public static void main(String[] args) {

        // أم لا A لنتأكد إذا كان يحتوي على الأشياء الموجودة في الكلاس B هنا قمنا بإنشاء كائن من الكلاس
        B b = new B();

        // A من الكلاس B التي ورثها الكلاس printA() هنا قمنا باستدعاء الدالة
        b.printA();

        // أيضاً, يمكننا إعطائه قيمة و عرض قيمته x يملك متغير إسمه b و بما أن الكائن
        b.x = 123;
        System.out.println("x: " +b.x);

    }

}

•سنحصل على النتيجة التالية عند التشغيل.

I am from class A
x: 123
الكلمة super
الكلمة super تستخدم للأهداف التالية:

للتمييز بين الأشياء (المتغيرات و الدوال) الموجودة في الـ Superclass و Subclass في حال كانت الأسماء مستخدمة في كلا الكلاسَين.
لإستدعاء الـكونستركتور الموجود في الـ Superclass.
إذاً الكلمة super تستخدم لإستدعاء الأشياء الموجودة في الـ Superclass.



طريقة استخدام الكلمة super لإستدعاء متغير من الـ Superclass
نضع الكلمة super, بعدها نقطة, ثم نضع إسم المتغير الذي نريد إستدعائه من الـ Superclass.

super.variableName
شاهد المثال »



طريقة استخدام الكلمة super لإستدعاء دالة من الـ Superclass
نضع الكلمة super, بعدها نقطة, ثم نضع إسم الدالة التي نريد إستدعائها من الـ Superclass.

super.methodName();
شاهد المثال »


إنتبه
في حال قام الـ Subclass بتعريف دالة كانت أصلاً موجودة في الـ Superclass يجب كتابة الكلمة @Override قبلها مباشرةً, و هكذا سيفهم المترجم أن الـ Subclass قام بتعريف الدالة التي ورثها من الـ Superclass من جديد.



طريقة استخدامها عند استدعاء كونستركتور
يمكن استدعاء كونستركتور الـ Superclass من داخل كونستركتور الـ Subclass من خلال الكلمة super.

super()                    // عند استدعاء كونستركتور فارغ نكتب هكذا فقط

// أو هكذا

super( parameter List )    // عند استدعاء كونستركتور يحتوي على باراميترات, عليك تمرير قيم له.

في حال كان الـ Superclass يملك فقط كونستركتور لا يحتوي أي باراميترات (أي مثل كونستركتور إفتراضي), سيقوم المترجم باستدعائه بشكل تلقائي في الـ Subclass حتى لو لم تقم باستدعائه بنفسك.

شاهد الأمثلة »


و في حال كان الـ Superclass يملك أكثر من كونستركتور, ستكون مجبر على تعريف كونستركتور في الـ Subclass يستدعي أي كونستركتور من الـ construtors الموجودين في Superclass.

شاهد الأمثلة »

أشكال الوراثة في جافا
في أي لغة برمجة, يوجد 4 أشكال أساسية للوراثة كما في الصورة التالية.




جافا لا تدعم تعدد الوراثة كما يوجد في بعض لغات البرمجة الأخرى, أي لا يمكن للكلاس الواحد الوراثة في نفس الوقت من أكثر من كلاس.
إذاً الكلاس الوحد لا يمكنه أن يفعل extends لأكثر من كلاس , بمعنى أن كل كلاس يمكنه وراثة كلاس واحد.

جافا تدعم تعدد الوراثة من خلال interface, أي من أجل الوراثة من أكثر من كلاس يجب إستخدام الكلمة interface بدلاً من class, و استخدام الكلمة implements بدلاً من extends ضمن شروط معينة. ستتعلم تعدد الوراثة في درس لاحق.


بالنسبة للوراثة المتتالية ( أو المتعددة المستويات ), دائماً آخر كلاس يرث جميع المتغيرات و الدوال الموجودة في الكلاسات الأعلى منه.



IS-A Relationship
IS-A هو مجرد أسلوب لفهم علاقة الكائنات مع بعضها. فمثلاً لنفترض أننا سنقوم بإنشاء برنامج لحفظ معلومات عن الحيوانات, يجب في البداية تجهيز كلاس أساسي يمثل جميع الخصائص المشتركة بين الحيوانات, بعدها يجب تقسيم الحيوانات إلى أربع فئات أساسية (ثدييات, زواحف, طيور, حيوانات مائية). بعدها يجب تعريف كل حيوان ضمن الفئة التي ينتمي لها.




الآن سنقوم بتمثيل المطلوب في الصورة كبرمجة.

مثال
// هنا قمنا بتعريف الكلاس الأساسي لجميع الحيوانات
class Animal { }

// هنا قمنا بتعريف فئات الحيوانات
class Mammals  extends Animal { }
class Reptiles extends Animal { }
class Birds    extends Animal { }
class Aquatic  extends Animal { }

// هنا قمنا بتعريف 4 حيوانات مع وضع كل حيوان ضمن فئته
class Dog      extends Mammals { }
class Snack    extends Reptiles { }
class Parrot   extends Birds { }
class Dolphin  extends Aquatic { }

إذاً هنا يمكننا قراءة الكود كالتالي:

Dog يعتبر من الـ Mammals و يعتبر Animal أيضاً.
Snack يعتبر من الـ Reptiles و يعتبر Animal أيضاً.
Parrot يعتبر من الـ Birds و يعتبر Animal أيضاً.
Dolphin يعتبر من الـ Aquatic و يعتبر Animal أيضاً.
العامل instanceof
العامل instanceof يستخدم لمعرفة إذا كان الكائن ينتمي إلى كلاس معين أم لا.
يرجع true في حال كان الكائن ينتمي لكلاس معين, غير ذلك يرجع false.


مثال
الآن سنقوم بكتابة الكود السابق و استخدام العامل instanceof لمعرفة الكلاسات التي ينتمي إليها الكائن.

ملاحظة: هنا قمنا بتعريف جميع الكلاسات كـ private وراء بعضهم في نفس ملف الجافا لأن جافا تسمح بذلك. لكننا لا ننصح باستخدام هذا الأسلوب في البرامج العادية.
كما أننا سنستخدم العامل instanceof في دروس لاحقة.

مثال
Test.java
package inheritance;

// هنا قمنا بتعريف الكلاس الأساسي لجميع الحيوانات
class Animal { }

// هنا قمنا بتعريف فئات الحيوانات
class Mammals  extends Animal { }
class Reptiles extends Animal { }
class Birds    extends Animal { }
class Aquatic  extends Animal { }

// هنا قمنا بتعريف 4 حيوانات مع وضع كل حيوان ضمن فئته
class Dog      extends Mammals { }
class Snack    extends Reptiles { }
class Parrot   extends Birds { }
class Dolphin  extends Aquatic { }


public class Test {

    public static void main(String[] args) {

        // Dog هنا قمنا بإنشاء كائن من الكلاس
        Dog dog1 = new Dog();

        // dog1 هنا قمنا باختبار الكلاسات التي ينتمي إليها الكائن
        System.out.println( dog1 instanceof Dog);
        System.out.println( dog1 instanceof Animal);

    }

}

•سنحصل على النتيجة التالية عند التشغيل.

true
true
الكلاس Object
في جافا, يوجد مئات الكلاسات الجاهزة التي تم بناءها بشكل هرمي كما في الصورة التالية.



جميع الكلاسات في جافا ترث بشكل تلقائي من الكلاس Object لأنه فعلياً يأتي في رأس الهرم و بالتالي فوق الجميع.
إذاً الكلاس Object هو أعلى Superclass في جافا.

و إذا لاحظت في الدروس السابقة أن أي كلاس جديد كنا نستخدمه يحتوي على الدوال equals(), hashcode(), toString() إلخ..
سبب وجود هذه الدوال في كل كلاس كنا نستخدمه أنه ورثهم من الكلاس Object.

تعليقات