برنامه نویسی جاوا – قسمت دهم (معرفی متدها)

جمعه, ۲۰ام فروردین , ۱۳۸۹ | ۲۰ دیدگاه

کلاس ها معمولا از دو چیز تشکیل می شوند : نمونه متغیرها و متدها . موضوع متدها بسیار گسترده است . چرا که جاوا قدرت و انعطاف پذیری زیادی را در آنجا جای داده است . در حقیقت ، بیشتر فصل آتی به متدها اختصاص یافته است. اما اصولی وجود دارند که می بایست هم اینک فرا بگیرند تابتوانید روند افزودن متدها به کلاس های خود را آغاز نمایید .
شکل کلی هر متد به صورت زیر است :

Type name (Parameter-list) {
// body of method
}

.type نوع داده هایی را مشخص می کند که متد باز می گرداند . type می تواند هر یک انواع مورد بررسی قبلی باشد ، از جمله انواع کلاس هایی که خودتان ایجاد می کنید. چنانچه متد چیزی را برنگرداند، type باید void باشد . نام متد نیز به وسیله name مشخص می شود. از هر شناسه معتبری می توانید به عنوان نام استفاده کنید ؛ البته به غیر از مواردی که برای اقلام موجود در همان محدوده جاری استفاده شده اند . parameter-list ، فهرست زوجهایی (نوع و شناسه) اشت کهبا کاما از یکدیگر جدا می شوند . پارامترها اساسا متغیرهایی هستند که مقدار آرگومان های ارسالی به متد را هنگام فراخوانی آن دریافت می کنند . چنانچه متد پارامتری نداشته باشد ، این فهرست خالی خواهد بود .
متدهایی که نوع مقدار حاصل از فراخوانی آنها چیزی به غیر از void باشد، مقداری را با استفاده از عبارت return به روتین فراخوان بازمی گردانند

return value;

Value ، مقداری است که برگردانده می شود .
در چند قسمت آتی با چگونگی ایجاد انواع گوناگون متدها ، از جمله متدهایی که پارامترهایی را دریافت و مقادیری را باز می گردانند ، آشنا خواهید شد .

افزودن متد به کلاس Box
اگر چه ایجاد کلاس هایی که تنها حاوی داده باشند کاملا درست است ، اما این امر به ندرت رخ می دهد. در بیشتر مواقع از متدها برای دستیابی به نمونه متغیرهای تعریف شده در کلاس ها استفاده خواهدشد . در حقیقت ، متدها ، رابط دستیابی به بیشتر کلاس ها را تعریف می کنند . این امر به ایجاد کننده کلاس ها امکان می دهد تا شمای ساختارهای داده ای مرتبط با کلاس را پشت سر متدهای شفاف تر پنهان نماید . علاوه بر تعریف متدهایی که دستیابی به داده ها را فراهم می سازند ، امکان تعریف متدهایی که توسط خود کلاس ها و به طور داخلی مورد استفاده قرار می گیرند نیز فراهم شده است .
اینک کار خود کلاس ها و به طور داخلی مورد استفاده قرار می گیرند نیز فراهم شده است .
اینک کار خود را با افزودن متدی به کلاس Box آغاز می کنیم . ممکن است در حین بررسی برنامه های پیشین این فکر به ذهنتان خطور کرده باشد که محاسبه حجم مکعب بهتر است توسط خودکلاس Box انجام شود ، و نه کلاس BoxDemo ، به ذهنتان خطور کرده باشد که محاسبه حجم مکعب بهتر است توسط خود کلاس Box بیفزایید .

// This program includes a method inside the box class .
Class Box {
Double width;
Double height;
Double depth;

// display volume of a box
Void volume ()  {
System.out.print(“volume is “);
System.out.println(width * height * depth);
}
}
Class BoxDemo3  {
Public static void main (string args [])  {
Box mybox1 = new Box ();
Box mybox2 = new Box();

// assign values to mybox1’s instance variables
Mybox1 . width = 10 ;
Mybox1.height = 20 ;
Mybox1 . depth = 15 ;
/* assign different values to mybox2’s
Instance variables */
Mybox2 . width = 3 ;
Mybox2.height = 6;
Mybox2.depth = 9;

// display volume of first box
Mybox1.volume () ;
//display volume of second box
Mybox2.volume();
}
}

خروجی برنامه در ذیل آورده شده است که البته با خروجی مثال پیش یکسان است .

Volume is 3000.0
Volume is 162.0

به دو سطر زیر توجه کنید :

Mybox1 .volume() ;
Mybox2.volume();

سطر نخست سبب فعال شدن متد volume() برای mybox1 می شود . یعنی ، با استفاده از نام شیء و سپس عملگر نقطه (.) ، volume () برای کار بر روی داده های mybox1 فراخونده می شود. از این رو ، mybox1.volume() موجب می شود که توسط mybox2 تعریف شده است . هر بار که volume() فعال می شود، حجم مکعب مربوط نمایش داده می شود.
اگر با مفهوم فراخوانی متدها آشنا نباشید ، بحث زیر به شفافیت موضوع کمک خواهد کرد . وقتی mybox1.volume() اجرا می شود ، سیستم «زمان – اجرای» جاوا ، کنترل را به روتین volume() انتقال می دهد . پس از اجرای عبارات موجود در volume () ، کنترل به روتین فراخوان بازگردانده می شود ، و سطر بعدی آن اجرا می شود . به طور کلی تر ، هر متد در جاوا ، روشی برای پیاده سازی سابر روتین هاست .
نکته بسیار مهمی درباره محتوای متد volume () وجود دارد کهباید به آن توجه کنید : ارجاع به نمونه متغیرهای depth , height , width به صورت مستقیم و بدون قرار گرفتن نام شیء و عملگر نقطه (.) پیش از آنها ، صورت می گیرد . وقتی متدی از نمونه متغیری استفاده می کند که توسط کلاس خودش تعریف شده است ، این کار به طور مستقیم و بدون ارجاع به شیء و بدون استفاده از عملگر نقطه (.) انجام می شود . اگر کمی در این باره فکر کنید ، درک آن آسان است . هر متد همیشه نسبت به شیئی از کلاس خودش فعال می شود . از این رو ف نیازی به مشخص کردن مجدد شیء در متد نیست . این بدین معناست که depth, height , width در متد volume () ، به طور ضمنی به نسخه هایی از این متغیرها ارجاع دارند که در همان شیئی قرار دارند که volume () را فعال می کند .
این موضوع را سریعا مرور می کنیم . وقتی دستیابی به نمونه متغیرها به وسیله روتینی انجام می گیرد که در همان کلاس تعریف متغیرها تعریف نشده است ، در آن صورت باید این کار از طریق نام شیء و عملگر نقطه (.) انجام شود اما ، وقتی این کار به وسیله روتینی انجام می شود که بخشی از همان کلاس مربوط به متغیرهاست، در آن صورت متغیرها به طور مستقیم قابل ارجاع می باشند . این مطلب درباره متدها نیز صادق است .

بازگرداندن مقادیر
اگر چه پیاده سازی volume () سبب انجام محاسبه حجم مکعب در همان کلاس box می شود ، اما بهترین روش برای انجام این کار به شمار نمی آید . به عنوان مثال ، اگر بخش دیگری از برنامه تان نیاز به داشتن حجم مکعب توسط volume () محاسبه و مقدارش به روتین فراخوان ، بازگردانده شود .مثال زیر ، نگارش بهبود یافته ای از برنامه پیشین است که همین کار را انجام می دهد :

// Now , volume () returns the volume of a box .

Class box {
Double width;
Double height;
Double depth;

// compute and return volume
Double volume () {
Return width * height * depth;
}
}
Class boxdemo4 {
Public static void main (string args[])  {
Box mybox1 = new box() ;
Box mybox2 = new box ();
Double vol ;

// assign values to mybox1’s instance variables
Mybox1.width=10;
Mybox1.height=20;
Mybox1.depth = 15 ;
/* assign different values to mybox2’s
Instance variables */
Mybox2.width=3;
Mybox2.height=6;
Mybox2.depth = 9 ;
// get volume of first box
Vol = mybox1.volume();
System.out.println(“volume is” + vol) ;

// get volume of decond box
Vol = mybox2.volume() ;
System .out.println(“volume is” + vol);
}
}

همانگونه که ملاحظه می کنید ، وقتی volume() فرا خوانده می شود ، در سمت راست عبارت تخصیص مورد نظر قرار داده می شود. متغیر سمت چپ عبارت تخصیص (vol در این مثال)، مقدار حاصل از فراخوانی volume() را دریافت خواهد کرد . از این رو ، پس از اجرای

Vol = mybox1.volume();

مقدار mybox1.volume() عدد ۳۰۰۰ خواهد بود که در vol ذخیره می شود .
دو نکته مهم درباره مقدادیر حاصل از فراخوانی متدها وجود دارد که باید به خوبی با آنها آشنا باشید :
• نوع داده های حاصل از فراخوانی متد باید با نوعی که در تعریف متد مشخص شده است ، سازگار باشد . به عنوان مثال ، اگر نوع مقداری که یک متد باز می گرداند ، Boolean باشد ، نمی توانید مقدار صحیحی را بازگردانید .
• متغیر دریافت کننده مقدار حاصل از فراخوانی متد (مثلا vol دراین مثال )، باید با نوعی که در تعریف متد مشخص شده است ، سازگار باشد .
یک نکته دیگر : برنامه بالا را می توان به صورت کاآمدتری بازنویسی کرد ، چرا که نیازی به متغیر vol نیست . فراخوانی volume () رامی توان مستقیما درعبارت println انجام داد .

System.out.println(“volume is” + mybox1.volume());

در این حالت ، وقتی println اجرا می شود ، mybox1.volume() به طور خودکار فراخوانده می شود و مقدار حاصل از آن به println() ارسال خواهد شد .
افزودن متدهای پارامتریک
اگر چه برخی از متدها نیاز به پارامتر ندارند ، اما بیشتر متدها این گونه نیستند . پارامترها امکان عمومیت بخشیدن به متدها را فراهم می سازند . یعنی متدهای پارامتریک می توانند بر روی انواع داده ها عمل کنند ، و یا در شرایط نسبتا مختلف مورد استفاده قرار گیرند .برای درک این نکته به مثال بسیار ساده زیر توجه کنید . متد زیر ، مجذور عدد ۱۰ را باز می گرداند :

Int square()
{
Return 10 * 10;
}

اگر چه این متد واقعا واقعا مجذور عدد ۱۰ را باز می گرداند ، اما کاربرد آن بسیار محدود است . اما اگر متد رابه گونه ای تغییر دهیم تا پارامتری را همچون مثال زیر دریافت کند ، در آن صورت square() بسیار مفید تر می شود .

Int square(int i)
{
Return i+1*I;
}

بدین ترتیب square() اینک مجذور هر مقداری که با آن فراخوانده می شود را باز می گرداند . یعنی ، square() به متد همه منظوره ای مبدل شده است که به جای عدد ۱۰ ، مجذور هر عدد صحیح را محاسبه می کند .
به مثال زیر توجه کنید :

Int x,y;
X = square(5) ,
// x equals 25
X = square(9) ,
// z equals 81
Y=2;
X = square (y) ,
// x equal 4

در نخستین عبارت فراخوانی square() ، عدد ۵ به پارامتر i ارسال خواهد شد . در عبارت دوم ، i مقدار ۹ را دریافت خواهد کرد . در سومین عبارت فراخوانی ، مقدار y ، که در این مثال ۲ می باشد ، ارسال خواهد شد .
حفظ تمایز بین دو واژه پارامتر و آرگومان از اهمیت خاصی برخوردار است . منظور از پارامتر ، متغیری است که توسط متد تعریف می شود و وقتی متد فراخوانده می شود ، مقداری را دریافت می کند . به عنوان مثال ، در متد بالا ، i پارامتر به شمار می آید . منظور از آرگومان ، مقداری است که هنگام فعال سازی متد به آن ارسال می شود . به عنوان مثال ، در square(100)، عدد ۱۰۰ به عنوان آرگومان ارسال می شود . در متد square() پارامتر i، آن مقدار را دریافت می کند .
با استفاده از یک متد پارامتری می توانید کلاس box رابهبود بخشید . در مثالهای پیش ، ابعاد هر مکعب می بایست به طور جداگانه و با استفاده از چند عبارت به صورت زیر مشخص می شدند :

Mybox1.width=10;
Mybox1.height=20;
Mybox1.depth = 15 ;

اگر چه عبارات بالا به درستی کار می کنند ، اما به دو دلیل دردسرساز هستند.نخست اینکه ، مقداردهی جداگانه متغیرها مشکل و مستعد خطاست . به عنوان مثال ، فراموش کردن مقدار دهی هر یک از ابعاد بسیار آسان است . دوم اینکه ، در برنامه هایی که به خوبی طراحی شده باشند ، دستیابی به نمونه متغیرها باید تنها از طریق متدهای تعریف شده در کلاس خودشان صورت پذیرد . در آینده ، به راحتی می توانید رفتار هر متدی را تغییر دهید : اما نمی توانید رفتار نمونه متغیرهای قابل دسترسی را تغییر دهید .
از این رو ، رویه بهتر برای تعیین ابعاد مکعب ، ایجاد متدی است که ابعاد مکعب را در پارامترهایش دریافت و هر یک از نمونه متغیرها را مقدار دهی کند . این مفهوم به وسیله برنامه زیر پیاده سازی است :

//this program uses a parameterized method.
Class box {
Double width;
Double height;
Double depth;
//compute and return volume
Double volume() {
Returne width *height*depth;
}
// sets dimensions of box
Void setdim(double w,double h, double d) {
Width = w;
Height = h;
Depth = d;
}
}
Class boxdemo5 {
Public static void main (string arg[]) {
Box mybox1 = new box();
Box mybox2 =newbox();
Double vol;
// initialize each box
Mybox1.setdim(10,20,15);
Mybox2.setdim(3,6,9);
//get volume of first box
Vol = mybox1.volume();
System.out.println(“volume is”+vol);
//get volume of second box
Vol=mybox2.volume();
System.out.println(“volume is”+ vol);
}
}

همانگونه که ملاحظه می کنید ، متد setdim() برای تعیین ابعاد هر مکعب به کار برده می شود . به عنوان مثال ، وقتی عبارت زیر اجرا می شود،

Mybox1.setdim(10,20,15);

۱۰ به پارامتر w، ۲۰ به پارامتر h و ۱۵ به پارامتر d کپی می شود . مقادیر w,h,d سپس در setdim() به ترتیب به width,height, depth کپی می شوند .
مفاهیم مطروحه در قسمتهای پیش برای بسیاری از خوانندگان آشنا خواهند بود . اما ، اگر مواردی چون فراخوانی متدها ، آرگومان ها و پارامترها برایتان تازگی دارند ، بهتر است پیش از ادامه ، زمانی را صرف آزمایش آنها نمایید فعال سازی متدها ، پارامترها و مقادیر حاصل از فراخوانی متدها ، از مفاهیم اساسی و پایه برنامه سازی در جاوا به شمار می آیند .

Constructorها
مقدار دهی اولیه تمام متغیرهای یک کلاس هر بار به هنگام ایجاد نمونه ای از آن، کسل کننده خواهد بود . حتی با وجود افزودن توابع مناسبی چون setdim()، ساده تر و دقیقتر است که تمام کارهای مقدار دهی اولیه را در همان لحظه ایجاد شیء انجام دهید . چون این نیاز بسیار متداول است ، جاوا این امکان را فراهم ساخته تا شیء ها خودشان رابه هنگام ایجاد ، مقدار دهی کنند . این مقدار دهی خودکار ، از طریق استفاده از یک Constructor انجام می شود .
Constructor ، یک شیء را به محض ایجاد مقدار دهی می کند . نام آن با نام کلاسی که در آن قرار دارد یکسان بوده و از نظر ساختار گرامری نیز مشابه متدهاست . هر Constructor پس از تعریف ، به طور خودکار به محض ایجاد شیء فراخوانده می شود ؛ پیش از تکمیل کار عملگر Constructor.new ها قدری عجیب به نظر می آیند ، چرا که هیچ مقداری را باز نمی گردانند ، حتی void . دلیل این امر آن است که نوع ضمنی مقدارشان ف همان نوع کلاس است . این وظیفه Constructor هاست که وضعیت داخلی یک شیء را در همان ابتدای کار تعیین کنند (مقدار دهی اولیه)، تا روتینی که نمونه ای از کلاس را ایجاد می کند ،فورا شیء قابل استفاده و مقدار دهی شده ای داشته باشد .
مثال box را می توانید به گونه ای بازنویسی کنید تا ابعاد مکعب به طور خودکار هنگام ایجاد شیء مقدار دهی شوند . برای انجام این کار ، setdim() را با یک Constructor جایگزین کنید . این کار را با تعریف Constructor ساده ای که ابعاد هر مکعب را با مقادیر یکسان مقدار دهی می کند ، آغاز خواهیم کرد . نسخه تجدید نظر شده در ذیل نشان داده شده است :

/*Here , box uses a Constructor to initialize the
Dimensions of a box .
*/
Class box {
Doublewidth;
Double height;
Double depth;
// this is the Constructor for box .
Box() {
System.out .println(“Constructing box”);
Width = 10;
Height = 10;
Depth = 10;
}
//compute and return volume() {
Return width * height*depth;
}
}
Class boxdemo6   {
Public static void main (string arg[])  {
// declare, allocate, and initialize box objects
Box mybox1 = new box ();
Box mybox2 = new box ();
Double vol;
// get volume of first box
Vol = mybox1.volume();
System.out.println(“volumeis” + vol);
// get volume of second box
Vol = mybox2.volume();
System.out.println(“volumeis” + vol);
}
}

وقتی برنامه اجرا می شود ، نتایج آن به شکل زیر خواهد بود :

Constructing box
Constructing box
Volume is 1000.0
Volume is 1000.0

همان گونه که ملاحظه می کنید ، mybox1 و mybox2 هر دو هنگام ایجاد به وسیله Constructor ای که box() نام دارد ، مقدار دهی شده اند . از آنجایی که Constructor ابعاد ۱۰*۱۰*۱۰ را به تمام مکعب ها نسبت می دهد ، حجم mybox1 و mybox2 برابر خواهد شد . عبارت println() در box() صرفا به خاطر نمایش صحت عملکرد آناست . بیشتر Constructor ها چیزی رانمایش نخواهند داد . آنها صرفا عمل مقدار دهی اولیه را برای شیء انجام می دهند .
پیش از ادامه کار ، عملگر new رایک مرتبه دیگر بررسی می کنیم . همان گونه که می دانید وقتی حافظه ای رابه یک شیء تخصیص می دهید ، می بایست از شکل کلی زیر استفاده کنید :

Class-var = new classname();

اینک می توانید حدس بزنید که چرا وجود پرانتزها پس از نام کلاس ضروری است . آنچه که واقعا رخ می دهد ، آن است که Constructor کلاس فراخوانده می شود . از این رو ، در سطر زیر ،

Box mybox1 = new box();

New box() موجب فراخوانی Constructor کلاس می شود که همنام با خود کلاس است (یعنی box()). وقتی Constructor ای را صریحا برای کلاسی تعریف نمی کنید ، جاوا این کار رابه طور پیش فرض انجام می دهد . به همین دلیل است که سطر بالا در نگارشهای پیشین مثال box ، که فاقد تعریف Constructor بودند ، به خوبی کار می کرد . Constructor پیش فرض تمام نمونه متغیرها رابه طور خودکار با صفر مقدار دهی می کند . Constructor خاص خودتان را تعریف می کنید . Constructor پیش فرض دیگر به کار برده نمی شود .
Constructor های پارامتریک
اگر چه Constructor مثال پیش (box()) عمل مقدار دهی اولیه را برای شیء box انجام می دهد ، اما چندان مفید نیست – ابعاد تمام مکعبها یکسان خواهدبود . باید به دنبال روشی برای ساخت شیء های box با ابعاد گوناگون باشیم . راه حل آسان برای انجام این کار ، افزودن پارامترهایی به Constructor است . همان گونه که احتمالا حدس زده اید ، انجام این کار موجب مفید تر شدن آن می شود . به عنوان مثال ، در نگارش جدید box ، یک Constructor پارامتریک تعریف شده است که ابعاد هر مکعب را بر اساس تعداد پارامترها تعیین می کند . به روش ایجاد شیء های box دقت نمایید .

/*Here , box uses a Constructor to
 initialize the Dimensions of a box .
*/
Class box {
Double width;
Double height;
Double depth;
// this is the Constructor for box .
Box(double w,double h, double d) {
Width = w;
Height = h;
Depth = d;
}
//compute and return volume
 Double volume() {
Return width * height*depth;
}
}
Class boxdemo7   {
Public static void main (string args[])  {
// declare, allocate, and initialize box objects
Box mybox1 = new box (10,20,15);
Box mybox2 = new box (3,6,9);
Double vol;
// get volume of first box
Vol = mybox1.volume();
System.out.println(“volumeis” + vol);
// get volume of second box
Vol = mybox2.volume();
System.out.println(“volumeis” + vol);
}
}

خروجی برنامه در زیر نشان داده شده است :

Volume is 3000.0
Volume is 162.0

همان گونه که ملاحظه می کنید ، مقدار دهی هر شیء بر اساس پارامترهای Constructor خودش انجام می گیرد . به عنوان مثال ، در سطر زیر ،

Box mybox1 = new box (10,20,15);

مقادیر ۱۰و۲۰و۱۵ هنگام ایجاد شیء به وسیله new به Constructor کلاس ارسال می شوند . از این رو ، مقادیر ۱۰و۲۰و۱۵ به ترتیب به width , , height depth تخصیص می یابند .
کلمه کلیدی this
گاهی اوقات متدها نیاز به ارجاع به شیئی دارند که آنها را فعال کرده است .جاوا برای فراهم ساختن این امکان ، کلمه کلیدی this را تعریف کرده است . با استفاده از this در هر متد می توان به شیء جاری ارجاع نمود . یعنی ، this همیشه ارجاع به شیئی دارد که متد برای آن فعال شده است . هر جا که ارجاع به شیئی از کلاس جاری مجاز باشد . می توان از this همیشه ارجاع به شیئی دارد که متد برای آن فعال شده است . هر جا که ارجاع به شیئی از کلاس جاری مجاز باشد ، می توان از this استفاده نمود.
برای درک بهتر اینکه this به چه چیزی ارجاع دارد ، به نگارش زیر از box() توجه کنید :

//A redundant use of this.
Box(double w, double h, double d)   {
This.width = w;
This .height = h;
This.depth =d;
}

این نگارش از box() دقیقا همچون نگارش قبلی کار می کند . استفاده از this بی مورد است ، اما کاملا صحیح است . this در این نگارش ، همیشه به شیئی که متد را فرا می خواند، ارجاع خواهد داشت . اگر چه کاربرد آن در این مثال بی مورد است ، اما در سایر موارد مفید واقع می شود . یکی از آن موارد در قسمت آتی شرح داده شده است .
پنهان کردن نمونه متغیرها
همان گونه که می دانید ، تعریف کردن دو متغیر محلی همنام در یک محدوده در جاوا غیر قانونی است . جالب است بدانید که در جاوا می توانید متغیرهای محلی ، از جمله پارامترهای نرمال متدها ، با نام های یکسان با نمونه متغیرهای کلاس ها داشته باشید . اما ، وقتی نام یک متغیر محلی با نمونه متغیری از یک کلاس یکسان می شود ، آن متغیر محلی سبب پنهان شدن نمونه متغیر همنام با خود می شود . به همین دلیل است که از width. Height , depth در کلاس bos به عنوان پارامتر های constructor (box()) استفاده نشد . اگر این گونه می شد ، در آن صورت width به پارامتر نرمال خود constructor ارجاع می کرد و نمونه متغیر width کلاس پنهان می شد. اگر چه معمولا استفاده از نامهای دیگر آسانتر است ، اما روش دیگری هم برای حل این مشکل وجود دارد . چون this به شما امکان می دهد تا مستقیما به شیء ارجاع داشته باشید ، با استفاده از آن می توانید مشکل تداخل نام در بین نمونه متغیرها و متغیرهای محلی را حل کنید . به عنوان مثال ، به نگارش دیگری از box() توجه کنید که از width. Height , depth به عنوان نام پارامترها استفاده نموده ، و سپس از this برای دستیابی به نمونه متغیرهای همنام با پارامترها استفاده می کند :

//use this to resolve name-space collisions.
Box(double width, double height, double depth) {
His.width = width;
This.height =  height;
This.depth = depth;
}

نکته ای که باید به آن توجه کنید: استفاده این گونه از this گاهی اوقات می تواند گمراه کننده باشد ، و برخی از برنامه سازان دقت می کنند تا برای متغیرهای محلی و پارامترهای نرمال از نامهایی استفاده نکنند که منجر به پنهان شدن نمونه متغیرهای کلاس شود . البته ، برخی برنامه سازان دیگر ، خلاف این امر را باور دارند – یعنی معتقدند که باید برای شفافیت بیشتر از نامهای یکسان استفاده نمود ، و برای بر طرف کردن مشکل پنهان ماندن نمونه متغیرهای کلاس ، از this استفاده کرد.
اگر چه استفاده از this در مثالهای پیش گفته چندان ارزشمند نیست ، اما در برخی شرایط بسیار مفید واقع می شود .
بازپس گیری حافظه بلا استفاده
از آنچایی که شیء ها با استفاده از عملگر new به طور پویا تخصیص می یابند ، ممکن است از خود بپرسید که چگونه از بین برده می شوند و حافظه آنها چگونه برای استفاده های آتی آزاد می شود . در برخی از زبانها ، از قبیل C++ ، شیءهایی که به طور پویا تخصیص داده می شوند را باید به صورت دستی با استفاده از عملگر delete، آزاد نمود . جاوا از رویه دیگری استفاده می کند؛ آزادسازی را به طور خودکار برایتان انجام می دهد . تکنیکی که از آن برای انجام این کار استفاده می شود ، garbage collection نام دارد . عملکرد آن به این شرح است ؛ وقتی هیچ گونه ارجاعی به یک شیء وجود نداشته باشد ، فرض می شود که شیء دیگر مورد نیاز نبوده و حافظه آن نیز با پس گرفته می شود . در زبان جاوا ، برخلاف c++ دیگر نیازی به از بین بردن شیء ها نیست . این تنها به صورت نامنظم و گاه و بیگاه در طی اجرای برنامه انجام می شود . انجام آن صرفا به خاطر عدم نیاز به یک یا چند شیء صورت نمی گیرد . به علاوه ، نسخه های مختلف محیط اجرای جاوا ، رویه های مختلفی برای انجم این کار دارند ، اما آنچه لازم است بدانید ، آن است که هنگام نوشتن برنامه ها لازم نیست نگران این مسئله باشید.
متد finalize()
گاهی اوقات برخی از شیءها نیاز به انجام عملیات خاص پیش از از بین بردن دارند . به عنوان مثال ، اگر شیئی از منابع غیر جاوا، از قبیل handle یک فایل یا فونت خاص ، استفاده می کند ، در آن صورت بهتر است پیش از آزاد سازی آن شیء از آزاد شدن آن منابع اطمینان حاصل نمایید . جاوا برای مدیریت این گونه شرایط، مکانیزمی به نام finalization دارد . با استفاده از این مکانیزم می توانید عملیات خاصی را مشخص کنید تا درست پیش از آزاد سازی یک شیء ، تماما انجام شوند .
برای پیاده سازی این مکانیزم در هر کلاس ، کافی است متد finalize() راتعریف کنید . محیط زمان اجرای جاوا این متد را هنگام بازیافت شیئی از آن کلاس فرا می خواند . در متد finalize() باید آن عملیاتی را مشخص کنید که باید پیش از از بین بردن یک شیء انجام شوند . قسمتی که مسئولیت بازپس گیری حافظه بلااستفاده را دارد ، به طور متناوب اجرا شده و شیء هایی را جستجو می کند که دیگر ارجاعی به آنها صورت نمی گیرد و به طور غیر مستقیم نیز از طریق سایر شیءها به آنها ارجاع نمی شود . درست پیش از آزاد کردن هر شیء ، سیستم «زمان اجرای» جاوا متد finalize() را برای آن شیء فرا می خواند .
شکل کلی این متد در زیر نشان داده شده است :

Protected void finalize()
{
// finalization code here
}

کلمه کلیدی protected، مشخصه ای است که از دستیابی به finalize() توسط روتین های خارج از همان کلاس جلوگیری می کند . این مشخصه و سایر مشخصه های دستیابی در فصل ۷ تشریح شده اند .
مهم است بدانید که finalize() تنها پیش از بازپسگیری حافظه شیء ها فراخوانده می شود . مثلا زمانی که یک شیء در خارج از محدوده اش قرار میگیرد ، این متد فراخوانده نمی شود . این بدین معناست که هیچگاه نمی توانید تشخیص دهید که finalize() چه وقت اجرا خواهدشد – یا حتی اینکه اصلا فراخوانده خواهد شد یا خیر . بنابراین ، برنامه تان باید روشهای دیگری برای آزاد سازی منابع (وغیره) مورد استفاده شیء فراهم کند . یعنی ، برنامه تان نباید برای عملیات متعارف به این متد اتکا داشته باشد .
توجه : اگر با C++ آشنایی داشته باشید ، در آن صورت می دانید که C++ امکان تعریف destructor را برای هم کلاس فراهم میکند . destructor زمانی فراخوانده یم شود که شیء خارج از محدوده قرار می گیرد . جاوا از این ایده پشتیبانی نمی کند و امکان نوشتن destructorها را نیز فراهم فراهم نساخته است . متد finalize() وظیفه destructor ها را به طور تقریبی انجام می دهد . به مرور که تجربه بیشتری در خصوص کار با جاوا کسب می کنید ، خواهید دید که نیاز به عملیات destructor ها به دلیل سیستم «باز پس گیری حافظه بلا استفاده » در جاوا کمینه شده است .
کلاس stack
اگر چه کلاس box برای نشان دادن عناصر پایه یک کلاس مفید است ، اما در عمل ارزش چندانی ندارد . این فصل را برای نشان دادن قدرت واقعی کلاس ها ، با ارائه مثال پیچیده تری به پایان می رسانیم . همان گونه که احتمالا از بحث برنامه سازی شیء گرا (OPP) در فصل ۲ به یاد دارید ، یکی از مهمترین مزایای opp، نهان سازی داده ها و روتین هایی است که آنها را پردازش و مدیریت می کنند . همان گونه که دیده اید ، «کلاس» مکانیزمی است که نهان سازی در جاوا از طریق آن تحقق می یابد . وقتی کلاسی را ایجاد می کنید ، در واقع نوع جدیدی از داده ها ایجادمی شود که هم ماهیت داده ها را تعریف می کند ، و هم روتین هایی که برای پردازش و مدیریت آنها مورد استفاده قرار می گیرند . افزون بر این متدها ، رابط یکپارچه و تحت کنترلی را برای داده های کلاس تعریف می کنند . از این رو ، بی آنکه نگران جزئیات پیاده سازی کلاس یا چگونگی مدیریت داده های درون آن باشید ، به راحتی می توانید کلاس را از طریق متدهایش به کار برید . کلاس ها از یک جهت ، «موتور داده ها » هستند . برای به کارگیری موتور از طریق اقلام کنترل کننده آن ، نیازی به داشتن اطلاعات درباره داخل موتور نیست . در حقیقت ، از آنجایی که جزئیات پنهان می شوند ، عملیات داخلی را می توان متناسب با نیازها تغییر داد . تا زمانی که برنامه تان کلاس را از طریق متدهایش به کار برد ، جزئیات داخلی را می توان بدون تاثیرات جانبی بر خارج کلاس تغییر داد .
برای آنکه با یکی از کاربردهای عملی بحث بالا آشنا شوید ، اینک یکی از مثالهای قدیمی نهان سازی را پیاده سازی می کنیم . پشته . هر پشته ، داده ها را با استفاده از ترتیب “FILO” (First In , First Out) ذخیره می کند . یعنی ، هر پشته همچون یک دسته بشقاب بر روی یک میز است – نخستین بشقابی که بر روی میز قرار می گیرد ، آخرین بشقابی است که مورد استفاده قرار می گیرد . پشته ها از طریق دو عملی که از ابتدا “Push” و “Pop” نامیده شده اند کنترل می شوند . برای قرار دادن هر عنصر جدید بر روی پشته ، از “Push” استفاده می شود . برای برداشتن اقلام نیز از “Pop” نامیده شده اند کنترل می شوند . برای قراردادن هر عنصر جیدید بر روی پشته ، از “Push” استفاده می شود . برای برداشتن اقلام نیز از “Pop” نامیده شده اند کنترل می شوند . برای قرار دادن هر عنصر جدید بر روی پشته ، از “Pop” استفاده می شود . برای برداشتن اقلام نیز از “Pop” استفاده می شود. همان گونه که خواهید دید ، نهان سازی کل مکانیزم پشته آسان است .
کلاس زیر که Stack نامیده شده است ، پشته ای از اعداد صحیح را پیاده سازی می کند :

// this class defines an integer stack that can hold 10 values .
Class stack   {
Int stack[] = new int[10];
Int tos;
// Intialize top-of-stack
Stack()   {
Tos = -1
}
// push an item onto the stack
Void push (int item)   {
If(tos==9)
System.out.println(“stack is full.”);
Else
Stack[+tos] = item;
}
// pop an item from the stack
Int pop()   {
If (tos<0)  {
System.out.println(“stack undeflow.”);
Return 0;
}
}

همان گونه که ملاحظه می کنید ، در کلاس stack دو آیتم داده ای و سه متد تعریف شده است . پشته اعداد صحیح به وسیله آرایه stack نگهداری می شود . از متغیر tos به عنوان شاخص این آرایه استفاده می شود که همیشه حاوی شاخص بالاترین عنصر پشته است . مقدار اولیه ۱٫ توسط stack() (constructor کلاس) به tos تخصیص می یابد که نشانگر یک پشته خالی است . متد Push() ، آیتم جدید را بر روی پشته قرار می دهد . برای بازیابی هر یک از آیتم ها نیز ، Pop() فراخواندهمی شود . چون دستیابی به پشته از طریق Push() و Pop() صورت می گیرد ، اینکه پشته در یک آرایه نگهداری می شود ، واقعا هیچ ارتباطی به استفاده از آن ندارد . به عنوان مثال ، پشته را می شد در ساختار داده ای پیچیده تری چون فهرستهای زنجیره ای نگهداری نمود ، و در عین حال دو رابط push() و Pop() صورت می گیرد ، اینکه پشته دریک آرایه نگهداری می شود ، واقعا هیچ ارتباطی به استفاده از آن ندارد . به عنوان مثال ، پشته را می شد در ساختار داده ای پیچیده تری چون فهرستهای زنجیره ای نگهداری نمود ، و در عین حال دو رابط Push() و Pop() را به همین صورت حفظ کرد .
کلاس TestStack ای که در ذیل نشان داده شده است ، عملکرد کلاس Stack را نشان می دهد . دو پشته از اعداد صحیح ایجاد می شود ، مقادیری به هر یک از آنها افزوده می شود ، و سپس از روی پشته برداشته می شوند.

Class TestStack    {
Public static void main (string arg[])    {
Stack mystack1 = new stack();
Stack mystack2 = new stack ();
// push some numbers onto the stack
For(int i=0; i<10; i++) mystack1.push(i);
For(int i=10; i<20; i++) mystack2.push(i);
// pop those numbers off the stack
System.out.println(“stack in mystack1:”);
For (int i=0; i<10; i++)
System .out.println(mystack1.pop());

System.out.println(“stack in mystack2:”);
For (int i=0; i<10; i++)
System.out.println(mystack2.pop());

خروجی حاصل از برنامه در ذیل نشان داده شده است :

Stack in mystack1:
۹۸۷۶۵۴۳۲۱۰
Stack in mystack2:
۱۹ ۱۸ ۱۷ ۱۶ ۱۵ ۱۴ ۱۳ ۱۲ ۱۱ ۱۰

همان گونه که می بینید ، محتوای هر یک از پشته ها ، جدای از دیگری است .
واپسین نکته درباره کلاس stack : آرایه ای که برای نگهداری پشتهبه کار می رود (stack) ، با به کارگیری روش پیاده سازی مثال بالا ، از خارج از کلاس stack قابل تغییر خواهد بود . این امر موجب استفاده نادرست یا مخرب از stack خواهد شد .
در فصل آتی با روش حل این مشکل آشنا خواهید شد .
خوب این هم از unit 10. سال خوبی رو براتون آرزو می کنم ؛ سرشار از شادی، سلامتی و موفقیت. از همین جا از کامران هم برای تلاشهاش و حمایت هاش تشکر می کنم و بهترین آرزو ها رو براش دارم.
نوروز مبارک

پستهای مرتبط :

برنامه نویس جاوا – قسمت ۹(تعریف کلاسها)

برنامه نویس جاوا – قسمت ۸

برنامه نویس جاوا – قسمت ۷

برنامه نویس جاوا – قسمت ۶

برنامه نویس جاوا – قسمت ۵

برنامه نویس جاوا – قسمت ۴

برنامه نویس جاوا – قسمت ۳

برنامه نویس جاوا – قسمت ۲

برنامه نویس جاوا – قسمت ۱

Be Sociable, Share!


۲۰ دیدگاه


  1. mehrdad
    ۲۲ فروردین ۱۳۸۹

    سلام اگه میشه کل این اموزش روبه ایمیلم بفرستید




  2. sağlık market
    ۵ مرداد ۱۳۸۹

    سلام اگه میشه کل این اموزش روبه ایمیلم بفرستید




  3. مانی
    ۸ مرداد ۱۳۸۹

    واقعا کار عالی و مفیدیه جزوه های آموزشیتون. ساده و زود فهم. البته اگه بشه pdf اونرو برا دانلود بزارید باعث امتنان هست.




  4. سیوا
    ۱۷ مرداد ۱۳۸۹

    عالی




  5. niloofar
    ۲۵ مرداد ۱۳۸۹

    kheili khobe faghat age kolesho baram mail konid mamnon misham makhsosan age darbare java aplet ha ham matlab darid




  6. مهرداد
    ۳ آبان ۱۳۸۹

    سلام اگه میشه کل اموزش جاوارو به ایمیلم بفرستید




  7. محمد
    ۷ آبان ۱۳۸۹

    با سلام به همه خوانندگان و مسولین سایت

    من از سایت شما بسیار استفاده کردم و مطالب مفیدی داشته اید

    من به دوستان پیشنهاد می کنم برای بالارفتن سطح معلومات خود به سایت———————————- نیز سری بزنند




  8. هستی
    ۲۰ آذر ۱۳۸۹

    سلام مینا جون و کامران عزیز واقعا از زحماتتون تشکر می کنم مرسییییییییییییییییییییییییییییییییییی
    بوووووووووووووووووووووووووووووووووس




  9. فولاد
    ۲۹ آذر ۱۳۸۹

    سلام
    کار شما بی نظیر است. در صورت امکان آموزشی های متوسط و پیشرفته را به ایمیل آدرس بنده بفرست.ممنون




  10. سمیرا
    ۳۰ فروردین ۱۳۹۰

    سلام خیلی عالی بود ،اگه میشه مطالب رو روی ایمیلم بفرستید




  11. کامران
    ۳۰ فروردین ۱۳۹۰

    سمیرا خانم عضو خبرنامه بشو ایمیلها به صورت خودکار میاد. بالای سایت سمت راست




  12. sima
    ۱۵ خرداد ۱۳۹۰

    salam pas chera edame darsa ro nemizarin?




  13. tannaz
    ۱۸ خرداد ۱۳۹۰

    to dars aval gofte bodin 15 unit darim ama inja man 10 ta bishtar peyda nemikonam edamasho pas key mizarin




  14. amir
    ۱۲ مرداد ۱۳۹۰

    سلام
    ادامه درسها رو میذارین ؟




  15. سمیرا
    ۱۶ مرداد ۱۳۹۰

    سلام کارتون عالیه
    من دنبال کامپایلر javac میگردم میشه لطفا رو سایتتون واسه دانلود بزارید؟؟؟؟




  16. شهرام
    ۶ شهریور ۱۳۹۰

    سلام

    معمولا مشکل بچه های برنامه نویس در ایران کتاب های ترجمه شده بد هستند. من ترجمه های دیگه ای از این کتاب دیدم ولی ترجمه شما درکل خیلی حوبه .
    اگر هر برنامه نویس از کتاب های زبان اصلی استفاده کنه همیشه فوت ها رو هم یاد می گیره .
    The Complete Refrence JAVA J2SE
    Herbert Schildt
    بهتون تبریک میگم سادگی نوشتار شما و توضیحاتی که اضاقه کردید زیبا هستند.
    آرزوی ادامه موفقیت .
    شاد باشید




  17. سوده
    ۲۲ آبان ۱۳۹۰

    ممنون میشم برام ایمیل کنید.اطلاعات مربوط به جاوا تمتم جلساتو.ممنون




  18. 30na
    ۱ آذر ۱۳۹۰

    عالی بود.
    خلاصه
    و صد البته جامع
    و مهمتر از همه،واضح.

    حیف این آموزش که فقط تا یونیت ۱۰ پیش رفته….




  19. مسعود
    ۲۲ فروردین ۱۳۹۱

    فوق العاده و بی نظیر . ممنونم از شما




  20. زینب زیبای دانشگاه ببخشید بد حرف زدم در متن هام
    ۱۸ مرداد ۱۳۹۱

    سلام کارتون درسته



دیدگاه خود را بنویسید