![מושגי OOP ב-Java - 1](https://cdn.codegym.cc/images/article/937955ff-0a01-4de5-b641-1c6f5fbcbd12/800.jpeg)
האובייקט לאובייקטים של ג'אווה ולאובייקטים בעולם האמיתי יש שני מאפיינים: מצב והתנהגות. לדוגמה, לאובייקט אנושי יש מצב (שם, מין, שינה או לא...) והתנהגות (לומד ג'אווה, הולך, מדבר...). כל אובייקט Java מאחסן את מצבו בשדות וחושף את התנהגותו באמצעות שיטות. |
כימוס
קיבולת נתונים היא הסתרת נתונים פנימיים מהעולם החיצון, וגישה אליהם רק באמצעות שיטות שנחשפו לציבור. מה זה אומר? איזה נתונים? מסתתר ממי? הסתרה פירושו להגביל גישה ישירה לחברי הנתונים (שדות) של מחלקה.איך זה עובד בג'אווה:
- השדות נעשים פרטיים
- כל שדה בכיתה מקבל שתי שיטות מיוחדות: גטר ו-seter. שיטות גטר מחזירות את הערך של השדה. שיטות Setter מאפשרות לך לשנות את ערך השדה בצורה עקיפה אך מותרת.
דוגמה של אנקפסולציה בקוד Java:
public class Student {
private int age;
private String name;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public class Test{
public static void main(String[] args) {
Student firstStudent = new Student();
firstStudent.setName("John");
// The name field is private, so you can no longer do this: firstStudent.name = "John";
}
}
למה כדאי להשתמש באנקפסולציה?
הסיבה העיקרית היא להקל על שינוי הקוד שלך. תאר לעצמך שיש לך בקשה לבית ספר להוקי ויש כיתת HockeyStudent עם שני שדות המאחסנים את שמו וגילו של התלמיד כאשר הוא נרשם לבית הספר. משהו כזה:public class HockeyStudent {
public String name;
public int ageOfEnrollment;
}
השדה ageOfEnrollment הוא ציבורי, ללא מגבירים או מגדירים... מחלקה זו משמשת מחלקות רבות אחרות, והכל היה בסדר עד שמפתח כלשהו החליט ששדה אינט בודד אינו מספיק. יש שחקני הוקי בקבוצה מבוגרים בכמעט שנה מבני גילם, כך שיהיה נוח יותר לפצל אותם לשתי קבוצות בהתאם לחודש שבו נולדו. אז יש לשנות את השדה ageOfEnrollment למערך int (int[][]) : המספר הראשון הוא לשנים שלמות והשני הוא לחודשים. עכשיו אתה צריך לשנות את כל הקוד שמשתמש בכיתה Student ! אבל אם שדה ה-ageOfEnrollment שלך הוא פרטי ויש לך מגדירים ומגדירים, אז הכל קל יותר. אם הדרישה להגדרת גיל של תלמיד משתנה, פשוט עדכן את ההיגיון בשיטת setAgeOfEnrollment() מגדיר והכיתות שלך יוכלו להמשיך להשתמש ב- Student ללא בעיות! הדוגמה הזו מעט מומצאת, אבל אני מקווה שהיא מסבירה מדוע שימוש באנקפסולציה הוא רעיון מצוין.
יְרוּשָׁה
עיקרון זה קל יותר להבנה גם ללא כל ניסיון מעשי. אל תחזור על עצמך (DRY) יכול להיות המוטו למושג הירושה. ירושה מאפשרת לך ליצור כיתת צאצא שיורשת את השדות והשיטות של כיתת האב מבלי להגדיר אותם מחדש. בטח, אתה יכול לעקוף את השדות והשיטות של כיתת האב בכיתת הילד, אבל זה לא הכרחי. מה שכן, ניתן להוסיף מצבים והתנהגויות חדשות בכיתת הילד. כיתות הורים נקראות לפעמים כיתות-על או כיתות בסיס, וכיתות ילדים ידועות בתור כיתות משנה. מילת המפתח extends של Java משמשת ליישום עקרון ההורשה בקוד.איך זה עובד בג'אווה:
- צור את כיתת האב.
- צור את כיתת הילד באמצעות מילת המפתח מרחיב .
- בקונסטרוקטור של המחלקה Child, השתמש בשיטת super(parentField1, parentField2, ...) כדי להגדיר את השדות של האב.
בנאי הוא שיטה מיוחדת המשמשת לאתחול אובייקט חדש שנוצר. לבנאי יש שם זהה לשם המחלקה שלו. ישנם שני סוגים של בנאים: ברירת מחדל (בנאי ללא ארגומנט) ובנאי עם פרמטרים. למחלקה חייבת להיות בנאי אחד לפחות (יש לה את הבנאי ברירת המחדל אם לא הוגדרו בנאים אחרים) והיא יכולה להכיל הרבה מהם. בכל פעם שאתה יוצר אובייקט חדש, אתה קורא לבנאי שלו. בדוגמה למעלה, אתה עושה זאת בשורה זו:
אתה משתמש במילת המפתח החדשה כדי לקרוא לבנאי ברירת המחדל של הכיתה Student : tudent() . |
כמה חוקים:
- לכיתה אחת יכול להיות הורה אחד בלבד.
- כיתת הורה אחת יכולה לכלול כיתות ילדים רבים.
- לכיתת ילד יכולים להיות כיתות ילדים משלה.
דוגמה להורשה בקוד Java
בואו ניצור כיתת טלפון .public class Phone {
int price;
double weight;
// Constructor
public Phone(int price, double weight) {
this.price = price;
this.weight = weight;
}
void orderPhone(){
System.out.println("Ordering phone...");
}
}
כמובן, ישנם סוגים שונים של טלפונים, אז בואו ניצור שני כיתות ילדים: אחת לטלפונים אנדרואיד והשנייה לאייפון. לאחר מכן נוסיף כמה שדות ושיטות שאין להורה. ונשתמש בסופר() כדי לקרוא לבנאים כדי לאתחל את השדות שיש למחלקת האב.
דוגמה להורשה בג'אווה
public class Android extends Phone {
// Some new fields
String androidVersion;
int screenSize;
String secretDeviceCode;
// Constructor
public Android(int price, double weight, String androidVersion, int screenSize, String secretDeviceCode) {
super(price, weight); // Android inherits Phone’s fields
//this - reference to the current object
//super - reference to the parent object
this.androidVersion = androidVersion;
this.screenSize = screenSize;
this.secretDeviceCode = secretDeviceCode;
}
// New Android-specific method, does not exist in the Phone class
void installNewAndroidVersion() {
System.out.println("installNewAndroidVersion invoked...");
}
}
public class IPhone extends Phone {
boolean fingerPrint;
public IPhone(int price, double weight, boolean fingerPrint) {
super(price, weight);
System.out.println("IPhone constructor was invoked...");
this.fingerPrint = fingerPrint;
}
void deleteIPhoneFromDb() {
System.out.println("deleteIPhoneFromDb invoked...");
}
@Override // This is about polymorphism, see below
void orderPhone(){
System.out.println("Ordering my new iPhone and deleting the old one...");
}
}
אז, כדי לחזור: ב-Java, הירושה מאפשרת לך להרחיב מחלקה עם כיתות ילד שיורשות את השדות והשיטות של מחלקת האב. זוהי דרך מצוינת להשיג שימוש חוזר בקוד.
רב צורתיות
פולימורפיזם הוא יכולתו של אובייקט להתעצב, ללבוש צורות שונות או ליתר דיוק לפעול בדרכים שונות. ב-Java, פולימורפיזם מתרחש בדרך כלל כאשר משתמשים בהפניה לכיתה אב כדי להתייחס לאובייקט של מחלקה צאצא.מה זה אומר ואיך זה עובד ב-Java:
מהו פולימורפיזם בג'אווה? באופן כללי, זה אומר שאתה יכול להשתמש באותו שם שיטה למטרות שונות. ישנם שני סוגים של פולימורפיזם בג'אווה: דחיפת שיטה (פולימורפיזם דינמי) ועומס יתר של שיטות (פולימורפיזם סטטי).עוקפת השיטה
אתה יכול לעקוף את השיטה של כיתת הורה בכיתת ילד, ולהכריח אותה לעבוד בצורה שונה. בואו ניצור כיתת הורה מוזיקאית עם שיטת play() .דוגמה לפולימורפיזם בקוד Java
public class Musician {
String name;
int age;
// Default constructor
public Musician() {
}
// Parameterized constructor
public Musician(String name, int age) {
this.name = name;
this.age = age;
}
void play() {
System.out.println("I am playing my instrument...");
}
}
מוזיקאים שונים משתמשים בכלים שונים. בואו ניצור שתי כיתות ילדים : פסנתרן וכנר . הודות לפולימורפיזם, כל אחד יבצע את הגרסה שלו לשיטת play() . בעת ביטול, אתה יכול להשתמש בהערת @Override , אך אין צורך בכך.
public class Pianist extends Musician {
String favoritePianoType;
public Pianist(String name, int age, String favoritePianoType) {
super(name, age);
this.favoritePianoType = favoritePianoType;
}
@Override
void play(){
System.out.println("I am playing the piano...");
}
}
נגן הכינור יכול להיות סולן או חבר בתזמורת. בואו ניקח את זה בחשבון בעת עקיפת שיטת ה-play() שלנו .
public class Violinist extends Musician {
boolean isSoloist;
public Violinist(String name, int age, boolean isSoloist) {
super(name, age);
this.isSoloist = isSoloist;
}
@Override
void play(){
if (isSoloist)
System.out.println("I am playing the violin solo...");
else
System.out.println("I am playing the violin in an orchestra...");
}
}
בואו ניצור מחלקה Demo , שבה ניצור שלושה אובייקטים, מופע אחד של כל אחת מהמחלקות שנוצרו קודם לכן. נראה אילו תוצאות נקבל.
public class Demo {
public static void main(String[] args) {
Musician musician = new Musician();
Violinist violinist = new Violinist("John", 32, true);
Pianist pianist = new Pianist("Glen", 30, "Acoustic");
System.out.println("Musician said:");
musician.play();
System.out.println("Violinist said:");
violinist.play();
System.out.println("Pianist said:");
pianist.play();
}
}
הנה מה שאנחנו מקבלים:
Musician said:
I am playing my instrument...
Violinist said:
I am playing the violin solo…
Pianist said:
I am playing the piano...
כל כנר ופסנתרן הוא מוזיקאי, אבל לא כל מוזיקאי הוא כנר או פסנתרן. זה אומר שאתה יכול להשתמש בשיטת הנגינה של המוזיקאי אם אתה לא צריך ליצור אחד חדש. או שאתה יכול לקרוא לשיטת ההורה מהילד באמצעות מילת המפתח העל . בוא נעשה את זה בקוד של פסנתרן:
public class Pianist extends Musician {
String favoritePianoType;
@Override
void play(){
super.play();
System.out.println("I am playing the piano...");
}
}
עכשיו בואו נקרא לשיטת main() שלנו במחלקה Demo . הנה התוצאה:
Musician said:
I am playing my instrument...
Violinist said:
I am playing the violin solo...
Pianist said:
I am playing my instrument...
I am playing the piano...
עומס יתר בשיטה
עומס יתר של שיטות פירושו שימוש בשיטות שונות עם אותו שם באותה מחלקה. הם חייבים להיות שונים מבחינת המספר, הסדר או סוגי הפרמטרים שלהם. נניח שפסנתרן יכול לנגן על פסנתר אקוסטי ופסנתר חשמלי. כדי לנגן חשמלי, המוזיקאי צריך חשמל. בואו ניצור שתי שיטות play() שונות . הראשון ללא פרמטרים, לפסנתר אקוסטי, והשני עם פרמטר המציין האם יש חשמל.public class Pianist extends Musician {
String name;
int age;
String favoritePianoType;
@Override
void play(){
super.play();
System.out.println("I am playing the piano...");
}
void play(boolean isElectricity){
if (isElectricity) {
System.out.println("The electricity is on.");
System.out.println("I am playing the piano...");
}
else System.out.println("I can't play this without electricity.");
}
}
אגב, אתה יכול להשתמש בשיטת ה-play() הראשונה בתוך שיטת ה-play(בוליאנית) השנייה באופן הזה:
void play(boolean isElectricity){
if (isElectricity) {
System.out.println("The electricity is on.");
play();
}
else System.out.println("I can't play this without electricity.");
}
בואו נוסיף כמה שורות לשיעור ההדגמה שלנו כדי להדגים את עומס היתר שלנו:
public class Demo {
public static void main(String[] args) {
Musician musician = new Musician();
Violinist violinist = new Violinist("John", 23, true);
Pianist pianist = new Pianist("Glen", 30, "Acoustic");
System.out.println("Musician said:");
musician.play();
System.out.println("Violinist said:");
violinist.play();
System.out.println("Pianist said:");
pianist.play();
System.out.println("The pianist will now try the electric piano:");
pianist.play(true);
System.out.println("The electricity has been shut off. Now when trying the electric piano, the pianist says:");
pianist.play(false);
}
}
הנה התוצאה:
Musician said:
I am playing my instrument...
Violinist said:
I am playing the violin solo...
Pianist said:
I am playing my instrument...
I am playing the piano...
The pianist will now try the electric piano:
The electricity is on.
I am playing my instrument...
I am playing the piano...
The electricity has been shut off. Now when trying the electric piano, the pianist says:
I can't play this without electricity.
Java יודעת באיזו שיטה יש להשתמש בהתבסס על הפרמטרים שלה וסוג האובייקט. זה פולימורפיזם.
הַפשָׁטָה
כאשר אנו מגדירים מחלקה, אנו מנסים לבנות מודל של משהו. לדוגמה, נניח שאנחנו כותבים משחק וידאו בשם MyRacer עם מכוניות מירוץ שונות. שחקן יכול לבחור אחד מהם ולאחר מכן לעדכן אותו או לקנות אחד אחר. אז... מה זה מכונית? מכונית היא דבר די מסובך, אבל אם אנחנו מנסים ליצור משחק וידאו מירוצים (בניגוד לסימולטור נהיגה), אז אנחנו לא צריכים לתאר את כל אלפי ההילוכים והאטמים שהוא מכיל. אנחנו צריכים את הדגם, המהירות המרבית, מאפייני התמרון, המחיר, הצבע שלו... ואולי זה מספיק. זה הדגם של מכונית למשחק שלנו. בהמשך MyRacer 2, נניח שנחליט להוסיף צמיגים שמשפיעים על ההתנהלות בכביש. כאן הדגם שונה, כי הוספנו פרטים נוספים. הבה נגדיר הפשטת נתונים כתהליך של זיהוי רק את המאפיינים החשובים (או ההכרחיים) של אובייקט והתעלמות מכל פרט לא רלוונטי. ישנן רמות שונות של הפשטה. לדוגמה, אם אתה נוסע באוטובוס, אתה צריך לדעת איך האוטובוס שלך נראה ולאן הוא נוסע, אבל אתה לא צריך לדעת איך לנהוג בו. אם אתה נהג אוטובוס, אתה לא צריך לדעת איך ליצור אוטובוס חדש - אתה רק צריך לדעת איך לנהוג בו. אבל אם אתה יצרן אוטובוסים, אתה צריך ללכת לרמת הפשטה נמוכה יותר, כי הפרטים של עיצוב האוטובוס חשובים לך מאוד. אני מקווה שהבנת למה אני מתכוון.איך זה עובד בג'אווה:
בואו נבנה ארבע רמות של הפשטה ב-Java, או ליתר דיוק ב-OOP - מהנמוכה ביותר (הספציפית ביותר) לגבוהה (המופשטת ביותר).-
רמת ההפשטה הנמוכה ביותר היא אובייקט ספציפי. זוהי ישות עם סט של מאפיינים השייכים למחלקה מסוימת. יש לו ערכי שדה ספציפיים
-
תבנית ליצירת אובייקטים היא מחלקה. זה תיאור של קבוצה של אובייקטים עם מאפיינים דומים ומבנה פנימי.
-
מחלקה מופשטת היא תיאור מופשט של המאפיינים של קבוצה של מחלקות (היא פועלת כתבנית להורשה על ידי מחלקות אחרות). יש לו רמת הפשטה גבוהה, כך שאי אפשר ליצור אובייקטים ישירות ממחלקה מופשטת. ניתן להשתמש רק בכיתות צאצא של מחלקות מופשטות ליצירת אובייקטים. מחלקה מופשטת עשויה לכלול שיטות עם מימוש, אך זו אינה דרישה.
-
ממשק הוא מבנה של מבנה שפת התכנות Java שמכיל רק שיטות ציבוריות מופשטות ושדות קבועים סטטיים (סטטי סופי). במילים אחרות, לא ניתן להשתמש במחלקות מופשטות או בממשקים ליצירת אובייקטים.
דוגמה לממשק בקוד Java
interface Human {
public void struggle();
public void protect();
}
interface Vulcan {
int angleOfPointyEars;
public void turnOffEmotions(boolean isOn);
public void telepathy();
}
אתה יכול ליישם יותר מממשק אחד
The Spock class implements Human and Vulcan {
public void struggle() {
System.out.println("I am struggling...");
}
public void protect() {
System.out.println("You are under my protection!”);
}
public void turnOffEmotions(boolean isOn){
If (isOn) {
System.out.println("I am turning off my emotions.");
isOn= !isOn;
}
}
public void telepathy() {
System.out.println("Connecting to your brain...");
}
}
לסטודנטים מתחילים, זה מכסה את כל המושגים העיקריים של תכנות מונחה עצמים ב-Java. מלבד 4 עקרונות ה-OOP העיקריים, ל-Java יש גם שיוך, צבירה והרכב. אתה יכול לקרוא להם "עקרונות OOP נוספים". מגיע להם מאמר נפרד משלהם.
GO TO FULL VERSION