اللبنة الأساسية للبرمجة الكائنية في جافا
تهانينا على وصولك إلى هذه المرحلة المتقدمة! بعد أن أصبحتَ بارعاً في تنظيم الكود باستخدام الدوال والتعامل مع مجموعات البيانات باستخدام المصفوفات، حان الوقت للانتقال إلى المفهوم الذي تُبنى عليه قوة جافا الحقيقية: **البرمجة الكائنية التوجه (Object-Oriented Programming - OOP)**. هذه الوحدة هي نقطة تحول جوهرية في رحلتك البرمجية.
سنغوص في فهم كيف يمكننا نمذجة الكيانات الحقيقية (مثل شخص، سيارة، منتج) داخل برامجنا كـ **كائنات (Objects)**، وكيف تُستخدم **الكلاسات (Classes)** كقوالب لإنشاء هذه الكائنات. هذا الأسلوب البرمجي سيجعل كودك أكثر مرونة، قابلية للتوسع، وإعادة الاستخدام.
بنهاية هذه الوحدة، ستكون قادرًا على:
تخيل أنك مهندس معماري وتريد بناء عدة منازل متشابهة. لن تبدأ كل مرة من الصفر في رسم مخطط جديد لكل منزل. بدلاً من ذلك، ستقوم بإنشاء **مخطط (Blueprint)** واحد يحدد جميع التفاصيل: عدد الغرف، مكان الأبواب والنوافذ، نوع المواد، إلخ.
في البرمجة الكائنية، **الكلاس (Class)** هو هذا المخطط. إنه **القالب (Template)** أو **الفئة (Category)** التي تُعرّف الخصائص (ماذا تملك الكائنات؟) والسلوكيات (ماذا تفعل الكائنات؟) التي ستشترك فيها مجموعة من الكائنات المتشابهة.
يحتوي الكلاس عادةً على:
بشكل أساسي، الكلاس يصف **نوعًا** معينًا من الكائنات. هو ليس الكائن نفسه، بل هو مجرد وصف لكيفية بناء هذا الكائن.
class ClassName { // اسم الكلاس يبدأ بحرف كبير (convention)
// خصائص (Attributes / Fields)
// نوع_بيانات خاصية1;
// نوع_بيانات خاصية2;
// كونستركتورات (Constructors) - دوال خاصة لإنشاء الكائن
// public ClassName(...) { ... }
// دوال (Methods) - سلوكيات الكائن
// public void methodName() { ... }
}
هذا الكلاس يصف الخصائص والسلوكيات الأساسية التي يمكن أن يمتلكها أي "شخص":
class Person {
// الخصائص (Attributes / Fields)
String name; // اسم الشخص
String gender; // جنس الشخص
String job; // وظيفته
int age; // عمره
// الدالة (Method) - سلوك يمكن أن يقوم به الشخص
void printInfo() {
System.out.println("Name: " + name);
System.out.println("Gender: " + gender);
System.out.println("Job: " + job);
System.out.println("Age: " + age);
}
}
في هذا المثال، `Person` هو الكلاس. إنه يحدد أن كل كائن من نوع `Person` سيكون لديه `name` و`gender` و`job` و`age`، وسيكون قادرًا على تنفيذ دالة `printInfo()`.
إذا كان الكلاس هو المخطط الهندسي للمنزل، فإن **الكائن (Object)** هو المنزل **الحقيقي الملموس** الذي تم بناؤه فعلاً بناءً على هذا المخطط. الكائن هو **نسخة (Instance)** حية ومحددة من الكلاس. كل كائن له خصائصه الخاصة التي تميزه عن غيره من الكائنات المشتقة من نفس الكلاس.
يتم إنشاء الكائن في جافا باستخدام الكلمة المفتاحية **`new`** متبوعة باسم الكونستركتور الخاص بالكلاس.
public class Main {
public static void main(String[] args) {
// إنشاء كائن جديد اسمه 'ahmad' من الكلاس Person
Person ahmad = new Person();
// تعيين قيم للخصائص الخاصة بالكائن 'ahmad'
ahmad.name = "أحمد";
ahmad.gender = "ذكر";
ahmad.job = "مهندس";
ahmad.age = 30;
// استدعاء الدالة printInfo() الخاصة بالكائن 'ahmad'
System.out.println("معلومات أحمد:");
ahmad.printInfo();
System.out.println("\n------------------\n");
// إنشاء كائن آخر اسمه 'sara' من نفس الكلاس Person
Person sara = new Person();
sara.name = "سارة";
sara.gender = "أنثى";
sara.job = "طبيبة";
sara.age = 28;
System.out.println("معلومات سارة:");
sara.printInfo();
}
}
// الكلاس Person (يجب أن يكون في نفس الملف أو ملف منفصل Person.java)
class Person {
String name;
String gender;
String job;
int age;
void printInfo() {
System.out.println("Name: " + name);
System.out.println("Gender: " + gender);
System.out.println("Job: " + job);
System.out.println("Age: " + age);
}
}
الناتج المتوقع:
معلومات أحمد:
Name: أحمد
Gender: ذكر
Job: مهندس
Age: 30
------------------
معلومات سارة:
Name: سارة
Gender: أنثى
Job: طبيبة
Age: 28
في هذا المثال، `ahmad` و `sara` هما كائنان منفصلان، كل منهما يمتلك نسخته الخاصة من الخصائص (`name`, `gender`, `job`, `age`). فـ `ahmad.name` مختلفة عن `sara.name`. لكن كلاهما يستخدم نفس الدالة `printInfo()` المعرفة في الكلاس `Person`.
لفهم العلاقة بين الكلاس والكائن بشكل أفضل، يمكننا استخدام التشبيهات التالية:
الكلاس موجود في مرحلة **التصميم (Design time)** أو **التصنيف (Categorization)**، بينما الكائن موجود في مرحلة **التنفيذ (Runtime)** أو **الواقع (Reality)**.
عند تعديل الكلاس (مثلاً، إضافة خاصية جديدة أو دالة جديدة)، فإن جميع الكائنات التي ستُنشأ لاحقاً من هذا الكلاس **ستَرث** هذه التعديلات تلقائياً. هذا يعكس مرونة البرمجة الكائنية وسهولة التوسع والصيانة.
في جافا، نستخدم مصطلح **خصائص (Attributes)** للإشارة إلى المتغيرات التي تُعرّف داخل الكلاس ولكن خارج أي دالة. هذه الخصائص هي التي تحدد حالة الكائن. هناك أنواع مختلفة من المتغيرات في جافا، ومن المهم التمييز بينها:
public class VariablesTypes {
// 1. متغير كائن (Instance Variable / Attribute)
// كل كائن من VariablesTypes سيكون له نسخة خاصة به من 'instanceCounter'
int instanceCounter;
// 2. متغير كلاس (Class Variable / Static Variable)
// جميع كائنات VariablesTypes ستشترك في نفس النسخة من 'classCounter'
static int classCounter = 0;
// الكونستركتور
public VariablesTypes() {
instanceCounter++; // زيادة عداد الكائن الحالي
classCounter++; // زيادة عداد الكلاس (يؤثر على جميع الكائنات)
}
// دالة (Method)
public int sum(int x, int y) {
// 3. متغير محلي (Local Variable)
// 'z' موجودة فقط داخل دالة sum
int z = x + y;
return z;
}
public static void main(String[] args) {
// إنشاء الكائن الأول
VariablesTypes obj1 = new VariablesTypes();
obj1.instanceCounter = 10; // تعيين قيمة لمتغير الكائن لـ obj1
// إنشاء الكائن الثاني
VariablesTypes obj2 = new VariablesTypes();
obj2.instanceCounter = 20; // تعيين قيمة لمتغير الكائن لـ obj2
System.out.println("obj1.instanceCounter: " + obj1.instanceCounter); // 10
System.out.println("obj2.instanceCounter: " + obj2.instanceCounter); // 20
// الوصول لمتغير الكلاس مباشرة من اسم الكلاس
System.out.println("VariablesTypes.classCounter: " + VariablesTypes.classCounter); // 2 (لأننا أنشأنا كائنين)
// مثال على استخدام دالة مع متغير محلي
System.out.println("مجموع 5 و 7 هو: " + obj1.sum(5, 7)); // z = 12 داخل الدالة
}
}
**الكونستركتور (Constructor)** هو دالة خاصة (ولكنها ليست دالة بالمعنى التقليدي) تُستدعى تلقائيًا في كل مرة يتم فيها **إنشاء كائن جديد** من الكلاس. وظيفته الأساسية هي **إعطاء قيم أولية لخصائص الكائن** وضمان أن الكائن في حالة صالحة عند إنشائه.
خصائص الكونستركتور:
هو كونستركتور بدون باراميترات، ويتم توفيره تلقائيًا من قبل جافا إذا لم تُعرف أي كونستركتورات أخرى في الكلاس. يقوم بتهيئة الخصائص بالقيم الافتراضية المذكورة سابقًا (0، 0.0، null، false).
class Person {
String name;
int age;
// الكونستركتور الافتراضي (أو الذي ستكتبه إذا أردت)
public Person() {
System.out.println("تم إنشاء كائن Person باستخدام الكونستركتور الافتراضي.");
}
void printInfo() {
System.out.println("Name: " + name + ", Age: " + age);
}
}
هو كونستركتور نقوم نحن بتعريفه ويستقبل باراميترات. يُستخدم لتهيئة خصائص الكائن بقيم معينة تُمرر عند إنشاء الكائن.
class Person {
String name;
int age;
// كونستركتور مخصص يستقبل الاسم والعمر
public Person(String n, int a) {
name = n; // تعيين قيمة الباراميتر n لخاصية name
age = a; // تعيين قيمة الباراميتر a لخاصية age
System.out.println("تم إنشاء كائن Person باستخدام الكونستركتور المخصص.");
}
void printInfo() {
System.out.println("Name: " + name + ", Age: " + age);
}
}
public class ConstructorExample {
public static void main(String[] args) {
// استخدام الكونستركتور المخصص لإعطاء قيم أولية
Person p1 = new Person("علي", 20); // يتم استدعاء public Person(String n, int a)
System.out.println("معلومات p1:");
p1.printInfo();
System.out.println("\n------------------\n");
// استخدام الكونستركتور الافتراضي (إذا لم يتم تعريف أي كونستركتورات أخرى)
// أو الكونستركتور الافتراضي الذي قمنا بتعريفه يدوياً
Person p2 = new Person(); // يتم استدعاء public Person()
p2.name = "سارة"; // يمكن تعيين القيم لاحقًا يدويًا
p2.age = 22;
System.out.println("معلومات p2:");
p2.printInfo();
}
}
// الكلاس Person (يجب أن يكون في نفس الملف أو ملف منفصل Person.java)
class Person {
String name;
int age;
// الكونستركتور الافتراضي
public Person() {
System.out.println("تم إنشاء كائن Person باستخدام الكونستركتور الافتراضي.");
}
// الكونستركتور المخصص
public Person(String n, int a) {
name = n;
age = a;
System.out.println("تم إنشاء كائن Person باستخدام الكونستركتور المخصص.");
}
void printInfo() {
System.out.println("Name: " + name + ", Age: " + age);
}
}
الناتج المتوقع:
تم إنشاء كائن Person باستخدام الكونستركتور المخصص.
معلومات p1:
Name: علي, Age: 20
------------------
تم إنشاء كائن Person باستخدام الكونستركتور الافتراضي.
معلومات p2:
Name: سارة, Age: 22
الكلمة المحجوزة **`this`** في جافا هي مرجع (reference) يشير إلى **الكائن الحالي (Current Object)** الذي يتم تنفيذ الكود داخله. تُستخدم بشكل أساسي داخل دوال الكلاس والكونستركتورات.
أهم استخدامات `this`:
class Employee {
String name;
int id;
// هنا، باراميترات الكونستركتور لها نفس أسماء الخصائص
public Employee(String name, int id) {
// 'this.name' تشير إلى خاصية الكائن 'name'
// 'name' بدون this تشير إلى باراميتر الكونستركتور
this.name = name;
this.id = id;
}
void printDetails() {
System.out.println("Employee Name: " + this.name + ", ID: " + this.id);
}
}
public class ThisKeywordExample {
public static void main(String[] args) {
Employee emp1 = new Employee("محمد", 101);
emp1.printDetails(); // الناتج: Employee Name: محمد, ID: 101
}
}
إذا لم تستخدم `this` والأسماء متطابقة، فإن جافا ستعتبر أنك تشير إلى المتغير المحلي (الباراميتر)، وليس خاصية الكائن. هذا يؤدي إلى عدم تعيين القيمة لخاصية الكائن.
class BadEmployee {
String name; // خاصية
int id; // خاصية
public BadEmployee(String name, int id) {
name = name; // هنا، أنت تسند قيمة الباراميتر 'name' إلى نفسه!
id = id; // ونفس الشيء للـ 'id'
}
void printDetails() {
System.out.println("Employee Name: " + name + ", ID: " + id);
}
}
public class BadThisExample {
public static void main(String[] args) {
BadEmployee emp2 = new BadEmployee("علي", 202);
emp2.printDetails(); // الناتج سيكون: Employee Name: null, ID: 0 (القيم الافتراضية)
}
}
في المثال الخاطئ، لم يتم تعيين قيم "علي" و 202 لخصائص الكائن `name` و `id`، بل تم إسناد قيم الباراميترات إلى نفسها، تاركةً خصائص الكائن بالقيم الافتراضية (`null` للنص و `0` للعدد الصحيح). استخدام `this` يحل هذه المشكلة بوضوح.
حان الوقت لتطبيق ما تعلمته عملياً! أنشئ كلاسًا جديدًا يمثل كيان "السيارة".
أنشئ كلاس اسمه Car يحتوي على ما يلي:
printCarInfo() (ترجع `void`) وظيفتها طباعة جميع معلومات السيارة (الماركة، الموديل، السنة) على سطر واحد أو عدة أسطر بشكل منسق.بعد إنشاء كلاس Car، انتقل إلى كلاس Main (أو أي كلاس آخر يحتوي على دالة `main`) وقم بـ:
Car.printCarInfo()** لعرض بياناته.توقع الناتج: يجب أن ترى معلومات ثلاث سيارات مختلفة مطبوعة على الكونسول.
// كلاس Car
class Car {
// الخصائص
// ...
// الكونستركتور
// ...
// الدالة
// ...
}
public class MainCarExample {
public static void main(String[] args) {
// أنشئ 3 كائنات من Car هنا
// مثال: Car car1 = new Car("...", "...", ...);
// ثم استدعِ دالة printCarInfo() لكل كائن
// example: car1.printCarInfo();
}
}
لقد وصلتَ بنجاح إلى نهاية هذه الوحدة الهامة، واكتسبتَ أساسيات البرمجة الكائنية التوجه في جافا. إليك أبرز النقاط التي تعلمتها:
تُعد البرمجة الكائنية فلسفة قوية ستمكنك من بناء برامج منظمة، قابلة للتوسع، وإعادة الاستخدام بشكل لم يكن ممكنًا باستخدام البرمجة الإجرائية فقط. هذه الوحدة هي نقطة البداية لمفاهيم أكثر عمقًا في OOP مثل الوراثة (Inheritance) والتغليف (Encapsulation) وتعدد الأشكال (Polymorphism)، والتي ستتعرف عليها في مستويات متقدمة من دراستك للبرمجة.
في هذه المرحلة، ستكون قد أتممت معظم وحدات المقرر الأساسية. الوحدة القادمة ستكون هي **الوحدة الختامية** حيث سنقوم بملخص شامل لكل ما تعلمته وتقديم بعض التوصيات لمواصلة رحلتك في عالم البرمجة والأمن السيبراني.