آموزش مقدماتی جاوا (بخش اول) — از صفر تا صد

۱۷۳ بازدید
آخرین به‌روزرسانی: ۰۶ شهریور ۱۴۰۲
زمان مطالعه: ۱۰ دقیقه
آموزش مقدماتی جاوا (بخش اول) — از صفر تا صد

در این سری مقالات با عنوان «راهنمای مقدماتی جاوا» به روشی ساده و مؤثر با مفاهیم و برنامه‌نویسی زبان جاوا آشنا خواهیم شد. اگر از قبل آشنایی مختصری با جاوا دارید در هنگام مطالعه این سری مطالب با مشکلی مواجه نخواهید شد و اگر در زمینه برنامه‌نویسی جاوا خبره هستید، مطالعه این راهنما به یادآوری مجدد برخی مفاهیم کمک می‌کند.

JVM ،JRE و JDK

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

گردش کار JVM، JRE و JDK
گردش کار JVM، JRE و JDK
  • JVM ماشین مجازی جاوا است که بایت‌کد جاوا را اجرا می‌کند.
  • JVM می‌تواند روی پلتفرم‌های مختلف سخت‌افزاری اجرا شود، بایت‌کد زبان ماشین است JVM است. بنابراین جاوا یک زبان پرتابل بهتر است. JVM نهادی است که باعث شده جاوا پرتابل شود؛ پیاده‌سازی‌های متفاوتی از JVM برای سیستم عامل (مک، ویندوز، لینوکس) و غیره وجود دارند.
  • JRE محیط زمان اجرای جاوا است که برای اجرای برنامه کافی است.
  • JRE برابر با JVM + فایل‌های کتابخانه + کلاس‌های بسته‌های جاوا (Util ،Lang ،Math و غیره) است.
  • JDK کیت توسعه جاوا است که برای نوشتن، کامپایل کردن و اجرای یک برنامه مورد نیاز است.
  • JDK = JRE + ابزارهای مورد نیاز برای توسعه برنامه جاوا است.

تخصیص حافظه

در ادامه در خصوص روش تخصیص حافظه در پس‌زمینه جاوا به چند نکته اشاره می‌کنیم:

  • هر بار که شیئی در جاوا ایجاد می‌شود، در حافظه هیپ ذخیره می‌شود.
  • متغیرهای ابتدایی و محلی در پشته ذخیره می‌شوند و متغیرهای عضو (member variables) در هیپ ذخیره می‌شوند.
  • در حالت چند نخی، هر نخ پشته خاص خود را دارد؛ اما در بخشی از هیپ نیز سهیم است. در خصوص چند نخی در جاوا در بخش دوم این مقاله بیشتر توضیح خواهیم داد.
  • متدها و متغیرها زمانی که متد فراخوانی می‌شود به پشته ارسال می‌شوند و اشاره‌گر پشته زمانی که فراخوانی پایان می‌یابد حذف می‌شود.
  • سیستم‌های عامل 32 بیتی نمی‌توانند از بیش از 4 گیگابایت رم برای اپلیکیشن‌های جاوا استفاده کنند. سیستم‌های 64 بیتی برای شیء یکسان از حافظه بیشتری و در اکثر موارد به میزان دو برابر استفاده می‌کنند.
  • نوع ابتدایی int 4 بار کمتر از Integer حافظه را اشغال می‌کند.
بازنمایی از تخصیص حافظه
بازنمایی از تخصیص حافظه

در جدول زیر ایده‌ای از انواع داده مختلف و بازه مقادیری که می‌توانند ذخیره بکنند ارائه شده است:

انواع داده و تخصیص حافظه
انواع داده و تخصیص حافظه

برنامه‌نویسی شیءگرا: کپسوله‌سازی، وراثت، چندریختی و تجرید

برنامه‌نویسی شیءگرا (OOP) یک مفهوم برنامه‌نویسی است که بر اساس چهار اصل فوق که در تیتر بالا ذکر شده‌اند کار می‌کند و در ادامه به توضیح یک‌ به یک این اصول می‌پردازیم:

1. کپسوله‌سازی

کپسوله‌سازی به قرار دادن داده‌ها (متغیرها) و کارکردها (متدها) در کنار هم و در یک قالب واحد گفته می‌شود. منظور از کارکردها همان متد و منظور از داده نیز همان متغیرهای برنامه‌نویسی هستند. همه این موارد درون یک کلاس قرار می‌گیرند که به عنوان نقشه اولیه‌ای برای تعیین دستورالعمل‌ها مورد استفاده قرار می‌گیرد.

  • کلاس: کلاس یک نقشه اولیه یا پروتوتایپ است که متغیرها و متدها را تعریف می‌کند. به مثال کلاس خودرو زیر توجه کنید:

کلاس: خودرو

اعضای داده‌ای یا اشیا: رنگ، نوع، مدل و غیره.

متدها: توقف، گاز، کروز.

  • شیء: اینک که با مفهوم کلاس آشنا شدید، باید بگوییم که شیء نمونه‌ای از یک کلاس است. همانند مثال فوق هر خودرویی یک شیء از کلاس خودرو است.
  • متغیر: متغیرها می‌توانند محلی، وهله‌ای و استاتیک باشند. متغیرهای محلی درون بدنه یک متد اعلان می‌شوند. متغیرهای وهله‌ای بیرون متد اعلان می‌شوند و خاص آن شیء هستند. متغیرهای استاتیک تنها یک بار و در آغاز برنامه مقداردهی می‌شوند. متغیرهای استاتیک ابتدا مقداردهی می‌شوند. در مورد آن‌ها در ادامه بیشتر توضیح خواهیم داد.
  • متد: متدها دارای کارکردهای مختلفی هستند. در واقع آن‌ها چیزی به جز مجموعه‌ای کد نیستند که به وسیله نام مورد ارجاع قرار می‌گیرند و می‌توان آن‌ها را از هر نقطه برنامه فراخوانی (اجرا) کرد. می‌توان چندین مقدار به یک متد ارسال کرد و مقادیری را بازگشت داد.
  • بسته: یک بسته مجموعه‌ای از کلاس‌های مرتبط با هم است. این بسته به سازماندهی کلاس‌ها درون یک ساختار پوشه کمک می‌کند و بدین ترتیب به راحتی می‌توان آن‌ها را یافته و مورد استفاده مجدد قرار داد.
1package com.example;
2class Car {
3    String color = "black"; //instance variable  
4    void accelerate() { 
5        int speed = 90; //local variable
6    }
7}

2. تجرید

تجرید به عمل انتخاب داده‌ها از یک مجموعه بزرگ‌تر برای نمایش جزییاتی که صرفاً با یک شیء مرتبط هستند، گفته می‌شود. در ادامه نموداری را می‌بینید که modiffer-های مختلف دسترسی و شیوه محدودسازی داده‌ها از یک کلاس را نشان می‌دهد.

Abstraction

3. وراثت

وراثت سازوکاری است که به وسیله آن یک کلاس مشخصات کلاس دیگری را به دست می‌آورد. برای نمونه یک کودک خلق و خوی خود را از والدینش به ارث می‌برد.

1class Developer{
2  public void writeCode(){
3  // writeCode method
4  
5}
6class BackendDeveloper extends Developer{
7  public void writeCode(){
8  // writeCode method
9  }
10}
11Class run{
12  public static void main (String args[]){
13    Developer developerObject = new Developer()
14	// writeCode method in class Developer will be executed
15    developerObject.writeCode();
16   
17    BackendDeveloper backendDeveloperObj = new BackendDeveloper();
18    // writeCodemethod in class BackendDeveloper will be executed
19    backendDeveloperObj.writeCode();
20  }
21}

4. چندریختی

چندریختی یک مفهوم شیءگرایی است که در آن یک نام می‌تواند اشکال مختلفی داشته باشد. این وضعیت به نام overloading نیز مشهور است. چندریختی دینامیک سازوکاری است که به وسیله آن چندین متد را می‌توان با یک نام واحد و امضای مختلف در سوپرکلاس و زیرکلاس تعریف کرد. این وضعیت overriding نیز نامیده می‌شود. به طور خلاصه:

  • Overloading به حضور چند متد در یک کلاس گفته می‌شود که همگی دارای نام واحد ولی با امضاهای متفاوت هستند.
  • در Overriding دو متد وجود دارند که یکی در کلاس والد و دیگری در کلاس فرزند است و هر دو نام و امضای یکسان دارند.
  • متد زیرکلاس متد سوپرکلاس را منسوخ (Override) می‌کند.
  • در Overriding باید modiffer-های دسترسی کلاس‌های زیرین بزرگ‌تر از کلاس والد باشند، یعنی اگر در کلاس والد از ()public abc و در کلاس فرزند از ()private abc استفاده شود، با یک استثنا (exception) مواجه می‌شویم.

بارگذاری کلاس استاتیک و بارگذاری کلاس دینامیک

  • بارگذاری کلاس به JVM جهت اجرا، بارگذاری کلاس نامیده می‌شود.
  • کلاس‌ها با استفاده از عملگر new به صورت استاتیک بارگذاری می‌شوند.
  • نخستین کلاس با استفاده از متد استاتیک ()main بارگذاری می‌شود و سپس کلاس‌های متعاقب بارگذاری می‌شوند.
  • پروژه‌های مبتنی بر سرور کلاً متد ()mian را ندارند، چون سرور زیرساخت را تأمین می‌کند. بدین ترتیب کلاسی که باید ابتدا بارگذاری شود، در فایل پیکربندی مورد اشاره قرار می‌گیرد. بنابراین فریمورک، متد ()main را پیاده‌سازی کرده و API را در موارد مختلف ارائه می‌کند مثلاً کانتینر متد را در سرورلت (serverlet) ها اجرا می‌کند.
  • متد Main زمانی که برنامه جاوا از اعلان فرمان و روی JVM اجرا می‌شود ضروری است.
  • در صورتی که ارجاع کلاس در طی بارگذاری کلاس استاتیک پیدا نشود، استثنای NoClassDefinationFoundException ایجاد می‌شود.
  • استثنای ClassNotFoundException در مورد بارگذاری کلاس دینامیک صادر می‌شود.

کلاس و اینترفیس مجرد

  • اینترفیس هیچ کد پیاده‌سازی ندارد و همه متدها مجرد هستند یعنی همه متدها تنها اعلان می‌شوند و هیچ کدام تعریف نمی‌شوند.
  • کلاس مجرد متدهای قابل‌ اجرا و متدهای مجرد دارد.
  • یک کلاس می‌تواند هر تعداد از اینترفیس‌ها را پیاده‌سازی کند؛ اما تنها یک کلاس مجرد را می‌تواند بسط دهد.
  • در کلاس مجرد، متدها می‌توانند مجرد یا غیر مجرد باشند.
  • یک کلاس مجرد نمی‌تواند وهله‌سازی شود و صرفاً می‌تواند یک زیرکلاس باشد.
  • همه متدهای مجرد باید در زیرکلاس تعریف شوند؛ در غیر این صورت زیرکلاس باید مجرد باشد.
  • اینترفیس نمی‌تواند وهله‌سازی شود، چون نمی‌تواند از سوی کلاس‌های دیگر پیاده‌سازی شده یا از سوی اینترفیس‌های دیگر بسط یابد.
  • متغیرهای اینترفیس از نوع final و static هستند؛ متدهای اینترفیس public و به طور پیش‌فرض مجرد هستند.
  • اینترفیس نمی‌تواند شامل پیاده‌سازی باشد و همچنین نمی‌توان زیرکلاسی از آن ساخت از این رو متغیرها باید ثابت باشند.

بسته‌های جاوا

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

Java Packages

سازنده (Constructor)

  • تنها مقصود از داشتن سازنده این است که یک وهله از کلاس ایجاد شود. سازنده‌ها هنگام ایجاد یک شیء از کلاس فراخوانی می‌شوند.
  • اگر سازنده‌ای در کلاس با آرگومان تعریف شده باشد، دیگر نمی‌توان از سازنده پیش‌فرض بدون آرگومان استفاده کرد و باید آرگومان‌ها را نیز ذکر کرد.
  • جاوا از سازنده‌های کپی (Copy Constructor) پشتیبانی نمی‌کند.
  • سازنده همان نام کلاس را دارد.
  • زمانی که سازنده‌ای را بتوان از جای دیگر با استفاده از ساختار this فراخوانی کرد، بدین معنی است که با یک شیء مواجه هستیم.
  • جاوا سازنده پیش‌فرض را ارائه می‌کند.
    • سازنده‌های خصوصی:
    • امکان وهله‌سازی صریح از کلاس را منع می‌کنند.
    • شیء نمی‌تواند ساخته شود مگر به صورت درونی.
    • برای ساخت سینگلتون استفاده می‌شوند.

آیا می‌توان سازنده‌ها را در جاوا همگام‌سازی کرد؟

جاوا امکان دسترس چندنخی به سازنده‌های شیء را نمی‌دهد و از این رو به همگام‌سازی هم نیاز نداریم.

آیا سازنده‌ها به ارث می‌رسند؟ یعنی آیا یک زیرکلاس می‌تواند سازنده کلاس والد خود را فراخوانی کند؟

سازنده را نمی‌توان به ارث برد. در واقع با override کردن سازنده سوپرکلاس، قابلیت‌های کپسوله‌سازی زبان را نقض می‌کنیم. ما با استفاده از کلیدواژه super می‌توانیم سازنده کلاس والد را فراخوانی کنیم.

استاتیک

  • استاتیک به این منظور استفاده می‌شود که تنها یک کپی داشته باشیم یعنی می‌خواهیم یک متغیر یا متد وجود داشته باشد که همه اشیای کلاس از آن استفاده کنند.
  • استاتیک برای اشتراک اطلاعات در میان همه اشیا استفاده می‌شود
  • استاتیک برای متغیرها، متدها و بلوک استفاده می‌شود.
  • متغیرهای یا متدهای استاتیک به کلاس تعلق دارند و نه شیء
  • متغیرها یا متدهای استاتیک یک بار و پیش از مقداردهی متغیر، مقداردهی می‌شوند.
  • متغیرها یا متدهای استاتیک می‌توانند به صورت مستقیم از نام کلاس به صورت <className>.<variableName> فراخوانی شوند.
  • متد استاتیک می‌تواند تنها به داده‌های استاتیک دسترسی داشته باشد.
  • متد استاتیک نمی‌تواند به this یا super ارجاع دهد.
  • متد استاتیک تنها می‌تواند متدهای استاتیک دیگر را فراخوانی کند.
  • متد main استاتیک است، زیرا باید در دسترسی یک اپلیکیشن باشد تا پیش از انجام مقداردهی اجرا شود.
  • سازنده نمی‌تواند استاتیک باشد، زیرا کامپایلر با آن مانند یک متد رفتار می‌کند، همچنین سازنده برای مقداردهی یک شیء استفاده می‌شود در حالی که استاتیک خلاف آن است.
  • متغیر استاتیک در ابتدا و سپس بلوک استاتیک بارگذاری می‌شود، گرچه توالی این دو مهم نیست. متدهای استاتیک در انتها بارگذاری می‌شوند.
  • سلسله‌مراتب بارگذاری به صورت زیر است:

Static parent → Static child → Instance parent → Constructor parent → Instance child → Constructor child.

  • در زمان override کردن متد استاتیک، کامپایلر هیچ خطایی صادر نمی‌کند و به خوبی اجرا می‌شود؛ اما این کار override کردن نام ندارد؛ بلکه مخفی سازی (hiding) نامیده می‌شود، چون ما بدین ترتیب از مزیت‌های چندریختی در زمان اجرا محروم می‌شویم.

Final ،Finalize و Finally

  • کلیدواژه Final، زمانی استفاده می‌شود که بخواهیم مقداری تغییر نیابد.
  • کلاس final نمی‌تواند بسط یابد.
  • متد final نمی‌تواند override شود.
  • متغیرهای final معادل ثابت هستند.
  • بلوک Finally در همه موارد برای یک بلوک try catch فراخوانی می‌شود و برای رهاسازی منابع سیستم مانند اتصال‌ها، گزاره‌ها و غیره استفاده می‌شود. در ادامه بلوک‌های try ،catch و Finally را به تفصیل بررسی می‌کنیم.
  • متد ()finalize به گردآوری موارد اضافی (garbage collection) کمک می‌کند. این متد پیش از آن که شیئی از سوی garbage collector حذف شود فراخوانی می‌شود.

کلاس شیء

هر کلاس یک شیء به عنوان سوپرکلاس دارد. این شیء دارای متدهای غیر final زیر است:

  • ()equal
  • ()hashCode
  • ()toString
  • ()clone
  • ()finalize

همچنین متدهای final زیر را دارد:

  • ()wait
  • ()notify
  • ()notifyAll
  • ()getClass

Equals و HashCode

  • متدهای ()Equals و ()HashCode برای مقایسه دو شیء override می‌شوند.

  • متد ()equal به مقایسه برابری دو شیء می‌پردازد و متد ()HashCode نیز یک کد هش در اختیار ما قرار می‌دهد.
1public class Tiger {
2
3private String color;
4private String stripePattern;
5private int height;
6
7public String getColor() {
8 return color;
9}
10
11public String getStripePattern() {
12 return stripePattern;
13}
14
15public Tiger(String color, String stripePattern, int height) {
16 this.color = color;
17 this.stripePattern = stripePattern;
18 this.height = height;
19}
20
21@Override
22public boolean equals(Object object) {
23 boolean result = false;
24 if (object == null || object.getClass() != getClass()) {
25   result = false;
26 } else {
27   Tiger tiger = (Tiger) object;
28   if (this.color == tiger.getColor() && this.stripePattern == tiger.getStripePattern()) {
29       result = true;
30       } 
31  }
32return result;
33}
34
35@Override
36public int hashCode() {
37 int hash = 3;
38 hash = 7 * hash + this.color.hashCode();
39 hash = 7 * hash + this.stripePattern.hashCode();
40 return hash;
41}
42}

Clone

  • متد Clone برای کپی یک شیء به کار می‌آید.
  • متد Clone دارای modiffer دسترسی به صورت حفاظت شده (protected) است.
  • برای این که شیئی بتواند متد حفاظت شده را فراخوانی کند، باید اینترفیس Clonable را پیاده‌سازی کرده باشد؛ در غیر این صورت با استثنای CloneNotSupportedException مواجه می‌شود.
  • اینترفیس Clonable اینترفیس نشانگرها است، یعنی هیچ متدی اینترفیس را تعریف نمی‌کند. آن‌ها صرفاً به کلاس اعلام می‌کنند که باید به روشی متفاوت عمل کند.
  • مزیت داشتن Clonable این است که می‌توان تنها آن اشیایی را که امکان clone دارند، کلون کرد.
  • اگر هر فیلدی از یک شیء در شیء دیگر مورد ارجاع قرار گیرد، از کپی سطحی استفاده می‌کنیم و در کپی سطحی تنها آدرس حافظه کپی می‌شود، یعنی یک شیء به اشتراک گذارده می‌شود.
  • در کپی عمیق، شیء ایجاد شده و حافظه جدید به صورت دینامیک تخصیص می‌یابد.
1Public Object Clone(){
2Try{
3Return super.clone();
4}}
5Public Object Clone(){
6Try{
7Object obj = (Object) super.clone();
8Return obj;
9}}

نباید با دیدن گزاره try دچار نگرانی شوید، زیرا آن را در ادامه مورد بررسی قرار خواهیم داد.

Aggregation و Composition

  • Aggregation یک رابطه از نوع «است» محسوب می‌شود، برای نمونه «خانه یک ساختمان است».
  • Composition یک رابطه از نوع «دارد» محسوب می‌شود، برای نمونه «خانه یک حمام دارد». این بخشی از یک رابطه کلی است که یک بخش از آن نمی‌تواند بدون کل وجود داشته باشد.
  • Aggregation یک رابطه ضعیف‌تر است و Composition از آن قوی‌تر است.
  • Aggregation عموماً با بسط دادن یک کلاس و Composition از طریق پیاده‌سازی یک اینترفیس به دست می‌آید.

نوع ابتدایی (Primitive) و پوششی (Wrapper)

یک متغیر از نوع ابتدایی مستقیماً حاوی مقدار نوع است. جاوا هشت نوع ابتدایی به نام‌های byte، short، int، long، char، boolean، float و double دارد.

یک کلاس پوششی (Wrapper) کلاسی است که اشیای آن شامل انواع داده ابتدایی هستند و آن‌ها را پوشش می‌دهند. زمانی که یک شیء برای یک کلاس پوششی ایجاد می‌کنیم، شامل یک فیلد است که در این فیلد می‌توان انواع داده ابتدایی و دیگر انواع مورد پشتیبانی و متدهای عملیاتی را ذخیره کرد. استفاده از پوشش‌های شیء برای انواع ابتدایی نسبت به استفاده مستقیم از انواع ابتدایی کندتر است. بدین ترتیب ما هزینه وهله‌سازی از شیء، فراخوانی متد و چیزهای دیگر را نیز داریم. هر کدام از هشت نوع داده ابتدایی جاوا یک کلاس اختصاصی برای خود دارند که به صورت Byte، Short، Integer، Long، String، Boolean، Float و Double نامگذاری شده‌اند.

Autoboxing و Unboxing

  • کامپایلر جاوا 1.5 امکان تبدیل خودکار انواع داده ابتدایی به انواع پوششی را می‌دهد. این وضعیت به نام Autoboxing و معکوس آن Unboxing شناخته می‌شود.
  • کامپایلر به صورت درونی از متدهای ()valueOf و ()intValue به طور یکسانی استفاده می‌کند.

Casting

به فرایند انتساب یک نوع ابتدایی به نوع دیگر cast کردن گفته می‌شود.

byte → short → int → long → float → double

امکان Upcasting وجود دارد. برای مثال:

1int i = 5; long j = i;

Downcast کردن ممکن نیست و به cast صریح نیاز دارد:

1long j = 5;
2int i = j; (THIS IS WRONG، it will give classCastException)
3int i = (int) j;

cast کردن int به string ممکن نیست.

بدین ترتیب به پایان نخستین بخش از این سری مقالات راهنمای مقدماتی زبان برنامه‌نویسی جاوا می‌رسیم.

برای مطالعه قسمت بعدی این مطلب روی لینک زیر کلیک کنید:

اگر این مطلب برای شما مفید بوده است، آموزش‌های زیر نیز به شما پیشنهاد می‌شوند:

==

بر اساس رای ۱ نفر
آیا این مطلب برای شما مفید بود؟
اگر بازخوردی درباره این مطلب دارید یا پرسشی دارید که بدون پاسخ مانده است، آن را از طریق بخش نظرات مطرح کنید.
منابع:
madhupathy
نظر شما چیست؟

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *