Constructor در جاوا چیست و چه کاربردی دارد

سازنده ها 

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

هر کلاسی که ایجاد می شود دارای یک سازنده پیش فرض یا Default Constructor می باشد.

وقتی با عملگر new شی ای از یک کلاس ایجاد می کنید، این عملگر اعمال زیر را انجام می دهد

  • فیلد های عددی را برابر صفر قرار می دهد
  • فیلد های کاراکتری را برابر با یونیکد '\u0000' قرار می دهد
  • فیلد های بولین را برابر با false قرار می هد
  • فیلد های نوع شی را برابر با تهی (null) قرار می دهد

برای درک بهتر این موضوع کد زیر را در یک فایل Test.java قرار دهید و آن را کامپایل کنید

 

public class Test {
	public static void main(String[] args) {
		Test2 obj = new Test2();
		System.out.println("num : " + obj.num);
		System.out.println("ch : " + obj.ch);
		System.out.println("check : " + obj.check);
		System.out.println("str : " + obj.str);
		System.out.println("Test2 object : " + obj.test2obj);
	}
}

class Test2 {
	int num;
	char ch;
	boolean check;
	String str;
	Test2 test2obj;
}

 

توجه کنید که دو کلاس در فایل Test.java وجود دارد. یک کلاس public هم نام با نام فایل که متد main در آن قرار دارد و کلاس Test2 که دسترسی آن فقط در همین فایل می باشد.

خروجی زمانی که فایل را کامپایل کنید به صورت زیر می باشد

 

num : 0
ch :
check : false
str : null
Test2 object : null

 

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

 

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

مشخصات سازنده ها عبارت است از

  • همنام با نام کلاسی است که در آن تعریف می شود
  • فاقد نوع است. یعنی Data type ندارد حتی void هم نمی باشد
  • هنگام ایجاد شی ای از کلاس، به طور خودکار ایجاد می گردد (توسط عملگر new)
  • هیچ مقداری را بر نمی گرداند (return نمی کند)
  • یک کلاس می تواند بیش از یک سازنده داشته باشد

حالا مثال ساده ایی از یک کلاس با سازنده را در زیر می بینید

 

public class Example_1 {
	
	public Example_1() {
		System.out.println("Print From Example_1 Constructor");
	}
	
	public static void main(String[] args) {
		Example_1 obj = new Example_1();
	}
	
}

 

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

 به صورت پیش فرض شما زمانی که برای یک کلاس کانستراکتوری تعریف نمی کنید. یک کانستراکتور پیش فرض  یا Default Constructor برای آن کلاس به صورت خودکار ایجاد می شود.

چند سازنده در یک کلاس

همانطور که قبلا اشاره کردیم میتوانیم متد های همنام ولی با امضاهای متفاوت داشته باشیم. کد زیر را ببینید و برای درک مطلب آن را کامپایل کنید

 

public class MethodTest {
	public void m1(String s) {
		System.out.println("Print from m1(String s) : " + s);
	}
	
	public void m1(int i) {
		System.out.println("Print from m1(int i) : " + i);
	}
	
	public static void main(String[] args) {
		new MethodTest().m1(10);
		new MethodTest().m1("string");
	}
	
}

 

همانطور که در کد بالا مشاهده می کنید. دو متد m1 از نوع void وجود دارد. یک متد پارامتری از نوع int می گیرد و یک متد پارامتری از نوع String.

بنابراین چون مقدار بازگشتی متدهای m1 با هم برابر است، می توانیم متد های هم نام اما با پارامتر های متفاوت در ورودی متد داشته باشیم. در متد main مشاهده میکنید که به صورت زیر فضای حافظه تخصیص داده شده است

 

new MethodTest().m1(10);
new MethodTest().m1("string");

 

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

 

MethodTest obj = new MethodTest();
obj.m1(10);
obj.m1("string");

 

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

به سورس زیر نگاه کنید

 

public class Example_2 {
	
	int integerNumber;
	float floatNumber;
	
	public Example_2(int i) {
		integerNumber = i;
		System.out.println("print from constructor by integer parameter : " + integerNumber);
	}
	public Example_2(float f) {
		floatNumber = f;
		System.out.println("print from constructor by float parameter : " + floatNumber);
	}
	
	public static void main(String[] args) {
		new Example_2(10);
		new Example_2(10.3f);
	}
	
}

 

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

 

print from constructor by integer parameter : 10
print from constructor by float parameter : 10.3

 

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

 

فراخوانی سازنده توسط سازنده ایی دیگر

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

به مثال زیر توجه کنید (مثال زیر اشتباه است و در زمان کامپایل با خطا مواجه می شود)

 

 

public class Example_3 {
	public Example_3() {
		Example_3(10);
	}
	
	public Example_3(int i) {
		System.out.println(i);
	}
	
	public static void main(String[] args) {
		new Example_3();
	}
}

 

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

 

public Example_3() {
    Example_3(10);
}

public Example_3(int i) {
    System.out.println(i);
}

 

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

this را در جلسه بعد به صورت مفصل بررسی خواهیم کرد. ولی در حال حاضر در همین حد برای استفاده از فراخوانی سازنده توسط سازنده ایی دیگر توضیح خواهیم داد. در واقع زمانی که بخواهید سازنده ایی در کلاس جاری را فراخوانی کنید از مرجع this استفاده می کنید. کد زیر را ببنید.

 

public class Example_4 {
	public Example_4() {
		this(10);
		//this("abcd");
		//this(10,"abcd");
	}
	
	public Example_4(int i) {
		System.out.println("Print from constructor by Integer parameter");
	}
	
	public Example_4(String s) {
		System.out.println("Print from constructor by String parameter");
	}
	
	public Example_4(int i, String s) {
		System.out.println("Print from constructor by two parameters");
	}
	
	public static void main(String[] args) {
		new Example_4();
	}
}

 

خروجی برنامه به صورت زیر است

 

Print from constructor by Integer parameter

 

نکته بسیار مهم در فراخوانی سازنده ها با استفاده از مرجع this

دستور this باید اولین خط دستور در هر جایی که در حال فراخوانی است باشد. در سازنده پیش فرض Example_4 سه دستور this وجود دارد که دو خط از دستورات کامنت می باشد. اگر شما دو دستور this در آن جا داشته باشید با خطای زمان کامپایل مواجه می شوید. متن خطا به صورت زیر است

 

error: call to this must be first statement in constructor

 

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

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

 

public Example_4() {
    System.out.println("Print from default constructor");
    this(10);
}

 

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

 

این یکی از کاربرد های this بود. در جلسه بعد کاربرد های دیگر را نیز توضیح خواهیم داد.

 

بحث تکمیلی

حالا برای تکمیل بحث سازنده ها یک برنامه می نوسیم که دارای دو کلاس با نام های Student و  Example_3 می باشد.

کلاس Student سه فیلد name ، age و field دارد. با استفاده از کانستراکتور با سه پارامتر در ورودی این مقادیر را در زمان ایجاد آبجکت، مقدار دهی اولیه میکنیم.

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

کد های زیر را در فایل Student.java قرار دهید

 

public class Student {
	private int age;
	private String name;
	private String field;
      
    public Student () {
        this("NoName", 0, "NoField");
    }

	public Student (String n, int a, String f) {
		name = n;
		age = a;
		field = f;
	}
	
	public String getName() {
		return name;
	}
		
	public int getAge() {
		return age;
	}
	
	public String getField() {
		return field;
	}
}

 

کد زیر را در فایل Example_3.java قرار دهید و با استفاده از دستور javac این فایل را کامپایل کنید

 

public class Example_3 {
	
	public static void main(String[] args) {
		Student student1 = new Student("Ali", 23, "IT");
		System.out.println("student 1 -> " + student1.getName() + ", " + student1.getAge() + ", " + student1.getField());
        Student student2 = new Student();
		System.out.println("student 2 -> " + student2.getName() + ", " + student2.getAge() + ", " + student2.getField());
	}
	
}

 

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

 

student 1 -> Ali, 23, IT
student 2 -> NoName, 0, NoField

 

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

 

منبع: it3du.ir