Garbage Collection

در زبان های برنامه نویسی ای همچون زبان C؛ شما مجبورید خودتان حافظه را مدیریت کنید، یعنی اگر چیزی را باز می کنید، حتما آن را ببندید، یا اگر شیء ای ایجاد می کنید حتما آن را پاک کنید.

اما در زبان برنامه نویسی جاوا، این امر به صورت خودکار توسط ماشین مجازی (VM) انجام می شود، که به آن Garbage Collection می گویند.

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

نکته مهمی که وجود دارد، این است که شما به عنوان برنامه نویس، هیچ کنترلی روی عملکردِ این بخش از ماشین مجازی ندارید، تنها کاری که می توانید انجام دهید، این است که “درخواست” دهید شیء ای به زباله دان برود.

اینکه اینکار انجام شود یا نه، باز به تصمیم خودِ ماشین مجازی است.

این درخواست با دستور

System.gc()

انجام می شود.

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

 

public class Test{
public Test(){
...
}

public void finalize(){
System.out.println("Oh f..., You Killed Me...");
}

}

 

سطوح دسترسی

ما چهار سطح دسترسی داریم:

1- سطح دسترسی public: برای کلاس ها این به این معنی است که تمامی کلاس های دیگر می توانند از آن شیء ایجاد کنند، برای متد ها به این معنی است که با استفاده از شیء ایجاد شده از آن کلاس، همه کلاس های دیگر می توانند آن را فراخوانی کنند و برای متغیر ها به این معنی است که با استفاده از شیء ایجاد شده از کلاس مربوط، همه کلاس ها می توانند به آن مقدار دهی کرده یا مقدار آن را بخوانند.

 

2- سطح دسترسی private: برای کلاس ها سطح دسترسی private تعریف نشده (مگر برای کلاس های داخلی یا nested class ها)، توابع و متغیر های خصوصی فقط مربوط به کلاسی هستند که در آن تعریف می شوند، یعنی هیچ کلاس دیگری نمیتواند به آنها به صورت مستقیم دسترسی پیدا کند یا آنها را تغییر دهد.

 

3- سطح دسترسی …. (هیچ!): یعنی وقتی هیچ کلید واژه ای برای تعیین سطح دسترسی نمی گذاریم. در این حالت فقط کلاس هایی که در پکیج یکسان با کلاس مربوط قرار دارند، قادر به دسترسی به آن هستند.

 

4- سطح دسترسی protected: برای کلاس های اصلی سطح دسترسی محافظت شده تعریف نشده، اما متد ها و متغیر های protected، علاوه بر قابل دسترسی بودن در پکیج، برای کلاس های فرزند نیز قابل دسترسی می باشند.

 

(Variable argument (VarArgs

با استفاده از این تعریف، می توانیم تعداد دلخواهی از پارامتر ها را برای تابع خودمان در نظر بگیریم، قطعه کد زیر را ببینید:

 

public static void print(String... args) {
	System.out.println("Start");
	for (String s : args) {
		System.out.println(s);
	}
	System.out.println("End");
}

public static void main(String[] args) {
	print("1", "2", "3");
	print("Alireza", "Pir");
	print();
}

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

می شود ورودی تابع print را اینطور به آرایه تبدیل کرد:

String[] array = args;

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

public void print(String[] args){
...
}

 

autoboxing و inboxing

می دانیم جاوا زبانی شیء گراست. اما، یک چیز این وسط برای ما مشکل ایجاد می کند و اجازه نمی دهد به “همه” اجزای برنامه مان به طور شیء گرا نگاه کنیم.

چیزی که از آن به طور مداوم در برنامه هایمان استفاده می کنیم و اصلا حواسمان نیست که “شیء” نیستند!

متغیر های اولیه (primitive data type ها) یعنی متغیر های int؛ boolean, byte, char, float, double, long و short.

اینها هیچکدام شیء نیستند و از زبانِ اجداد جاوا یعنی سی به این زبان آمده اند، اما گاهی ما نیاز داریم تا تماما برنامه مان را شیء گرا کنیم چون برای مثال کنترل خطا را بر اساس تفکر و معماری شیء گرا بنا کرده ایم. اینجا کلاس های wrapper وارد کار می شوند. کلاسهایی که خودشان شیء هستند و داخلشان مقدارِ primitive ها را نگهداری می کنند، به ترتیب، Integer؛ Boolean, Byte, Character, Float, Double, Long  و Short.

و میدانیم که وقتی کلمه شیء می آید، کلید واژه new هم جزء لاینفکِ آن است.

بنابراین برای ایجاد یک شیء از نوع عدد صحیح باید بنویسیم:

Integer i = new Integer(2)

اما اگر بنویسیم

Integer i = 2;

 

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

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

Integer i = new Integer(2)

 ترجمه می کند.

برعکسِ این عمل هم اتفاق می افتد که به آن inboxing می گوییم، یعنی دستور زیر مشکلی ایجاد نمی کند:

int a = new Integer(13);

و به صورت خودکار، سمتِ راست به متغیر صحیح cast می شود.

 

overloading  و overriding

overloading به معنی پیاده سازی چندین تابع با نام یکسان و با پارامتر های مختلف در یک کلاس است:

 

public class OverLoadingTest {

	public void OverLoadedMethod(int a) {
		// Do Somethinh
	}

	public void OverLoadedMethod(int a, int b) {
		// Do SomeThing Else...
	}

	public void OverLoadedMethod(String a) {
		// Do Another Thing...
	}

}

توجه کنید که overloading صرفا با تغییر پارامتر های ورودی تابع امکان پذیر است، اگر نوع خروجی را عوض کنیم، با ارور مواجه می شویم.

 

 

Overriding به معنی بازنویسی یک تابع که در کلاس پدر تعریف شده، در کلاس فرزند یا فرزندان است به صورتی که عملکرد پدر را تکمیل، یا آن را از نو باز نویسی کنند:

 

public class ParentClass {

	public void OverRidedMethod(int a) {
		System.out.println("I am In parent Class");
	}
}

class Child1Class extends ParentClass {

	public void OverRidedMethod(int z) {
		super(z);
		System.out.println("Im in child1 class");
	}

}

class Child2Class extends ParentClass {
public void OverRidedMethod(int a){
System.out.println("Completely new Behaviour");
}

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

 

 

قسمت بعد