آموزش زبان برنامه نویسی جاوا: پکیج ها
Package
وجود پکیج در جاوا بسیار مفید است و نبود آن باعث بروز مشکلاتی میشود. همانطور که قبلا هم اشاره کردیم، در برنامههای بزرگ و سازمانی، تعداد کلاسها حتی به هزار تا هم خواهد رسید و از آنجایی که نام فایل java. باید با نام کلاسی که به صورت public تعریف شده است یکی باشد، ممکن است با مشکلاتی رو به رو شویم. به عبارت دیگر مدیریت کلاسها به دلیل زیاد بودن آنها دشوار میشود. اجازه دهید برای درک بهتر توضیحات فوق، مثالی را برای شما یادآوری کنم. در کامپیوتر خود وارد مدیریت فایلها (File Manager یا File Explorer) و بعد وارد یکی از درایوها مثلا درایو C شوید. در همان جا اگر بخواهیم دو فایل با نامها و پسوندهای یکسان ایجاد کنیم، با اروری مواجه میشویم که مینویسد در این مکان فایلی با این نام وجود دارد. اما اگر مجبور باشیم که در همان درایو C دو فایل با نامها و پسوندهای یکسان داشته باشیم، تنها راه حل، ایجاد یک دایرکتوری (Folder) دیگر است، و باید یکی از فایلها را در داخل فولدر جدید قرار دهیم. در این صورت با اروری مواجه نمیشویم. مفهوم پکیج در جاوا هم دقیقا همین است. زیرا کلاسهایی که ما در جاوا ایجاد میکنیم، همگی در یک دایرکتوری ذخیره میشوند (یک فایل پروژه است و همهی فایلهای برنامه در آن فایل پروژه ایجاد میشوند)، بنابراین فقط میتوانیم از طریق پکیجها، در یک برنامه فایلهایی هم نام ایجاد کنیم. استفاده از پکیجها فایدههای دیگری هم دارد. از آنجایی که بسیاری از کلاسهایی که افراد یا شرکتها مینویسند، جنبهی کتابخانهای (Library) دارند و ممکن است توسط شرکتهای دیگر به صورت کتابخانه استفاده شود، بهتر است نام گذاری پکیجها به گونهای باشد که اگر دو کلاس هم نام در دو کتابخانهی مختلف وجود داشته باشد و ما بخواهیم همزمان از دو کتابخانه در برنامهی خود استفاده کنیم، تداخلی از لحاظ اسم و پکیج رخ ندهد. برای همین منظور شرکت Sun پیشنهاد کرده است که شرکتها برای نام گذاری پکیجهای خود از آدرس URL همان شرکت به صورت معکوس استفاده کنند (زیرا هر آدرس URL با دیگری فرق دارد). (بعضی از مطالب فوق از کتاب احمدرضا صدیقی گرفته شده است). ابتدا ما نحوهی ساخت یک پکیج را در محیط توسعهی اکلیپس به شما آموزش میدهیم.
ابتدا بر روی پروژهی خود کلیک راست کنید و از منوی New گزینه Package را انتخاب کنید. تصویر زیر:
سپس با پنجرهی زیر مواجه میشوید:
همانطور که در قسمت Name مشاهده میکنید ما نام URL شرکت را به صورت معکوس یعنی: ir.zoomit نوشتهایم. فلسفهی معکوس نوشتن نام پکیجها این است که ما باید نام پکیجهای خود را از کل به جز بنویسیم. یعنی دامنهی ir یک دامنهی کلی است و خیلی از سایتهای دیگری هستند که با دامنهی ir ثبت شدهاند. اما zoomit یک نام جزئیتر از ir است و فقط مختص همین شرکت است. هرچه بخواهیم نام پکیج خود را جزئیتر کنیم، فقط کافی است یک نقطه (.) قرار دهیم و سپس نام دیگری را بنویسیم. وقتی نقطه قرار میدهیم، اکلیپس به صورت خودکار داخل دایرکتوری فعلی، یک دایرکتوری دیگری با نامی که ما تعیین کردهایم ایجاد میکند. حالا اگر در قسمت Package Explorer اکلیپس دقت کنید، در زیرشاخهی src یک پکیج با نامی که ما تعیین کردهایم ایجاد شده است. تصویر زیر:
همانطور که مشاهده میکنید رنگ پکیجی که ایجاد کردهایم سفید است. این به این دلیل است که در داخل این پکیج هنوز کلاسی وجود ندارد. به محض ایجاد یک کلاس در این پکیج، رنگ پکیج به قهوهای تغییر میکند. حال در داخل این پکیج یک کلاس با نام Main ایجاد کنید (در آموزش قبلی نحوهی ساخت کلاس توضیح داده شده است). بعد از ساختن کلاس Main، دوباره یک کلاس دیگر با همین نام (Main) در همان پکیج (ir.zoomit) ایجاد کنید. تصویر زیر:
همانطور که در پنجرهی بالا با یک مستطیل قرمز رنگ مشخص شده است، اکلیپس به ما یک ارور را نمایش میدهد و امکان ساختن چنین کلاسی را به ما نمیدهد. زیرا در حال حاضر کلاسی با نام Main در پکیج ir.zoomit قرار دارد. نکتهی دیگری که باید به آن توجه کنیم این است که ما میتوانیم در همان لحظهی ساختن کلاس، پکیج خود را هم ایجاد کنیم (در تصویر بالا با یک مستطیل آبی رنگ مشخص شده است). برای اینکه بتوانیم در همین برنامه یک کلاس دیگری با نام Main ایجاد کنیم، اقدام به ساخت یک پکیج دیگر مثلا با نام com.google میکنیم.
نکته: در نام گذاری پکیجها بهتر است همواره از حروف کوچک الفبای انگلیسی استفاده شود.
به تصویر زیر دقت کنید:
همانطور که مشاهده میکنید کلاسی ساختهایم با نام Main، اما در پکیج com.google. در این اینجا ما ساختن پکیج و کلاس را در یک مرحله انجام دادهایم. (یعنی وقتی بر روی پروژهی خود کلیک راست میکنید، از منوی New گزینهی کلاس را انتخاب کنید و در همانجا نام پکیج را هم بنویسید). به Package Explorer در اکلیپس توجه کنید:
همانطور که در تصویر فوق واضح است ما به وسیلهی پکیجها در یک برنامه، دو فایل جاوا با نامهای یکسان ایجاد کردهایم (بدون دریافت هیچگونه اروری). نکتهی دیگری که وجود دارد این است که ما میتوانیم در داخل یک پکیج، یک پکیج دیگر نیز ایجاد کنید. حالا میخواهیم در داخل پکیج com.google یک پکیج دیگری ایجاد کنیم. بنابراین چون پکیج جدید قرار است زیر مجموعهی پکیج com.google باشد، باید از ادامهی نام فعلی پکیج (com.google)، نام پکیج جدید (com.google.java) را انتخاب کنید. به تصویر زیر دقت کنید:
به قسمت نام پکیجها توجه کنید. من بین نام پکیج قبلی و نام پکیج جدید یک خط قرمز نازک کشیدهام. منظورم از کشیدن خط این بوده است که برای اینکه بخواهیم پکیجی زیر مجموعهی یک پکیج دیگر باشد، باید حتما نام پکیج قبلی، در ابتدای نام پکیج جدید باشد. نام کلاس را هم دوباره Main در نظر گرفتهام که متوجه شوید حتی در یک پکیج هم با ایجاد پکیجهای جدید میتوان کلاسهایی با نامهای یکسان داشت. (برای ایجاد یک پکیج در داخل یک پکیج دیگر، بر روی پکیج مورد نظر کلیک راست کرده و از منوی New گزینهی Package را انتخاب کنید. یک نقطه قرار دهید و نام پکیج جدید را در ادامهی نام پکیجی که نوشته شده است (بعد از نقطه)، بنویسید).
وضعیت فعلی Package Explorer را در زیر مشاهده میکنید:
حالا میخواهیم وضعیت پکیجهایی که ایجاد کردهایم را در سیستم فایل مشاهده کنیم. برای این کار ابتدا از پروژهی خود یک Properties میگیریم. برای اینکار بر روی پروژهی خود کلیک راست کرده و آخرین گزینه یعنی Properties را انتخاب کنید. تصویر زیر:
بعد از انتخاب گزینهی مورد نظر با پنجرهی زیر مواجه میشوید و از آنجا محل Workspace (محل ذخیره سازی پروژه بر روی هارد) پروژهی خود را نگاه کنید و به آدرس مورد نظر در سیستم فایل بروید. تصویر زیر:
Location محل ذخیره سازی پروژه بر روی سیستم فایل است. بعد از اینکه وارد محل آدرس مورد نظر شدید، با تصویر زیر مواجه میشوید.
اگر از محیط توسعهی اکلیپس استفاده میکنید، نحوهی چیدمان فولدرهای موجود در دایرکتوری پروژه به صورتی است که در تصویر بالا مشاهده میکنید. اگر دقت کرده باشید تمام کلاسها و پکیجهایی که در اکلیپس ایجاد کردیم، در زیر شاخهی src بود (اگر دقت نکردید، دقت کنید!!!). بنابراین در اینجا هم برای دسترسی به پکیجها و کلاسهای خود وارد دایرکتوری src میشویم. تصویر زیر:
همانطور که مشاهده میکنید دو پکیجی که در شاخهی اصلی (Root) فایل src ایجاد کردهایم وجود دارد. همانطور که دیدید من بعد از نام دامنه (ir و com) یک نقطه قرار دادم، نقطه باعث میشود داخل دایرکتوری مورد نظر، یک فولدر دیگر با نامی که ما انتخاب میکنیم ایجاد شود. مثلا پکیج ir.zoomit، ابتدا فولدر ir ساخته میشود و سپس داخل فولدر ir، فولدری دیگر با نام zoomit ساخته میشود و بعد هم کلاسی که ایجاد کردیم (Main) داخل دایرکتوری zoomit قرار میگیرد. به تصویر زیر دقت کنید:
شمارهی 1 شاخهی اصلی است. ابتدا دامنه را نوشتهایم (شمارهی 2)، یک نقطه قرار دادهایم و نام دیگری را نوشتهایم (شمارهی 3) و در آخر هم کلاس Main در آخرین فولدر ایجاد شده است (شمارهی 4).
همانطور که میدانید وقتی برنامههای جاوا کامپایل میشوند، کدهای برنامه به بایت کد تبدیل میشوند و پسوند فایل هم از java. به class. تغییر میکند. اکلیپس فایلهای کامپایل شده را در داخل فولدر bin قرار میدهد (به Location پروژهی خود مراجعه کنید تا فولدر bin را مشاهده کنید). نکتهای که است در داخل فولدر bin هم تمام پکیجهایی که ما ایجاد کردهایم (دقیقا به همان صورت) ایجاد میشود فقط پسوند فایلهای برنامه عوض میشود.
تا اینجای کار ما با مفهوم پکیج آشنا شدهایم و نحوهی ایجاد آن را هم یاد گرفتهایم. نکتهای که در کد نویسی باید به آن توجه کنید این است که اگر کلاس ما داخل پکیجی قرار دارد، در خط اول برنامه باید با استفاده از کلید واژهی package نام پکیج را مشخص کنیم. مثلا کد کلاسی که در پکیج ir.zoomit قرار دارد باید به صورت زیر باشد:
package ir.zoomit;public class Main {}
اگر برای کلاسی که ایجاد میکنیم، پکیجی در نظر نگیریم، اکلیپس به صورت خودکار پکیجی با نام default package در نظر میگیرد. البته این default package اصلا وجود خارجی ندارد و فقط در Package explorer در محیط اکلیپس نشان داده میشود. تصویر زیر:
حالا فرض کنید ما میخواهیم در کلاسی که در پکیج ir.zoomit قرار دارد، از کلاسی استفاده کنیم که در پکیج com.google است. ابتدا نام کلاسها را تغییر میدهیم. برای تغییر نام کلاس، ابتدا کلاس مورد نظر را انتخاب کنید و سپس کلیک راست کنید. از منوی باز شده، ابتدا گزینهی Refactor و سپس Rename را انتخاب کنید. تصویر زیر:
نام کلاسها در زیر تغییر کرده است و کلاسی که در شاخهی روت (در داخل default package ایجاد کرده بودیم را حذف کردیم).
ابتدا کلاس Main که در پکیج ir.zoomit قرار دارد را باز کنید و متد main را هم در آن بنویسید. کد زیر:
package ir.zoomit;public class Main { public static void main(String[] args) { }}
حالا ما میخواهیم در این کلاس از کلاس SecondClass که در پکیج com.google.java است یک شی ایجاد کنیم. در این صورت ما حتما باید به صورت زیر از کلاس SecondClass استفاده کنیم:
package ir.zoomit;public class Main { public static void main(String[] args) { com.google.java.SecondClass obj = new com.google.java.SecondClass(); }}
همانطور که مشاهده میکنید ما دقیقا باید نام کلاس را به همراه نام پکیج آن بیاوریم. زیرا SecondClass در یک پکیج دیگر قرار دارد و برای استفاده از آن حتما باید نام پکیج در ابتدای نام کلاس (SecondClass) آورده شود. اما همانطور که در کد مشاهده میکنید ما هر بار که بخواهیم از SecondClass استفاده کنیم باید نام پکیج را دقیقا بنویسیم که این کار هم سخت و زمانبر است و هم باعث شلوغ شدن کدهای کلاس ما میشود. اما نگران نباشید؛ جاوا برای اینکار راه حلی را اندیشیده است. ما میتوانیم از دستور import برای وارد کردن (import) کردن پکیجها در کلاس خود استفاده کنیم. در این صورت ما یک بار پکیج را import میکنیم و در طول برنامه هر چند بار که بخواهیم از کلاس مورد نظر بدون نوشتن نام کامل پکیج در ابتدای نام کلاس استفاده میکنیم. به کد زیر توجه کنید:
package ir.zoomit;import com.google.java.SecondClass;public class Main { public static void main(String[] args) { SecondClass obj = new SecondClass(); }}
دستور import را باید در ابتدای فایل برنامه بعد از معرفی پکیج قرار دهید. (یعنی اگر فایل برنامه در داخل پکیج است، در خط اول ابتدا پکیج آورده میشود و در خطهای بعدی importها نوشته میشوند. اما اگر پکیجی وجود نداشته باشد، خط اول برای importها میشود). منظور این است که اگر importها را داخل کلاس یا متد بنویسید با خطای کامپایل مواجه میشوید. در کد بالا هم ما بعد از معرفی پکیج، پکیج مورد نظر را import کردهایم و در طول برنامه (همانطور که مشخص است) فقط از اسم کلاس استفاده کردهایم (بدون آوردن نام پکیج در ابتدای آن).
حالا در داخل پکیج com.google.java یک کلاس دیگر با نام ThirdClass ایجاد کنید. تصویر زیر:
فرض کنید بخواهیم از این دو کلاس که در پکیج com.google.java ساخته شدهاند، در برنامهی خود استفاده کنیم. بنابراین یک import دیگر به برنامه اضافه میشود (برای مشخص کردن کلاس ThirdClass). کد زیر:
package ir.zoomit;import com.google.java.SecondClass;import com.google.java.ThirdClass;public class Main { public static void main(String[] args) { SecondClass obj = new SecondClass(); ThirdClass obj1 = new ThirdClass(); }}
همانطور که مشاهده میکنید هر دو کلاس ما برای یک پکیج هستند، اما دو دستور import نوشتهایم. در این گونه مواقع میتوانیم یک دستور import بنویسیم و بجای آوردن نام تک تک کلاسها، از علامت * استفاده کنیم. * به این معنی است که تمام کلاسهایی که در پکیج com.google.java قرار دارد را import کند. به کد زیر توجه کنید:
package ir.zoomit;import com.google.java.*;public class Main { public static void main(String[] args) { SecondClass obj = new SecondClass(); ThirdClass obj1 = new ThirdClass(); }}
همانطور که میبینید ما یک بار پکیج را import کردهایم و از تمام کلاسهای داخل آن در برنامهی خود استفاده کردهایم. در اینجا یک نکتهی دیگری وجود دارد که باید به آن اشاره کنیم. ابتدا در پکیج com.google نه پکیج com.google.java!!! یک کلاس دیگری با نام Test ایجاد کنید. تصویر زیر:
به کد زیر توجه کنید:
package ir.zoomit;import com.google.java.*;public class Main { public static void main(String[] args) { SecondClass obj = new SecondClass(); ThirdClass obj1 = new ThirdClass(); Test t = new Test(); }}
کد بالا با خطای کامپایل مواجه میشود. چرا؟ ما که تمام کلاسهای com.google.java را import کردهایم، پس چرا با خطای کامپایل مواجه میشویم؟ علت این است که کلاسهای پکیج com.google.java را import کردهایم، پس چرا ایراد میگیرد؟ درست است که com.google.java زیر مجموعهی پکیج com.google است، اما برای استفاده از کلاسهایی که در پکیج com.google قرار دارند، باید جدا کلاسهای com.google را import کنیم. کد زیر:
package ir.zoomit;import com.google.Test;import com.google.java.*;public class Main { public static void main(String[] args) { SecondClass obj = new SecondClass(); ThirdClass obj1 = new ThirdClass(); Test t = new Test(); }}
کد بالا صحیح است.
حالا فرض کنید ما دو کلاس هم نام در دو پکیج مختلف داریم. در حال حاضر ما کلاسی تحت عنوان Test در پکیج com.google وجود دارد. حالا یک کلاس دیگر با همین نام در پکیج com.google.java ایجاد کنید. تصویر زیر:
حالا ما میخواهیم از این دو کلاس Test در کلاس Main استفاده کنید. در اینصورت ما فقط باید یکی از کلاسها را import کنیم و کلاس دیگر را به همراه نام کامل آن در کلاس استفاده کنیم. کد زیر:
package ir.zoomit;import com.google.Test;public class Main { public static void main(String[] args) { Test test = new Test(); com.google.java.Test test2 = new com.google.java.Test(); }}
همانطور که مشاهده میکنید کلاسی که در پکیج com.google قرار دارد را import کردهایم، اما کلاسی که در پکیج com.google.java قرار دارد را به همراه نام پکیج آن در برنامه نوشته ایم.
java.lang
کلاسهای استاندارد جاوا در پکیجی با نام java تعریف شدهاند و عملیات اصلی در زبان جاوا نیز در کلاسهایی در پکیج دیگری با نام lang (که مخفف language است) داخل java تعریف شده است. به عبارت دیگر کلاسهای پایهی جاوا در پکیجی با نام java.lang تعریف شدهاند و برای استفاده از کلاسهایی که در این پکیج قرار دارند، نیازی به import کردن آنها ندارید. این بدین معناست که تمام کلاسهایی که در پکیج java.lang قرار دارند به صورت ضمنی در تمام کلاسهای جاوا import میشوند. کلاس String، کلاس Math، کلاس System از جمله کلاسهایی هستند که در پکیج java.lang قرار دارند.
یک نکتهی مهمی که وجود دارد این است که importها فقط در فایل java. قرار دارند و وقتی که برنامه کامپایل و فایل class. تولید میشود، دیگر خبری از importها نیست. یعنی کامپایلر هنگام کامپایل کردن برنامه، به کمک importها، کلاسهایی که در برنامه استفاده شده است را متوجه میشود و اسم کلاسها را با نام کامل آنها جایگزین میکند (یعنی نام پکیج + نام کلاس). همچنین بهتر است که importهای اضافی را از برنامه پاک کنید. هرچند هیچ تاثیری در اجرای برنامه ندارد (البته شاید کمی تاثیر در پروسهی کامپایل داشته باشد) اما در بعضی از منابع آموزشی گفته میشود که وجود importهای اضافی، گاهی باعث بروز باگ در برنامه میشود. پس بهتر است که importهای خود را سازماندهی کنید. اگر از محیط توسعهی اکلیپس استفاده میکنید، با نگه داشتن دکمههای ترکیبی Ctrl + Shift + O میتوانید importهای خودر را سازماندهی (Organize) کنید.
سطوح دسترسی در جاوا
در جاوا میتوانیم برای کلاسها، متغیرها و متدها سطوح دسترسی (Access Level)، با استفاده از کلید واژهی خاصی مشخص کنیم. به این کلید واژههای خاص، تعیین کنندهی سطوح (Access Modifier یا Access Specifier) گفته میشود. این سه کلید واژه عبارت اند از: public, private, protected که در این جلسه فقط public و private را بررسی میکنیم (protected را در مبحث ارث بری بررسی میکنیم). سطوح دسترسی مفهومی بسیار راحتی دارند. به کد زیر توجه کنید:
package ir.zoomit;public class Main { private int number = 0; public static void main(String[] args) { }}
در کد بالا متغیری از نوع عدد صحیح (number) با مقدار 0 تعریف کردهایم. اگر به ابتدای تعریف متغیر توجه کنید، این متغیر به صورت private تعریف شده است. وقتی یک فیلد یا متدی در یک کلاس به صورت private تعریف شود، آن فیلد و متد فقط در همان کلاس قابل استفاده است و در جای دیگر برنامه به آن متغیر و متد دسترسی نداریم. به عنوان مثال اگر ما از روی کلاس Main آبجکتی ایجاد کنیم تا به وسیلهی آن آبجکت بخواهیم به متدها و متغیرهای کلاس Main دسترسی پیدا کنیم، اجازهی دسترسی به متغیر number را نداریم و اگر این کار را انجام دهیم با خطای کامپایل مواجه میشویم. کلید واژهی public دقیقا نقطهی مقابل private است. یعنی اگر ما فیلد یا متدی را به صورت public یا عمومی تعریف کنیم، در هر جایی از برنامه مستقیما به آن متد یا فیلد دسترسی داریم. سطح دسترسی دیگری نیز وجود دارد که کلید واژهی خاصی ندارد. یعنی در اصل اگر ما یک متد یا فیلدی را بدون تعریف کردن حق دسترسی برای آن، در برنامهی خود تعریف کنیم، سطح دسترسی آن متد یا فیلد اصطلاحا Package Access است. یعنی اگر مثلا یک متد در یک کلاس تعریف شود، فقط کلاسهایی که در پکیج مشترک با آن متد هستند میتوانند به آن متد دسترسی داشته باشند، در غیر اینصورت دسترسی ندارند.
سطوح دسترسی کلاسها
همانطور که میدانید برای نوشتن یک برنامهی جاوا، ابتدا باید یک فایل متنی که پسوند آن java.است را انتخاب کنیم. مسلما هر برنامهی جاوا کم کم نیاز به یک کلاس و یک متد (متد main) دارد تا اجرا شود. همانطور که برنامهی ما میتواند شامل چندین متد باشد، همانطور هم میتواند شامل چندید کلاس باشد، اما با این شرط که فقط یک کلاس میتواند حق دسترسی عمومی یا public داشته باشد و نام فایل برنامه هم (که پسوند java.) دارد باید دقیقا همان نام کلاسی باشد که به صورت public تعریف شده است. کلاسهای دیگری که ایجاد میکنیم باید به صورت Package Access تعریف کنیم (مسلما کلاسها نمیتوانند private باشند، چون در این صورت یک فایل اضافی در برنامه هستند. چون هیچ گونه دسترسی به آنها نداریم).
میخواهیم در محیط اکلیپس یک کلاس در پکیج ir.zoomit ایجاد کنیم. بنابراین بر روی پکیج مورد نظر کلیک راست کنید، از منوی New گزینهی Class را انتخاب کنید. به تصویر زیر توجه کنید:
همانطور که در تصویر فوق با یک مستطیل قرمز رنگ مشخص شده است، در قسمت Modifiers، سطح دسترسی package را انتخاب کردهایم. بنابراین کلاس ما به صورت زیر است:
package ir.zoomit;class Test {}
همانطور که مشاهده میکنید سطح دسترسی مشخص نشده است (یعنی Package Access است).
در آموزش بعدی با مفهوم Encapsulation در برنامه نویسی شی گرا آشنا میشوید.
لینکدین
ما در شبکهی اجتماعی لینکدین گروهی ایجاد کردهایم با نام آموزش برنامه نویسی جاوا در زومیت. لطفا در این گروه عضو شوید و جدا از ارسال نظرات در سایت زومیت، در آنجا هم نظرات خود را بیان کنید تا کمبودهای این دورهی آموزشی رفع شود. لطفا در مورد حجم آموزشها نظر دهید.
نظرات