آموزش برنامه نویسی جاوا قسمت چهاردهم

آموزش برنامه نویسی جاوا قسمت چهاردهم

در این جلسه از آموزش می‌خواهیم به نکات کلیدی در جاوا بپردازیم و از هرکدام از آن‌ها مثال‌هایی را با هم کار کنیم. در ابتدا با متغیر‌های سراسری و محلی آشنا می‌شویم. بعد با عملگر ? آشنا می‌شویم که به کد نویسی کمتر کمک می‌کند. همچنین یاد می‌گیریم که چگونه برای ساختار‌های تکرار خود Label‌هایی را در نظر بگیریم و از آن‌ها در برنامه استفاده کنیم. همچنین با کلمه‌های کلیدی continue و break آشنا می‌شویم و در آخر هم بازی ریختن تاس را با استفاده از متد ()random شبیه سازی می‌کنیم.

متغیر‌های سراسری و محلی (Global And Local Variables)
همانطور که در آموزش‌های قبلی دیدیم، می‌توانیم متغیر‌هایی را در قسمت‌های مختلف برنامه‌ی خود تعریف کنیم. اما این متغیر‌هایی که تعریف می‌کنیم، فقط در قسمت‌های خاصی از برنامه قابل دستیابی و استفاده هستند. به آن محدوده‌ای که یک متغیر در آن قابل دستیابی است و ما می‌توانیم به آن متغیر دسترسی پیدا کنیم و از داده‌ی ذخیره شده در آن استفاده کنیم یا داده‌ی آن را تغییر دهیم، محدوده‌ی قابل مشاهده و یا Scope آن متغیر گفته می‌شود. نحوه‌ی تعریف کردن متغیر‌ها به دو صورت است. یا آن‌ها را به صورت سراسری (Global) تعریف می‌کنیم یا به صورت محلی (Local).

نکته‌ی مهمی که قبلا هم به آن اشاره شد این است که هر کلاس و متد دارای یک بدنه است که با استفاده از علامت‌های آکولاد باز } و آکولاد بسته { مشخص می‌شود. این آکولاد‌های باز و بسته محدوده‌ای را برای یک متد و یا یک کلاس مشخص می‌کنند که به آن بلوک (بلاک) آن متد یا کلاس گفته می‌شود. شاید سوالی برای شما پیش بیاید که منظور از محدوده چیست؟ فرض کنید مثلا ما یک متغیر از نوع عدد صحیح در بلاک یک متد تعریف کرده‌ایم. بنابراین آن متغیر فقط در همان بلاک متد قابل استفاده است و قبل و بعد از آن متد به هیچ وجه قابل استفاده نیست. به مثال زیر توجه کنید:

public class MainClass {

	// main method
	public static void main(String[] args) {

	}

	// first method
	static void first() {
		int number = 0;
		System.out.println(number);
	}

	// second method
	static void second() {
		System.out.println(number);
	}

}

همانطور که در کد بالا مشاهده می‌کنید در داخل بلوک کلاس MainClass سه متد وجود دارد. اولین متد، متد ()main که متد اصلی برنامه است. دومین متد، متدی است با نام first و سومین متد، متدی است با نام second. (در اینجا ما کاری به متد اصلی نداریم).

ابتدا در بلوک متد first متغیری از نوع عدد صحیح با نام number تعریف کرده‌ایم و سپس در خط بعد مقدار آن را در خروجی استاندارد نمایش داده‌ایم. اما ما خواستیم مقدار متغیر number را نیز از طریق متد second هم نمایش دهیم. اما اگر این برنامه را در یک IDE بنویسید، زیر متغیر number در متد second خطی قرمز می‌کشد و برنامه اجرا نمی‌شود. برنامه با خطای کامپایل مواجه شده است. علت چیست؟ علت این است که متغیر number به صورت لوکال (Local) یا محلی تعریف شده است. بنابراین پس از اجرای بلوک مربوط به متد first، متغیر number توسط JVM به صورت اتوماتیک از حافظه پاک می‌شود و دیگر همچین متغیری وجود ندارد که ما بخواهیم از آن استفاده کنیم. برای همین هم است که در متد second از number ایراد می‌گیرد. زیرا number یک چیز تعریف نشده است و جاوا آن را نمی‌شناسد.

نکته‌ی دیگری که باید به آن توجه کنید این است که ما وقتی یک متغیر را تعریف می‌کنیم، آن متغیر از آن خطی که تعریف شده به بعد قابل دسترسی است. به کد زیر توجه کنید:

public class MainClass {

	// main method
	public static void main(String[] args) {

	}

	// third method
	static void third() {
		System.out.println(number);
	}

	// first method
	static void first() {
		int number = 0;
		System.out.println(number);
	}

	// second method
	static void second() {
		System.out.println(number);
	}

}

ما در ادامه‌ی کد قبلی، متدی با نام third را قبل از متد first تعریف کرده‌ایم و باز خواسته‌ایم از متغیر number استفاده کنیم. اما باز هم با ارور مواجه می‌شویم.

توجه داشته باشید اگر در داخل یک بلاک، بلاکی دیگر تعریف کنیم، در داخل بلاک دوم (زیر بلاک) می‌توانیم به متغیر‌های بلاک اول دسترسی داشته باشیم. به کد زیر توجه کنید:

public class MainClass {

	public static void main(String[] args) {

		for (int i = 0; i < 10; i++) {
			int k = 0;
			for (int j = 0; j < 10; j++) {
				System.out.println(k);
			}
		}
	}

}

در کد بالا ما یک حلقه‌ی for ایجاد کرده‌ایم که در داخل بلاک حلقه‌ی اولی (حلقه‌ی خارجی) یک متغیر با نام k تعریف کرده‌ایم. و سپس یک حلقه‌ی for دیگر داخل حلقه‌ی اول تعریف کرده‌ایم (حلقه‌های تو در تو) و در داخل بلاک حلقه‌ی دوم از متغیری استفاده کرده‌ایم که در بلوک حلقه‌ی اول تعریف شده است (متغیر k). ما از برنامه هیچ اروری دریافت نمی‌کنیم و برنامه کامپایل و اجرا می‌شود. اما متغیر j در داخل بدنه‌ی حلقه‌ی اولی قابل استفاده نیست.

حالت دومی که ما می‌توانیم یک متغیر را تعریف کنیم، به صورت سراسری، عمومی و یا Global است که در محدوده‌ی یک کلاس تعریف می‌شود. به کد زیر توجه کنید:

public class MainClass {

	int number = 0;

	// main method
	public static void main(String[] args) {

	}

	// third method
	void third() {
		System.out.println(number);
	}

	// first method
	void first() {
		int number = 0;
		System.out.println(number);
	}

	// second method
	void second() {
		System.out.println(number);
	}

}

کد قبلی را کمی تغییر داده‌ایم. این‌بار متغیر number را در محدوده‌ی کلاس تعریف کرده‌ایم. یعنی متغیر number به صورت عمومی، سراسری و یا Global تعریف شده است. (متغیر number قبل از متد ()main تعریف شده است). بنابراین ما می‌توانیم آن را در داخل محدوده‌ی کلاس خود استفاده کنیم. در واقع متد‌های main, first, second, third در داخل بلاک کلاس قرار دارند و می‌توانند از متغیر‌هایی که در داخل کلاس تعریف می‌شوند استفاده کنند.

نکته: به متغیر‌هایی که داخل بدنه‌ی کلاس و خارج از تمام متد‌ها تعریف می‌شوند، Property‌های یک کلاس و یا فیلد‌های یک کلاس گفته می‌شود. در مورد این موضوع در مبحث شی گرایی مفصلا صحبت می‌شود.

عملگر ?
در آموزش‌های قبلی ما با ساختار شرطی if…else آشنا شدیم و مثال‌هایی را کار کردیم. به عنوان مثال یک شرط را بررسی می‌کردیم و اگر آن شرط برقرار بود مقدار متغیری از نوع boolean را true و اگر شرط برقرار نبود مقدار متغیر را false در نظر می‌گرفتیم. در جاوا روش دیگری وجود دارد که ما بجای استفاده از ساختار if…else، از عملگر ? استفاده و یک شرط را بررسی می‌کنیم. شکل کلی استفاده از این عملگر به صورت زیر است:

بخوانید  اینستاگرام چگونه می‌تواند به رونق کسب و کار کمک کند؟

variable = condition?expresion1:expresion2

کد بالا به این صورت است که اگر حاصل ارزیابی شرط (condition) مقدار true داشته باشد، expresion1 اجرا می‌شود، در غیر این صورت expresion2 اجرا می‌شود و نتیجه به variable نسبت داده می‌شود. به کد زیر توجه کنید:

public class MainClass {

	public static void main(String[] args) {
		int num = 10;
		boolean b = false;

		b = num <= 10 ? true : false;

		System.out.println(b);
	}

}

در کد بالا ما ابتدا یک متغیر از نوع عدد صحیح تعریف کرده‌ایم. و همچنین یک متغیر دیگر از نوع boolean که مقدار پیش فرض آن false است. حالا می‌خواهیم متغیر b را مقدار دهی کنیم. به این صورت است که ابتدا متغیر b را می‌نویسیم و سپس یک علامت مساوی (انتساب) قرار می‌دهیم. بعد می‌آییم یک شرطی را بررسی می‌کنیم. شرط این است که اگر مقدار متغیر num کوچکتر و یا مساوی ۱۰ باشد، مقدار بعد از عملگر ? به متغیر b نسبت داده شود. در غیر این صورت مقدار بعد از دو نقطه (:) به متغیر b نسبت داده شود. در بالا با توجه به اینکه مقدار متغیر num برابر با ۱۰ است، بنابراین شرط ما برقرار است و مقدار true به متغیر b نسبت داده می‌شود. دو نقطه (:) نقش else در ساختار if…else را دارد.

استفاده از برچسب برای حلقه‌های تکرار
در جاوا امکانی وجود دارد که ما می‌توانیم برای حلقه‌های خود یک برچسب (Label) یا اصطلاحا نامی را در نظر بگیریم. به کد زیر توجه کنید:

public class MainClass {

	public static void main(String[] args) {

		outer: for (int i = 0; i < 10; i++) {

			inner: for (int j = 0; j < 10; j++) {

			}
		}
	}

}

من ابتدا یک حلقه‌ی for ایجاد کرده‌ام و سپس در داخل حلقه‌ی for، یک حلقه‌ی for دیگری ایجاد کرده‌ام. همانطور که قبلا هم اشاره کردیم به این مدل حلقه‌ها، حلقه‌های تو در تو (Nested Loops)  گفته می‌شود. همانطور که مشاهده می‌کنید من برای حلقه‌ی خارجی نام outer و برای حلقه‌ی داخلی نام inner را در نظر گرفته‌ام. (برای نوشتن برچسب فقط کافی است قبل از ایجاد حلقه یک نامی را نوشته و سپس دو نقطه قرار دهیم و بعد حلقه‌ی خود را بنویسیم). توجه داشته باشید که نوشتن برچسب برای حلقه‌ها، برای خوانایی برنامه نیست. یعنی برای این نیست که ما (برنامه نویس) راحت‌تر تشخیص دهیم که کدام حلقه داخلی و کدام یک خارجی است (البته می‌توان برای این هم استفاده کرد). اما در اصل کاربرد دیگری دارد که در ادامه به آن اشاره می‌کنیم.

کنترل اجرای حلقه‌ها با استفاده از continue و break
در این آموزش ما با ساختار switch آشنا شدیم و کاربرد کلمه‌ی کلیدی break را نیز فهمیدیم. نوشتن break باعث می‌شد که برنامه از ساختار switch خارج شود و دیگر ادامه‌ی آن را اجرا نکند. اما break فقط برای ساختار switch نیست و در جاهای دیگر برنامه هم استفاده می‌شود. به کد زیر توجه کنید:

public class MainClass {

	public static void main(String[] args) {
		int counter = 0;
		while(counter <= 10) {
			System.out.println("Num[" + counter + "]: " + counter);
			counter++;

			if (counter == 5) {
				System.out.println("Break");
				break;
			}
		}
	}

}

برنامه‌ی بالا به این صورت است که حلقه‌ی while تا زمانی که متغیر counter برابر با ۱۰ نشده است ادامه پیدا می‌کند و مقادیر متغیر counter را چاپ می‌کند. اما در داخل بلوک while یک شرطی قرار داده شده است و آن این است که اگر مقدار counter برابر با ۵ شد، ابتدا پیغام break را چاپ کند و سپس از حلقه خارج شود. اگر برنامه‌ی فوق را به همین شکلی که در بالا نوشته شده است اجرا کنید، خروجی برنامه تا عدد ۴ بیشتر نیست و در آخر هم عبارت Break چاپ می‌شود. خروجی برنامه را در زیر مشاهه می‌کنید:

Num[0]: 0
Num[1]: 1
Num[2]: 2
Num[3]: 3
Num[4]: 4
Break

اما اگر تغییراتی در ساختار if ایجاد کنیم، مثلا متغیر counter را برابر با عدد ۱۵ بجای ۵ قرار دهیم، دیگر break اجرا نمی‌شود و در خروجی برنامه اعداد یک تا ۱۰ چاپ می‌شود و عبارت Break هم چاپ نمی‌شود. توجه داشته باشید که بعد از دستور break اگر چیز دیگری بنویسید با خطای کامپایل مواجه می‌شوید و برنامه کامپایل نمی‌شود. در ادامه می‌خواهیم برنامه‌ای بنویسیم که کاربرد continue را آموزش دهیم.

continue به معنی ادامه دادن است و کاربرد آن در جاوا هم دقیقا به همین مفهوم است. به کد زیر توجه کنید:

public class MainClass {

	public static void main(String[] args) {
		for (int i = 0; i <= 10; i++) {
			System.out.println("NUM[" + i + "]: " + i);
		}
	}

}

این کد دقیقا کدی است که در قسمت قبل از آن استفاده کردیم. (البته این‌بار از ساختار for استفاده کرده‌ایم). در زیر خروجی برنامه را مشاهده کنید:

NUM[0]: 0
NUM[1]: 1
NUM[2]: 2
NUM[3]: 3
NUM[4]: 4
NUM[5]: 5
NUM[6]: 6
NUM[7]: 7
NUM[8]: 8
NUM[9]: 9
NUM[10]: 10

همانطور که مشاهده می‌کنید اعداد ۰ تا ۱۰ به درستی نمایش داده شده است. حالا می‌خواهیم برنامه را طوری تغییر دهیم که وقتی شمارنده‌ی برنامه به عدد ۵ رسید، عملیاتی را انجام ندهد و دوباره به بررسی شرط حلقه (ابتدای حلقه) برگردد. به کد زیر توجه کنید:

public class MainClass {

	public static void main(String[] args) {		
		for (int i = 0; i <= 10; i++) {
			if (i == 5)
				continue;

			System.out.println("NUM[" + i + "]: " + i);
		}
	}

}

همانطور که مشاهده می‌کنید ما با ساختار if شرطی را بررسی کردیم که اگر شمارنده‌ی حلقه برابر با عدد ۵ شد، ادامه‌ی حلقه اجرا نشود و به ابتدای حلقه بازگردد. خروجی اجرای برنامه‌ی فوق را در زیر با دقت نگاه کنید:

NUM[0]: 0
NUM[1]: 1
NUM[2]: 2
NUM[3]: 3
NUM[4]: 4
NUM[6]: 6
NUM[7]: 7
NUM[8]: 8
NUM[9]: 9
NUM[10]: 10

همانطور که مشاهده می‌کنید عدد پنج در خروجی چاپ نشده است. زیرا ما شرطی را در برنامه قرار دادیم که وقتی که شمارنده‌ی برنامه برابر ۵ شد حلقه ادامه پیدا کند (یعنی کد‌های بعد از  continue اجرا نشود و به سراغ ابتدای حلقه برود). خب وقتی برنامه به سراغ ابتدای حلقه می‌رود، مسلما به شمارنده‌ی برنامه یک واحد اضافه می‌شود. بنابراین عدد ۵ چاپ نمی‌شود.

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

حالا می‌خواهیم برگردیم به کاربرد برچسب‌ها در برنامه. ابتدا به کد زیر توجه کنید:

public class MainClass {

	public static void main(String[] args) {

		outer: for (int i = 0; i < 10; i++) {

			inner: for (int j = 0; j < 10; j++) {
				System.out.println("Inner: " + j);

				if (i == 5) {
					break;
				}
			}
	System.out.println("Outer: " + i);
		}
	}

}

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

public class MainClass {

	public static void main(String[] args) {

		outer: for (int i = 0; i < 10; i++) {

			inner: for (int j = 0; j < 10; j++) {
				System.out.println("Inner: " + j);

				if (i == 5) {
					break outer;
				}
			}
	System.out.println("Outer: " + i);
		}
	}

}

همانطور که مشاهده می‌کنید کلمه‌ی کلیدی break را نوشته و سپس در جلوی آن نام برچسب حلقه‌ی مورد نظر را نوشته‌ایم. حالا اگر برنامه را اجرا کنیم و اجرای برنامه به ساختار if برسد و شرط برقرار باشد، break برای حلقه‌ی خارجی اجرا می‌شود.

استفاده از متد ()random از کلاس Math
 ()random یکی از متد‌های کلاس Math است که با استفاده از آن می‌توانیم یک عدد تصادفی اعشاری تولید کنیم. عدد تصادفی‌ای که این متد تولید می‌کند، یک عدد بین صفر و یک است که شامل صفر می‌شود ولی شامل یک نمی‌شود. به کد زیر توجه کنید:

public class MainClass {

	public static void main(String[] args) {

		double d = Math.random();

		System.out.println(d);
	}

}

در کد بالا با استفاده از متد ()random یک عدد اعشاری تصادفی ایجاد و در داخل متغیر d ذخیره کرده‌ایم. خروجی برنامه‌ی بالا به صورت زیر است:

۰.۷۱۹۷۲۷۴۳۲۰۷۲۲۳۹۳

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

حالا می‌خواهیم یکی از مباحث جذاب برنامه نویسی کاربردی یعنی شبیه سازی بازی ریختن تاس را مورد بررسی قرار دهیم. این برنامه را به شکل‌های مختلف می‌توان پیاده‌سازی کرد. تاس ۶ وجه دارد که با ریختن آن یکی از اعداد ۱ تا ۶ ظاهر می‌شود. نکته‌ی اول این است که اعداد تاس صحیح هستند، در صورتی که اعداد تولید شده توسط متد ()random اعشاری است. پس ابتدا باید عمل Casting را انجام دهیم تا عدد اعشاری را به عدد صحیح تبدیل کنیم. به کد زیر توجه کنید:

public class MainClass {

	public static void main(String[] args) {

		double d = Math.random();
		int i = (int) d;

		System.out.println(i);
	}

}

اگر برنامه‌ی بالا را اجرا کنید به جز صفر عدد دیگری را در خروجی مشاهده نمی‌کنید. زیرا همانطور که گفته شد متد ()random عددی اعشاری بین صفر و یک تولید می‌کند که عدد شامل صفر است اما شامل یک نیست. بنابراین قسمت صحیح عدد که صفر است اصلا تغییر نمی‌کند و با هر بار اجرای برنامه فقط قسمت اعشاری آن تغییر می‌کند. حالا ما در کد بالا با کست کردن قسمت اعشاری عدد را از دست داده‌ایم. بنابراین به جز صفر عدد دیگری نمی‌بینیم. حالا برای اینکه عدد تصادفی را به محدوده‌ی مورد نظر خود ببریم (یک عدد بین ۰ تا ۶) کافی است عدد بدست آمده از متد ()random را در ۶ ضرب کنیم تا اعدادی در بازه‌ی ۰ تا ۶ تولید شود. به شکل زیر:

public class MainClass {

	public static void main(String[] args) {

		double d = Math.random() * 6;
		int i = (int) d;

		System.out.println(i);
	}

}

حالا اگر برنامه را اجرا کنیم عدد بدست آمده، عددی است بین صفر تا ۶ که شامل صفر می‌شود اما شامل ۶ نمی‌شود. اما از آن‌جا که در تاس عدد صفر وجود ندارد و عدد ۶ وجود دارد، بنابراین باید برنامه را طوری تغییر دهیم که عدد‌های تولید شده بین ۱ تا ۷ باشد. که شامل عدد ۱ باشد اما شامل عدد ۷ نباشد. راه حل این کار بسیار ساده است. فقط کافی است حاصل عبارت فوق را یک واحد افزایش دهیم. به کد زیر توجه کنید:

public class MainClass {

	public static void main(String[] args) {

		double d = Math.random() * 6 + 1;
		int i = (int) d;

		System.out.println(i);
	}

}

حالا با هر بار اجرای برنامه‌ی فوق، همانند این است که شما یک تاس را ریخته‌اید تا یک عدد مشاهده شود که این عدد شامل عدد‌های ۱ تا ۶ می‌شود.

نکته: می توان برای تولید اعداد تصادفی از کلاس Random که در پکیج java.util است استفاده کرد و بعد با استفاده از متد‌های آن مانند ()nextInt و … اعداد تصادفی تولید کرد. اما اگر بخواهیم از این روش استفاده کنیم باید از روی کلاس Random یک شی ایجاد کنیم و به دلیل اینکه هنوز به مبحث شی گرایی نرسیده‌ایم، آن را توضیح نمی‌دهیم.

سایر اطلاعات

منبع: www.zoomit.ir