Java Object Oriented
Inheritance: به کلاس هایی که بعضی از خواصشون رو به ارث میبرن میگیم subclass یا child class و به کلاس هایی که باقی کلاسها از اونها خواصشون رو به ارث میبرن میگیم superclass یا parent class.
وقتی یک شی از کلاس فرزند درست میکنیم یه کپی از کلاس پدر ساخته میشه بخاطر اینکه کلاس فرزند باید به اشیای کلاس پدر دسترسی داشته باشه. دقت کنید که کلاس پدر میتونه رفرنس یک شی از کلاس فرزند رو نگهداره ولی در این حالت نمیتونه به اشیای کلاس فرزند دسترسی داشته باشه.
superclass x = new subclass();
ولی اگه با این حال اگه کلاس فرزند متدی رو بازنویسی کرده باشه رفرنس کلاس پدر متد بازنویسی شده رو میخونه.
class A { int num() { return 0; } }
class B extend A { @Override int num() { return 1; } }
A x = new B();
int data = x.num(); // data is 1;
تمام اعضای کلاس پدر به ارث میرسند بجز متد سازنده. اما میتونیم اون رو فراخوانی کنیم! برای توسعه کلاسها باید از کلمه extends استفاده کنیم و یک کلاس نمیتونه بیش از یک کلاس رو توسعه بده.
super: بسیار شبیه this هستش فقط دو تا فرق کوچیک داره.
زمانی استفاده میشه که اشیای کلاس فرزند هم نام اشیای کلاس پدر باشند و بخوایم به اشیای کلاس پدر دسترسی داشته باشیم.
زمانی که بخوایم متد سازنده کلاس پدر رو فراخوانی کنیم.
IS-A: فرض کنید کلاس dog از کلاس mammal ارث برده و کلاس mammal هم از کلاس animal ارث برده. تو این شرایط:
mammal IS-A animal
dog IS-A mammal
dog IS-A animal
با استفاده از اپراتور instanceof میتونیم وراثت رو چک کنیم
HAS-A: فرض کنید که animal یک شی از کلاس color داره به صورت زیر:
class Animal { Color c = new Color(); }
تو این شرایط:
animal HAS-A color
ما چهار نوع وراثت داریم:
single:
class A {}
class B extend A {}
multi level:
class A {}
class B extend A {}
class C extend B {}
hierarchical:
class A {}
class B extend A {}
class C extend A {}
multiple:
class A {}
class B {}
class C extend A,B {}
ما تو جاوا وراثت multiple رو نداریم ولی میتونیم این موضوع رو با interface حل کنیم.
interface A {}
interface B {}
class C implements A,B {}
خوب حالا ما میتونیم توی کلاس فرزند بعضی از توابع کلاس پدر رو بازنویسی کنیم. ولی باید از قوانین زیر پیروی کنیم:
- ورودی ها متد نباید تغییر کند.
- مقدار بازگشتی هم نباید تغییر کنه.
- دسترسی به متد نباید محدود تر بشه.
- public رو نباید private یا protected کنیم.
- به private دسترسی نداریم.
- فقط میتونیم protected رو public کنیم.
- فقط متدهایی که به اونها دسترسی داریم رو میشه بازنویسی کنیم. یعنی کلا private رو نمیشه و protected رو هم بیرون پکیج نمیشه.
- متد final قابل بازنویسی نیست.
- متد static قابل بازنویسی نیست ولی میشه هم نام اون متد ساخت به شرطی که یا static باشه یا ورودی های متد تغییر کرده باشه.
- متدی که نتونیم به ارث ببریم رو نمیتونیم بازنویسی کنیم. دوباره منظور static یا private هستش.
- متدی که بازنویسی میشه میتونه خطایی پرتاب کنه که متد اصلی پرتاب نمیکرده یا اگه متد اصلی خطایی پرتاب میکرده میتونه اون رو تغییر بده یا کلا اون رو حذف کنه.
- متد سازنده رو نمیتونیم بازنویسی کنیم.
Polymorphism: که فارسیش میشه چند ریختی. یکم از ماجرا رو میشه از اسمش متوجه شد ولی اگه بخوام دقیق بگم ما دو نوع polymorphism داریم پویا و ایستا. پویا همون override کردن متدها هستش که در وراثت راجع بهش صحبت کردیم و ایستا هم میشه overload کردن متدها. این یعنی ما متدهای هم نامی داریم که حتما باید متغیر های ورودیشون با هم فرق داشته باشه. توجه کنید که میتونیم مقدار بازگشتی متد رو تغییر بدیم ولی نمیشه تنها تمایز دو متد overload شده مقدار بازگشتی اونها باشه.
Abstraction: این مفهوم در OOP برای این است که به کار بر بگوییم یک شی چه کاری انجام میدهد what the object does بجای اینکه چگونه کار را انجام میدهد how the object does it.
- یک کلاس abstract باید شرایط زیر را داشته باشد:
- یک کلاس abstract میتونه متد abstract داشته باشه یا نداشته باشه.
- اگه یک کلاس حتی یک متد abstract داشته باشه باید خودش abstract باشه.
- از کلاس abstract نمیشه هیچ نمونهای ساخت.
- برای استفاده از یک کلاس abstract باید یک کلاس از اون ارثبری کنه و متدهای abstract رو پیاده سازی کنه.
- اگه کلاسی که از یک کلاس abstract ارثبری کرده خودش abstract باشه نیازی نداره که حتما همه متدهای abstract رو پیاده سازی کنه.
- متدهای abstract بدنه ندارن و بجای آکولاد باید ; بزاریم.
Encapsulation: خیلی ساده اگه بخوام بگم این یک مفهوم ساده برای جلوگیری از نمایش و دسترسی به داده غیر ضروری در بیرون از یک کلاس هستش. و یکسری راهکار داریم:
- میتونیم متغیر ها رو private تعریف کنیم و برای اونها وابسته به نیازمون متدها getter و setter رو به صورت public تعریف کنیم.
- میتونیم یکسری از متدها رو private کنیم کنیم تا فقط توی کلاس جاری بهشون دسترسی داشته باشیم.
- میتونیم با protected کردن اشیا و یا متد ها دسترسی به اونها رو محدود کنیم.
Interfaces: اینترفیسها خیلی شبیه کلاسها هستن با این تفاوت که کلاسها خصوصیتها و رفتارها رو توصیف میکنن ولی اینترفیسها حاوی رفتارهایی هستند که یک کلاس باید اونها رو پیاده سازی کنه. به عبارت دیگه اینترفیس دقیقا شبیه یک کلاس abstract هستش که تمام متدهاش هم abstract هستش. کار کردن با اینترفیسها یکسری شرایط داره:
- کلاسی که اینترفیس رو پیاده سازی میکنه باید تمام متدها رو پیاده سازی کنه مگر اینکه خودش abstract باشه.
- اینترفیسها میتونن هم رو توسعه بدن و لازم نیستش که چیزی رو پیاده سازی کنن.
- متدهای static توی اینترفیس باید بدنه داشته باشند و قابلیت بازنویسی یا پیاده سازی ندارن.
- با استفاده از کلمه default میتونیم برای متد بدنه بنویسیم و دیگه اجباری نیست حتما اون متد رو پیاده سازی کنیم. ولی اگه بخوایم میتونیم اونها رو بازنویسی کنیم.
- هیچ محدودیتی در تعداد متد های اینترفیس نداریم.
- ما میتونیم اینترفیس ها رو توی فایلهای java درست کنیم.
- بعد از تبدیل شدن یک اینترفیس به bytecode فایل آن class هستش مثل کلاسهای معمولی.
- فایل bytecode اینترفیس ها دقیقا مثل کلاسها توی همون پکیجی قرار میگیره که توی اون هستش.
- ما نمیتونیم یک شی از اینترفیس درست کنیم.
- اینترفیس ها متد سازنده ندارن.
- ما توی اینترفیس ها نمیتونیم متغیر تعریف کنیم و در صورت تعریف متغیر به صورت پیش فرض به static final تبدیل خواهد شد.
- یک کلاس نمیتونه یک اینترفیس رو توسعه بوده فقط میتونه اون رو پیاده سازی کنه.
- یک کلاس یا یک اینترفیس میتونه چندین اینترفیس رو پیاده سازی کنه.
- یک اینترفیس به صورت پیشفرض abstract هستش و لازم نیست که این رو بنویسیم.
- تمام متدهای یک اینترفیس به صورت پیشفرض abstract هستند و لازم نیست که این رو بنویسیم.
- تمام متدهای یک اینترفیس باید حتما public باشند.
- برای پیاده سازی اینترفیس های باید از کلمه implements استفاده کنیم.
- برای پرتاب کردن خطا در متد هایی بازنویسی شده از اینترفیس ها، باید حتما پرتاب شدن خطا رو توی اینترفیس تعریف کنیم.
- در زمان پیاده سازی یک متد از یک اینترفیس نمیتونیم مقدار بازگشتی اون رو تغییر بدیم.
گاهی اوقات ما اینترفیسها خالی و بدون متد تعریف میکنیم و بهش میگیم tagging interface که معمولا دو تا دلیل داره:
- Creates a common parent: وقتی که میخوایم یکسری اشیا پدر یکسانی داشته باشند.
- Adds a data type to a class: با این کار میتونیم روی اشیا برچسب بزنیم
Packages: پکیج توی جاوا برای این درست شده که از تداخل نام اشیا جلوگیری کنه و کنترل دسترسی به اشیا رو بهتر مدیریت کنه. این اشیا عبارتند از class ها، interface ها، enum ها و annotation ها. ما توی یک پکیج میتونیم یکسری اشیای مرتبط به هم رو نگهداریم. هر برنامه نویسی میتونه پکیج مورد نظر خودش رو تعریف کنه.
ما میتونیم پکیج خودمون رو درست کنیم و بعدش با نقطه ی نام جدید بهش اضافه کنیم و پکیج جدید بعدی رو درست کنیم. پکیج در اولین خط فایل نوشته میشه و هر فایل فقط میتونه یک پکیج داشته باشه. و اگه پکیج رو ننویسیم فایل ها میرن توی پکیج پیشفرض.
حالا ما برای دسترسی به پکیج های دیگه باید اونها رو توی کلاسمون معرفی کنیم. برای این کار باید از دستور import استفاده کنیم.
هر کلاس یک class name داره که به صورت خودکار بوجود میاد و میشه پکیج کلاس به همراه نام کلاس.
package com.apple;
class Dell { } // com.apple.Dell
البته برای تایپ های اولیه اینجوری نیست و class name شون دقیقا اسم کلاسشون هستش.
int // int
inner class ها یا کلاس هایی که توی یک کلاس دیگه ساخته میشن هم با $ از class name کلاس اصلی جدا میشن.
package com.apple;
class Dell { class Computer { } } // com.apple.Dell$Computer
و anonymous class ها به ترتیب ساخته شدن یک عدد میگیرن و با $ از کلاس میزبان جدا میشن، توجه کنید که این اعداد از یک شروع میشن.
package com.apple;
class Asus { }
class Dell {
new Asus() { }; // com.apple.Dell$1
new Asus() { }; // com.apple.Dell$2
}
برای آرایه ها دقیقا شرایط قبل برقرار هستش با این تفاوت که به ازای هر بعد یک کروشه به اول class name اضافه میشه.
package com.apple;
class Asus { }
class Dell {
new Asus[][][] { }; // [[[com.apple.Dell.Asus
new Asus[] { }; // [com.apple.Dell.Asus
}
یک مفهوم ساده class pass هم داریم که میشه پکیج کلاس به اضافه اسم کلاس با پسوند java.