یک برنامهٔ جای معمولاً اشیای زیادی ایجاد میکند که همانطور که میدانید، با فراخوانی Methodها با یکدیگر تعامل دارند. از طریق این تعاملات، برنامه میتواند وظایف مختلفی را انجام دهد — مثلاً پیادهسازی یک رابط کاربری گرافیکی (GUI)، اجرای یک انیمیشن، یا ارسال و دریافت اطلاعات از طریق شبکه. وقتی یک شیء کاری را که برایش ساخته شده تمام میکند، منابعش برای استفادهٔ سایر اشیا بازیافت میشود.
مثال زیر سه شیء ایجاد میکند: یک شیء Point و دو شیء Rectangle.
class Point {
int x, y;
Point(int x, int y) {
this.x = x;
this.y = y;
}
}
class Rectangle {
Point origin;
int height, width;
Rectangle(Point origin, int height, int width) {
this.origin = origin;
this.height = height;
this.width = width;
}
Rectangle(int height, int width) {
this.origin = new Point(0, 0);
this.height = height;
this.width = width;
}
}
Point origin = new Point(23, 94);
Rectangle rectangle = new Rectangle(origin, 100, 200);
IO.println("Width of rectangle: " + rectangle.width);
IO.println("Height of rectangle: " + rectangle.height);
اجرای کد بالا خروجی زیر را چاپ میکند:
Width of rectangle: 200
Height of rectangle: 100
سه بخش زیر از مثال بالا برای توضیح چرخهٔ عمر یک شیء در برنامه استفاده میکنند. از آنها یاد خواهید گرفت که چگونه کدی بنویسید که اشیا را ایجاد و استفاده کند. همچنین یاد خواهید گرفت که سیستم چگونه پس از پایان عمر یک شیء، منابع آن را پاکسازی میکند.
---همانطور که میدانید، کلاس نقشهٔ (Blueprint) اشیا را فراهم میکند. شما یک شیء را از یک کلاس میسازید. هر یک از عبارات زیر که از مثال قبلی برگرفته شدهاند، یک شیء ایجاد و آن را به یک متغیر اختصاص میدهند.
Point origin = new Point(23, 94);
Rectangle rectangle = new Rectangle(origin, 100, 200);
خط اول یک شیء از کلاس Point و خط دوم یک شیء از کلاس Rectangle ایجاد میکند.
هر کدام از این عبارات سه بخش دارد (که در ادامه بهتفصیل بررسی میشوند):
Point origin و Rectangle rectangle اعلامیههای متغیری هستند که یک نام متغیر را به یک نوع شیء پیوند میدهند.new یک عملگر جاوا است که شیء را میسازد.new با فراخوانی یک Constructor همراه است که شیء جدید را مقداردهی اولیه میکند.قبلاً آموختید که برای تعریف یک متغیر از ساختار زیر استفاده میکنید:
type name;
این به کامپایلر اطلاع میدهد که از name برای ارجاع به دادهای با نوع type استفاده خواهد شد. برای متغیرهای Primitive، این اعلامیه حافظهٔ لازم را نیز برای متغیر اختصاص میدهد.
همچنین میتوانید یک متغیر Reference را بهتنهایی تعریف کنید. برای مثال:
Point origin;
اگر origin را به این شکل تعریف کنید، مقدارش نامشخص خواهد بود تا زمانی که واقعاً یک شیء ایجاد و به آن اختصاص داده شود. صرفاً تعریف یک متغیر Reference، شیئی ایجاد نمیکند. برای ایجاد شیء باید از عملگر new استفاده کنید — همانطور که در بخش بعدی توضیح داده خواهد شد. قبل از استفاده از origin در کد، حتماً باید یک شیء به آن اختصاص دهید. در غیر این صورت، خطای کامپایل دریافت خواهید کرد.
متغیری که در این وضعیت قرار دارد، در حال حاضر هیچ شیئی را مرجع نمیدهد.
عملگر new با اختصاص حافظه برای یک شیء جدید و برگرداندن مرجعی به آن حافظه، نمونهای (Instance) از یک کلاس ایجاد میکند. عملگر new همچنین Constructor شیء را فراخوانی میکند.
تعریف: عبارت «ایجاد نمونه از یک کلاس» (Instantiating a Class) دقیقاً همان معنای «ایجاد یک شیء» را دارد. وقتی شیئی ایجاد میکنید، در واقع یک «نمونه» (Instance) از یک کلاس میسازید و بنابراین یک کلاس را «نمونهسازی» (Instantiate) میکنید.
عملگر new یک آرگومان پسوندی واحد میخواهد: فراخوانی یک Constructor. نام Constructor، نام کلاسی را که قرار است نمونهاش ساخته شود مشخص میکند.
عملگر new مرجعی به شیء ایجادشده برمیگرداند. این مرجع معمولاً به یک متغیر با نوع مناسب اختصاص داده میشود، مانند:
Point origin = new Point(23, 94);
مرجع برگرداندهشده توسط عملگر new لزوماً نباید به متغیری اختصاص داده شود. همچنین میتوان آن را مستقیماً در یک عبارت استفاده کرد. برای مثال:
int x = new Point(23, 94).x;
این عبارت در بخش بعدی بررسی خواهد شد.
کلاس Point از مثال قبلی یک Constructor دارد. میتوانید Constructor را از این نشانه بشناسید که نامی برابر با نام کلاس دارد و نوع خروجی ندارد. Constructor در کلاس Point دو پارامتر عدد صحیح میگیرد، همانطور که در کد (int x, int y) مشخص شده. عبارت زیر مقادیر 23 و 94 را برای این پارامترها ارسال میکند:
Point origin = new Point(23, 94);
نتیجهٔ اجرای این عبارت در شکل زیر نشان داده شده است:
[تصویر: شیء Point — مقداردهی اولیهٔ فیلدها x=23 و y=94 در حافظه]
میتوانید هر تعداد Constructor که نیاز دارید در یک کلاس تعریف کنید. در اینجا نمونهٔ کلاس Rectangle را میبینید که شامل چهار Constructor است.
class Point {
int x, y;
Point(int x, int y) {
this.x = x;
this.y = y;
}
}
class Rectangle {
int width, height;
Point origin;
// چهار Constructor
Rectangle() {
origin = new Point(0, 0);
}
Rectangle(Point p) {
this.origin = p;
}
Rectangle(int width, int height) {
this.origin = new Point(0, 0);
this.width = width;
this.height = height;
}
Rectangle(Point origin, int width, int height) {
this.origin = origin;
this.width = width;
this.height = height;
}
}
Point origin = new Point(10, 10);
Rectangle rectangle = new Rectangle(origin, 100, 200);
IO.println("Rectangle: ");
IO.println(" rectangle origin x = " + rectangle.origin.x);
IO.println(" rectangle origin y = " + rectangle.origin.x);
IO.println(" rectangle width = " + rectangle.width);
IO.println(" rectangle height = " + rectangle.height);
اجرای کد بالا خروجی زیر را چاپ میکند:
Rectangle:
rectangle origin x = 10
rectangle origin y = 10
rectangle width = 100
rectangle height = 200
هر Constructor به شما اجازه میدهد مقادیر اولیهٔ origin، width و height مستطیل را تعیین کنید و هم از انواع Primitive و هم از انواع Reference استفاده کنید. اگر یک کلاس Constructorهای متعددی داشته باشد، امضای آنها باید متفاوت باشد. کامپایلر جاوا Constructorها را بر اساس تعداد و نوع آرگومانها تشخیص میدهد. وقتی کامپایلر جاوا با کد زیر مواجه میشود، میداند که باید Constructorای در کلاس Rectangle را فراخوانی کند که یک آرگومان Point به همراه دو آرگومان عدد صحیح میخواهد:
Rectangle rectangle = new Rectangle(origin, 100, 200);
این فراخوانی، یکی از Constructorهای Rectangle را اجرا میکند که origin را مقداردهی اولیه میکند. همچنین Constructor عرض را 100 و ارتفاع را 200 تنظیم میکند. حالا دو مرجع به یک شیء Point واحد وجود دارد — یک شیء میتواند مراجع متعددی داشته باشد، همانطور که در شکل زیر نشان داده شده است:
[تصویر: شیء Rectangle — دو مرجع به یک شیء Point واحد]
خط کد زیر Constructorای از Rectangle را فراخوانی میکند که دو آرگومان عدد صحیح میخواهد. این آرگومانها مقادیر اولیهٔ width و height را فراهم میکنند. اگر کد داخل Constructor را بررسی کنید، میبینید که یک شیء Point جدید با مقادیر x و y برابر با 0 ایجاد میکند:
Rectangle rectangle = new Rectangle(50, 100);
Constructor در عبارت زیر هیچ آرگومانی نمیگیرد، به همین دلیل به آن Constructor بدون پارامتر (No-Argument Constructor) گفته میشود:
Rectangle rectangle = new Rectangle();
تمام کلاسها حداقل یک Constructor دارند. اگر کلاسی Constructor صریحی تعریف نکرده باشد، کامپایلر جاوا بهطور خودکار یک Constructor پیشفرض (Default Constructor) بدون پارامتر ایجاد میکند. این Constructor پیشفرض، Constructor بدون پارامتر والد کلاس را فراخوانی میکند، یا اگر کلاس والد دیگری نداشته باشد، Constructor کلاس Object را فراخوانی میکند. اگر والد Constructorی نداشته باشد (Object Constructor دارد)، کامپایلر برنامه را رد خواهد کرد.
پس از ایجاد یک شیء، احتمالاً میخواهید از آن استفاده کنید. ممکن است نیاز داشته باشید مقدار یکی از فیلدهایش را بخوانید، فیلدی را تغییر دهید، یا یکی از Methodهایش را برای انجام کاری فراخوانی کنید.
فیلدهای شیء از طریق نامشان قابل دسترسی هستند. باید از نامی بدابهام (Unambiguous) استفاده کنید.
در داخل خود کلاس میتوانید از نام ساده برای فیلد استفاده کنید. برای مثال، میتوانیم دستوری به کلاس Rectangle اضافه کنیم که width و height را چاپ کند:
IO.println("Width and height are: " + width + ", " + height);
در این مورد، width و height نامهای ساده هستند.
کدی که خارج از کلاس شیء قرار دارد باید از یک مرجع شیء یا عبارت، به همراه عملگر نقطه (.) و سپس نام سادهٔ فیلد استفاده کند، به این شکل:
objectReference.fieldName
برای مثال، کد زیر خارج از کد کلاس Rectangle قرار دارد. بنابراین برای ارجاع به فیلدهای origin، width و height در شیء Rectangle به نام rectangle، این کد باید به ترتیب از نامهای rectangle.origin، rectangle.width و rectangle.height استفاده کند. برنامه از دو تا از این نامها برای چاپ width و height شیء rectangle استفاده میکند:
IO.println("Width of rectangle: " + rectangle.width);
IO.println("Height of rectangle: " + rectangle.height);
تلاش برای استفاده از نامهای ساده width و height در کد خارج از کلاس Rectangle منطقی نیست — این فیلدها فقط داخل یک شیء وجود دارند — و منجر به خطای کامپایل میشود.
میتوانید از کد مشابهی برای نمایش اطلاعات یک نمونهٔ دیگر از Rectangle استفاده کنید. اشیای یک نوع واحد، هرکدام نسخهٔ مستقل خود را از فیلدهای Instance دارند. بنابراین هر شیء Rectangle فیلدهای origin، width و height خاص خود را دارد. وقتی از طریق یک مرجع شیء به فیلد Instance دسترسی پیدا میکنید، به فیلد آن شیء خاص ارجاع میدهید. اگر دو شیء rectangle1 و rectangle2 داشته باشید، فیلدهای origin، width و height متفاوتی دارند.
برای دسترسی به فیلد میتوانید از مرجع نامگذاریشده به شیء استفاده کنید، همانطور که در مثالهای قبلی دیدید. همچنین میتوانید از هر عبارتی که یک مرجع شیء برمیگرداند استفاده کنید. به یاد داشته باشید که عملگر new مرجعی به یک شیء برمیگرداند. پس میتوانید از مقدار برگرداندهشده توسط new برای دسترسی به فیلدهای شیء جدید استفاده کنید:
int height = new Rectangle().height;
این عبارت یک شیء Rectangle جدید ایجاد میکند و فوراً مقدار height آن را میخواند. در واقع، این عبارت ارتفاع پیشفرض یک Rectangle را محاسبه میکند. توجه کنید که پس از اجرای این عبارت، برنامه دیگر مرجعی به شیء Rectangle ایجادشده ندارد، چون برنامه هرگز مرجع را در جایی ذخیره نکرده است. شیء بدون مرجع شده و منابعش توسط JVM قابل بازیافت است.
برای فراخوانی Method یک شیء نیز از مرجع شیء استفاده میکنید. نام سادهٔ Method را پس از مرجع شیء و با یک عملگر نقطهٔ میانی (.) اضافه میکنید. همچنین هر آرگومانی را داخل پرانتز ارسال میکنید. اگر Method آرگومانی نیاز ندارد، از پرانتز خالی استفاده کنید.
objectReference.methodName(argumentList);
یا:
objectReference.methodName();
بیایید سه Method به کلاس Rectangle خود اضافه کنیم:
area() برای محاسبهٔ مساحت مستطیل،move() برای تغییر مبدأ مستطیل،print() برای چاپ اطلاعاتی دربارهٔ مستطیل در کنسول.class Point {
int x, y;
Point(int x, int y) {
this.x = x;
this.y = y;
}
}
class Rectangle {
int width, height;
Point origin;
Rectangle(Point origin, int width, int height) {
this.origin = origin;
this.width = width;
this.height = height;
}
Rectangle(int width, int height) {
this.origin = new Point(0, 0);
this.width = width;
this.height = height;
}
int area() {
return this.width * this.height;
}
void move(int dx, int dy) {
this.origin.x += dx;
this.origin.y += dy;
}
void print() {
IO.println("Rectangle");
IO.println(" origin x: " + origin.x);
IO.println(" origin y: " + origin.y);
IO.println(" width: " + width);
IO.println(" height: " + height);
}
}
Point origin = new Point(0, 0);
Rectangle rectangle = new Rectangle(origin, 20, 10);
rectangle.print();
IO.println("Area: " + rectangle.area());
IO.println("Moving rectangle");
rectangle.move(5, 15);
rectangle.print();
IO.println("Area: " + rectangle.area());
اجرای کد بالا خروجی زیر را چاپ میکند:
Rectangle
origin x: 0
origin y: 0
width: 20
height: 10
Area: 200
Moving rectangle
Rectangle
origin x: 5
origin y: 15
width: 20
height: 10
Area: 200
مانند فیلدهای Instance، objectReference باید مرجعی به یک شیء باشد. میتوانید از نام متغیر استفاده کنید، اما همچنین میتوانید از هر عبارتی که یک مرجع شیء برمیگرداند استفاده کنید. عملگر new مرجعی به یک شیء برمیگرداند، پس میتوانید از مقدار برگرداندهشده توسط new برای فراخوانی Methodهای شیء جدید استفاده کنید:
new Rectangle(100, 50).area();
عبارت new Rectangle(100, 50) مرجعی به یک شیء Rectangle برمیگرداند. همانطور که نشان داده شد، میتوانید از نتقطهگذاری برای فراخوانی Method area() شیء Rectangle جدید استفاده کنید و مساحت مستطیل جدید را محاسبه کنید.
برخی Methodها، مثل area()، یک مقدار برمیگردانند. برای Methodهایی که مقدار برمیگردانند، میتوانید فراخوانی Method را در عبارات استفاده کنید. میتوانید مقدار بازگشتی را به یک متغیر اختصاص دهید، از آن برای تصمیمگیری استفاده کنید یا یک حلقه را کنترل کنید. کد زیر مقدار برگرداندهشده توسط area() را به متغیر area اختصاص میدهد:
int area = new Rectangle(100, 50).area();
در این مورد، شیءای که area() روی آن فراخوانی میشود، همان مستطیلی است که Constructor برگردانده است.
برخی زبانهای شیگرا از شما میخواهند تمام اشیای ایجادشده را ردیابی کنید و بهصورت صریح آنها را زمانی که دیگر به آنها نیاز ندارید نابود کنید. مدیریت صریح حافظه خستهکننده و مستعد خطا است. پلتفرم جاوا به شما اجازه میدهد هر تعداد شیء که میخواهید ایجاد کنید (البته محدود به ظرفیت سیستمتان) و نیازی نیست نگران نابودی آنها باشید. محیط اجرای جاوا اشیایی را که دیگر استفاده نمیشوند حذف میکند. این فرایند Garbage Collection (جمعآوری زباله) نام دارد.
یک شیء زمانی برای جمعآوری زباله واجد شرایط (Eligible) میشود که دیگر هیچ مرجعی به آن وجود نداشته باشد. مراجعی که در یک متغیر نگهداشته شدهاند معمولاً وقتی متغیر از دامنه (Scope) خارج میشود از بین میروند. یا میتوانید بهصورت صریح یک مرجع شیء را با اختصاص مقدار خاص null به متغیر حذف کنید. به یاد داشته باشید که یک برنامه میتواند مراجع متعددی به یک شیء واحد داشته باشد. تمام مراجع به یک شیء باید حذف شوند تا آن شیء برای جمعآوری زباله واجد شرایط شود.
محیط اجرای جاوا یک Garbage Collector دارد که بهصورت دورهای حافظهٔ استفادهشده توسط اشیایی که دیگر مرجعی به آنها نیست را آزاد میکند. Garbage Collector کار خود را بهطور خودکار زمانی که مناسب تشخیص دهد انجام میدهد.
این محتوا کاملا رایگان توسط تیم کدلپر ترجمه شده و در اختیار شما کاربران عزیز قرار گرفته است، هر گونه کپی برداری برای مقاصد غیر رایگان و بدون ذکر منبع، مورد پیگیری قانونی قرار میگیرد.
ترجمه شده از منبع: https://dev.java/learn/