آموزش برنامه نویسی جاوا (بخش بیست و یکم: مفاهیم اولیه ارث بری)

استاندارد

مفهوم مهم دیگر در برنامه نویسی Object Oriented (آبجکت محور) مفهوم Inheritance می باشد.

وراثت را یکی از سنگ بناهای برنامه نویسی شی گراست ، زیرا امکان ایجاد طبقه بندیهای سلسله مراتبی را بوجود می آورد.

با استفاده از وراثت ، می توانید یک کلاس عمومی بسازید که ویژگیهای مشترک یک مجموعه اقلام بهم مرتبط را تعریف نماید.

این کلاس بعدا ممکن است توسط سایر کلاسها به ارث برده شده و هر کلاس ارث برنده چیزهایی را که منحصر بفرد خودش باشد به آن اضافه نماید.

در جاوا ، کلاسی که به ارث برده می شود را سوپر کلاس (SuperClass) و یا پدر می نامند.

کلاسی که عمل ارث بری را انجام داده و ارث برده است را زیر کلاس (SubClass) و یا فرزند می نامند.

زیر کلاس ، کلیه متغیرهای نمونه و روشهای توصیف شده توسط کلاس بالا را به ارث برده و منحصر بفرد خود را نیز اضافه می کند.

چگونه روابط ارث بری را در برنامه نویسی تشخیص دهیم؟

در واقع بین کلاس ها ممکن است روابطی وجود داشته باشد.

1- مانند is a part of که به معنی قسمتی است از … می باشد.

در این مورد، یک کلاس قسمتی از کلاس دیگر است. مثلا چرخ قسمتی از ماشین است. پس کلاس چرخ قسمتی از کلاس ماشین می شود.

2- روابطی مانند has knowlege of که یعنی اطلاع داشتن، مثلا یک کلاس ممکن است به صورتی حتی غیر مستقیم از کلاس دیگر اطلاع داشته باشد ولی عضوی از آن نباشد.

3- رابطه ی وراثت که موضوع مورد بحث ما است. یعنی یک کلاس فرزند، نوعی از کلاس پدر باشد.

مثلا یک ماشین نوعی از وسیله ی نقلیه است.

یک روباه یک نوع حیوان است.

یک پسر یک نوع انسان است.

یک مستطیل یک نوع از اشکال هندسی است.

Inheritance نيز به مانند بسیاری از اصولا Object Oriented از دنياي واقعي الگو برداري شده است و کاملا قابل درك مي باشد.

مبانی وراثت

فرض کنید بخواهیم یک کلاس کلی به نام Shape را تعریف کنیم. دو کلاس Triangle و Circle از کلاس Shape به صورت شکل زیر ارث می برند.

در کلاس Shape ، دو متغیر Color و Area وجود دارد.

این دو متغیر عینا در کلاس های Triangle و Circle نیز به ارث برده می شوند. یعنی انگار آن ها هم این متغیرها در خود تعریف کرده باشند. ارث بری کار را ساده می کند، زیرا از آنجایی که دو متغیر Color و Area که رنگ و مساحت هستند در همه اشکال وجود دارند.

پس نیازی به تعریف آن ها در همه ی کلاس ها نیست.

به جای این کار یک کلاس کلی Shape تعریف می کنیم و سپس کلاس های Triangle و Circle را طوری تعریف می کنیم که از کلاس Shape به ارث برده شوند.

حال این دو کلاس نیز هر کدام متغیرها و متدهایی را می توانند برای خود تعریف کنند.

در کدهای برنامه نویسی جاوا چگونه عمل می کنیم؟

برای ارث بردن از یک کلاس ، خیلی ساده کافیست تعریف یک کلاس را با استفاده از واژه کلیدی extends در کلاس دیگری قرار دهید.

به کد مثال زیر دقت نمایید:

public class JavaApplication1 {

static class Shape{
    String Color;
    int Area;

}
static class Circle extends Shape{
    int Radius;   
}

    public static void main(String[] args) {

        Circle MyCircle = new Circle();
        MyCircle.Color="Red";
        MyCircle.Area=113;
        MyCircle.Radius=6;
    } 
}

همان طور که مشاهده می کنید، شی MyCircle یک شی از کلاس Circle است.

کلاس Circle خود، دارای متغیر Radius است. این کلاس از کلاس Shape به عنوان پدر خود، ارث بری می کند.

حال شی MyCircle می تواند علاوه بر متغیرهای کلاس خود (Circle) و متغیرهای کلاس پدر خود (Shape) دسترسی داشته باشد و آن ها را مقداردهی و از آن ها استفاده نماید.

حتی اگر در کامپایلر NetBeans بعد از متغیر MyCircle نقطه بزنید، می توانید متغیرها و متدهایی که می تواند به آن ها دسترسی داشته باشد را مشاهده کنید:

متغیرهای Area و Color در کلاس Shape تعریف شده بودند.

آموزش برنامه نویسی جاوا (بخش بیستم: سازنده و مخرب در جاوا)

استاندارد

سازنده یا Constructor

در هنگام ساخت یک شی از یک نمونه کلاس، می توان متغیر های یک شی را مقداردهی اولیه کرد.

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

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

معمولا بعضی از اعضای کلاس قبل از استفاده نیاز به مقداردهی دارندکه همان طور که گفته شد، اين عمل توسط سازنده (constractor) انجام می گیرد که به شیء اين امکان را می دهد که هنگام ايجاد مقداردهی شود.

سازنده تابعی است هم اسم کلاس که وقتی یک نمونه از کلاس گرفته می شود اتوماتیک فراخوانی می شود.

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

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

سازندگان کمی بنظر عجیب می آیند زیرا فاقد نوع برگشتی و حتی فاقد void هستند.

در زبان های شی گرا مثل سی شارپ و جاوا چون همه چیز بر پایه شی گرایی و کلاس هست شما وقتی یک کلاس می سازید میتونید با استفاده از سازنده ها متغیر ها رو مقدار دهی کنید.

وقتی شما یکی شی از کلاس مورد نظرتون می سازین(new می کنید!) کامپایلر به سراغ سازنده ی شما می رود که اگه نباشه سازنده ی پیش فرض فراخونی میشه که بدون پارامتره و شما میتونید از سازنده استفاده کرده و اونطوری که دلتون می خواد متغیر هاتون رو مقدار دهی کنید و از همون اول برای شی ساخته شدتون بفرستید .

تابع سازنده می تواند دارای پارامتر باشد بنابراين زمان ايجاد شیء می توان به متغيرهای عضو مقادير اوليه داد. برای ارسال آرگومان به تابع سازنده بايد هنگام تعريف شیء مقدار آرگومان بعد از نام شیء درون پرانتز قرار گيرد.

یک کلاس می تواند دارای چند سازنده با پارامترهای مختلف باشد. بهتر است همیشه حداقل یک سازنده حتی اگر خالی باشد ساخته شود.

برای تابع سازنده مقدار برگشتی ذکر نمی شود (حتی Void).

برای درک بیشتر به مثال زیر توجه نمایید:

public class JavaApplication1 {

static class myClass{
    private int a;
    private char b;
// تابع سازنده
    myClass(){
    this.a=1; // مقدار دهی به متغیر در تابع سازنده
    this.b='a'; // مقدار دهی به متغیر در تابع سازنده
    }
}    
    public static void main(String[] args) {
        myClass newObject = new myClass(); // ایجاد شی
        System.out.print(newObject.a);
        System.out.print(newObject.b);
    } 
}

خروجی کد بالا، 1a می باشد. زیرا شی به هنگام ساخت توسط سازنده ی خودش مقدار دهی می شود.

همچنین سازنده می تواند مقادیری را نیز به صورت پارامتر ورودی در هنگام ساخت شی ، از شی بگیرد.

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

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

public class JavaApplication1 {

static class myClass{
    int a;
    char b;
// ساخت تابع سازنده
    myClass(int m, char n){
        a = m;
        b = n;
    }
}    
    public static void main(String[] args) {
        myClass newObject = new myClass(5,'m'); // مقدار دهی اولیه فیلدهای شی با استفاده از سازنده
        System.out.print(newObject.a); // خروجی مقدار 5 را نشان می دهد
        System.out.print(newObject.b); را نشان می دهد m خروجی مقدار

    } 
}

constructor مشخص می کند که هنگام ایجاد شیئی از نوع کلاس ، چه اتفاقی رخ می دهد . constructor های خاص خودشان را در تعریف کلاس مشخص می کنند. اما ، اگر constructor به طور صریح مشخص نشود ، در آن صورت جاوا آن را به طور خودکار تامین می کند. در خصوص کلاس ذکر شده در بخش قبل نیز، همین طور است.

مخرب یا Destructor

تابع مخرب کلاس (destructor) کم و بیش عکس سازنده عمل می کند. یک مخرب وقتی فراخوانی می شود که یک شی از بین می رود.

در ++C یک مخرب مشابه سازنده ساخته می شود فقط قبل از اسم آن علامت مد (~)قرار می گیرد.

تابع مخرب اتوماتیک وقتی متغیر شیء از حوزه دسترسی خارج می شود (برای متغیرهای سراسری وقتی از تابع اصلی خارج می شود و برای متغیر محلی هنگام خروج از بلاک تابع) فراخوانی می شود.

مشابه سازنده ها تابع مخرب نيز نوع برگشتی ندارد.

توابع مخرب یا destructor کاربرد زیادی در مدیریت حافظه برنامه های نوشته شده در ++C دارند.

عموما در مورد کلاسهای طراحی شده، با استفاده از این متد به آزاد سازی حافظه پرداخته میشود.

اما در زبانی مانند جاوا که مدیریت حافظه در اختیار ماشین مجازی میباشد، استفاده از این تابع چندان منطقی نیست، با این حال امکان پیاده سازی آن با استفاده از تابع finalize فراهم شده است.

برای انجام این کار در کلاس مربوطه یک تابع به نام finalize پیاده سازی گشته و سیستم Garbage Collector در زمان اتمام استفاده از کلاس و آزاد سازی تمام ارجاعات آن، تابه finalize را فراخوانی مینماید.

از آنجا که زمان عملکرد Grabage Collector نا مشخص است، با استفاده از دستورات gc و runFinalization میتوان ماشین مجازی را ملزم به آزاد سازی حافظه (جمع آوری زباله ها از حافظه) نمود.

مثال زیر نحوه انجام این کار را نشان میدهد:

import java.util.ArrayList;
 
public class Samples  {
    public static void main(String[] args){
        long mem = Runtime.getRuntime().freeMemory();
 
        SampleClass c1 = new SampleClass();
        SampleClass c2 = new SampleClass();
        SampleClass c3 = new SampleClass();
 
        System.out.println(String.format("Used memory: %d KB", (mem - Runtime.getRuntime().freeMemory())/1024));
        mem = Runtime.getRuntime().freeMemory();
 
        c1 = c2 = c3 = null;
 
        System.gc();
        System.runFinalization();
 
        System.out.println(String.format("Released memory: %d KB", (Runtime.getRuntime().freeMemory() - mem)/1024));
    }
}
 
class SampleClass {
    private ArrayList<Double> _obj;
 
    public SampleClass() {
        _obj = new ArrayList<Double>();
        for (int i=0; i<1000000; i++)
            _obj.add(Math.random());
 
        System.out.println("Created");
    }
 
    public void finalize() {
        _obj.clear();
        _obj = null;
        System.out.println("Finalized");
    }
 
}

آموزش برنامه نویسی جاوا (بخش نوزدهم: بررسی تخصصی کلاس ها در جاوا)

استاندارد

در بخش هفتم در مورد کلاس ها صحبت شد و به معرفی ابتدایی کلاس ها پرداختیم. ساختار کلاس را بررسی نموده و نحوه ساختن یک شی از کلاس را نیز یاد گرفتیم.

همچنین نحوه مقدار دهی به اجزای یک شی ، مورد دیگری بود که به آن پرداختیم.

حال پس از معرفی modifier ها و متدها بار دیگر به مبحث مهم کلاس ها پرداختیم.

زیرا مفاهیم بسیاری در مورد کلاس ها وجود دارد که هر برنامه نویسی در جاوا باید آن ها را بداند.

شی گرایی و کلاس

برنامه نویسی شیءگرائی (object oreinted programming) وسیله ای برای مدل کردن صحیح دنیای واقعی با استفاده از اشیا (objects) در برنامه و استفاده مجدد از کد است.

یک شی در برنامه دقیقا همان طور تعريف می شود که در دنيای واقعی است؛ خواص معینی دارد که آن را توصیف می کند و متدهایی که می توانید برای انجام کار معینی روی شیء استفاده کنید.

هدف کلی Java اضافه کردن شیءگرائی به زبان برنامه نویسی است.

یک شیء برای نگهداری داده استفاده می شود. داده و توابعی که روی داده کار می کنند به هم مربوط هستند بنابراين داده و توابع هردو با هم دریک بسته قرار می گیرند.

شیءگرائی بيشتر روی داده تاکيد دارد تا عمليات و توابعی که روی داده کار می کنند.

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

شرحی از داده ها و توابعی که می توانند روی داده کار کنند را کلاس (class) می نامند.

کلاس را به عنوان الگوئی برای توليد شیء می توان تصور کرد. کلاس در واقع يک نوع داده user-defined است.

اشياء نمونه هائی از کلاس ها هستند که در زمان اجرا ايجاد می شوند.

چهار مفهوم اصلی وجود دارند که اساس برنامه نویسی شیءگرائی را می سازند و توسط کلاس ها ارائه می شوند. این مفاهیم انتزاع (abstraction)، کپسوله کردن (encapsulation)، توارث (inheritance) و چندریختی (polymorphism) هستند.

انتزاع:

شیء گرائی ابزاری را برای برنامه نویس فراهم می کند که اجزای فضای مسئله را توسط اشيا نمایش دهد. مسئله به بخش های تشکيل دهنده تجزيه می شود.

هر مولفه يک شیء می شود که شامل داده های مرتبط و دستورالعمل های خود است.

به اين ترتيب مسئله به همان صورتی که در دنيای واقعی هست توصیف می شود نه به روشی کامپیوتری که مسئله را حل می کند. یعنی می توانید با همه چيز در یک ترتیب کلی سروکار داشته باشید. پيچيدگی عمليات کاهش يافته و جزييات پياده سازی مخفی می ماند.

مثال. درباره خصوصیات کلی وسيله نقليه بدون سروکارداشتن با یک وسيله و مدل خاص می توان بحث کرد. يک نمونه شیء MyVehicle از کلاس Vehicle که خواص کلی و توابع وسايل نقليه را در بر دارد می توان ايجاد کرد. تابع Print مشخصات کلی وسيله نقليه را نمايش می دهد.

Vehicle MyVehicle;
MyVehicle.Print();

کپسوله کردن:

قرار دادن داده و توابعی که روی داده کار می کنند را در یک بسته کپسوله کردن می گویند.

در برنامه نویسی رویه گرا (مشابه آنچه تا کنون انجام می داديد) معلوم نیست چه تابعی روی چه متغیری کار می کند. در برنامه های پیچیده تر این روابط تیره تر می شوند. در برنامه نویسی شیءگرائی داده و توابع مربوط به آن، که اغلب متد (method) ناميده می شوند، در يک بسته به نام کلاس قرار می گیرند بنابراین کاملا مشخص است چه تابعی روی چه داده ای کار می کند.

کپسوله کردن امکان پنهان کردن اطلاعات را نيز فراهم می کند.

هر عضو کلاس را می توان به صورت عمومی، خصوصی یا محافظت شده مشخص کرد. شی ايجاد شده از کلاس به داده از طريق يک سری توابع عمومی دسترسی دارد و اجازه دسترسی مستقيم به اعضای خصوصی کلاس و خراب کردن آنها داده نمی شود. به اين صورت جزئيات پياده سازی هم مخفی می ماند و به طراح اجازه می دهد پياده سازی را اساسی بدون تغيير در واسطه ها عوض کند.

وراثت:

یکی ديگر از جنبه های مفید برنامه نویسی شیءگرائی قابلیت استفاده مجدد از کد است.

یک کلاس می تواند اعضای عمومی را از کلاس دیگر را به ارث ببرد.

توارث اجازه می دهد کلاس جديدی شامل کليه داده ها و توابع کلاس (های) پياده سازی شود.

کلاس موجود را کلاس پايه (base) و کلاس جديد که اعضای کلاس پايه را به ارث می گيرد را کلاس مشتق شده (derived) می نامند.

مثال. فرض کنيد يک کلاس پايه به نام Shape داريم که در واقع منظورش اشکال کلی در ریاضی است.

حال این اشکال ممکن است، دایره، مربع و یا مستطیل را شامل شود که ممکن است هر يک از آنها احتياج به اطلاعات يا توابع اضافی داشته باشند.

بنابراين می توان برای هر کدام کلاس های فرعی را تعريف کرد که خواص کلاس Shape را به ارث می برند علاوه براين که دارای فيلدهای جديد ديگری هم هستند.

شکل زیر رابطه بین این کلاس ها را نشان می دهد. و نشان می دهد که همه ی آن ها از کلاس Shape به ارث می برند.

 

در رابطه با ارث بری در بخش های آینده بیشتر صحبت خواهد شد.

چند ریختی:

يک تابع در سلسله مراتب کلاس ها و زير کلاس ها می تواند به طرق مختلف پياده سازی شود و شکل های متعددی بگیرد.

چندریختی یک رابط مشترک را برای پیاده سازی های مختلف از يک تابع در اختيار می گذارد که برای اشیا تحت شرایط مختلف متفاوت عمل کند.

نگاهی دقیق تر به ایجاد شی با استفاده از new

اگر بخواهیم نگاهی دقیق تر به تعریف اشیا با عملگر new داشته باشیم باید بگوییم که فرآیند تعریف یک شی در جاوا به دو قسمت تقسیم می‌شود:

  1. تعریف متغیری برای نگهداری آدرس شی در حافظه
  2. ایجاد شی در حافظه و نسبت‌دادن آن به متغیر تعریف‌شده

مثلا فرض کنید Class زیر را داشته باشیم:

class myClass{
    int a;
    char b;
}

میخواهیم شی از کلاس myClass تعریف کنیم و به دو مورد بالا آن را تعمیم دهیم. به شکل زیر نگاه کنید:

عملگر new ، حافظه هر شی را به صورت پویا تخصیص می دهد.

شکل کلی آن در ذیل نشان داده شده است :

ClassVar ObjName= new ClassName();

ClassVar ، متغیری از نوع کلاسی است که ایجادمی شود.

ClassName نام کلاسی است که نمونه ای از آن ایجاد می شود.

همان طور که می دانید برای ایجاد متغیر ما نیازی به عملگر new داریم. چرا که انواع داده های پایه جاوا به صورت شی پیاده سازی نمی شوند. بلکه به صورت متغیرهای «معمولی» پیاده سازی می شوند.

این کار به خاطر بازدهی بیشتر انجام می گیرد.

همان گونه که خواهید دید ، شیء ها ،ویژگیها و خصوصیات زیادی دارند که لازم است طرز برخورد با آنها نسبت به انواع داده های پایه متفاوت باشد.

با عدم تحمیل سرباز خاص شیء ها به انواع داده های پایه ، جاوا قادر به پیاده سازی کارآمدتر انواع داده های پایه می شود.

در آینده ، با آن دسته از نگارشهای «شیئی» انواع داده های پایه آشنا خواهید شد که برای استفاده در شرایطی مهیا شده اند که همان نوع شیء ها مورد نیاز می باشند.

مهم است به خاطر داشته باشید که new حافظه شیء ها را در طی اجرا ایجاد کنند. اما ، از آنجایی که حافظه محدوداست، این احتمال وجود دارد که new به دلیل عدم وجود حافظه کافی نتواند حافظه لازم برای یک شیء را تخصیص دهد.

اگر چنین اتفاقی رخ دهد، استثنا زمان اجرا پیش خواهد آمد.
تمایز بین کلاس ها و شیء ها را یک مرتبه دیگر مرور می کنیم. هر کلاس، نوع جدیدی از داده ها را ایجاد می کند که می توان برای ایجاد شیء ها به کار برد.

یعنی ، هر کلاس نوعی چارچوب منطقی ایجاد می کند که رابطه بین اعضایش را تعریف می کند.

وقتی شیئی از یک کلاس معین را تعریف می کنید، در واقع نمونه ای از آن کلاس ایجاد می کنید.

از این رو ، هر کلاس ، نوعی ساختار منطقی است.

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

متغیرها یا Field های کلاس

در قسمت متدها دیدیم که می توانستیم، متغیرهایی را در یک متد تعریف نماییم. ولی به یاد داشته باشید که متغیرهایی که در متدها تعریف می شوند، قابل استفاده در همان متد هستند. و در متدهای دیگر قرار نمی گیرند، گفته می شود که دارای دامنه ی داخلی می باشند.

مگر آن که در بیرون از تابع، به صورت public تعریف شده باشند که آن هم خاصیت public بودن است که آن متغیر را به این صورت در میاورد و نه متد.

ولی متغیرهایی که در کلاس تعریف می شوند، قابل دسترسی از یک شی هستند. در بخش هفتم نحوه دسترسی و مقداردهی و استفاده از این متغیرها را مشاهده نمودید.

حال که با متدها و modifier ها آشنا شده اید به بررسی بیشتر متغیرهای کلاس می پردازیم.

می توانید متغیرهایی را در خارج از متدها تنظیم کنید که همه ی متدهای موجود در گروه شما می توانند ببینند.

این متغیرها، متغیرهای Field (یا متغیرهای Instance) نامیده می شوند. می توانید آنها را دقیقا به روش دیگر متغیرها تنظیم کنید.

متغیرهایی که در یک کلاس تعریف می کنید بهتر است Public نبوده و Private باشند.

جهانی ساختن متغیرهای فیلد،به شکل کد زیر، فکر خوبی نیست.

شما تمایل به از دست دادن پیگیری مقادیری را دارید که در این متغیرها می باشند، بنابراین اشکال زدایی کد شما بسیار سخت تر می شود.

محدود کردن دامنه ی متغیرهای فیلد، برنامه نویسی خوبی تلقی می شود.

به عنوان مثال Class زیر را مشاهده نمایید:

static class myClass{
    private int a;
    private char b;
}

هر دو متغیر a و b به صورت private تعریف شده اند.

برای دسترسی به این متغیرها بهتر است به جای آن که آن ها را public اعلام نموده، یک متد به صورت public تعریف کرده و آن متد این متغیرها را برگرداند:

static class myClass{
    private int a;
    private char b;
    public int geta(){
    return a;
    }
    public char getb(){
    return b;
    }
}

به عنوان مثال، کلاس بالا، دو متد geta و getb متغیرهای a و b را برای شما بر می گردانند.

در بخش های آینده همچنان به مبحث Class ها می پردازیم.

بخش بعدی ، سازنده و مخرب را در Class ها بررسی می کنیم.

آموزش برنامه نویسی جاوا (بخش هجدهم: کار با متدها در جاوا – قسمت دوم)

استاندارد

در بخش قبلی، کار با متدها توضیح داده شد. تقریبا بیشتر برنامه های سطح متوسطی که در ادامه خواهیم نوشت، با همان معلومات کافی خواهند بود. ولی بعضی اوقات حتی در یک برنامه ی ساده نیز ممکن است کمی کار پیچیده شود.

در این بخش به ادامه ی مباحثی در مورد کار با متدها می پردازیم. تا کار با متدها را تقریبا تمام کنیم.

این که می گوییم “تقریبا” ، بدان معنی ست که مباحثی تکمیلی در مورد متدها لازم است در آینده مطرح شود که احتیاج است مطالبی دیگر را ابتدا بلد باشید.

آرگومان های متغیر(Variable Arguments)

در بخش قبلی توضیح دادیم که اگر بخواهیم تعداد ورودی های مختلفی به تابع بدهیم باید از مفهوم Overload برای توابع استفاده نماییم.

از JDK 1.5 به بعد می توانیم توسط مفهوم Variable Arguments ، هر تعداد پارامتر ورودی از یک نوع که می خواهیم به تابع بدهیم.

این نوع ورودی ها آرگومان های متغیر نام دارد که به شکل زیر تعریف می شود.

typeName... parameterName

برای تعریف این گونه پارامترها در تابع، باید نوع متغیر را ابتدا مشخص کنید سپس از سه نقطه (…) بدون فاصله از نوع متغیر استفاده نمایید.

در مرحله ی بعد، نامی دلخواه برای پارامتر دریافتی در مقصد نیز مشخص کنید.

مثال:

public class JavaApplication1 {

    public static void main(String[] args) {

        int a = 1;
        int b = 2;
        int c = 3;
        int d = 4;
        VAMethod(a,b,c,d);

    }
    public static void VAMethod(int... numbers){
       System.out.print(numbers.length); // numbers نمایش طول متغیر آرایه ای 
       System.out.print(numbers[2]); // numbers نمایش خانه ای از آرایه ی
    }   
}

همان طور که در بالا مشاهده می نمایید، numbers یک متغیر آرایه است. و با آن به همان صورتی که با آرایه برخورد می کنیم.

متد finalize

بعضی اوقات در جاوا این امکان وجود دارد که در هنگام از بین رفتن یک شی، عمل خاصی انجام شود.

از بین رفتن شی یعنی این که آن شی از داخل حافظه پاک شود.

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

این عمل در جاوا توسط متد finalize انجام می شود.

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

finalize به شکل کلی زیر است:

protected void finalize( )
{
   //  finalize کدهای 
}

اگر دقت کنید متوجه می شوید که modifier این متد، از نوع protected تعریف شده است.

دلیل این کار این است که کسی نتواند از خارج از شئ به این متد دسترسی داشته باشد و نتواند آن را صدا بزند.

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

استاندارد

در بخش قبل با متدها در زبان برنامه نویسی جاوا ، آشنا شدیم. همچنین در مورد اجزای متدها و کمی در مورد نحوه عملکرد آن ها توضیح داده شد.

در این بخش با فراخوانی متدها در توابع مختلف آشنا خواهیم شد.

فراخوانی متدها

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

برای فراخوانی یک متد، دو راه وجود دارد:

1- زمانی که متد هیچ مقداری را بر نمی گرداند: در این نوع صدا زدن کافی ست، اسم متد را در تابع اصلی یا هر تابع دیگری که می خواهیم، بنویسیم. سپس در داخل پرانتز لیست آرگومان های ورودی آن متد را در صورت وجود بنویسیم. سپس در آخر از علامت “;” استفاده می کنیم.

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

به عنوان مثالی از این نوع فراخوانی، کد زیر را مشاهده نمایید:

public class JavaApplication1 {

    public static void main(String[] args) {
        ShowMessage(); // فرخوانی متد
    }
    // تعریف متد
    static void ShowMessage(){
        System.out.print(("Gsm Developers")); // چاپ خروجی
    } 
}

در کد بالا وقتی تابع main اجرا می شود، به سراغ متد و کدهای بدنه ی ShowMessage می رود. در بدنه ی متد ShowMessage نیز، رشته ی “Gsm Developers” چاپ می شود. پس خروجی کد بالا نیز Gsm Developers است.

2- زمانی که متد، مقداری را بر می گرداند: در این نوع صدا زدن، نمی توان فقط با نوشتن اسم متد، آن را صدا زد. زیرا متد مقداری را بر می گرداند و از آن مقدار باید در جایی استفاده شود. مثلا ذخیره شود و یا به عنوان خروجی چاپ شود و …

به عنوان مثال، کد زیر متدی را صدا می زند که مقدار برگشتی دارد:

public class JavaApplication1 {

    public static void main(String[] args) {
        float m = divide(5,6); // m فراخوانی تابع و ریختن مقدار برگشتی آن در متغیر
        System.out.print(m); // m چاپ متغیر 
    }
    //تابع تقسیم
    static float divide(float num1 , float num2){
        float z = num1 / num2;
        return z; // مقدار برگشتی
    }
}

در کد بالا ، بعد از آن که تابع main اجرا شد، به سراغ متد divide می رود. سپس بعد از اجرای کدهای متد divide ، مقدار خروجی متد divide به متغیر m ریخته می شود.

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

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

overload کردن متدها

در جاوا همان طور که دیدید، با استفاده از اسم یک متد می توان آن را فراخوانی نمود. اسم متد، همان اسمی است که در هنگام ساخت برای آن تعریف کردیم.

ولی آیا نمی توان با همین اسم، متدی با بدنه و ساختاری دیگر تعریف کرد؟

در جاوا پیش می آید که بخواهیم از این روش استفاده کنیم. زیرا گاهی پیش می آید که بخواهیم متدها را با پارامترهای مختلفی فراخوانی کنیم.

مثلا بخواهیم متد مورد نظر ما، یک بار بدون پارامتر فراخوانی شود و یک بار با پارامتر String و یک بار با پارامتر int. ولی ما فقط یک پارامتر می خواهیم به آن متد ارسال نماییم ، نه بیشتر.

تعریف دو متد با نام یکسان و نوع خروجی و متغیر های یکسان باعث خطا می شود.

به این گونه تعریف متد ها با نام یکسان ولی پارامترهای متفاوت overload کردن متدها یا method overloading گفته می شود.

به عنوان مثال، به کد زیر خوب دقت کنید:

public class JavaApplication1 {

    public static void main(String[] args) {
        ShowMessage(); // تابع اول
        ShowMessage("Gsm Developers 2"); // تابع دوم

    }
    // تابع اول
    static void ShowMessage(){
        System.out.print(("Gsm Developers 1\n"));
    }
    // تابع دوم
    static void ShowMessage(String message){
        System.out.print(message);
    }
}

خروجی کد بالا به صورت زیر است:

Gsm Developers 1

Gsm Developers 2

اولین ShowMessage ی که در کد بالا در تابع اصلی main فراخوانی شد، بدون پارامتر است. پس تابع اولی که مشاهده می کنید، فراخوانی می شود.

ولی تابع دومی که در main فراخوانی می کنیم، بدنه ی تابع دومی که متغیر message به عنوان پارامتر ارسالی، تعریف شده است، فراخوانی می شود.

آموزش برنامه نویسی جاوا (بخش شانزدهم: معرفی متد در جاوا)

استاندارد

این بخش به بررسی یکی از مفاهیم اصلی در هر زبان برنامه نویسی ، یعنی متدها می پردازد. در واقع در فصول قبل، ما مقداری از الزامات و مقدمات برنامه نویسی را یاد گرفتیم و تا کنون برنامه خاصی ننوشتیم.

وقتی خطوط برنامه ما زیاد می شود درک، پیگیری، خطایابی و دیگر اعمال بر روی برنامه دشوار خواهد شد. توابع ابزاری هستند که به ما در بهبود برنامه کمک می کنند و برنامه نویسی ساخت یافته را ارائه می دهند، بدین معنا که برنامه اصلی به قسمتهای منطقی و مستقل کوچکتری تقسیم می شود که توابع نام دارند.

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

 

برای بکارگیری توابع به سه جزء نیازمندیم: تعریف تابع، اعلان تابع، فراخوانی تابع

می توانید شکل کلی از تعریف یک متد (تابع) را به همراه اجزای آن، در زیر مشاهده می کنید:

هر بار که ما کدهای جاوا را در بخش های قبلی کامپایل می کردیم، تابع اصلی Main این کار را برای ما انجام می داد.

به بررسی متد main در جاوا می پردازیم. در زیر مشاهده می کنید که ابتدا یک کلمه ی کلیدی به نام public آورده شده است. این کلمه یک Modifier است که در بخش پانزدهم به آن پرداختیم. کلمه کلیدی public و باعث می شود که به این متود بتوان در کل برنامه دسترسی داشت.

public static void main(String[] args) {
// کدهای اصلی برنامه
}

سپس کلمه void آورده شده است که باعث می شود این متود چیزی را به عنوان بازگشتی ارسال نکند.

در ادامه متدهای دیگری معرفی می شود که می توانند هر نوع داده ای و حتی کلاس را به عنوان مقدار بازگشتی داشته باشند.

بعد از کلمه کلیدی Modifier ، نام متد آورده شده است. در اینجا نام متد main است.

سپس در داخل پرانتز، آرگومان ورودی این تابع ( یعنی String[] args ) آورده شده است.

برای تعریف بدنه اصلی یک تابع، کدهای برنامه را در داخل بلاک {} می نویسند.

نوع تابع (مثلا Void) ، نام تابع به همراه پرانتز و بلاک ، حداقل مواردی هستند که باید در کدهایمان برای تعریف یک تابع بنویسیم.

مثلا تابع زیر، شامل این حداقل موارد است:

void myMethod(){
        //بدنه تابع
}

گفته شد که هر متد می تواند مانند متغیر، انواع داده ای مختلف را برگرداند. در بالا Void معرفی شد. Void مقداری را به عنوان خروجی برنمیگرداند. ولی اگر متد ما، Void نباشد، پس حتما باید یک نوع داده ای را به عنوان خروجی برگرداند.

این نوع خروجی می تواند یک داده ی عددی صحیح (int) ، یک داده عددی اعشار (float ) ، یک رشته (String) و یا از نوع Boolean) Bool) و … باشد.

ولی حتما دقت داشته باشید که اگر متد شما Void نباشد، حتما باید از دستور return برای برگرداندن مقدار مورد نظر، استفاده کنید.

مثلا تابع زیر باعث ایجاد خطا می شود:

int myMethod(){
}

پس باید به شکل زیر اصلاح شود:

int myMethod(){
    return 0;
}

مشاهده می کنید که تابع بالا، عدد 0 را برای شما به صورت خروجی برمی گرداند.

حال اگر مقدار خروجی تابع شما از نوع رشته (String) باشد، باید در جایی از بدنه، باید کد return مقداری به صورت String برگرداند:

String myMethod(){
       return "Gsm";
}

می توان یک ساختمان داده (آرایه و…) و یا یک کلاس را نیز به عنوان خروجی تابع برگرداند.

مثلا متد زیر از نوع آرایه ی کاراکتری تعریف شده است که مقدار myChars را به صورت خروجی برمیگرداند:

char[] myMethod(){
       char[] myChars = {'A','B','C'};
       return myChars;
}

ارسال پارامتر به متد

قسمت بعدی که می خواهیم به آن بپردازیم، آرگومان های ورودی تابع است که در پرانتز نوشته می شوند. اگر بخواهید مقادیر و یا متغیرهایی را به تابع ارسال نمایید چه کار می کنید؟

یک راه اینست که متغیر را به صورت Public تعریف کنید و در داخل تابع، عملیات مورد نظر را بر روی آن انجام دهید. ولی هر تغییری بر روی این متغیر در هر جای تابع، مقدار این متغیر را به کل عوض می کند.

ولی ما نمیخواهیم این کار انجام شود.

مثال دیگری که به آن می پردازیم در ریاضی به آن برخورده اید.

اگر در ریاضی به یاد داشته باشید، تابع y=x+2 ، یعنی y بر حسب x تعریف می شود. پس اگر به عنوان مقدار ورودی به x ، عدد 3 را بدهیم، مقدار y برابر 5 می شود. عدد 3 در اینجا یک پارامتر و آرگومان ارسالی به این تابع است.

در واقع با استفاده از این آرگومان ها می توان پارامترهایی را به تابع ، ارسال نمود.

فرض کنید بخواهید تابعی برای عمل تقسیم بنویسید. برای این کار به دو عدد احتیاج است. حال تابع شما، در لیست پارامترهای خود این دو عدد را دریافت می کند سپس یکی از آن اعداد را بر دیگری تقسیم می کند و نتیجه را به صورت int و یا float بر می گرداند.

جایگاه این پارامترها در تعریف تابع کجاست؟

بار دیگر به این شکل نگاه کنید:

 

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

برای تعریف لیست پارامترها، ابتدا نوع متغیر و سپس نامی دلخواه برای متغیر، درنظر می گیریم. اگر از بیش از یک متغیر استفاده نمودیم، باید آن ها را با ” , ” از هم جدا کنیم.

در واقع شبیه تعریف نمودن یک متغیر است با این تفاوت که پس از تعریف از ” ; ” در آخر هیچ کدام از متعیر ها و پارامترهای داخل پرانتز، استفاده نمی کنیم.

به عنوان مثال می توانید کد زیر را که عمل تقسیم انجام می دهد، مشاهده کنید. این تابع دارای دو پارامتر ورودی است.

static float divide(float num1 , float num2){
        float z = num1 / num2;
        return z;
}

در کد بالا، در داخل پرانتز دو متغیر num1 و num2 از نوع float تعریف شده اند. سپس این تابع در داخل بدنه ی خود، بر روی این دو متغیر عمل تقسیم را انجام می دهد. ( پارامتر اول (num1) بر روی پارامتر دوم(num2) تقسیم می شود) .

سپس نتیجه به عنوان بازگشتی به تابع برگردانده می شود.

پارامترهایی که هنگام فراخوانی به متد داده می شوند باید به همان ترتیبی باشند که در تعریف متد آورده شده اند و همچنین نوع آنها نیز باید به همان شکل باشد. وقتی که یک پارامتر به متد ارسال می شود دوحالت عمده ممکن است پیش بیاید.

  • هر تغییری که در بدنه متد بر روی پارامتر انجام می دهیم در خارج از متد نیز آن تغییرات اعمال شوند
  • هر تغییر که در بدنه متد برروی پارامترها انجام می دهیم در خارج از متد آن تغییرات بی تاثیر می باشند.

نوع اول ارجاع با استفاده از آدرس متغیر، و نوع دوم ارسال پارامتر با استفاده از مقدار است.

در بخش بعدی که فراخوانی متدها را نیز آموزش می دهیم، با این دو بیشتر آشنا می شویم.

آموزش برنامه نویسی جاوا (بخش پانزدهم: معرفی Modifier ها در جاوا)

استاندارد

در این بخش به بررسی Modifierها در جاوا می پردازیم. اما Modifier چیست؟

Modifier ها کلمات کلیدی هستند که در ابتدای معرفی توابع (متدها) ، متغیرها و کلاس ها می آیند. یعنی نام آن ها در ابتدای متودها، متغیرها و کلاس ها می آیند و سپس نوع آن ها و… تعریف می شود.

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

  • Modifierهای دسترسی
  • Modifierهای غیردسترسی

همان طور که گفته شد، برای استفاده از Modifier ها باید، نام آن ها را قبل از تعریف متغیر، متود و یا کلاس ها بیاورید. اگر یک پروژه ی جاوا در کامپایلرهای اکلیپس ، NetBeans و .. ایجاد کنید متوجه می شوید که تابع اصلی شما یعنی تابع Main از Modifier از نوع Public استفاده می کند:

public static void main(String[] args) {

// بدنه اصلی
    }

برای درک بهتر و چگونگی کارکرد و استفاده از Modifier ها، در ادامه به بررسی این دو دسته از Modifier ها می پردازیم.

Modifierهای دسترسی

با استفاده از این دسته از Modifier ها می توان سطح دسترسی را در برنامه های جاوا مشخص نمود. این مشخص کردن سطح دسترسی، می تواند برای متغیر ، متود و یا کلاس باشد.

در جاوا 4 نوع سطح دسترسی داریم:

1- قابل استفاده در همه جای برنامه:

این نوع دسترسی امکان استفاده از متغیر، متود و یا کلاس را در همه جای برنامه حتی خارج از پکیج را به جاوا می دهد. برای این که این سطح دسترسی را مشخص کنیم، از کلمه کلیدی Public استفاده می کنیم. پس کد تابع Main که در بالا آورده شد، می تواند مثال خوبی از این نوع سطح دسترسی باشد.

2- قابل دسترسی در کل پکیج:

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

String mySite = "Gsm Developers;";

void mySiteMethod(){
           //code
       }

3- قابل استفاده در کلاس:

این نوع سطح دسترسی باعث می شود که آن متغیر و یا تابع، فقط در آن کلاس قابل دسترسی باشند. و بیرون از آن قابل استفاده نباشند.

برای این سطح دسترسی از private استفاده می شود. این سطح دسترسی محدود کننده ترین سطح دسترسی است. استفاده از دسترسی private راه کپسوله کردن و پنهان کردن اطلاعات کلاس از سایر کلاس ها است. برای مثال می توانید کد زیر را مشاهده کنید:

public class myJava{
       private String mySite = "Gsm Developers";

       public String getmySite(){
           return mySite;
       } 
    }

در کد بالا متغیر mySite یک متغیر از نوع String بوده که فقط در همین کلاس قابل استفاده است. حال اگر بخواهید از این متغیر در جای دیگری استفاده نمایید، می توانید یک تابع از نوع Public را تعریف نموده و متغیر mySite را برگرداند. در جاهای دیگری غیر از این کلاس از این تابع برای استفاده از متغیر mySite می توان بهره برد.

4- قابل استفاده در subClass:

برای این که متغیر یا متدی در کلاس پدر قابل دسترس باشد و همچنین در کلاس فرزند نیز قابل استفاده باشد ولی در جای دیگر این امر ممکن نباشد از این سطح دسترسی استفاده می کنیم. برای مشخص کردن این سطح دسترسی باید از کلمه کلیدی protected قبل از تعریف شناسه استفاده کرد. همچنین در هنگام استفاده از این Modifier شناسه در داخل پکیج نیز قابل دسترسی خواهد بود. کلاس ها و اینترفیس ها را نمی توان به صورت protected تعریف کرد.

در مورد وراثت و استفاده از Modifier از نوع Protected در کلاس ها در بخش وراثت ها بیشتر صحبت خواهیم کرد.

Modifierهای غیر دسترسی

Modifierهایی که در ادامه در مورد آن ها صحبت می کنیم، ربطی به سطح دسترسی ندارند. و هرکدام معنی جداگانه ای دارند.

1- Static : 

در بخش معرفی کلاس ها در مورد ساخت شی از کلاس، صحبت کردیم. برای این که بتوانیم به متدهای کلاس ها، مثلا کلاس String دسترسی داشته باشیم، باید ابتدا شی را از نوع کلاس String تعریف می نمودیم.

با استفاده از Static می توان، به متدها و متغیر های کلاس، بدون تعریف نمودن شی از آن کلاس، دسترسی پیدا کرد. همچنین بین همه اشیا مشترک نیز می باشد. به عبارتی، بدون آن که یک شی از کلاس بسازیم، اگر متد یا متغیری را از نوع Static معرفی کنیم، با استفاده از نام کلاس قابل دسترسی خواهد بود. و لازم به ساخت شی نخواهد بود.

برای مثال Static ، کد زیر را می توانید مشاهده کنید:

package javaapplication1;

public class JavaApplication1 {

    public static class myJava{

        public static int myCount=0;

    }
    /**
     * @param args the command line arguments
     */
public static void main(String[] args) {

       myJava.myCount++;

       System.out.print(myJava.myCount);

    }

}

مشاهده می کنید که در تابع Main با استفاده از نام کلاس ، به متغیر دسترسی داشتیم و توانستیم مقدار آن را عوض کنیم. در هر جای برنامه نیز می توان به این متغیر دسترسی پیدا کرد.

همچنین اگر بعد از اجرای دستوری که در کد بالا، باعث شده مقدار متغیر myCount ، یک (1) شود، در جای دیگری از برنامه به این متغیر دسترسی پیدا کنیم، همین مقدار 1 را مشاهده می کنیم.

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

2- abstract :

این Modifier زمانی استفاده می شود که بخواهیم از کلاسی فقط به منظور ارث بری استفاده نماییم. یعنی کلاس مورد نظر پدر باشد. معمولا پیش می آید که در برنامه نویسی حرفه ای، بخواهیم کلاسی را به عنوان قالب، تعریف نماییم. مثلا متغیرها و متود هایی را برای کلاس تعریف کرده ولی این که در آن متود چه کدهایی اجرا می شود را در کلاسی که از آن به ارث می برد، مشخص می کنیم.

توجه داشته باشید که نمیتوانید از کلاسی که از نوع abstract ساخته شده است، شی به صورت new بسازید.

همچنین اگر بخواهید متدی را در کلاسی به صورت abstract تعریف نمایید، باید خود آن کلاس به صورت abstract تعریف شود.

در مورد ارث بری در بخش های آینده صحبت خواهد شد.

public abstract class myJava{

        public abstract void myMethod();
        public void myMethod2(){
        // myMethod2 کدهای تابع 
        }       
    }

در کد بالا مشاهده می کنید که myMethod از نوع abstract تعریف شده است و اگر بخواهید بدنه آن را در این کلاس تعریف نمایید، کامپایلر خطا می دهد. پس باید در فرزند آن، بدنه ی تابع myMethod تعریف شود.

همچنین myMethod2 نیز که abstract نیست، برعکس myMethod اگر بدنه ی آن را تعریف نکنیم به مشکل می خوریم!

3- final :

در اینجا با یک Modifier مهم و پرکاربرد، مواجه هستیم. زمانی که کلاسی از نوع final تعریف می شود، دیگر نمیتواند فرزندی داشته باشد.

اولین نکته ای که به ذهن شما باید برسد اینست که اگر کلاسی از نوع final تعریف شود، نمی تواند از نوع abstract نیز تعریف شود و برعکس.

کاربرد این Modifier برای تعریف متود به این گونه است که، اگر متودی در کلاس پدر از نوع final تعریف شود، دیگر در کلاس فرزند ( در صورتی که کلاس پدر خود final نباشد) نمی توانید، متود را باز نویسی (Override) نمایید.

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

کلاس:

public final class myJava{
} // نمی تواند فرزند داشته باشد..

متود:

public final void myMethod(){
        //
        } // در کلاس فرزند نمی تواند، باز نویسی شود

متغیر:

این کد خطاست زیرا متغیر m که از نوع final استفاده شده است، نمی تواند دو بار مقدار بگیرد.

final int m = 5;
m=6;

کد زیر صحیح است:

final int m;
m=6;

4- transient :

از کلمه کلیدی transient برای این منظور استفاده می شود که به ماشین مجازی جاوا (JVM) بفهمانیم که هنگامی که محتویات شی را سریالیز می کند، متغیری که به صورت transient تعریف شده است را نادیده بگیرد.

5- synchronized :

این کلمه کلیدی بدین منظور مورد استفاده قرار می گیرد که یک متد در یک زمان فقط توسط یک Thread قابل دسترسی باشد. این Modifier همراه با Modifierهای دسترسی نیز به کار می رود.

5- volatile :

اگر بخواهیم همزمان از چند Thread در برنامه استفاده کنیم، از کلمه کلیدی volatile استفاده می کنیم. منظور از استفاده از این Modifier این است که وقتی از این Modifier برای یک متغیر استفاده می کنیم به ماشین مجازی جاوا می گوییم که همه ی thread ها اگر متغیر مورد نظر را در حافظه کش خود تغییر بدهند باید آن تغییر بر روی متغیر در حافظه هم اعمال شود.

دلیل این کار این است که اگر چند thread به صورت همزمان از یک متغیر استفاده می کنند مقدار متغیر داده قدیمی نداشته باشد و همیشه همگام پیش برویم. این Modifier فقط بر روی فیلدهای کلاس قابل استفاده است. یک متغیر volatile می تواند null باشد.

public class MyThread implements Runnable{
    private volatile boolean active;

    public void run(){
        active = true;
        while (active){ 
            // Code
        }
    }

    public void stop(){
        active = false; 
    }
}

اگر کد run را توسط یک thread اجرا کنیم و اگر متد stop را توسط یک thread دیگر اجرا کنیم ممکن است که در خط while(active) از متغیر active یک کپی در حافظه کش مربوط به thread اجرا کننده موجود باشد.

بنابراین حتی اگر متد stop هم اجرا شود برنامه متوقف نمی شود. اما در این برنامه به دلیل این که متغیر active به صورت volatile معرفی شده است، اگر در متد stop مقدار این متغیر برابر False قرار بگیرد در همان زمان متد run هم متوقف خواهد شد

آموزش برنامه نویسی جاوا (بخش چهاردهم: معرفی و آموزش نحوه استفاده از کلاس های StringBuilder و StringBuffer )

استاندارد

در این مطلب، به بررسی دو کلاس مهم در برنامه نویسی جاوا می پردازیم. این کلاس ها نیز برای کار با رشته ها مورد استفاده قرار می گیرند. اهمیت این دو کلاس در هنگامی مشخص می شود که بخواهید در حد پردازش های خیلی بزرگ، که Cpu و حافظه قوی می خواهد، از این دو کلاس استفاده کنید. زیرا یک برنامه نویس مبتدی می تواند کار را به نحو دیگری پیش ببرد ولی استفاده از این دو کلاس برای یک برنامه نویسی حرفه ای قطعا پیشنهاد می شود.

کلاس StringBuilder در جاوای 5 معرفی شد.

تفاوت بین StringBuffer و StringBuilder در این است که متدهای موجود در کلاس StringBuilder ویژگی tread Safe بودن را ندارند به عبارت دیگر متدهای این کلاس باهم سنکرون نیستند. اما باید گفت که کلاس StringBuilder سرعت بیشتری دارد. پس تا حد امکان از این کلاس استفاده کنید.

اما اگر سنکرون بودن و tread safe بودن برایتان اهمیت دارد از کلاس StringBuffer استفاده کنید. روش استفاده از این کلاس ها برای الحاق رشته ها با هم یا concat کردن رشته ها به شکل کد زیر است:

در اینجا کار را با متدهای StringBuffer به جلو پیش می بریم ولی همان طور که گفته شد می توانید از متد StringBuilder نیز استفاده نمایید.

متدهای کلاس StringBuffer

اولین متدی که به آن می پردازیم متد append است. این متد برای اتصال دو رشته به هم به کار می رود. در واقع رشته ی دوم، به انتهای رشته ی اول ، چسبیده می شود.

در شکل زیر، انواع متغیرهایی که این متد می تواند به عنوان آرگومان ورودی اش بپذیرد را مشاهده می کنید. خروجی این متد نیز یک شی از نوع StringBuffer است.

کد زیر، مثالی برای این متد است:

StringBuffer myString1 = new StringBuffer("Gsm");
myString1.append(" Developers");
System.out.print(myString1);

خروجی کد بالا برابر است با:

Gsm Developers

مشاهده می کنید که رشته ای که در ابتدا به عنوان مقدار برای myString1 تعریف کرده بودیم (“Gsm”) با استفاده از متد append، به رشته ی دومی (“Developers “) متصل شده است.

دومین متدی که به آن می پردازیم، متد Reverse است. این متد زمانی به کار می رود که بخواهیم رشته را برعکس کنیم. یعنی کاراکتر انتهایی به کاراکتر اول بیاید و کاراکتر ماقبل انتهایی به کاراکتر دوم بیاید و به همین ترتیب رشته را بر عکس کند تا کاراکتر اول، به جای کاراکتر انتهایی بشیند.

نکته: دقت کنید که این متد ورودی نمی پذیرد.

کد زیر به عنوان مثالی از متد Reverse ، نحوه کار با این متد را به شما آموزش می دهد:

StringBuffer myString1 = new StringBuffer("Gsm Developers");
myString1.reverse();
System.out.print(myString1);

خروجی کد بالا :

srepoleveD msG

سومین متدی که به آن می پردازیم، متد Insert است. این متد مقداری را داخل یک StringBuffer درج می کند. یعنی داخل یک متن که به صورت رشته است. این متد دو آرگومان ورودی از کاربر دریافت میکند. اولین آرگومان به محلی که آن مقدار باید در StringBuffer درج شود اشاره می کند. و دومین آرگومان همان مقداری است که در StringBuffer درج می شود.

آرگومان های قابل پذیرش را می توانید در شکل زیر مشاهده کنید:

مثال این متد را هم می توانید در زیر مشاهده کنید:

StringBuffer myString1 = new StringBuffer("Gsm Developers");
myString1.insert(3, 123);
myString1.insert(17,".com");
System.out.print(myString1);

خروجی کد بالا:

Gsm123 Developers.com

چهارمین متد به حذف قسمتی از رشته StringBuffer می پردازد. این متد که delete نام دارد یک آرگومان ورودی شروع محل حذف رشته و یک آرگومان ورودی دوم به عنوان پایان محل حذف رشته از کاربر می گیرد و اقدام به حذف قسمت های خواسته شده می کند.

مثالی از این متد را در زیر مشاهده می کنید:

نکته: به قسمت هایی که بر اساس دو مقداری که داده شد، حذف می شوند دقت کنید.

StringBuffer myString1 = new StringBuffer("0123456");
myString1.delete(3,5);
System.out.print(myString1);

خروجی کد بالا ( به عدد 5 که در رشته حذف نشده است دقت کنید):

01256

آخرین متدی که به آن می پردازیم متد Replace است. این متد قسمتی از متن داخل رشته را با متنی که به عنوان آرگومان به آن می دهیم جایگزین می کند.

مشاهده می کنید که اولین آرگومان این متد، محل شروع قسمتی که می خواهیم جایگزین شود، آرگومان دوم، محل پایان آن قسمت و آرگومان سوم یک رشته است که آن رشته جایگزین آن محل می شود.

کد زیر یک مثال از این متد را نشان می دهد:

StringBuffer myString1 = new StringBuffer("0123456");
myString1.replace(3, 5, "Gsm");
System.out.print(myString1);

خروجی این کد:

012Gsm56

متدهای length ، index و substring در این دو کلاس نیز وجود دارند. این متدها در بخش های قبلی توضیح داده شد و کار با آن ها همانند کار را کلاس String است.

آموزش برنامه نویسی جاوا (بخش سیزدهم: آموزش متد Substring در جاوا)

استاندارد

در بخش قبلی متدهایی را از کلاس String بررسی کردیم. در این بخش می خواهیم متدی دیگر به نام Substring را معرفی کنیم و با نحوه کار آن آشنا شیم. این متد به شما اجازه می دهد تا یک متن را از متن دیگر جدا سازید. ( یک رشته String از رشته String دیگر)

به عنوان مثال می توان یک آدرس ایمیل که به صورت رشته است را گرفته و بررسی کرد که آیا [email protected] در آخر آن هست یا خیر.

برای اولین مثال ما کد زیر را تعریف میکنیم. کد زیر یک رشته به نام myString1 با مقدار “Gsm Developers” را تعریف کرده و سپس با استفاده از Substring کاری می کنیم که از کاراکتر اول تا سوم را جدا کرده و داخل متغیر myString2 بریزد.

String myString1 = "Gsm Developers";      
String myString2 = myString1.substring(0, 3);
System.out.print(myString2);

خروجی کد بالا برابر “Gsm” خواهد بود.

مثال بعدی یک مقدار آرگومان ورودی را در متد substring می گیرد:

String myString1 = "Gsm Developers";      
String myString2 = myString1.substring(3);
System.out.print(myString2);

خروجی بالا برابر با مقدار “Developers ” خواهد بود. زیرا سه کاراکتر اول یعنی “Gsm” را از رشته جدا کردیم. همچنین Space نیز در خروجی به عنوان کاراکتر چهارم وجود دارد که اگر میخواستیم آن را نیز حذف کنیم باید مقدار 4 را در آرگومان ورودی متد substring قرار می دادیم:

String myString2 = myString1.substring(4);

در مثال بعدی می خواهیم کاری کنیم که رشته “rs” از “Gsm Developers” جدا شود. پس یعنی کاراکتر 13ام به بعد یعنی “rs” را می خواهیم جدا کنیم. در واقع از کاراکتر 12 شروع کرده و دو کاراکتر بعدی را جدا می کنیم.

کد زیر را در ابتدا اجرا می کنیم:

String myString1 = "Gsm Developers";      
String myString2 = myString1.substring(12,2);
System.out.print(myString2);

اگر کد بالا را اجرا کنید مشاهده می کنید که با خطا مواجه می شوید. زیرا متد substring در قسمت اول از آرگومان ورودی اش، به آخرین کاراکتر از قسمتی که می خواهیم حذف شود، اشاره کرده ( در اینجا کاراکتر 12ام یعنی: e)  و سپس تا آخرین کاراکتری که می خواهیم جدا شود به جلو حرکت می کند.

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

در تصحیح کد بالا، کد زیر را می نویسیم:

String myString1 = "Gsm Developers";      
String myString2 = myString1.substring(12,14);
System.out.print(myString2);

نتیجه برابر مقدار “rs” خواهد شد.

نکته : در کد بالا دیدید که ما کاراکتر 12 را در آرگومان ورودی متد substring وارد نمودیم. ولی موقعیت کاراکتر ‘r’ در قسمت 13 بود. پس همیشه در ورودی به کاراکتر قبل از کاراکتری که می خواهیم جدا شود، اشاره کنیم.

اگر بخواید کلمه Developers را از رشته “Gsm Developers” جدا کنید، آیا کد می توان در ورودی متد substring دو عدد 4 و 14 را قرار داد؟ جواب درست است ولی بهینه نیست. آیا باید 14 کاراکتر شمارش کنید؟ اگر جمله طولانی در رشته نوشته شده بود و ما فقط می خواستیم قسمت اول از رشته را حذف کنیم آیا باید تمام کاراکترها را شمارش می کردیم؟ پس راه حل چیست؟

در قسمت قبل دیدید که با متد ()length می توانستیم طول یک رشته را به دست آوریم. پس به جای شمارش 14 کاراکتر طول رشته را به دست می آوریم و به عنوان پارامتر دوم آرگومان ورودی متد substring قرار می دهیم:

String myString1 = "Gsm Developers";      
String myString2 = myString1.substring(4,myString1.length());
System.out.print(myString2);

خروجی بالا مقدار “Developers” را به ما می دهد.

در مثال بعدی، می خواهیم از جایی که کاراکتر Space شمارش می شود، از بقیه رشته جدا شود. ولی نمی خواهیم کاراکترها را بشماریم پس چه کار کنیم؟

در بخش قبل دیدید که با استفاده از متد ()indexOf می توانستیم، محل یک کاراکتر را در رشته به صورت عدد صحیح int به دست آوریم. پس کافیست که محل کاراکتر خالی یا همان ‘ ‘ را به دست آورده و در ورودی متد substring قرار دهیم:

String myString1 = "GsmDevelopers yek site ast";
int pos = myString1.indexOf(' ');
String myString2 = myString1.substring(pos+1);
System.out.print(myString2);

خروجی کد بالا مقدار “yek site ast” است.

آموزش برنامه نویسی جاوا (بخش دوازدهم: معرفی و آموزش نحوه استفاده از کلاس String و کار با رشته ها در جاوا)

استاندارد

در بخش معرفی متغیرها ، متغیر String برای شما معرفی شد. گفتیم که متغیر String رشته است که میتوانید با آن کلمات و یا جملات را ذخیره کنید. رشته، مجموعه ای از کاراکترهاست. پس هر نوع مجموعه کاراکتری را می توان به صورت رشته درنظر گرفت.

یک رشته می تواند با استفاده از کلمه کلیدی String به صورت زیر تعریف شود:

String myString = "Hello world!";

دقت کنید که مقدار رشته در مثال بالا، یعنی !Hello World در داخل دابل کوتیشین (“) قرار گرفته است. در واقع وقتی کدی شبیه کد بالا برای تعریف رشته می نویسیم، یک شی از کلاس String تعریف می شود که مقدار آن !Hello World است.

در بخش معرفی کلاس ها به شما گفته شد که برای تعریف یک شی از کلمه کلیدی new استفاده کنید. پس برای تعریف رشته نیز می توانید از کلمه new استفاده نمایید:

همان طور که به شما گفته شد برای تعریف آرایه می توانید به صورت زیر عمل کنید:

char[] myArray = { 'S','T','R','I','N','G'};

حال یک شی از کلاس String ساخته و آرایه بالا را به عنوان آرگومان برای آن شی تعریف می کنیم:

String myString = new String(myArray);

در کد بالا myString که یک String است، مقدار کاراکترهای آرایه ی myArray را به صورت رشته دریافت می کند. پس اگر از آن خروجی بگیریم، مقدار STRING را به ما نشان می دهد:

System.out.print(myString);

خروجی : STRING

متدهای کلاس String

کلاس String دارای متدهای فراوانی می باشد که با نوشتن اسم متغیر String که خود یک شی از کلاس String است و سپس زدن نقطه (dot) می توان متدهای آن را در کامپایلر مورد استفاده دید. به عنوان مثال عکس زیر در کامپایلر NetBeans من، برای متغیر myString که در بالا معرفی کرده بودیم، نشان دهنده آن متدهاست:

اگر من بخواهم از متد ()length استفاده کنم، این متد مقدار int را به من بر میگرداند. این متد دارای ورودی نمی باشد. محل توضیحات مربوط به این متد در عکس مشخص شده است.

طول رشته در جاوا

برای به دست آوردن طول رشته که همان تعداد کاراکترهای به کار رفته در آن است، می توانید از متد ()length استفاده نمایید:

int ToolReshte = myString.length();
System.out.print(ToolReshte);

از آنجایی که مقدار متغیر myString برابر “STRING” است، کد بالا همانطور که انتظار می رود، مقدار 6 را بر می گرداند.

آیا رشته ما حاوی این رشته است؟

متد بعدی که می خواهیم به آن بپردازیم، به بررسی این موضوع می پردازد که آیا این رشته در رشته ی ما به کار رفته است یا خیر. اگر به کار رفته باشد، مقدار True و در غیر این صورت مقدار False به کار می رود.

به عنوان مثال، کد زیر مقدار True را به ما نمایش می دهد:

String Jomle = "Emrooz Hava Abri Ast."; 
System.out.print(Jomle.contains("Hava"));

الحاق دو رشته

برای آن که دو رشته را به همدیگر اتصال دهیم، تا یک رشته ی جدید حاوی دو رشته ی قبلی به دست آید، از متد ()concat استفاده می شود:

String myString1 = "Gsm"; 
String myString2 = "Developers";

System.out.print(myString1.concat(myString2));

به دست آوردن محل دقیق اولین کاراکتر در یک رشته

اگر بخواهیم محل دقیق کاراکتری را در یک رشته به دست آوریم از متد ()indexOf استفاده می کنیم. این متد، کاراکتری که می خواهیم را از آرگومان ورودی اش گرفته، و سپس محل آن را در رشته به صورت یک عدد از نوع int بر میگرداند. به عنوان مثال کد زیر مقدار 5 را برمیگرداند. که محل اولین کاراکتر ‘e’ است.

String myString3= "Gsm Developers";
int TheIndex = myString3.indexOf('e');
System.out.print(TheIndex);

به دست آوردن محل دقیق آخرین کاراکتر در یک رشته

در مثال بالا دیدید که محل اولین کاراکتر ‘e’ در رشته یعنی محل 5ام به دست آمد. حال اگر بخواهیم محل آخرین کاراکتر را در رشته به دست آوریم از متد ()lastIndexOf استفاده می کنیم:

String myString5 = "Gsm Developers";
System.out.print(myString5.lastIndexOf('e'));

کد بالا مقدار 11 را بر میگرداند.

متد مقایسه compareTo

وظیفه این متد مقایسه می باشد. این متد دو نوع مقایسه را انجام می دهد. اول یک رشته را با یک شی دیگر مقایسه می کند و دوم دو رشته را با یکدیگر مقایسه می نماید. خروجی این متد یک عدد int می باشد که اگر 0 باشد به این معنی است که دو رشته باهم کاملا مساوی هستند و اگر خروجی متد کمتر از 0 باشد ورودی متد از خود رشته صدا زننده متد از لحاظ کاراکتری کوچکتر می باشد و اگر خروجی بزرگتر از 0 باشد به این معنی است که از لحاظ کد اسکی کاراکتر ها خود رشته بزرگتر از آرگومان ورودی اش می باشد. برای مثال به کد زیر دقت کنید:

String myString1 = "Gsm"; 
String myString2 = "Developers";
String myString3 = "Gsm Developers";
String myString4 = "Gsm Developers";
System.out.print(myString1.compareTo(myString2));
System.out.print(myString3.compareTo(myString4));

در کد بالا، اولین کد چاپ، خروجی 3 را بر میگرداند و دومی خروجی 0 را برمی گرداند که نشان می دهد رشته ی myString3 و myString4 با هم یکی هستند.

تبدیل رشته به آرایه

با استفاده از متد ()toCharArray می توان، یک رشته را به صورت آرایه تبدیل نمود.

String myString5 = "Gsm Developers";
char[] StringToArray = myString5.toCharArray();

با استفاده از متد ()charAt می توان، کاراکتری که در محل مشخصی در رشته است را مشخص کرد. آن محل مشخص را می توان به ورودی متد ()charAt داد. به عنوان مثال (6)charAt ، کاراکتر 6ام را در رشته بر میگرداند.

String myString5 = "Gsm Developers";
char myCh = myString5.charAt(5);
System.out.print(myCh);

کد بالا مقدار خروجی ‘e’ را بر میگرداند.