صفحات

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

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

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

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 خواهد شد .
در فصل آتی با روش حل این مشکل آشنا خواهید شد .
ادامه مطلب

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

کلاس ها در هسته مرکزی جاوا جا دارند. کلاس ها؛ساختار منطقی هستند که کل زبان جاوا بر روی آن ساخته شده است زیرا شکل و ماهیت شی ء ها را تعریف می کنند. بدین ترتیب کلاس ها پایه و اساس برنامه سازی شی ء گرا را در جاوا تشکیل می دهند . هر موضوعی که بخواهید در برنامه های جاوا پیاده سازی کنید؛می بایست در یک کلاس نهان encapsulated شود.
اصول کلاس ها :
شاید مهمترین نکته ای که باید در مورد کلاس ها یاد بگیریم آن است که نوع جدیدی از داده ها را تعریف می کند. داده های نوع جدید را پس از تعریف شدن می توان برای ایجاد شی ء های نوع مورد نظر به کار برد. از این رو هر کلاس؛ الگویی template برای یک شیء است و هر شیء هم نمونه ای instance از یک کلاس به شمار می آید. چون هر شی ء نمونه ای از یک کلاس است تغلب می بینیم که دو واژه شیء و نمونه به جای یکدیگر به کار برده می شوند.
شکل عمومی کلاس ها :
وقتی کلاسی را تعریف می کنید؛ ماهیت و فرم دقیق آن معرفی می شود. این کار با مشخص کردن داده های درون آن و روتین هایی که بر روی آن داده ها عمل می کنند؛انجام می گردد.روتین های هر کلاس؛ رابز منتهی به داده های آن را تعریف می کنند.
هر کلاس با استفاده از کلمه کلیدی class تعریف می شود. شکل عمومی تعریف هر کلاس در ذیل نشان داده شده است:

01Class classname{
02Type instance-variable1;
03Type instance-variable2;
04//…
05Type instance-variable N;
06
07Type methodname1(parameter-list){
08//body of method
09}
10Type methodname2(parameter-list){
11//body of method
12//…
13Type methodnameN(parameter-list){
14//body of method
15}

داده ها یا متغیر هایی که در هر کلاس تعریف می شوند؛ نمونه متغیر instance variable نامیده می شوند. روتین ها نیز در متد ها جای میگیرند. به طور کلی؛ به متد ها و متغیر هایی که در هر کلاس تعریف می شوند؛اعضای member کلاس گفته می شود. در بیشتر کلاس ها؛ متد های تعریف شده برای هر کلاس هستند که بر روی نمونه متغیر ها کار می کنند و به آنها دستیابی دارند. از این رو؛ این متد ها هستند که چگونگی استفاده از داده های هر کلاس را تعیین می کنند.
دلیل اینکه متغیرهای هر کلاس ، نمونه متغیر خوانده می شوند ، آن است که هر نمونه از یک کلاس (یعنی ، هر شی از یک کلاس) ، کپی خاص خود را از متغیرها دارد . از این رو ، داده های هر شیء ،جداگانه و خاص خود آن بوده و با داده های یک شیء دیگر یکسان نیستند . به زودی به بررسی این نکته خواهیم پردداخت ، اما این نکته مفهوم مهمی است که باید پیشاپیش فرابگیرید.
تمام متدها همان شکل عمومی main() را دارند که تا به حال به کاربرده ایم . اما ، بیشتر متدها به عنوان یک متد ایستا یا عمومی مشخص نمی شوند . توجه داشته باشید که در شکل عمومی کلاس ها ، متدی به نام main() مشخص نمی شود . کلاس های جاوا نیاز به متد main() ندارند . تنها زمانی چنین متدی مشخص می شود که کلاس مورد نظر ، نقطه آغازین برنامه تان باشد . به علاوه ، اپلت ها اصلا نیاز به متدی به نام main() ندارند .
توجه : برنامه سازان C++ توجه داشته باشند که معرفی کلاس و پیاده سازی متدها در یکچا ذخیره می شوند و به طور جداگانه تعریف نمی شوند . این امر گاهی اوقات سبب ایجاد فایل های java . بسیار بزرگ می شود ، چه آنکه هر کلاس باید کلا در یک فایل واحد تعریف شود . دلیل گنجاندن این ویژگی در طراحی جاوا آن است که احساس می شد که در دراز مدت ، داشتن

یک کلاس ساده
مطالعه کلاس ها را با یک مثال ساده آغاز می کنیم . برای این کار کلاسی به نام BOX تعریف می کنیم که دارای سه نمونه متغیر به نام Width , height , depth است . در حال حاضر ، BOX فاقد هر گونه متد است .

1Class box {
2 Double width ;
3 Double height ;
4 Double depth ;
5}

همان گونه که گفته شد ، هر کلاس ، نوع جدیدی از داده ها را تعریف می کند . در این مثال خاص ، نوع جدیدی که ایجاد می شود ، BOX نامیده شده است . از این نام برای تعریف شیء های نوع BOX استفاده خواهد شد . مهم است به خاطر بسپارید که تعریف هر کلاس جدید تنها سبب اینجاد یک الگو می شود ؛ یک شیء واقعی ایجاد نمی شود . از این رو ، تعریف کلاس BOX در بالا سبب ایجاد هیچ شیئی از نوع BOX نمی شود .
برای آنکه یک شیء BOX ایجاد شود ، می بایست از عباراتی همچون سطر زیر استفاده کنید .

1BOX mybox = new box () ;
2 // creat a box object called mybox

پس از آنکه عبارت بالا اجرا شد ، mybox به عنوان نمونه ای از BOX ایجاد خواهد شد . از این رو ، یک واقعیت «فیزیکی» از کلاس BOX ایجاد خواهد شد . فعلا ، نگران جزئیات این عبارت نباشید .
باز هم لازم به ذکر است که هر گاه نمونه ای از یک کلاس را ایجاد می کنید ، شیئی ایجاد می شود که نسخه خاص خودش را از هر یک از نمونه متغیرهای تعریف شده در آن کلاس خواهد داشت . از این رو ، هر شی BOX ، نسخه های خاص خودش را از نمونه متغیرهای Width , height , depth خواهد داشت . برای دستیابی به این متغیرها باید از عملگر نقطه (.) استفاده کنید . این عملگر ، نام شیء را با نام «نمونه متغیر» مرتبط می کند . به عنوان مثال ، برای آنکه مقدار ۱۰۰ را به متغیر Width از mybox تخصیص دهید ، از عبارت زیر استفاده کنید :

1Mybox . width = 100

عبارت بالا برای کامپایلر مشخص می کند که مقدار ۱۰۰ را به نسخه ای از width که در شی mybox است تخصیص دهد . به طور کلی ، از عملگر نقطه (.) برای دستیابی به نمونه متغیرها و متدهای موجود در یک شیء استفاده می شود . در برنامه زیر از کلاس BOX استفاده شده است :

01/* A program that uses the BOX class.
02Call this file BOXDemo.java
03*/
04Class BOX {
05 Double width ;
06 Double height ;
07 Double depth ;
08}
09
10//this class declares an object of type BOX .
11Class boxvemo {
12Public static void main (string args[]) {
13Box mybox = new box ();
14Double vol ;
15// assign values to mybox’s instance variables
16Mybox.width = 10;
17Mybox.height = 20;
18Mybox.depth = 15;
19// compute volume of box
20Vol = mybox.width*mybox .height *mybox.depth;
21System.out.println(“volume is ”+vol) ;
22 }
23}

می بایست فایل حاوی این برنامه را Boxdemo.java بنامید ، چرا که متد main() در کلاسی است که Boxdemo نام دارد ، و نه BOX . کامپیوتر جاوا هر کلاس را به طور خودکار در فایل .class ایجاد خواهد شد ؛ یکی برای Box و یکی هم برای Boxdemo . کامپیوتر جاوا هر کلاس را به طور خودکار در فایل .class خاص خودش قرار می دهد . لزومی ندارد که هر دو کلاس BOX و Boxdemo دریک فایل دخیره شوند. هر یک از کلاس ها را می توانید در فایل های خاص خودشان قرار دهید : box.java و boxdemo.java .
برای آنکه این برنامه را اجرا کنید ، باید boxdemo.class را اجرا کنید. وقتی این کار را انجام می دهید ، خروجی زیر را خواهید دید :

Volume is 3000.0

همان گونه که پیش از این گفته شد ، هر شیء نسخه های خاص خودش را از نمونه متغیرها خواهد داشت. این بدین معناست که اگر دو شی نوع BOX داشته باشید ، هر یک از آنها ، نسخه های خاص خودشان را از width.depth و height خواهند داشت . مهم است بدانید که تغییراتی که در نمونه متغیرهای یک شی ایجاد می شوند، هیچ تاثیری بر نمونهه متغیرهای شیء دیگر نخواهند داشت. به عنوان مثال ، در برنامه زیر دو شیء BOX تعریف شده است :

01// this program declares two box objects .
02Class Box {
03 Double width ;
04 Double height ;
05 Double depth ;
06}
07Class boxDemo2 {
08Public static void main (string args[]) {
09Box mybox1 = new box ();
10Box mybox2 = new box ();
11Double vol ;
12
13// assign values to mybox’s instance variables
14Mybox1.width = 10;
15Mybox1.height = 20;
16Mybox1.depth = 15;
17/* assign different values to mybox’s instance variables*/
18Mybox2.width = 3;
19Mybox2.height = 6;
20Mybox2.depth = 9;
21
22// compute volume of first box
23Vol = mybox1.width*mybox1 .height *mybox1.depth;
24System.out.println(“volume is ”+vol) ;
25
26// compute volume of second box
27Vol = mybox2.width*mybox2 .height *mybox2.depth;
28System.out.println(“volume is ”+vol) ;
29 }
30}

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

Volume is 3000.0
Volume is 162.0

همانگونه که می بینید ، داده های mybox1 ، کاملا جدای از داده های موجود در mybox2 هستند .

شیوه تعریف کردن شیءها
همان گونه که در بالا شرح داده شد ، وقتی کلاسی را ایجاد می کنید ، در واقع یک نوع جدید برای داده ها ایجاد می شود . از این نوع جدید می توانید برای تعریف کردن شیئ هایی از آن نوع استفاده کنید . اما رسیدن به شئ های یک کلاس ، نوعی فرآیند دو مرحله ای است . نخست اینکه ، باید متغیری است که می تواند به یک شئ ارجاع داشته باشد. دوم اینکه ، می بایست یک نسخه فیزیکی واقعی از شئ به دست آورید و آن را به آن متغیر تخصیص دهید. این کار را می توانید با استفاده از عملگر new انجام دهید. عملگر new ، حافظه ای را به طور پویا (یعنی در زمان اجرا) به شئ تخصیص می دهد و نشانی آن را بر می گرداند. این نشانی سپس در متغیر ذخیره می شود . از این رو ، تمام شئ های نوع کلاس در جاوا باید به طور پویا تخصیص یابند . اینک به جزئیات این رویه می پردازیم .
درنمونه برنامه های زیر از سطری مشابه عبارت زیر برای تعریف شیئی از نوع BOX استفاده خواهد شد:

1Box mybox = new box();

دو مرحله پیش گفته در عبارت بالا ترکیب شده اند . عبارت بالا را می توان برای نشان دادن هر یک از مراحل به صورت زیر بازنویسی کرد :

1Box mybox ; // declare refrence to object
2Mybox = new box (); // allocate a box object

درسطر نخست ، mybox به عنوان نشانی شیئی از نوع box تعریف می شود . پس از اجرای این خط ، مقدار null در mybox ذخیره خواهد شد که نشانگر آن است که متغیر هنوز به هیچ شیء واقعی ارجاع ندارد . هر گونه اقدام برای استفاده از mybox در این مرحله منجر به بروز خطای زمان کامپایل خواهد شد . سطر دوم هم موجب تخصیص شیء واقعی و تخصیص نشانی آن به mybox می شود . پس از اجرای سطر دوم ، می توانید از mybox می شود . پس از اجرای سطر دوم ، می توانید از mybox به گونه ای استفاده کنید که گویی یک شیء Box است. اما mybox صرفا نشانی حافظه شیء BOX واقعی را نگهداری می کند . تاثیر این دو سطر در شکل ۱-۶ به تصویر کشیده شده است .
توجه : خوانندگانی که با C/C++ آشنایی دارند ، احتمالا متوجه شده اند که به نظر می رسد که نشانی شیء ها مشابه نشانه روها باشد . این گمان و تصور واقعا درست است . نشانی هر شیء ف مشابه نشانه رو نقطه ای از حافظه است . تفاوت اصلی – و کلید امنیت جاوا – آن است که نشانی همچون نشانه رو ها قابل پردازش و مدیریت نیست . از این رو ، ارجاع آنها را نمی توان به نقطه دلخواهی از حافظه تغییر داد و یا همچون یک صیحیح با آنها کار کرد .

نگاهی دقیقتر به new
همانگونه که در قسمت پیش شرح داده شد ، عملگر new ، حافظه هر شیء را به طور پویا تخصیص می دهد .شکل کلی آن در ذیل نشان داده شده است :

Class-var = new classname();

Class-var ، متغیری از نوع کلاسی است که ایجادمی شود ، classname نام کلاسی است که نمونه ای از آن ایجاد می شود . constructor کلاس با نام کلاس و دو پرانتزی که پس از آن قرار گرفته اند ، مشخص می شود . constructor مشخص می کند که هنگام ایجاد شیئی از نوع کلاس ، چه اتفاقی رخ می دهد . constructor های خاص خودشان را در تعریف کلاس مشخص می کنند . اما ، اگر constructor به طور صریح مشخص نشود ، در آن صورت جاوا آن را به طور خودکار تامین می کند . در خصوص کلاس box همین طور است . فعلا از constructor پیش فرض استفاده می کنیم . به زودی خواهید دید که چگونه می توانید constructor های خاص خود را تعریف کنید .
در این مقطع ممکن است از خود بپرسید که چرا نیازی به استفاده از new برای مواردی چون اعداد صحیح یا کارکترها نیست . پاسخ این پرسش آن است که انواع داده های پایه جاوا به صورت شیء پیاده سازی نمی شوند. بلکه ، به صورت متغیرهای «معمولی» پیاده سازی می شوند . این کار به خاطر بازدهی بیشتر انجام می گیرد . همان گونه که خواهید دید ، شیء ها ،ویژگیها و خصوصیات زیادی دارند که لازم است طرز برخورد با آنها نسبت به انواع داده های پایه متفاوت باشد . با عدم تحمیل سرباز خاص شیء ها به انواع داده های پایه ، جاوا قادر به پیاده سازی کارآمدتر انواع داده های پایه می شود . در آینده ، با آن دسته از نگارشهای «شیئی» انواع داده های پایه آشنا خواهید شد که برای استفاده در شرایطی مهیا شده اند که همان نوع شیء ها مورد نیاز می باشند .
مهم است به خاطر داشته باشید که new حافظه شیء ها را در طی اجرا ایجاد کنند . اما ، از آنجایی که حافظه محدوداست ، این احتمال وجود دارد که new به دلیل عدم وجود حافظه کافی نتواند حافظه لازم برای یک شیء را تخصیص دهد . اگر چنین اتفاقی رخ دهد . استثنا زمان اجرا پیش خواهد آمد (چگونگی مدیریت این استثنا ها را د آینده بررسی خواهیم کرد)..
تمایز بین کلاس ها و شیء ها را یک مرتبه دیگر مرور می کنیم . هر کلاس، نوع جدیدی از داده ها را ایجاد می کند که می توان برای ایجاد شیء ها به کار برد . یعنی ، هر کلاس نوعی چارچوب منطقی ایجاد می کند که رابطه بین اعضایش را تعریف می کند . وقتی شیئی از یک کلاس معین را تعریف می کنید ، در واقع نمونه ای از آن کلاس ایجاد می کنید . از این رو ، هر کلاس ، نوعی ساختار منطقی است . هر شیء نیز نوعی واقعیت فیزیکی است (یعنی هر شیء فضایی را در حافظه اشغال می کند) . مهم است که این تمایز را به ذهن خود بسپارید .
تخصیص متغیرهای ارجاع به شیء
وقتی عمل تخصیص انجام می گیرد، عملکرد متغیرهای ارجاع به شیء با آنچه انتظار دارید تفاوت دارد . به عنوان مثال ، فکر می کنید دو عبارت زیر چه عملی انجام می دهند؟

1Box b1 = new Box () ;
2Box b2 = b1;

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

اگر چه b1 و b2 هر دو به یک شیء ارجاع دارند ، اما به هیچ شکل دیگر مرتبط نیستند . به عنوان مثال ، تخصیص مقداری دیگر به b1 صرفا سبب قطع ارتباط آن با شیء اولیه می شود که البته ، هیچ تاثیری بر b2 نخواهد داشت . به عنوان مثال :

1Box b1 = new Box();
2Box b2 = b1 ;
3// …
4B1= null ;
B1 در اینجا با null مقدار دهی شده ، اما b2 هنوز به همان شیء اولیه ارجاع دارد .
نکته : وقتی مقدار متغیر ارجاع به شیء را به متغیر دیگری از همان نوع تخصیص می دهید ، هیچ نسخه ای شیء ایجاد نمی شود ، بلکه تنها نسخه ای از همان نشانی (ارجاع) ایجاد می شود.
ادامه مطلب
 
ساخت سال 1388 Java Tutorial.قدرت گرفته با بلاگر تبدیل شده به سیستم بلاگر توسط Deluxe Templates. طراحی شده بوسیله Masterplan. . بهینه شده برای سیستم فارسی مجتبی ستوده