وسیله ترکیبی از یک شناسه ، یک نوع و یک مقدار ده اولیه اختیاری تعریف خواهد
شد . علاوه بر این ، کلیه متغیرها دارای یک قلمرو هستند که رویت پذیری آنها را
تعریف می کند و یک زمان حیات نیز دارند. متعاقبا” این اجزائ را مورد بررسی
قرار می دهیم .
اعلان یک متغیر Declaring a variable
در جاوا کلیه متغیرها قبل از استفاده باید اعلان شوند . شکل اصلی اعلان متغیر
بقرار زیر می باشد : type identifier [=value] [/identifier[=value]…];
مقدار شناسه مقدار شناسه نوع
نوع (type) یکی از انواع اتمی جاوا یا نام یک کلاس یا رابط است . ( انواع
کلاس و رابط بعدا” بررسی خواهد شد . ) شناسه نام متغیر است . می توانید با
گذاشتن یک علامت تساوی و یک مقدار ، متغیر را مقدار دهی اولیه نمایید . در ذهن
بسپارید که عبارت مقدار دهی اولیه باید منتج به یک مقدار از همان نوعی ( یا
سازگار با آن نوع ) که برای متغیر مشخص شده ، گردد . برای اعلان بیش از یک نوع
مشخص شده ، از فهرست کاماهای (‘) جدا کننده استفاده نمایید .
در زیر مثالهایی از اعلان متغیر از انواع گوناگون را مشاهده می کنید . دقت
کنید که برخی از آنها شامل یک مقدار دهی اولیه هستند .
+ int a/ b/ c; // declares three ints/ a/ b/ and c.
+ int d = 3/ e/ f = 5; // declares three more ints/ initializing
+ // d and f.
+ byte z = 22; // initializes z.
+ double pi = 3.14159; // declares an approximation of pi.
+ char x = ‘x’; // the variable x has the value ‘x’.
شناسه هایی که انتخاب می کنید هیچ عامل ذاتی در نام خود ندارند که نوع آنها
را مشخص نماید . بسیاری از خوانندگان بیاد می آورند زمانی را که FORTRAN کلیه
شناسه های از Iتا Nا را پیش تعریف نمود تا از نوع INTEGER باشند ، در حالیکه
سایر شناسه ها از نوع REAL بودند . جاوا به هر یک از شناسه های متناسب شکل گرفته
امکان اختیار هر نوع اعلان شده را داده است .
مقدار دهی اولیه پویا Dynamic intialization
اگر چه مثالهای قبلی از ثابت ها بعنوان مقدار ده اولیه استفاده کرده اند
اما جاوا امکان مقداردهی اولیه بصورت پویا را نیز فراهم آورده است . این موضوع
با استفاده از هر عبارتی که در زمان اعلان متغیر باشد ، انجام می گیرد .
بعنوان مثال ، در زیر برنامه کوتاهی را مشاهده می کنید که طول ضلع یک مثلث
قائم الزاویه را با داشتن طول دو ضلع مقابل محاسبه می کند :
+ // Demonstrate dynamic initialization.
+ class DynInit {
+ public static void main(String args[] ){
+ double a = 3.0/ b = 4.0;
+ // c is dynamically initialized
+ double c = Math.sqrt(a * a + b * b);
+
+ System.out.println(“Hypotenuse is ” + c);
+ }
+ }
در اینجا سه متغیر محلی a، b،، c، اعلان شده اند . دو تای اولی توسط
ثابت ها مقدار دهی اولیه شده اند . اما متغیر C بصورت پویا و بر حسب طول اضلاع
مثلث قائم الزاویه ( بنابر قانون فیثاغورث ) مقدار دهی اولیه می شود . این
برنامه از یکی از روشهای توکار جاوا یعنی ()sqrt که عضوی از کلاس Math بوده و
ریشه دوم آرگومانهای خود را محاسبه میکند استفاده کرده است . نکته کلیدی اینجا
است که عبارت مقدار دهی اولیه ممکن است از هر یک از اجزائ معتبر در زمان مقدار
دهی اولیه ، شامل فراخوانی روشها ، سایر متغیرها یا الفاظ استفاده نماید .
قلمرو زمان حیات متغیرها
تابحال کلیه متغیرهای استفاده شده ، در زمان شروع روش ()main اعلان می شدند.
اما جاوا همچنین به متغیرها امکان می دهد تا درون یک بلوک نیز اعلام شوند .
همانطوریکه قبلا” توضیح دادیم ، یک بلوک با یک ابرو باز و یک ابرو بسته محصور
می شود : یک بلوک تعریف کننده یک قلمرو است . بدین ترتیب هر بار که یک بلوک
جدید را شروع میکنید ، یک قلمرو جدید نیز بوجود می آورید . همانطوریکه احتمالا”
از تجربیات برنامه نویسی قبلی بیاد دارید ، یک قلمرو (scope) تعیین کننده آن
است که چه اشیائی برای سایر بخشهای برنامه قابل رویت هستند . این قلمرو همچنین
زمان حیات (lifetime) آن اشیائ را تعیین می کند .
اکثر زبانهای کامپیوتری دو طبقه بندی از قلمروها را تعریف می کنند : سراسری
(global) و محلی (local) . اما این قلمروهای سنتی بخوبی با مدل موکد شی ئ گرایی
جاوا مطابقت ندارند . اگر چه در جاوا هم می توان مقادیری را بعنوان قلمرو
سراسری ایجاد نمود ، اما این فقط یک نوع استثنائ است و عمومیت ندارد . در جاوا
قلمرو اصلی همانهایی هستند که توسط یک کلاس یا یک روش تعریف می شوند . حتی همین
تمایز نیز تا حدی ساختگی و مصنوعی است . اما از آنجاییکه قلمرو کلاس دارای
مشخصات و خصلتهای منحصر بفردی است که قابل استفاده در قلمرو تعریف شده توسط
روش نیست ، این تمایز تا حدی محسوس خواهد بود . بخاطر تفاوتهای موجود ، بحث
قلمرو کلاس ( و متغیرهای اعلان شده داخل آن ) این مبحث بتعوق افتاده است . در حال
حاضر فقط قلمروهای تعریف شده توسط یک روش یا داخل یک روش را بررسی می کنیم .
قلمرو تعریف شده توسط یک روش با یک ابروی باز شروع می شود. اما اگر آن روش
دارای پارامترهایی باشد ، آنها نیز داخل قلمرو روش گنجانده خواهند شد . بعدا”
نگاه دقیقتری به پارامترها خواهیم داشت و فعلا” کافی است بدانیم که پارامترها
مشابه هر متغیر دیگری در یک روش کار می کنند .
بعنوان یک قانون عمومی ، متغیرهای اعلان شده داخل یک قلمرو برای کدهایی که
خارج از قلمرو تعریف می شوند ، قابل رویت نخواهند بود ( قابل دسترسی نیستند ).
بدین ترتیب ، هنگامیکه یک متغیر را درون یک قلمرو اعلان می کنید ، در حقیقت آن
متغیر را محلی دانسته و آن را در مقابل دستیابیها و تغییرات غیر مجاز محافظت
می کنید . در حقیقت ، قوانین قلمرو اساس کپسول سازی را فراهم می کنند .
قلمروها را می توان بصورت تودرتو (nesting) محفوظ داشت . بعنوان مثال ، هر
زمان یک بلوک کد ایجاد کنید ، یک قلمرو جدید تودرتو ایجاد نموده اید . هنگامیکه
این واقعه روی می دهد ، قلمرو بیرونی ، قلمرو درونی را دربرمی گیرد . این بدان
معنی است که اشیائ اعلان شده در قلمرو بیرونی برای کدهای داخل قلمرو درونی قابل
رویت هستند اما عکس این قضیه صادق نیست . اشیائاعلان شده داخل قلمرو درونی برای
بیرون قلمرو قابل رویت نخواهند بود .
برای درک تاثیر قلمروهای تودرتو ، برناه ریز را در نظر بگیرید :
+ // Demonstrate block scope.
+ class Scope {
+ public static void main(String args[] ){
+ int x; // known to all code within main
+
+ x = 10;
+ if(x == 10 ){ // start new scope
+ int y = 20; // known only to this bock
+
+ // x and y both known here.
+ System.out.println(“x and y :” + x + ” ” + y);
+ x = y * 2;
+ }
+ // y = 100 :// Error! y not known here
+
+ // x is still known here.
+ System.out.println(“x is ” + x);
+ }
+ }
همانطوریکه توضیحات نشان می دهند ، متغیر x در ابتدای قلمروی ()main اعلان
شده و برای کلیه کدهای متعاقب داخل ()main قابل دسترسی می باشد . داخل بلوک if
متغیر y اعلان شده است . از آنجاییکه یک بلوک معرف یک قلمرو است ، y فقط برای
سایر کدهای داخل بلوک خود قابل رویت است . این دلیل آن است که خارج بلوک
مربوطه ، خط y=100 در خارج توضیح داده شده است . اگر نشانه توضیح راهنمایی را
تغییر مکان دهید ، یک خطای زمان کامپایل (compile-time error) اتفاق می افتد
چون y برای بیرون از بلوک خود قابل رویت نیست . داخل بلوک if متغیر x قابل
استفاده است زیرا کدهای داخل یک بلوک ( منظور یک قلمرو تودرتو شده است ) به
متغیرهای اعلان شده در یک قلمرو دربرگیرنده دسترسی دارند .
داخل یک بلوک ، در هر لحظه ای می توان متغیرها را اعلان نمود ، اما فقط زمانی
معتبر می شوند که اعلان شده باشند . بدین ترتیب اگر یک متغیر را در ابتدای یک
روش اعلان می کنید، برای کلیه کدهای داخل آن روش قابل دسترس خواهد بود. بالعکس
اگر یک متغیر را در انتهای یک بلوک اعلان کنید ، هیچ فایده ای ندارد چون هیچیک
از کدها به آن دسترسی ندارند . بعنوان مثال این قطعه از برنامه غیر معتبر است
چون نمی توان از count قبل از اعلان آن استفاده نمود :
+ // This fragment is wrong!
+ count = 100; // oops! cannot use count before it is declared!
+ int count;
یک نکته مهم دیگر در اینجا وجود دارد که باید بخاطر بسپارید: متغیرها زمانی
ایجاد می شوند که قلمرو آن ها وارد شده باشد ، و زمانی خراب می شوند که قلمرو
آنها ترک شده باشد . یعنی یک متغیر هربار که خارج از قلمروش برود ، دیگر مقدار
خود را نگهداری نخواهد کرد . بنابراین ، متغیرهای اعلان شده داخل یک روش مقادیر
خود را بین فراخوانی های آن روش نگهداری نمی کنند . همچنین یک متغیر اعلان شده
داخل یک بلوک ، وقتی که بلوک ترک شده باشد ، مقدار خود را از دست خواهد داد .
بنابراین ، زمان حیات (lifetime) یک متغیر محدود به قلمرو آن می باشد .
اگر اعلان یک متغیر شامل مقدار دهی اولیه آن باشد ، آنگاه هر زمان که به
بلوک مربوطه وارد شویم ، آن متغیر مجددا” مقدار دهی اولیه خواهد شد . بعنوان
مثال برنامه زیر را در نظر بگیرید :
+ // Demonstrate lifetime of a variable.
+ class LifeTime {
+ public static void main(String args[] ){
+ int x;
+
+ for(x = 0; x < 3; x++ ){
+ int y =- 1; // y is initialized each time block is entered
+ System.out.println(“y is :” + y); // this always prints- 1
+ y = 100;
+ System.out.println(“y is now :” + y);
+ }
+ }
+ }
خروجی تولید شده توسط این برنامه بقرار زیر است :
y is- :1
y is now:100
y is- :1
y is now:100
y is- :1
y is now:100
همانطوریکه مشاهده می کنید ، هر بار که به حلقه for داخلی وارد می شویم ، y
همواره بطور مکرر مقدار اولیه ۱- را اختیار می کند . اگر چه بلافاصله به این
متغیر مقدار ۱۰۰ نسبت داده می شود، اما هر بار نیز مقدار خود را از دست میدهد.
و بالاخره آخرین نکته : اگر چه میتوان بلوکها را تودرتو نمود، اما نمیتوانید
متغیری را اعلان کنید که اسم آن مشابه اسم متغیری در قلمرو بیرونی باشد. از این
نظر جاوا با زبانهای Cو C++و متفاوت است . در زیر مثالی را مشاهده می کنید که
در آن تلاش شده تا دو متغیر جدا از هم با اسم اعلان شوند . در جاوا اینکار مجاز
نیست . در Cو C++و این امر مجاز بوده و دو bar کاملا” جدا خواهند ماند .
+ // This program will not compile
+ class ScopeErr {
+ public static void main(String args[] ){
+ int bar = 1;
+ { // creates a new scope
+ int bar = 2; // Compile-time error — bar already defined!
+ }
+ }
+ }
تبدیل خودکار و تبدیل غیر خودکار انواع
اگر تجربه قبلی برنامه نویسی داشته اید ، پس می دانید که کاملا” طبیعی است که
مقداری از یک نوع را به متغیری از نوع دیگر نسبت دهیم . اگر این دو نوع سازگار
باشند ، آنگاه جاوا بطور خودکار این تبدیل (conversion) را انجام می دهد .
بعنوان مثال ، همواره امکان دارد که مقدار int را به یک متغیر long نسبت داد .
اما همه انواع با یکدیگر سازگاری ندارند ، بنابراین هر گونه تبدیل انواع مجاز
نخواهد بود . بعنوان نمونه ، هیچ تبدیلی از doubleبه byte تعریف نشده است .
خوشبختانه ، امکان انجام تبدیلات بین انواع غیر سازگار هم وجود دارد . برای
انجام اینکار ، باید از تبدیل cast استفاده کنید که امکان یک تبدیل صریح بین
انواع غیر سازگار را بوجود می آورد . اجازه دهید تا نگاه دقیقتری به تبدیل
خودکار انواع و تبدیل cast داشته باشیم .
تبدیل خودکار در جاوا Java’s Automatic conyersions
هنگامیکه یک نوع داده به یک متغیر از نوع دیگر نسبت داده می شود ، اگر دو
شرط زیر فراهم باشد ، یک تبدیل خودکار نوع انجام خواهد شد :
ؤ دو نوع با یکدیگر سازگار باشند .
ؤ نوع مقصد بزرگتر از نوع منبع باشد .
هنگامیکه این دو شرط برقرار باشد ، یک تبدیل پهن کننده (widening) اتفاق
می افتد . برای مثال نوع int همواره باندازه کافی بزرگ است تا کلیه مقادیر
معتبر byte را دربرگیرد، بنابراین نیازی به دستور صریح تبدیل cast وجود ندارد.
در تبدیلات پهن کننده ، انواع رقمی شامل انواع عدد صحیح و عدد اعشاری با هر
یک از انواع سازگاری دارند . اما انواع رقمی با انواع charو booleanو سازگار
نیستند . همچنین انواع charو booleanو با یکدیگر سازگار نیستند .
همانطوریکه قبلا” ذکر شد ، جاوا هنگام ذخیره سازی یک ثابت عدد صحیح لفظی
(Literal integer constant) به متغیرهای از انواع byte، short،و longو ، یک
تبدیل خودکار نوع را انجام می دهد .
تبدیل غیر خودکار انواع ناسازگار
اگر چه تبدیلات خودکار انواع بسیار سودمند هستند ، اما جوابگوی همه نیازها
نیستند . بعنوان مثال ، ممکن است بخواهید یک مقدار int را به یک متغیر byte
نسبت دهید. این تبدیل بطور خودکار انجام نمی گیرد، زیرا یک byteاز intز کوچکتر
است .این نوع خاص از تبدیلات را گاهی تبدیل باریک کننده (narrowing conversions)
می نامند ، زیرا بطور صریح مقدار را آنقدر باریک تر و کم عرض تر می کنید تا با
نوع هدف سازگاری یابد .
برای ایجاد یک تبدیل بین دو نوع ناسازگار ، باید از cast استفاده نمایید . cast
یک تبدیل نوع کاملا” صریح است . شکل عمومی آن بقرار زیر می باشد : ( target – type )value
نوع نوع مقصد یا هدف
در اینجا نوع هدف ، همان نوعی است که مایلیم مقدار مشخص شده را به آن تبدیل
کنیم . بعنوان مثال ، قطعه زیر از یک برنامه تبدیل غیر خودکار از intبه byte
را اجرا می کند . اگر مقدار integer بزرگتر از دامنه یک byte باشد ، این مقدار
به مدول ( باقیمانده تقسیم یک integer بر دامنه ) byte کاهش خواهد یافت . + int a;
+ byte b;
+ //…
+ b =( byte )a;
هر گاه که یک مقدار اعشاری به یک عدد صحیح نسبت داده شود ، شکل دیگری از
تبدیل اتفاق می افتد : بریدن ، truncation . همانطوریکه می دانید ، اعداد صحیح
دارای قسمت اعشاری نیستند . بنابراین هنگامیکه یک مقدار اعشاری به یک نوع عدد
صحیح نسبت داده می شود ، جزئ اعشاری از بین خواهد رفت ( بریده خواهد شد ) .
بعنوان مثال ، اگر مقدار ۱٫۲۳ را به یک عدد صحیح نسبت دهیم ، مقدار حاصله فقط
عدد ۱ می باشد . مقدار ۰٫۲۳ بریده (truncated) خواهد شد . البته اگر اندازه
اجزائ عدد کلی آنچنان بزرگ باشد که در نوع عدد صحیح مقصد نگنجد ، آنگاه مقدار
فوق به مدول دامنه نوع هدف کاهش خواهد یافت .
برنامه زیر نشان دهنده برخی از تبدیلات انواع است که مستلزم تبدیل cast
می باشند :
+ // Demonstrate casts.
+ class Conversion {
+ public static void main(String args[] ){
+ bytt b;
+ int i = 257;
+ double d = 323.142;
+
+ System.out.println(“\nConversion of int to byte.”);
+ b =( byte )i;
+ System.out.println(“i and b ” + i + ” ” + b);
+
+ System.out.println(“\nConversion of double to int.”);
+ i =( int )d;
+ System.out.println(“d and i ” + d + ” ” + i);
+
+ System.out.println(“\nConversion of double to byte.”);
+ b =( byte )d;
+ System.out/println(“d and b ” + d + ” ” + b);
+ }
+ }
خروجی این برنامه بقرار زیر می باشد :
Conversion of int to byte.
i and b 257 1
Conversion of double to int.
d and i 323.142 323
Conversion of double to byte.
d and b 323.142 67
دیدگاه خود را بیان کنید.
باید وارد سایت شده باشید برای دیدگاه دادن