آموزش برنامه نویسی جاوا: برنامه نویسی شی گرا
شی گرایی یا Object Oriented چیست؟
در دنیا زبانهای برنامه نویسی متفاوتی در دو سطح مختلف وجود دارد. سطح پایین و سطح بالا. هرچه سطح زبان برنامه نویسی بالاتر باشد، برنامه نویسی به آن زبان سادهتر است. زیرا به زبان محاورهای انسان نزدیکتر است. به عبارت دیگر هرچه زبان برنامه نویسی سطح پایینتر باشد، برنامه نویس، کدهای بیشتری باید بنویسد و باید با مفاهیم سخت افزاری بیشتر سر و کله بزند. برای درک بهتر این مسئله به تصویر زیر نگاه کنید:
همانطور که در عکس فوق مشاهده میکنید یک حالت چند لایه وجود دارد. یعنی ابتدا قطعات سخت افزاری وجود دارند، لایهی بالایی سخت افزار، زبان ماشین وجود دارد که همان صفر و یک است، لایهی بعدی زبان اسمبلی است که تا حدودی برنامه نوشتن با این زبان آسان است. بعد از اینها ما به سراغ لایهی سوم یعنی زبانهای سطح بالا میرویم. بنابراین نتیجهای که ما از این تقسیم بندی میگیریم این است که هرچه زبان برنامه نویسی ما سطح پایینتر باشد، سختتر اما سریعتر است.
اما این موضوعات چه ربطی به برنامه نویسی شی گرا دارد؟ نکتهای که وجود دارد و در بالا هم به آن اشاره شد این است که هرچه سطح زبان به سمت پایین برود، برنامه نویس باید اطلاعات سخت افزاری بیشتری داشته باشد و اینکه هنگام نوشتن برنامه، تفکری سخت افزاری داشته باشد! یعنی بجای اینکه در مورد راه حلهای مسئله (طراحی نرم افزار) فکر کند، باید ذهن خود را درگیر مفاهیمی مانند سی پی یو، رم و ... کند. حالا فرض کنید اگر روشی وجود داشته باشد که برنامه نویس بجای تفکر سخت افزاری، تفکری از یک دنیای واقعی داشته باشد. یعنی اگر قرار است برنامهای را بنویسد، دقیقا همان چیزهایی که در واقعیت هستند را در قالب یک برنامه پیادهسازی کند.
در دنیای واقعی هر شی (به زبان انگلیسی: Object) سه خصوصیات متمایز دارد:
- وضعیت، ویژگیها یا متعلقات (State)
- رفتار (Behavior)
- هویت (Identity)
اجازه دهید که این سه ویژگی را با یک مثال توضیح دهم:
خودرو (ماشین) را در دنیای واقعی تصور کنید. تمام ماشینهایی که در تمام دنیا ساخته میشوند ویژگیها و متعلقاتی دارند. یعنی همهی آنها دارای فرمان هستند، همهی آنها دارای چهار چرخ هستند، همهی آنها یک موتور دارند تا به وسیلهی آن حرکت کنند و سایر ویژگیهای دیگر. از طرفی تمام خودروها یکسری رفتارهایی را از خود نشان میدهند. یعنی یک ماشین ممکن است روشن باشد، خاموش باشد و یا در حال حرکت باشد. اینها رفتارهایی هستند که یک ماشین میتواند از خودش نشان دهد. همچنین تمام خودروها یک سری ویژگیهایی دارند که فقط و فقط مخصوص به آن خودرو است و آن ویژگیها هویت آن ماشین را مشخص میکند. به عنوان مثال شما دو خودروی بنز را که هر دو یک مدل هستند و هردو در یک سال تولید شدهاند و حتی از لحاظ ظاهری هم هیچ فرقی با یگدیگر ندارند را فرض کنید. این ماشینها هریک هویت مخصوص به خود را دارند. یعنی کارخانهی سازنده، یک شمارهی سریال بدنه (VIN) برای ماشین در نظر میگیرد که همانند اثر انگشت میماند. همچنین بعد از خرید ماشین، شمارهی پلاکی برای آن در نظر گرفته میشود که شمارهی پلاک هر ماشین با ماشین دیگر متفاوت است.
کلاس چیست؟
در برنامه نویسی شی گرا مفهومی است با نام کلاس. اگر توضیحات فوق را با دقت مطالعه کرده باشید، سه خصوصیت شی را برای تمام خودروها در نظر گرفتیم. یعنی اگر بخواهیم لیستی از خودروها را بنویسیم، هم میتوانیم بنز را جز آن لیست بنویسیم و هم پراید را. به عبارت دیگر یک طرح کلی برای طراحی خودرو تعریف شده است و تمام شرکتهای خودرو سازی برای ساخت خودرو از آن طرح استفاده میکنند. یعنی اگر شرکتی بیاید و ماشینی با سه چرخ بسازد، دیگر آن جز خودروها به حساب نمیآید. حالا شرکتهای سازنده از روی آن طرح کلی، انواع و اقسام خودروها را در مدلها و رنگهای مختلف تولید میکنند (در حقیقت شی ایجاد میکنند، شی خودرو). در برنامه نویسی، مفهوم کلاس دقیقا همان طرح ساخت خودروها است. به این طرح کلی که در حقیقت خصوصیات و رفتارهای مشترک بین اشیا را تعریف میکند، کلاس آن اشیا گفته میشود. نکتهای که بسیار مهم است این است که تا زمانی که آبجکتی (شی) از روی کلاسها ساخته نشود، آن کلاس به تنهایی هیچ کاربردی ندارد. به عنوان مثال شما یک نقشهی ساختمانی را در نظر بگیرد. تا زمانی که آن نقشه روی کاغذ است ما نمیتوانیم از آن ساختمان استفاده کنیم. بنابراین ما حتما باید ساختمان واقعی را از روی آن طرح (نقشه) بسازیم تا بتوانیم استفاده کنیم.
از مثالهای زیادی میتوان استفاده کرد تا مبحث شیگرایی را آموزش داد. یعنی هرچیزی که شما در اطرافتان میبینید یک شی است و آن شی یک طرح کلی دارد. به عنوان مثال آخر، خودمان (انسانها) را در نظر بگیرید.همهی انسانها دارای دو چشم هستند، دو گوش هستند، دو دست و دو پا هستند، قلب دارند و ... . همچنین همهی انسانها رفتارهایی را از خودشان نشان میدهند. یعنی یک انسان ممکن است خواب یا بیدار باشد، ممکن است در حال حرف زدن باشد، ممکن است در حال فریاد زدن باشد و تمام رفتارهایی که ما انسانها از خودمان نشان میدهیم. از طرفی با اینکه ما انسانها یکسری ویژگیها و رفتارهای مشترک داریم (البته استثنا هم است)، اما یک هویت داریم که مخصوص خودمان است. به عنوان مثال یک خواهر و یا برادر دوقلو را در نظر بگیرید، این خواهرها و برادرها با اینکه از یک پدر و مادر زاده شدهاند و از یک خانواده هستند و در شرایط یکسانی هم به دنیا آمدهاند و حتی با توجه به اینکه دو قلو هستند از لحاظ ظاهری هم بسیار شبیه به هم هستند، اما هر یک هویت مخصوص به خودشان را دارند. به عنوان مثال هریک اثر انگشت مخصوص به خود را دارد و یا شمارهی شناسنامهی هریک با دیگری تفاوت دارد.
در برنامه نویسی هم شیهایی که ما ایجاد میکنیم یک هویت دارند. میتوان گفت که هویت آنها جایی است که در حافظه کامپیوتر ذخیره میشوند. همچنین رفتارهای آنها، متدهایی است که ما در کلاسها تعریف میکنیم. (در ادامهی این آموزش و آموزشهای بعدی من تمام مفاهیم فوق را به صورت عملی برای شما توضیح میدهم).
چگونه در جاوا یک کلاس بسازیم؟
ساختن کلاس در جاوا بسیار راحت است و ما به هر اندازهای که بخواهیم میتوانیم کلاس ایجاد کنیم. در برنامههایی که ما در این سری آموزشی مینویسیم، هرکدام شامل شاید حداکثر ۳ الی ۴ کلاس باشد. اما در پروژههای صنعتی ممکن است روی سیستمهایی کار کنید که شامل صدها و حتی هزارها کلاس باشند.
نکته: ما در آموزشهای قبلی نحوهی ساختن کلاس در اکلیپس را توضیح دادهایم. اما در این آموزش ما یک بار دیگر توضیح میدهیم تا هم یک یادآوری شود و هم اینکه با نکات جزئیتر آشنا شوید.
به مثال زیر توجه کنید:
ابتدا محیط توسعهی اکلیپس را اجرا و یک پروژهی جاوایی ایجاد کنید، سپس بر روی پروژه کلیک راست کنید و از منوی New گزینهی Class را انتخاب کنید. تصویر زیر:
بعد از طی کردن مراحل فوق، شما با یک پنجره همانند پنجرهی زیر مواجه میشود:
همانطور که در تصویر بالا با یک بیضی آبی رنگ مشخص شده است، نام کلاس خود را Main در نظر بگیرید و بعد در قسمت پایین پنجره که با یک مستطیل قرمز رنگ مشخص شده است، گزینهی public static void main را تیک بزنید و بعد بر روی دکمهی Finish کلیک کنید. (با تیک زدن گزینهی مشخص شده، هنگام ساخته شدن کلاس، به صورت خودکار متد اصلی (main) کلاس که نقطهی شروع هر برنامهی جاوا است نوشته میشود). (بعد از ساخته شدن کلاس، توضیحات (Comment) اضافی را پاک کنید).
تا اینجای کار ما یک کلاس داریم و آن هم کلاس اصلی برنامه است. حالا میخواهیم یک کلاس دیگر ایجاد کنیم. پس بنابراین همانطور که توضیح داده شد اقدام به ساخت یک کلاس دیگر کنید. فقط نکتهای که باید رعایت کنید این است که این بار تیک گزینهی public static void main را نزنید و نام کلاس را، GradeBook یعنی دفتر ثبت نمرات در نظر بگیرید.
در تصویر فوق به بالای کلاسها که با یک مستطیل قرمز رنگ مشخص شده است دقت کنید. کلاسهایی که ما میسازیم در قسمت بالایی کلاسها در کنار هم قرار میگیرند.
حالا میخواهیم یک متد را در کلاس GradeBook بنویسیم. بنابراین ابتدا یک توضیحات (Comment) برای متد خود مینویسیم. کد زیر:
public class GradeBook { // display a welcome message to the GradeBook}
حالا میخواهیم متدی با نام displayMessage ایجاد کنیم. کد زیر:
public class GradeBook { // display a welcome message to the GradeBook public void displayMessage() { System.out.println("Welcome to the GradeBook!"); } // end method displayMessage} // end class GradeBook
توضیحات در مورد متد فوق: سطح دسترسی متدی که ما در بالا تعریف کردهایم، عمومی یا public است. (با سطوح دسترسی در جاوا در ادامهی همین آموزشهای شی گرایی آشنا میشوید). بعد از آن باید نوع برگشتی متد را مشخص کنیم. در اینجا نوع برگشتی متد ما void است یعنی متد ما هیج مقداری را بر نمیگرداند. بعد از آن هم نام متد را نوشتهایم و بعد هم آکولادهای باز و بسته قرار دادهایم تا بدنهی متد مشخص شود. همانطور که مشاهده میکنید در بدنهی متد کد خاصی نوشته نشده است. فقط با اجرای متد displayMessage پیغامی بر روی خروجی استاندارد چاپ میشود. همچنین دوباره با استفاده از کامنتها، آخر متد و آخر کلاس را مشخص کردهایم.
حالا ما اگر برنامه را اجرا کنیم هیچ خروجیای در کنسول مشاهده نمیکنیم. علت چیست!؟ دلیل آن این است که نقطهی شروع برنامههای جاوا متد main است و در حال حاضر متد main ما خالی از هرگونه کد است. پس بنابراین به سراغ کلاس اصلی که در آن متد main پیادهسازی شده است میرویم.
همانطور که در توضیحات در مورد شی گرایی گفته شد، تا وقتی که از روی یک کلاس، شیای ساخته نشود، آن کلاس هیچ ارزشی ندارد و به هیچ دردی نمیخورد. همانطور که مشاهده کردید، برنامه را اجرا کردیم، اما هیچ خروجیای دریافت نکردیم. پس بنابراین برای اینکه بتوانیم از کلاس GradeBook و متدی که در آن پیادهسازی شده است استفاده کنیم، باید از روی کلاس GradeBook یک شی یا یک آبجکت ایجاد کنیم تا با استفاده از آن آبجکت به متد موجود در کلاس GradeBook دسترسی پیدا کنیم.
همانطور که میدانید ما در جاوا هنگامی که میخواهیم یک متغیر تعریف کنیم، به صورت زیر این کار را انجام میدهیم:
int number;
یعنی ابتدا نوع متغیری که میخواهیم ایجاد کنیم را مینویسیم و بعد یک نام برای آن در نظر میگیریم. برای ساختن آبجکت هم دقیقا باید به همین شکل رفتار کنیم. در حقیقت ما باز هم میخواهیم یک متغیر تعریف کنیم. اما این بار متغیر ما از جنس دادههای اولیه (Primitive Data Type) ها نیست، از جنس کلاس است (کلاس GradeBook). بنابراین ابتدا نام کلاس را مینویسیم و بعد هم یک نام برای آن در نظر میگیریم (این عملیات باید داخل متد main نوشته شود). به صورت زیر:
public class Main { public static void main(String[] args) { // Object Creation GradeBook book; }}
در کد بالا همانطور که مشاهده میکنید ما یک آبجکت تعریف کردهایم، اما هنوز ایجاد نکردهایم. در جاوا برای ایجاد و ساختن یک شی باید از کلمهی کلیدی new استفاده کنیم. به کد تکمیل شده در زیر توجه کنید:
public class Main { public static void main(String[] args) { // Object Creation GradeBook book = new GradeBook(); }}
در کد بالا بعد از نام آبجکت (book) علامت مساوی (Assignment) قرار دادهایم. سپس اُپراتور new را نوشته (که با این کار یک شی ایجاد میشود) و بعد نام کلاس را دوباره مینویسیم و یک پرانتز باز و بسته در جلوی آن قرار میدهیم و در آخر هم علامت سمیکالن را می نویسیم.
تا اینجای کار ما یک آبجکت از روی کلاس GradeBook ساختهایم. حالا میخواهیم با استفاده از آبجکتی که ایجاد کردهایم، به متد موجود در کلاس GradeBook دسترسی پیدا کنیم. برای این کار ابتدا نام آبجکت را مینویسیم (book)، سپس یک نقطه (.) قرار میدهیم و بعد نام متد مورد نظر را که در اینجا displayMessage است را مینویسیم. کد زیر:
public class Main { public static void main(String[] args) { // Object Creation GradeBook book = new GradeBook(); book.displayMessage(); }}
حالا اگر برنامه را اجرا کنید در خروجی استاندارد متن !Welcome to the GradeBook را مشاهده میکنید.
بخشهای حافظه
تا اینجای کار ما یک کلاس بسیار ساده ایجاد کردیم و از روی آن کلاس آبجکتی ساختیم. در مورد ساخته شدن آبجکت در حافظهی کامپیوتر نکاتی وجود دارد که باید بسیار به آنها دقت کنید.
نکته: حافظه در برنامههای جاوا به دو بخش تقسیم میشود. حافظهی Stack و حافظهی Heap. هنگامی که با استفاده از اُپراتور new یک آبجکتی ایجاد میکنیم، این آبجکت ما در بخشی از حافظه به نام Heap ذخیره میشود. در حقیقت book یک آبجکت (شی) نیست!، بلکه یک ارجاعی است به شی ساخته شده در حافظهی Heap. (درست است که همه می گویند با استفاده از آبجکت مثلا book به فیلدها و متدهای یک کلاس میتوانیم دسترسی داشته باشیم، اما book آبجکت نیست. این طرز بیان فقط برای راحت تلفظ کردن است. اگر بخواهیم درست بیان کنیم باید بگوییم با استفاده از متغیر book که در حافظهی Stack ایجاد شده است، میتوانیم به به شیای که در Heap ساخته شده است اشاره کنیم.). به تصویر زیر توجه کنید:
در تصویر فوق در حافظهی استک، ما یک متغیری داریم با نام parent که اشاره میکند به یک شی ساخته شده در حافظهی هیپ. در کدی که ما نوشتیم متغیر book در حافظهی استک (Stack) قرار میگیرد و ما با استفاده از این متغیر میتوانیم به شیای که در حافظهی Heap ایجاد شده است، دسترسی پیدا کنیم.
نکته: متغیرهای محلی هر متد روی استک قرار میگیرند، همانند book که یک متغیر محلی است، از جنس کلاس GradeBook که به شیای در هیپ اشاره میکند.
Garbage Collector یا زباله روب
همانطور که ما آبجکت ایجاد میکنیم و آن آبجکتها یا اشیا بر روی حافظهی Heap قرار میگیرند، همانطور هم باید آن آبجکتها را از روی حافظهی Heap پاک کنیم. آزاد سازی حافظه توسط برنامه نویس فرآیندی پرخطا و پیچیده است. زیرا ممکن است برنامه نویس بخشی از حافظه که تخصیص داده شده است را delete نکند و با نشت حافظه (Memory Leak) مواجه شود و یا اینکه ممکن است به اشتباه شیای را پاک کند. این مسئله در برنامههای بزرگ و پیچیدهتر، سختتر میشود، زیرا تعداد آبجکتها افزایش پیدا میکند، بنابراین اشیا با یکدیگر ارجاعات متعددی دارند.
نشتی حافظه به علت عدم مدیریت صحیح در تخصیص حافظهها و آزاد سازی آنها رخ میدهد.
Garbage Collector یا زوباله روب بخشی از JVM است که وظیفهی پاک کردن حافظهی Heap را بر عهده دارد. وجود Garbage Collector در جاوا یک موهبت الهی برای برنامه نویسان جاوا است.
در زبانهایی مانند ++C پاک کردن حافظهی Heap برعهدهی خود برنامه نویس است که کاری تقریبا دشوار و پیچیده است. اما برنامه نویسان جاوا اصلا درگیر این قضیه نمیشوند.
نکتهای که باید به آن توجه کنیم این است که متغیرهای محلی هر متد بر روی حافظهی Stack قرار میگیرند و بعد از پایان اجرای متد، تمام آن متغیرهایی که توسط آن متد بر روی حافظهی استک ذخیره شدهاند پاک میشوند. نکته این است که این کار اصلا نیازی به زباله روبی (GC) ندارد و تمام زبانهای برنامه نویسیای که متد و یا تابع دارند (مثل سی پلاس پلاس) این کار انجام میشود. به کد زیر دقت کنید:
public class Main { public static void show() { int number = 20; System.out.println(number); } public static void main(String[] args) { System.out.println(number); }}
در کلاس بالا ما دو متد داریم. یکی متد main است و دیگری متد show. ما در متد show متغیری از جنس عدد صحیح با مقدار ۲۰ تعریف کردهایم و سپس آن را در خروجی استاندارد نمایش دادهایم. حالا در داخل متد main ما باز هم میخواهیم مقدار متغیر number را نمایش دهیم، اما با خطای کامپایل مواجه میشویم. زیرا وقتی متد show شروع میشود، برای متغیر number در Stack حافظهای اختصاص داده میشود و وقتی هم که متد به پایان میرسد، به صورت خودکار حافظهی اختصاص داده شده از روی استک پاک میشود. بنابراین ما نمیتوانیم از متغیر number در متد main استفاده کنیم. این کار در زبانهایی مانند ++C که Garbage Collector ندارد، دقیقا به همین صورت اتفاق میافتد. بنابراین نتیجهای که میگیریم این است که GC فقط حافظهی Heap را پاک میکند.
تنظیم کردن اندازهی حافظههای Heap و Stack
میخواهیم برنامهای بنویسیم که در آن اندازهی حافظههای Heap و Stack را به طور دستی مشخص کنیم. یعنی ما میخواهیم اندازهی این دو حافظه را دستکاری کنیم و برنامهای بنویسیم که با اجرای آنها برنامه با کمبود حافظه مواجه شود و کرش کند و دوباره به صورت دستی اندارهی حافظهها را افزایش دهیم تا برنامه بدون مشکل اجرا شود.
به جدول زیر توجه کنید:
نکته: با استفاده از آرگومان Xms- میتوان اندازهی اولیهی حافظهی Heap را مشخص کرد. همچنین توسط Xmx- میتوان حداکثر اندازهی حافظهی هیپ را مشخص کرد و با Xss- هم حداکثر اندازهی Stack.
مثال
ابتدا محیط توسعهی اکلیپس را اجرا کنید و از منوی Run گزینهی Run Configurations را انتخاب کنید. تصویر زیر:
بعد از انتخاب گزینهی مورد نظر وارد پنجرهی زیر میشوید:
همانطور که در تصویر با یک فلش سبز رنگ مشخص شده است، تب Arguments را انتخاب کنید. سپس در قسمت VM Arguments یا همان آرگومانهای ماشین مجازی، عبارت مشخص شده را بنویسید: Xmx-500m. در اینجا ما حداکثر اندازهی حافظهی Heap را برابر با ۵۰۰ مگابایت در نظر گرفتهایم. بر روی دکمهی Run کلیک کنید (البته چیزی اجرا نمیشود، زیرا هنوز برنامهای ننوشتهایم).
حالا کلاس اصلی برنامه را باز کرده و کد زیر را در آن بنویسید:
public class Main { public static void main(String[] args) { int[] numArray = new int[100000000]; }}
در اینجا ما یک آرایهای با طول ۱۰۰ میلیون ایجاد کردهایم.
نکته: در اینجا میتوانیم از یکی از قابلیتهای جاوا ۸ استفاده کنیم. همانطور که در ریاضیات ما اعداد را سه تا سه تا جدا میکنیم، در جاوا ۸ هم با استفاده از Underscore (_) میتوانیم اعداد را سه تا سه تا جدا کنیم. به صورت زیر:
public class Main { public static void main(String[] args) { int[] numArray = new int[100_000_000]; }}
حالا برنامه را اجرا کنید. با اجرای این برنامه با ارور: OutOfMemoryError: Java heap space مواجه میشوید. اما راه حل چیست؟ ما قبل از اجرای برنامه، حداکثر اندازهی حافظهی Heap را مشخص کردیم. اما این حافظه برای اجرای چنین برنامهای کافی نیست. بنابراین دوباره به پنجرهی Run Configurations مراجعه کرده و حداکثر اندازهی حافظه را برابر با 1024m و یا یک گیگ قرار دهید و سپس برنامه را اجرا کنید. با افزایش حافظه دیگر برنامهی ما کرش نمیکند.
حالا میخواهیم برنامهای بنویسیم که حافظهی Stack را پر کند و سر ریز شود. اصطلاحا StackOverflow گفته میشود و با خطای StackOverflowError مواجه میشویم. یکی از سادهترین برنامههایی که میتوان نوشت تا استک سر ریز شود، استفاده از متدهای بازگشتی (Recursive) است. به کد زیر توجه کنید:
public class Main { public static void main(String[] args) { System.out.println(function(1)); } static int function(int i) { if (i < 100000) { return function(i + 1); } else { return 0; } }}
ما هنوز به مبحث بازگشتی نرسیدهایم، اما کدی که در بالا نوشتهایم به این صورت است که داخل کلاس Main یک متدی با نام function تعریف کردهایم که یک پارامتر به عنوان ورودی دریافت میکند. در داخل متد function ما یک عبارت شرطیای قرار دادهایم که تا زمانی که مقدار متغیر i کوچکتر از ۱۰۰۰۰۰ است، بلاک if اجرا شود. که در داخل بلاک if دوباره خود متد function فراخوانی میشود که یک مقدار به به پارامترش اضافه میکند. در اینجا برنامه همانند یک حلقه اجرا میشود، تا زمانی که متغیر i کوچکتر از ۱۰۰۰۰۰ شود. بعد از آن بلاک else اجرا میشود. حالا اگر برنامه را با همین وضعیت فعلی اجرا کنید، برنامه کرش میکند. زیرا حافظهی Stack پر میشود. اما میتوانیم با استفاده از آرگومان Xss- اندازهی حافظهی Stack را افزایش دهیم. برای این کار دقیقا مانند قبل به پنجرهی Run Configurations بروید و این بار این را بنویسید: Xss5m- . حالا اگر برنامه را اجرا کنیم، دیگر خطایی دریافت نمیکنیم و مقدار صفر (0) در خروجی استاندارد چاپ میشود. علت درست اجرا شدن برنامه این است که برنامه حافظهی کافی را در اختیار دارد، بنابراین بلاک دستور if کامل اجرا میشود و در آخر هم وقتی شرط حلقه نقض میشود، بلاک else اجرا میشود و مقدار صفر را بر میگرداند.
برای مشاهدهی کامل مطالب آموزش جاوا میتوانید بر روی صفحهی آموزش جاوا کلیک کنید.
لینکدین
ما در شبکهی اجتماعی لینکدین گروهی ایجاد کردهایم با نام آموزش برنامه نویسی جاوا در زومیت. لطفا در این گروه عضو شوید و جدا از ارسال نظرات در سایت زومیت، در آنجا هم نظرات خود را بیان کنید تا کمبودهای این دورهی آموزشی رفع شود.
نظرات