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

الصفحات

مفهوم ال Modifiers والتغليف في جافا

مفهوم الـ Modifiers

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

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


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

  • Access Modifiers

  • Non Access Modifiers


في المثال التالي قمنا باستخدام الكلمتين public و private اللتين تعتبران من الـ Access Modifiers.
و قمنا باستخدام الكلمتين final و static اللتين تعتبران من الـ Non Access Modifiers.

مثال

Student.java
	  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 حتى يكون متاح للإستخدام من أي مكان, مع عدم القدرة على تعديل الأشياء التي تم تعريفها بداخله.


أمثلة

Javaمثال حول الكلمة static

في هذا المثال سنقوم بتعريف كلاس إسمه Example يحتوي على الأشياء التالية:

  1. متغير إسمه a معرف كـ static.

  2. دالة إسمها print تعرض قيمة المتغير a.

  3. دالة إسمها staticPrint تعرض أيضاً قيمة المتغير a لكنها معرفة كـ static.

بعدها سنقوم بتغير و عرض قيمة المتغير a بعدة طرق.

Example.java
	  public class Example {

	  // Example.a إذاً يمكننا الوصول إليه من خلال كائن أو من أي مكان مباشرةً بهذا الشكل .static كـ a قمنا بتعريف المتغير
	  public static int a;

	  // Example هذه الدالة لا يمكن استدعاءها إلا من خلال كائن من الكلاس
	  public void print() {
	  System.out.println( "a: " +a );
	  }

	  // Example.staticPrint(); هذه الدالة يمكن استدعائها مباشرةً من أي مكان بهذا الشكل
	  public static void staticPrint() {
	  System.out.println( "a: " +a );
	  }

	  }
	

Main.java
	  public class Main {

	  public static void main(String[] args) {

	  // Example من الكلاس e2 و e1 هنا قمنا بإنشاء كائنين
	  Example e1 = new Example();
	  Example e2 = new Example();

	  Example.a = 10;          // Example مباشرةً من الكائن الكلاس a هنا قمنا بإعطاء قيمة لـ
	  Example.staticPrint();   // static التي يمكننا استدعائها مباشرةً من الكلاس لأن نوعها staticPrint() من خلال الدالة a هنا قمنا بعرض قيمة
	  e1.staticPrint();        // أيضاً e1 التي يمكننا استدعائها من الكائن staticPrint() من خلال الدالة a هنا قمنا بعرض قيمة
	  e2.staticPrint();        // أيضاً e2 التي يمكننا استدعائها من الكائن staticPrint() من خلال الدالة a هنا قمنا بعرض قيمة

	  e1.a = 22;    // e1 من خلال الكائن a هنا قمنا بتغير قيمة
	  e1.print();   // e1 التي وصلنا إليها من خلال الكائن print() ثم قمنا بعرضها من خلال الدالة

	  e2.a = 75;    // e2 من خلال الكائن a هنا قمنا بتغير قيمة
	  e2.print();   // e2 التي وصلنا إليها من خلال الكائن print() ثم قمنا بعرضها من خلال الدالة

	  }

	  }
	

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

	  a: 10
	  a: 10
	  a: 10
	  a: 22
	  a: 75 
	

ملاحظة

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

Javaمثال عملي يوضح فائدة الكلمة static

الآن لنفترض أننا نريد إنشاء كلاس إسمه book لتخزين الكتب مع مراعاة الشروط التالية:
كل كتاب يجب ذكر إسمه bookName, إسم المؤلف author, و عدد الصفحات pageNumbers. بالإضافة أنه يجب حفظ عدد الكتب الكلي.
لحفظ عدد الكتب, يمكننا إنشاء متغير نوعه private و static يزيد واحداً كلما قمنا بإنشاء كائن جديد ( أي كتاب جديد, لأن كل كتاب عبارة عن كائن من الكلاس book ).

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

Book.java
	  public class Book {
	  // هنا قمنا بتعريف الخصائص التي يجب أن تتوفر في كل كتاب
	  public String bookName;            // سنستخدم هذا المتغير لحفظ إسم الكتاب
	  public String bookAuthor;          // سنستخدم هذا المتغير لحفظ إسم المؤلف
	  public int    pageNumbers;         // سنستخدم هذا المتغير لحفظ عدد صفحات الكتاب

	  private static int bookCounter;    // private سنستخدم هذا المتغير لحفظ عدد الكتب, لا يمكن لأحد من خارج هذا الكلاس تغيير عدد الكتب لأنه

	  // عند إنشاء كائنات من هذا الكلاس سنستخدم هذا الكونستركتور لإدخال معلومات الكتب مباشرةً عند تعريفهم
	  public Book(String bookName, String bookAuthor, int pageNumbers) {
	  this.bookName = bookName;
	  this.bookAuthor = bookAuthor;
	  this.pageNumbers = pageNumbers;

	  bookCounter++;     // سيكون موحد لجميع الكائنات. هنا كلما قمنا بإنشاء كائن جديد سيزيد واحداً و هكذا سنحصل على عدد جميع الكتب التي أنشأت static بما أن نوعه
	  }

	  // هذه الدالة تطبع محتوى الكائن (أي الكتاب) الذي قام باستدعائها
	  public void printBookInfo() {
	  System.out.println("Book: " +bookName);
	  System.out.println("Author: " +bookAuthor);
	  System.out.println("Number of pages: " +pageNumbers);
	  System.out.println("------------------------------");
	  }

	  // static هذه الدالة تطبع عدد الكائنات (أي الكتب) و يمكن استدعائها مباشرةً من الكلاس لأن نوعها
	  public static void printTotalNumberOfBooks() {
	  System.out.println("The total number of books is: " +bookCounter);
	  }

	  }
	

Main.java
	  public class Main {

	  public static void main(String[] args) {

	  // هنا قمنا بإنشاء ثلاث كتب
	  Book b1 = new Book("java", "Mhamad Harmush", 500);
	  Book b2 = new Book("HTML", "Hala Harmush"  , 320);
	  Book b3 = new Book("C++" , "Omar El Koussa", 210);

	  // هنا قمنا بعرض خصائص كل كتاب
	  b1.printBookInfo();
	  b2.printBookInfo();
	  b3.printBookInfo();

	  // هنا قمنا بعرض عدد جميع الكتب
	  Book.printTotalNumberOfBooks();

	  }

	  }
	

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

	  Book: java
	  Author: Mhamad Harmush
	  Number of pages: 500
	  ------------------------------
	  Book: HTML
	  Author: Hala Harmush
	  Number of pages: 320
	  ------------------------------
	  Book: C++
	  Author: Omar El Koussa
	  Number of pages: 210
	  ------------------------------
	  The total number of books is: 3 
	

Javaأمثلة شاملة حول أماكن وضع الكلمة final

في المثال التالي سنقوم بتعريف 3 متغيرات نوعها final.
الهدف هنا معرفة الطرق المسموح فيها إعطاء قيمة للمتغير المعرف كـ final.

المثال الأول

Example.java
	  public class Example {

	  public final int a = 10;   // و أعطيناه قيمة مباشرةً عند تعريفه final هنا قمنا بتعريف متغير نوعه
	  public final int b;        // بدون تحديد قيمته final هنا قمنا بتعريف متغير نوعه
	  public final int c;        // بدون تحديد قيمته final هنا قمنا بتعريف متغير نوعه

	  public Example(int b) {
	  this.b = b;            // من الكائن, أي أن الكائن هو من سيقوم بتحديدها b هنا سيتم تحديد قيمة المتغير
	  c = 50;                // مباشرةً عند إنشاء كائن, أي أن الكائن سيملكها هكذا c هنا سيتم تحديد قيمة المتغير
	  }

	  }
	


في المثال التالي سنقوم بإنشاء 2 كلاس, الكلاس A و الكلاس B الذي سيرث منه.
في الكلاس A سنقوم بتعريف دالة عادية و دالة نوعها final.
في الكلاس B سنفعل override للدالة التي ليس نوعها final.
بعدها سنقوم بإنشاء الكلاس Main لتجربة الكود.
الهدف هنا معرفة أن الدوال المعرفة كـ final لا يمكن تعريفها من جديد في الكلاس الذي يرثها. إذاً الكلمة final تمنع الـ override.

المثال الثاني

A.java
	  public class A {

	  // final لهذه الدالة لأنها معرفة كـ Override أي كلاس سيرث من هذا الكلاس, لا يمكنه أن يفعل
	  public final void firstPrint() {
	  System.out.println("welcome to java");
	  }

	  // final لهذه الدالة لأنها غير معرفة كـ Override أي كلاس سيرث من هذا الكلاس, يمكنه أن يفعل
	  public void secondPrint() {
	  System.out.println("welcome to java");
	  }

	  }
	

B.java
	  // و كأنها موجودة فيه تماماً A إستخدام الأشياء الموجودة في B أي يمكن للكلاس .A يرث من الكلاس B هنا قلنا أن الكلاس
	  public class B extends A {

	  // B بالنسبة للكلاس secondPrint هنا قمنا بإعادة كتابة محتوى الدالة
	  @Override
	  public void secondPrint() {
	  System.out.println("class B override my content!");
	  }

	  }
	

Main.java
	  public class Main {

	  public static void main(String[] args) {

	  A a = new A();      // a إسمه A هنا قمنا بإنشاء كائن من الكلاس
	  B b = new B();      // b إسمه B هنا قمنا بإنشاء كائن من الكلاس

	  a.firstPrint();     // A هنا سيتم تنفيذ الدالة كما تم تعريفها في الكلاس
	  a.secondPrint();    // A هنا سيتم تنفيذ الدالة كما تم تعريفها في الكلاس

	  b.firstPrint();     // A هنا سيتم تنفيذ الدالة كما تم تعريفها في الكلاس
	  b.secondPrint();    // B هنا سيتم تنفيذ الدالة كما تم تعريفها في الكلاس

	  }

	  }
	

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

	  welcome to java
	  welcome to java
	  welcome to java
	  class B override my content! 
	


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

المثال الثالث

FatherOfJava.java
	  // لمنع الوراثة منه فقط final هنا قمنا بتعريف الكلاس كـ
	  public final class FatherOfJava {

	  public String name = "James Arthur Gosling";
	  public String born = "May 19, 1955";

	  public void story() {
	  System.out.println(name+ ", born on " +born+ ". He is a Canadian computer scientist,"
	  + " best known as the father of the Java programming language");
	  }

	  }
	

Main.java
	  public class Main {

	  public static void main(String[] args) {

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

	  // كما تم تعريفها في الكلاس الأساسي name هنا سيتم عرض قيمة المتغير
	  System.out.println("Name of the father of java: " +obj.name);

	  // FatherOfJava هنا سيتم تنفيذ الدالة كما تم تعريفها في الكلاس
	  obj.story();

	  }

	  }
	

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

	  Name of the father of java: James Arthur Gosling
	  James Arthur Gosling, born on May 19, 1955. He is a Canadian computer scientist, best known as the father of the Java programming language 
	

Javaمثال عملي يوضح فائدة الكلمة final

الآن لنفترض أننا نريد إنشاء كلاس إسمه Student لتخزين بيانات الطلاب مع مراعاة الشروط التالية:
كل طالب يجب ذكر إسمه name, إختصاصه Specialization, بالإضافة أنه يجب إعطاء كل طالب رقم تسلسلي id لا يمكن تغييره و تخزين عدد جميع الطلاب studentsCounter.
لحفظ عدد الطلاب, يمكننا تعريف المتغير studentsCounter كـ private و static يزيد واحداً كلما قمنا بإنشاء كائن جديد ( أي طالب جديد, لأن كل طالب عبارة عن كائن من الكلاس Student ).
و بالنسبة لرقم الـ id الذي يجب توليده بشكل تسلسلي لكل طالب جديد, فيمكننا تعريف متغير نوعه final و جعله يزيد واحداً بشكل تسلسلي لكل طالب جديد.

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

Student.java
	  public class Student {
	  // هنا قمنا بتعريف الخصائص التي يجب أن تتوفر في كل طالب
	  public String name;                   // سنستخدم هذا المتغير لحفظ إسم الطالب
	  public String specialisation;         // سنستخدم هذا المتغير لحفظ إسم تخصص الطالب

	  public final int id;                  // سنستخدم هذا المتغير لحفظ الرقم التسلسلي للطالب و الذي يمكن الوصول إليه من خلال أي كائن مع عدم القدرة على تبديل قيمته
	  private static int studentsCounter;   // private سنستخدم هذا المتغير لحفظ عدد الطلاب, لا يمكن لأحد من خارج هذا الكلاس تغيير عدد الطلاب لأنه

	  // عند إنشاء كائنات من هذا الكلاس سنستخدم هذا الكونستركتور لإدخال معلومات الطلاب مباشرةً عند تعريفهم
	  public Student(String name, String specialisation) {
	  this.name = name;
	  this.specialisation = specialisation;

	  studentsCounter++;                // سيكون موحد لجميع الكائنات. هنا كلما قمنا بإنشاء كائن جديد سيزيد واحداً و هكذا سنحصل على عدد جميع الطلاب التي أنشأت static بما أن نوعه
	  id = studentsCounter;             // هنا كلما أضيف طالب جديد سيأخذ عدد الطلاب الحالي كرقم تسلسلي, و هكذا سيملك كل طالب رقم تسلسلي مختلف لا يمكن تغييره
	  }

	  // هذه الدالة تطبع محتوى الكائن (أي بيانات الطالب) الذي قام باستدعائها
	  public void printStudentInfo() {
	  System.out.println("Student: " +name);
	  System.out.println("ID: " +id);
	  System.out.println("Specialization: " +specialisation);
	  System.out.println("------------------------------");
	  }

	  // static هذه الدالة تطبع عدد الكائنات (أي الطلاب) و يمكن استدعائها مباشرةً من الكلاس لأن نوعها
	  public static void printTotalNumberOfStudents() {
	  System.out.println("The Total number of Students is: " +studentsCounter);
	  }

	  }
	

Main.java
	  public class Main {

	  public static void main(String[] args) {

	  // هنا قمنا بإنشاء خمس طلاب
	  Student s1 = new Student("Mhamad", "Computer Science");
	  Student s2 = new Student("Hala"  , "Computer Science");
	  Student s3 = new Student("Marwan", "IT");
	  Student s4 = new Student("Ahmad" , "Civil Engineer");
	  Student s5 = new Student("Salam" , "Telecom");

	  // هنا قمنا بعرض خصائص كل طالب
	  s1.printStudentInfo();
	  s2.printStudentInfo();
	  s3.printStudentInfo();
	  s4.printStudentInfo();
	  s5.printStudentInfo();

	  // هنا قمنا بعرض عدد جميع الطلاب
	  Student.printTotalNumberOfStudents();

	  }

	  }
	

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

	  Student: Mhamad
	  ID: 1
	  Specialization: Computer Science
	  ------------------------------
	  Student: Hala
	  ID: 2
	  Specialization: Computer Science
	  ------------------------------
	  Student: Marwan
	  ID: 3
	  Specialization: IT
	  ------------------------------
	  Student: Ahmad
	  ID: 4
	  Specialization: Civil Engineer
	  ------------------------------
	  Student: Salam
	  ID: 5
	  Specialization: Telecom
	  ------------------------------
	  The Total number of Students is: 5 
	

مفهوم التغليف في جافا

التغليف: يعني Encapsulation في اللغة الإنجليزية. و هو عبارة عن أسلوب يمكن اتباعه لإخفاء البيانات الأساسية في الكلاس, أي لإخفاء الخصائص الموجودة فيه ( Global Variables ), و جعل الكلاسات الأخرى قادرة على التعامل مع هذه الخصائص فقط من خلال دوال يقوم بإنشائها المبرمج الأساسي للكلاس.


الأسلوب المتبع في عملية التغليف

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

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

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

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


مفهوم دوال الـ Setter و الـ Getter

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

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

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

أمثلة شاملة حول التغليف في جافا

الآن سنقوم بإنشاء كلاس إسمه Employee و فكرته تخزين معلومات الموظفين مثل الإسم name, الراتب salary,

العمر age.
بعدها سنقوم بتجربة الكلاس Employee في الكلاس Main.

المثال الأول

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

	}
  

Main.java
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
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
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
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
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.



فوائد التغليف في جافا

  • يمكنه جعل الأشياء الموجودة في الكلاس قابلة للقراءة أو للكتابة من قبل الكلاسات الخارجية.

  • يسمح للكلاس بوضع شروط أثناء تخزين البيانات.

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