سلام! در سخنرانی های قبلی، قبلاً یاد گرفته اید که چگونه کلاس های کامل خود را با فیلدها و روش ها ایجاد کنید. این پیشرفت جدی است، آفرین! اما اکنون باید یک حقیقت ناخوشایند را به شما بگویم. ما کلاس های خود را کاملا درست ایجاد نکردیم! چرا؟ در نگاه اول، هیچ خطایی در این کلاس وجود ندارد:
این نام از انگلیسی " get " - " دریافت " (یعنی "روش به دست آوردن مقدار یک فیلد") و " set " (به عنوان مثال "روش برای تنظیم مقدار یک فیلد") گرفته شده است. بیایید ببینیم که آنها با استفاده از کلاس ما به عنوان مثال چه شکلی هستند
می توان با تلفن همراه قیاس کرد. تصور کنید به جای یک تلفن همراه معمولی روشن، یک گوشی با قاب باز به شما داده شد که تمام سیم ها، مدارها و غیره در آن قرار دارند. بیرون زده تلفن کار می کند: اگر سخت تلاش کنید و با نمودارها سر و کار داشته باشید، ممکن است حتی بتوانید تماس بگیرید. اما احتمالاً آن را می شکنند. در عوض، شرکت سازنده یک رابط به شما می دهد: مشتری به سادگی شماره های مورد نیاز را شماره گیری می کند، دکمه سبز رنگ را با گوشی فشار می دهد و تماس شروع می شود. و برای او اهمیتی ندارد که در داخل مدارها و سیم ها چه می گذرد و آنها چگونه وظیفه خود را انجام می دهند. در این مثال، این شرکت دسترسی محدودی به «داخلی» (دادههای) تلفن دارد و فقط رابط (روشها) را بیرون گذاشته است. در نتیجه، مشتری به چیزی که میخواست میرسد (تماس برقرار میکند) و قطعاً چیزی را در داخل نمیشکند.
public class Cat {
public String name;
public int age;
public int weight;
public Cat(String name, int age, int weight) {
this.name = name;
this.age = age;
this.weight = weight;
}
public Cat() {
}
public void sayMeow() {
System.out.println("Meow!");
}
}
در واقع وجود دارد. تصور کنید که در حالی که در محل کار نشسته اید، کلاسی مانند این نوشته اید Cat
که نشان دهنده گربه است. و به خانه رفت. زمانی که شما دور بودید، برنامه نویس دیگری سر کار آمد، کلاس خود را ایجاد کرد Main
و در آنجا شروع به استفاده از کلاسی که شما نوشتید کرد Cat
.
public class Main {
public static void main(String[] args) {
Cat cat = new Cat();
cat.name = "";
cat.age = -1000;
cat.weight = 0;
}
}
مهم نیست که چرا او این کار را کرد یا چگونه اتفاق افتاد: شاید فرد خسته بوده یا به اندازه کافی نخوابیده است. یک چیز دیگر مهم است: کلاس فعلی ما Cat
به شما امکان می دهد مقادیر دیوانه وار را به فیلدها اختصاص دهید. در نتیجه، برنامه حاوی اشیایی با حالت نادرست است، مانند این گربه با سن -1000 سال. در نهایت چه اشتباهی مرتکب شدیم؟ وقتی کلاس را ایجاد کردیم، داده های آن را در معرض دید قرار دادیم. فیلدها name
و در مالکیت عمومی هستند age
. weight
آنها را می توان در هر نقطه ای از برنامه در دسترس قرار داد: فقط یک شی ایجاد کنید Cat
- و تمام، هر برنامه نویسی مستقیماً از طریق اپراتور " .
" به داده های خود دسترسی دارد.
Cat cat = new Cat();
cat.name = "";
در اینجا مستقیماً به فیلد دسترسی پیدا کرده name
و مقدار آن را تعیین می کنیم. ما باید به نحوی از داده های خود در برابر تداخل نادرست خارجی محافظت کنیم. چه چیزی برای این مورد نیاز است؟ ابتدا، تمام متغیرهای نمونه (فیلدها) باید با یک اصلاح کننده علامت گذاری شوند private
. Private سخت ترین اصلاح کننده دسترسی در جاوا است. اگر از آن استفاده کنید، فیلدهای کلاس Cat
خارج از آن قابل دسترسی نخواهند بود.
public class Cat {
private String name;
private int age;
private int weight;
public Cat(String name, int age, int weight) {
this.name = name;
this.age = age;
this.weight = weight;
}
public Cat() {
}
public void sayMeow() {
System.out.println("Meow!");
}
}
public class Main {
public static void main(String[] args) {
Cat cat = new Cat();
cat.name = "";//error! The name field in the Cat class has private access!
}
}
کامپایلر این را می بیند و بلافاصله یک خطا ایجاد می کند. اکنون به نظر می رسد که زمینه ها محافظت شده است. اما معلوم می شود که دسترسی به آنها "به شدت" بسته است: این برنامه حتی نمی تواند وزن یک گربه موجود را در صورت لزوم دریافت کند. این نیز یک گزینه نیست: در این شکل کلاس ما عملاً غیرممکن است. در حالت ایدهآل باید به نوعی دسترسی محدود به دادهها اجازه دهیم:
- سایر برنامه نویسان باید قادر به ایجاد اشیاء باشند
Cat
- آنها باید بتوانند داده ها را از اشیاء موجود بخوانند (به عنوان مثال، نام یا سن یک گربه از قبل موجود را دریافت کنند)
- همچنین باید امکان اختصاص مقادیر فیلد وجود داشته باشد. اما در عین حال - فقط مقادیر صحیح. اشیاء ما باید از اشیاء نادرست محافظت شوند (بدون "سن = -1000 سال" و موارد مشابه).
Cat
:
public class Cat {
private String name;
private int age;
private int weight;
public Cat(String name, int age, int weight) {
this.name = name;
this.age = age;
this.weight = weight;
}
public Cat() {
}
public void sayMeow() {
System.out.println("Meow!");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public int getWeight() {
return weight;
}
public void setWeight(int weight) {
this.weight = weight;
}
}
همانطور که می بینید، همه چیز بسیار ساده است :) نام آنها اغلب شامل کلمه get/set + نام فیلدی است که مسئولیت آن را بر عهده دارند. برای مثال، یک متد getWeight()
مقدار فیلد weight
شیئی را که برای آن فراخوانی شده است، برمی گرداند. این چیزی است که در برنامه به نظر می رسد:
public class Main {
public static void main(String[] args) {
Cat barsik = new Cat("Barsik", 5, 4);
String barsikName = barsik.getName();
int barsikAge = barsik.getAge();
int barsikWeight = barsik.getWeight();
System.out.println("Cat name: " + barsikName);
System.out.println("Cat's age: " + barsikAge);
System.out.println("Weight of the cat: " + barsikWeight);
}
}
خروجی کنسول:
Name кота: Барсик
Возраст кота: 5
Вес кота: 4
اکنون از یک کلاس دیگر ( Main
) به فیلدها دسترسی وجود دارد Cat
اما فقط از طریق دریافت کننده ها . لطفاً توجه داشته باشید که دریافتکنندهها یک اصلاحکننده دسترسی دارند public
، به این معنی که از هر جایی در برنامه قابل دسترسی هستند. در مورد تخصیص مقادیر چطور؟ متدهای تنظیم کننده این امر را بر عهده دارند
public void setName(String name) {
this.name = name;
}
کار آنها همانطور که می بینید نیز ساده است. setName()
یک متد را روی یک شی فراخوانی می کنیم Cat
، رشته ای را به عنوان آرگومان به آن ارسال می کنیم و این رشته به فیلدی name
از شی ما اختصاص داده می شود.
public class Main {
public static void main(String[] args) {
Cat barsik = new Cat("Barsik", 5, 4);
System.out.println("The original name of the cat is " + barsik.getName());
barsik.setName("Basil");
System.out.println("The new name of the cat -" + barsik.getName());
}
}
در اینجا ما از هر دو گیرنده و ستتر استفاده کردیم. ابتدا با استفاده از یک گیرنده، نام اولیه گربه را دریافت کرده و به کنسول خروجی می دهیم. سپس با استفاده از یک تنظیم کننده، name
یک مقدار جدید به فیلد آن اختصاص داده شد - "Vasily". و سپس، با استفاده از یک گیرنده، دوباره نام را گرفتیم تا بررسی کنیم که آیا واقعاً تغییر کرده است یا خیر. خروجی کنسول:
Изначальное Name кота — Барсик
Новое Name кота — Васorй
به نظر می رسد، تفاوت چیست؟ همچنین میتوانیم مقادیر نادرستی را به فیلدهای شی اختصاص دهیم، حتی اگر تنظیمکننده داشته باشیم:
public class Main {
public static void main(String[] args) {
Cat barsik = new Cat("Barsik", 5, 4);
barsik.setAge(-1000);
System.out.println("Age of Barsik -" + barsik.getAge() + " years");
}
}
خروجی کنسول:
Возраст Барсика — -1000 лет
تفاوت این است که ستر یک روش تمام عیار است . و در یک متد، بر خلاف یک فیلد، می توانید منطق تأیید مورد نیاز خود را برای جلوگیری از مقادیر غیرقابل قبول قرار دهید. به عنوان مثال، می توانید به راحتی تخصیص یک عدد منفی به عنوان سن را غیرفعال کنید:
public void setAge(int age) {
if (age >= 0) {
this.age = age;
} else {
System.out.println("Error! Age cannot be negative!");
}
}
و اکنون کد ما به درستی کار می کند!
public class Main {
public static void main(String[] args) {
Cat barsik = new Cat("Barsik", 5, 4);
barsik.setAge(-1000);
System.out.println("Age of Barsik -" + barsik.getAge() + " years");
}
}
خروجی کنسول:
Ошибка! Возраст не может быть отрицательным числом!
Возраст Барсика — 5 лет
یک محدودیت در تنظیم کننده وجود دارد و از تلاش برای تنظیم داده های نادرست محافظت می کند. سن بارسیک بدون تغییر باقی ماند. گیرنده ها و تنظیم کننده ها همیشه باید ایجاد شوند. حتی اگر فیلدهای شما هیچ محدودیتی در مقادیر احتمالی نداشته باشند، هیچ آسیبی از آنها وارد نخواهد شد. موقعیتی را تصور کنید: شما و همکارانتان در حال نوشتن یک برنامه با هم هستید. شما یک کلاس Cat
با فیلدهای عمومی ایجاد کردید و همه برنامه نویسان از آنها همانطور که می خواهند استفاده می کنند. و سپس یک روز خوب به شما می گوید: "لعنتی، دیر یا زود ممکن است شخصی به طور تصادفی یک عدد منفی به یک متغیر اختصاص دهد weight
! ما باید تنظیم کننده ایجاد کنیم و همه فیلدها را خصوصی کنیم!» شما آنها را ایجاد می کنید و تمام کدهای نوشته شده توسط همکاران شما فوراً خراب می شود. از این گذشته، آنها قبلاً یک سری کد نوشته بودند که در آنجا Cat
مستقیماً به فیلدها دسترسی داشتند.
cat.name = "Hippopotamus";
و حالا فیلدها خصوصی شده اند و کامپایلر یک سری خطا تولید می کند!
cat.name = "Hippopotamus";//error! The name field of the Cat class has private access!
در چنین شرایطی، بهتر است از همان ابتدا زمینه ها را پنهان کنید و گیربکس ها را ایجاد کنید . همه همکاران شما از آنها استفاده میکنند، و اگر دیر متوجه میشوید که باید مقادیر فیلد را محدود کنید، به سادگی یک چک در تنظیمکننده اضافه میکنید. و هیچ کس کد از قبل نوشته شده را نمی شکند. البته، اگر می خواهید به یک فیلد خاص دسترسی فقط خواندنی داشته باشید، می توانید یک دریافت کننده برای آن ایجاد کنید. "Outside"، یعنی خارج از کلاس شما، فقط متدها باید در دسترس باشند. داده ها باید پنهان شوند.
GO TO FULL VERSION