جاوا و تکنولوژی های آن

java programming language

در این وبلاگ به بررسی نکات موجود در جاوا و تکنولوژی های آن می پردازیم

طبقه بندی موضوعی

هرگاه متدی را final کنیم دیگر توسط پدران کلاس قابل override نخواهد بود

هرگاه کلاسی را final کنیم توسط هیچ کلاسی قابل ارث بری نخواهد بود و در اصطلاح میگویند کلاس عقیم شده است مانند String

هرگاه یک متغییر primitive از نوع final باشد دیگر مقدار آن قابل تغییر نخواهد بود

هرگاه یک Object Reference را final کنیم آن Reference دیگر نمیتواند Object جدید را بپذیرد و همواره به همان Object اولیه ارجاع خواهد داشت


متغییر های final با متغییر های immutable فرق دارند

checked exception به خطاهایی میگویند که در زمان کامپایل کامپایلر چک میکند

Unchecked exception به خطاهایی که در زمان runtime ممکن است پرتاب شوند


اگر Exception تولید شده از نوع RuntimeException باشد ما مجبور نیستم آنرا Catch یا Throws کنیم مثل NullPointerException , DivisionByZeroException

بدون Catch کردن کد کامپایل میشود 




گاهی مواقع منطق کد ما در شرایط مختلف یکسان است و فقط جنس آن فرق میکند و اگر برای شرایط مختلف کد هایی که عملکرد یکسانی دارند و فقط از نظر دیتا تایپ آن فرق دارد پیاده سازی های مشابه ای را داشته باشیم اینجا دچار Code Redundancy شده ایم در این شرایط نگه داری کد و توسعه کد بسیار سخت تر خواهد شد و اصول طراحی قوی ای نخواهد داشت

داده های عمومی یا Generic ها در جاوا برای مشخص کردن نوع خاص از Object که قرار است با آن درون کلاس یا متد کار کنیم استفاده میکنیم ، منظق پیاده سازی یکسان است ولی نوع داده را محدود میکنیم به آنچیزی که میخواهیم در Runtime با آن کار کنیم و این انعطاف پذیری بالایی در شی گرایی به برنامه نویس میدهد 


Type Parameter : به آن نوع دیتا تایپی که در Generic استفاده میکنیم گفته میشود که نمیتوان از نوع primitive data types استفاده کرد و حتما باید از نوع کلاس باشد

List<String> list ;

وقتی ما Type Parameter خاصی را در Generic تعریف میکنیم موقع کار با آن چون محدود به متد و فیلد های ان نوع از کلاس هستیم جلوی اشتباهات آتی کد نویسی را هم میگیرد و در صورت وقوع خطای Syntax Error میدهد


ایجاد محدودیت از طریق درخت وراثت :  ما در Generic میتوانیم Type Parameter را محدود به پدر خاصی کنیم تا یک خانواده از کلیه فرزندان آن پدر را در بر بگیرد و اینطوری قابلیت بیشتری را به کدمان بدهیم

class MyNumber <E extends Number > {...}


Raw Type : وقتی ما روی Generic ها Type Parameter را مشخص نکرده باشیم میتوان هر نوع کلاسی را استفاده کنیم مگر توسط extends محدود شده باشد ولی  دیگر نوع خاص تر آن را مشخص نکرده ایم


Type Interface : از جاوا 7 به بعد اضافه شده و مفهمومش به این طورت است که وقتی در متغییر Reference نوع Type Parameter را مشخص کردیم دیگر نیازی نیست در Constructor هم انرا مشخص کنیم با این عمل Diamond Operator میگویند


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


generic method : یک متد به تنهایی میتواند از خاصیت Generic استفاده کند برای تعریف آن باید قبل از تعیین مقدار برگشتی متد آنرا تعریف کنیم :


public class GenericClass <E> {

    public <T> void genericMethod1 (int i , T t , E e ){...}

    public <A> A genericMethod2 (A a){...}

}

همانطور که در کد بالا میبینیم هم کلاس نوع Generic است و هم متد به تنهایی نوع Generic و در آرگومان متد از هر دو Type Parameter استفاده میکند در متد دوم مقدار برگشتی از نوع Type Parameter است


Erasure یا فرآیند محو : کنترلی که روی Generic ها انجام میشود فقط در زمان Compile Time است وقتی کامپایل کد تمام میشود و برنامه اجرا میگردد اثری از Type Parameter در زمان Runtime وجود ندارد 

اگر یک شی از نوع Type Parameter ایجاد شود فقط توسط کامپایلر این موضوع چک میشود که دیتا تایپ مورد استفاده از همان نوع باشد

در واقع همه Generic Type ها به صورت Raw Type تفسیر میشود و به این رفتار جاوا در بحث Generic ها Erasure گفته میشود 


محدودیت Generic : 

روی Type Parameter نمیتوان عملیاتی انجام داد چون نوعش مشخص نیست

نمیتوان از آن new کرد

نمیتوان نوعش را با instanceof مشخص کرد

کلاس Generic نمیتواند از Exception ها ارث بری کند ولی خود Type Parameter ها میتوانند از Exception ارث بری داشته باشند


Wildcard ها در Generic : فرض کنید یک Generic Class دارید که نوع های مختلفی را می پذیرد و از آن چند نمونه داریم 

class Box<T> {

public T getValue(){...}

}


Box<Student> student = new Box<>();

Box<Teacher> teacher = new Box<>();

و در متدی دیگر میخواهیم با استفاده از getValue مقدار آنرا بخوانیم و بعد toString آنرا چاپ کنیم ولی کلاس Box را با Type Parameter های مختلفی ساخته ایم و برای هر نوع باید یک متد برای print کردن بنویسیم راه حل استفاده از wildcard است :


public void doPrint(Box<?> box){

  System.out.println ( box.getValue() .toString() );

}


 علامت ؟ به unknown (ناشناخته) معروف است و میتواند شامل هر نوع دیگری از Type Parameter ها شود و در حین اجرا نوع آن مشخص میشود

unknown type parameter ها میتوانند توسط ارث بری و extends به نوع خاص تری محدود شوند 


در جاوا میتوانیم در داخل کلاس یک کلاس دیگر تعریف کنیم که به آن inner class میگویند 

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

برای استفاده از کلاس داخلی در کلاس دیگر باید از کلاس خارجی حتما یک instance بسازیم


کلاس داخلی خود میتواند ارث بری هم داشته باشد 

اگر کلاس داخلی private باشد دیگر نمیتوان از آن Object جدیدی ساخت

کلاس های داخلی نمیتوانند عناصر static داشته باشند


کلاس های inner که بصورت static تعریف شود را nested class میگوییم


کلاس هایی بدون Reference و instance هستند و بیشتر در داخل متد استفاده میشوند 

anonymous class ها constractor ندارند 


local class به کلاس هایی گفته میشود که در داخل متد ایجاد شوند 

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

خیلی به ندرت کاربرد دارد 




public class OuterClass {


    protected static class InnerClass{


        public void method1() {

            System.out.println("method 1 called from inner class");

        }

    }


    public void method1() {

    

        System.out.println("method 1 called from outer class");

        

        class LocalClass extends InnerClass{


            @Override

            public void method1() {

                System.out.println("method 1 called from local class");

            }

        }


        LocalClass lc = new LocalClass();

        lc.method1();


        LocalClass lc2 = new LocalClass() {


            @Override

            public void method1() {

                System.out.println("method 1 called from local class with @Override");

            }

        };

        

        lc2.method1();


    }

    


    public InnerClass method2(){

        

        class LocalClass2 extends InnerClass{


            @Override

            public void method1() {

                System.out.println("method 2 called from local class 2");

            }

        }

        

        LocalClass2 lc2 = new LocalClass2();

        return lc2;

    }


    public static void main(String[] args) {

        

        //anonymous class        

        new OuterClass().method1();


        //anonymous class        

        new OuterClass() {

            @Override

            public void method1() {

                System.out.println("method 1 called from outer class with @Override");

            }

        }.method1();

         

        OuterClass.InnerClass out = new InnerClass();

        out.method1();

    }

    

}


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

این بلاک فقط توسط جاوا اجرا میشود و هر بار که Object جدید از کلاس ساخته میشود یا کلاس دارای initialize block ارث برده شود این بلاک اجرا میشود 


static block : در کلاس میتوانیم یک بلاک static داشته باشیم این هر باری که کلاس در حافظه مقیم شود یکبار اجرا میشود و زود تر از initialize block اجرا میشود