صفحات

۱۳۸۹ اردیبهشت ۱, چهارشنبه

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

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

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

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

1return value;

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

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

01// This program includes a method inside the box class .
02Class Box {
03Double width;
04Double height;
05Double depth;
06
07// display volume of a box
08Void volume () {
09System.out.print(“volume is “);
10System.out.println(width * height * depth);
11}
12}
13Class BoxDemo3 {
14Public static void main (string args []) {
15Box mybox1 = new Box ();
16Box mybox2 = new Box();
17
18// assign values to mybox1’s instance variables
19Mybox1 . width = 10 ;
20Mybox1.height = 20 ;
21Mybox1 . depth = 15 ;
22/* assign different values to mybox2’s
23Instance variables */
24Mybox2 . width = 3 ;
25Mybox2.height = 6;
26Mybox2.depth = 9;
27
28// display volume of first box
29Mybox1.volume () ;
30//display volume of second box
31Mybox2.volume();
32}
33}

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

Volume is 3000.0
Volume is 162.0

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

1Mybox1 .volume() ;
2Mybox2.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 () محاسبه و مقدارش به روتین فراخوان ، بازگردانده شود .مثال زیر ، نگارش بهبود یافته ای از برنامه پیشین است که همین کار را انجام می دهد :

01// Now , volume () returns the volume of a box .
02
03Class box {
04Double width;
05Double height;
06Double depth;
07
08// compute and return volume
09Double volume () {
10Return width * height * depth;
11}
12}
13Class boxdemo4 {
14Public static void main (string args[]) {
15Box mybox1 = new box() ;
16Box mybox2 = new box ();
17Double vol ;
18
19// assign values to mybox1’s instance variables
20Mybox1.width=10;
21Mybox1.height=20;
22Mybox1.depth = 15 ;
23/* assign different values to mybox2’s
24Instance variables */
25Mybox2.width=3;
26Mybox2.height=6;
27Mybox2.depth = 9 ;
28// get volume of first box
29Vol = mybox1.volume();
30System.out.println(“volume is” + vol) ;
31
32// get volume of decond box
33Vol = mybox2.volume() ;
34System .out.println(“volume is” + vol);
35}
36}

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

1Vol = mybox1.volume();

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

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

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

1Int square()
2{
3Return 10 * 10;
4}

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

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

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

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

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

1Mybox1.width=10;
2Mybox1.height=20;
3Mybox1.depth = 15 ;

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

01//this program uses a parameterized method.
02Class box {
03Double width;
04Double height;
05Double depth;
06//compute and return volume
07Double volume() {
08Returne width *height*depth;
09}
10// sets dimensions of box
11Void setdim(double w,double h, double d) {
12Width = w;
13Height = h;
14Depth = d;
15}
16}
17Class boxdemo5 {
18Public static void main (string arg[]) {
19Box mybox1 = new box();
20Box mybox2 =newbox();
21Double vol;
22// initialize each box
23Mybox1.setdim(10,20,15);
24Mybox2.setdim(3,6,9);
25//get volume of first box
26Vol = mybox1.volume();
27System.out.println(“volume is”+vol);
28//get volume of second box
29Vol=mybox2.volume();
30System.out.println(“volume is”+ vol);
31}
32}

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

1Mybox1.setdim(10,20,15);

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

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

01/*Here , box uses a Constructor to initialize the
02Dimensions of a box .
03*/
04Class box {
05Doublewidth;
06Double height;
07Double depth;
08// this is the Constructor for box .
09Box() {
10System.out .println(“Constructing box”);
11Width = 10;
12Height = 10;
13Depth = 10;
14}
15//compute and return volume() {
16Return width * height*depth;
17}
18}
19Class boxdemo6 {
20Public static void main (string arg[]) {
21// declare, allocate, and initialize box objects
22Box mybox1 = new box ();
23Box mybox2 = new box ();
24Double vol;
25// get volume of first box
26Vol = mybox1.volume();
27System.out.println(“volumeis” + vol);
28// get volume of second box
29Vol = mybox2.volume();
30System.out.println(“volumeis” + vol);
31}
32}

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

Constructing box
Constructing box
Volume is 1000.0
Volume is 1000.0

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

1Class-var = new classname();

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

1Box mybox1 = new box();

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

01/*Here , box uses a Constructor to
02 initialize the Dimensions of a box .
03*/
04Class box {
05Double width;
06Double height;
07Double depth;
08// this is the Constructor for box .
09Box(double w,double h, double d) {
10Width = w;
11Height = h;
12Depth = d;
13}
14//compute and return volume
15 Double volume() {
16Return width * height*depth;
17}
18}
19Class boxdemo7 {
20Public static void main (string args[]) {
21// declare, allocate, and initialize box objects
22Box mybox1 = new box (10,20,15);
23Box mybox2 = new box (3,6,9);
24Double vol;
25// get volume of first box
26Vol = mybox1.volume();
27System.out.println(“volumeis” + vol);
28// get volume of second box
29Vol = mybox2.volume();
30System.out.println(“volumeis” + vol);
31}
32}

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

Volume is 3000.0
Volume is 162.0

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

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

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

1//A redundant use of this.
2Box(double w, double h, double d) {
3This.width = w;
4This .height = h;
5This.depth =d;
6}

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

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

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

1Protected void finalize()
2{
3// finalization code here
4}

کلمه کلیدی 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 نامیده شده است ، پشته ای از اعداد صحیح را پیاده سازی می کند :

01// this class defines an integer stack that can hold 10 values .
02Class stack {
03Int stack[] = new int[10];
04Int tos;
05// Intialize top-of-stack
06Stack() {
07Tos = -1
08}
09// push an item onto the stack
10Void push (int item) {
11If(tos==9)
12System.out.println(“stack is full.”);
13Else
14Stack[+tos] = item;
15}
16// pop an item from the stack
17Int pop() {
18If (tos<0) {
19System.out.println(“stack undeflow.”);
20Return 0;
21}
22}

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

01Class TestStack {
02Public static void main (string arg[]) {
03Stack mystack1 = new stack();
04Stack mystack2 = new stack ();
05// push some numbers onto the stack
06For(int i=0; i<10; i++) mystack1.push(i);
07For(int i=10; i<20; i++) mystack2.push(i);
08// pop those numbers off the stack
09System.out.println(“stack in mystack1:”);
10For (int i=0; i<10; i++)
11System .out.println(mystack1.pop());
12
13System.out.println(“stack in mystack2:”);
14For (int i=0; i<10; i++)
15System.out.println(mystack2.pop());

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

Stack in mystack1:
9876543210
Stack in mystack2:
19 18 17 16 15 14 13 12 11 10

همان گونه که می بینید ، محتوای هر یک از پشته ها ، جدای از دیگری است .
واپسین نکته درباره کلاس stack : آرایه ای که برای نگهداری پشتهبه کار می رود (stack) ، با به کارگیری روش پیاده سازی مثال بالا ، از خارج از کلاس stack قابل تغییر خواهد بود . این امر موجب استفاده نادرست یا مخرب از stack خواهد شد .
در فصل آتی با روش حل این مشکل آشنا خواهید شد .
  • Digg
  • Sphinn
  • del.icio.us
  • Facebook
  • Google
  • Furl
  • Reddit
  • StumbleUpon
  • Donbaleh
  • Technorati
  • Balatarin
  • twitthis

هیچ نظری موجود نیست:

ارسال یک نظر

 
ساخت سال 1388 Java Tutorial.قدرت گرفته با بلاگر تبدیل شده به سیستم بلاگر توسط Deluxe Templates. طراحی شده بوسیله Masterplan. . بهینه شده برای سیستم فارسی مجتبی ستوده