JavaRush /בלוג Java /Random-HE /קונסטרוקטים בג'אווה

קונסטרוקטים בג'אווה

פורסם בקבוצה
שלום! היום נבחן נושא חשוב מאוד הנוגע לחפצים שלנו. כאן, ללא הגזמה, אנו יכולים לומר שתשתמש בידע הזה כל יום בעבודה אמיתית! נדבר על קונסטרוקטורים. אולי אתה שומע את המונח הזה בפעם הראשונה, אבל למעשה כנראה השתמשת בקונסטרוקטורים, אבל פשוט לא שמת לב לזה בעצמך :) נראה את זה מאוחר יותר.

מהו קונסטרוקטור בג'אווה ולמה הוא נחוץ?

בואו נסתכל על שתי דוגמאות.
public class Car {

   String model;
   int maxSpeed;

   public static void main(String[] args) {

       Car bugatti = new Car();
       bugatti.model = "Bugatti Veyron";
       bugatti.maxSpeed = 407;

   }
}
יצרנו את המכונית שלנו וקבענו את הדגם והמהירות המרבית שלה. עם זאת, בפרויקט אמיתי, לאובייקט המכונית יהיו בבירור יותר מ-2 שדות. וגם, למשל, 16 שדות!
public class Car {

   String model;//model
   int maxSpeed;//max speed
   int wheels;// disk width
   double engineVolume;//engine capacity
   String color;//color
   int yearOfIssue;//year of issue
   String ownerFirstName;//Owner's name
   String ownerLastName;//owner's last name
   long price;//price
   boolean isNew;//new or not
   int placesInTheSalon;//number of seats in the cabin
   String salonMaterial;// interior material
   boolean insurance;//is it insured
   String manufacturerCountry;//manufacturer country
   int trunkVolume;// trunk volume
   int accelerationTo100km;//acceleration to 100 km/h in seconds


   public static void main(String[] args) {
       Car bugatti = new Car();

       bugatti.color = "blue";
       bugatti.accelerationTo100km = 3;
       bugatti.engineVolume = 6.3;
       bugatti.manufacturerCountry = "Italy";
       bugatti.ownerFirstName = "Amigo";
       bugatti.yearOfIssue = 2016;
       bugatti.insurance = true;
       bugatti.price = 2000000;
       bugatti.isNew = false;
       bugatti.placesInTheSalon = 2;
       bugatti.maxSpeed = 407;
       bugatti.model = "Bugatti Veyron";

   }

}
יצרנו אובייקט רכב חדש . בעיה אחת: יש לנו 16 שדות, אבל אתחולנו רק 12 ! נסה כעת להשתמש בקוד כדי למצוא את אלה ששכחנו! לא כל כך קל, נכון? במצב כזה, המתכנת יכול בקלות לטעות ולדלג על אתחול של שדה כלשהו. כתוצאה מכך, התנהגות התוכנית תהיה שגויה:
public class Car {

   String model;//model
   int maxSpeed;//max speed
   int wheels;// disk width
   double engineVolume;//engine capacity
   String color;//color
   int yearOfIssue;//year of issue
   String ownerFirstName;//Owner's name
   String ownerLastName;//owner's last name
   long price;//price
   boolean isNew;//new or not
   int placesInTheSalon;//number of seats in the cabin
   String salonMaterial;// interior material
   boolean insurance;//is it insured
   String manufacturerCountry;//manufacturer country
   int trunkVolume;// trunk volume
   int accelerationTo100km;//acceleration to 100 km/h in seconds


   public static void main(String[] args) {
       Car bugatti = new Car();

       bugatti.color = "blue";
       bugatti.accelerationTo100km = 3;
       bugatti.engineVolume = 6.3;
       bugatti.manufacturerCountry = "Italy";
       bugatti.ownerFirstName = "Amigo";
       bugatti.yearOfIssue = 2016;
       bugatti.insurance = true;
       bugatti.price = 2000000;
       bugatti.isNew = false;
       bugatti.placesInTheSalon = 2;
       bugatti.maxSpeed = 407;
       bugatti.model = "Bugatti Veyron";

       System.out.println("Model Bugatti Veyron. Engine size - " + bugatti.engineVolume + ", trunk - " + bugatti.trunkVolume + ", salon is made of" + bugatti.salonMaterial +
       ", disc width - " + bugatti.wheels + ". Was acquired in 2018 by Mr. " + bugatti.ownerLastName);

   }

}
פלט מסוף:
דגם בוגאטי וירון. נפח מנוע - 6.3, תא מטען - 0, פנים עשוי אפס, רוחב חישוק - 0. נרכש בשנת 2018 על ידי מר null
הקונה שלך, ששילם 2 מיליון דולר עבור מכונית, ברור שלא יאהב שיקראו לו "מר ריק"! אבל ברצינות, בסופו של דבר, התוכנית שלנו הסתיימה עם אובייקט שנוצר בצורה שגויה - מכונית עם רוחב חישוקים 0 (כלומר, ללא חישוקים כלל), תא מטען חסר, פנים עשוי מחומר לא ידוע, ואפילו שייך למישהו לא ידוע. . אפשר רק לדמיין איך שגיאה כזו יכולה להתרחש בזמן שהתוכנית פועלת! אנחנו צריכים איכשהו להימנע ממצבים כאלה. אנחנו צריכים שתהיה לתוכנית שלנו מגבלה: בעת יצירת אובייקט רכב חדש, למשל, יש לציין עבורו תמיד את הדגם ואת המהירות המרבית. אחרת, אל תאפשר יצירת אובייקט. פונקציות קונסטרוקטור מתמודדות בקלות עם משימה זו. הם קיבלו את שמם מסיבה כלשהי. הקונסטרוקטור יוצר מעין "שלד" של המחלקה, שאליו כל אובייקט חדש של המחלקה חייב להתאים. מטעמי נוחות, בואו נחזור לגרסה פשוטה יותר של מחלקת הרכב עם שני שדות. בהתחשב בדרישות שלנו, הקונסטרוקטור למחלקת מכוניות ייראה כך:
public Car(String model, int maxSpeed) {
   this.model = model;
   this.maxSpeed = maxSpeed;
}
ויצירת אובייקט נראית כעת כך:
public static void main(String[] args) {
   Car bugatti = new Car("Bugatti Veyron", 407);
}
שים לבכיצד נוצר הבנאי. היא דומה לשיטה רגילה, אך אין לה סוג החזרה. במקרה זה, שם המחלקה מצוין בבנאי, גם באות גדולה. במקרה שלנו - רכב . בנוסף, הבנאי משתמש במילת המפתח חדש לך זה . "זה" באנגלית פירושו "זה, זה". מילה זו מתייחסת לאובייקט מסוים. קוד בקונסטרוקטור:
public Car(String model, int maxSpeed) {
   this.model = model;
   this.maxSpeed = maxSpeed;
}
ניתן לתרגם כמעט מילולית: " מודל עבור מכונה זו (שאנו יוצרים כעת) = ארגומנט המודל , שצוין בקונסטרוקטור. maxSpeed ​​​​עבור מכונה זו (שאנו יוצרים) = הארגומנט maxSpeed ​​​​, אשר מצוין בקונסטרוקטור." זה מה שקרה:
public class Car {

   String model;
   int maxSpeed;

   public Car(String model, int maxSpeed) {
       this.model = model;
       this.maxSpeed = maxSpeed;
   }

   public static void main(String[] args) {
       Car bugatti = new Car("Bugatti Veyron", 407);
       System.out.println(bugatti.model);
       System.out.println(bugatti.maxSpeed);
   }

}
פלט מסוף:
בוגאטי ויירון 407
הבנאי הקצה בהצלחה את הערכים הנדרשים. אולי שמתם לב שקונסטרוקטור דומה מאוד לשיטה רגילה! ככה זה: קונסטרוקטור זו שיטה, רק קצת ספציפית :) ממש כמו בשיטה, העברנו פרמטרים לבנאי שלנו. ובדיוק כמו קריאה למתודה, קריאה לבנאי לא תעבוד אם לא תציין אותם:
public class Car {

   String model;
   int maxSpeed;

   public Car(String model, int maxSpeed) {
       this.model = model;
       this.maxSpeed = maxSpeed;
   }

   public static void main(String[] args) {
       Car bugatti = new Car(); //error!
   }

}
אתה מבין, המעצב עשה את מה שניסינו להשיג. עכשיו אתה לא יכול ליצור מכונית בלי מהירות או בלי דגם! קווי הדמיון בין בנאים ושיטות לא מסתיימים בכך. בדיוק כמו שיטות, ניתן להעמיס על בנאים. תאר לעצמך שיש לך 2 חתולים בבית. לקחת אחד מהם כחתלתול, ואת השני הבאת הביתה מהרחוב כמבוגר ואתה לא יודע בדיוק בן כמה הוא. זה אומר שהתוכנית שלנו צריכה להיות מסוגלת ליצור חתולים משני סוגים - עם שם וגיל לחתול הראשון, ורק עם שם - לחתול השני. לשם כך, נעמיס על הבנאי:
public class Cat {

   String name;
   int age;

   //for the first cat
   public Cat(String name, int age) {
       this.name = name;
       this.age = age;
   }

   //for the second cat
   public Cat(String name) {
       this.name = name;
   }

   public static void main(String[] args) {

       Cat barsik = new Cat("Barsik", 5);
       Cat streetCatNamedBob = new Cat("Bob");
   }

}
לבנאי המקורי עם הפרמטרים "שם" ו"גיל", הוספנו עוד אחד, רק עם שם. העמסנו שיטות באותו אופן בשיעורים קודמים. עכשיו נוכל ליצור בהצלחה את שתי הגרסאות של חתולים :) למה צריך בנאים?  - 2אתה זוכר בתחילת ההרצאה אמרנו שכבר השתמשת בבנאים, אבל פשוט לא שמת לב לזה? זה נכון. העובדה היא שלכל מחלקה ב-Java יש מה שנקרא בנאי ברירת מחדל. אין לו טיעונים, אבל הוא נורה בכל פעם שנוצר אובייקט כלשהו מכל מחלקה.
public class Cat {

   public static void main(String[] args) {

       Cat barsik = new Cat(); //this is where the default constructor worked
   }
}
במבט ראשון זה לא מורגש. ובכן, יצרנו אובייקט ויצרנו אותו, איפה העבודה של המעצב? כדי לראות את זה, בוא נכתוב בנאי ריק למחלקת החתול במו ידינו , ובתוכו נדפיס איזה ביטוי לקונסולה. אם הוא מוצג, הבנאי עבד.
public class Cat {

   public Cat() {
       System.out.println("Created a cat!");
   }

   public static void main(String[] args) {

       Cat barsik = new Cat(); //this is where the default constructor worked
   }
}
פלט מסוף:
הם יצרו חתול!
הנה האישור! בנאי ברירת המחדל קיים תמיד באופן בלתי נראה בכיתות שלך. אבל אתה צריך לדעת עוד תכונה אחת שלו. בנאי ברירת המחדל נעלם מהמחלקה כאשר אתה יוצר בנאי כלשהו עם ארגומנטים. את ההוכחה לכך, למעשה, כבר ראינו לעיל. כאן בקוד הזה:
public class Cat {

   String name;
   int age;

   public Cat(String name, int age) {
       this.name = name;
       this.age = age;
   }

   public static void main(String[] args) {

       Cat barsik = new Cat(); //error!
   }
}
לא יכולנו ליצור חתול ללא שם וגיל כי הגדרנו בנאי עבור Cat : מחרוזת + מספר. בנאי ברירת המחדל נעלם מהמחלקה מיד לאחר מכן. לכן, הקפד לזכור: אם אתה צריך כמה בנאים בכיתה שלך, כולל אחד ריק, עליך ליצור אותו בנפרד. לדוגמה, אנחנו יוצרים תוכנית למרפאה וטרינרית. המרפאה שלנו רוצה לעשות מעשים טובים ולעזור לחתולים חסרי בית, שאיננו יודעים את שמם או גילם. אז הקוד שלנו אמור להיראות כך:
public class Cat {

   String name;
   int age;

   //for domestic cats
   public Cat(String name, int age) {
       this.name = name;
       this.age = age;
   }

   //for street cats
   public Cat() {
   }

   public static void main(String[] args) {

       Cat barsik = new Cat("Barsik", 5);
       Cat streetCat = new Cat();
   }
}
כעת, לאחר שכתבנו במפורש בנאי ברירת מחדל, נוכל ליצור חתולים משני הסוגים :) לבנאי (כמו לכל שיטה), יש חשיבות רבה לסדר הטיעונים. בואו נחליף את טיעוני השם והגיל בבנאי שלנו.
public class Cat {

   String name;
   int age;

   public Cat(int age, String name) {
       this.name = name;
       this.age = age;
   }

   public static void main(String[] args) {

       Cat barsik = new Cat("Barsik", 10); //error!
   }
}
שְׁגִיאָה! הבנאי קובע בבירור שכאשר אובייקט Cat נוצר, יש להעביר לו מספר ומחרוזת, בסדר הזה. זו הסיבה שהקוד שלנו לא עובד. הקפד לזכור זאת ולזכור זאת בעת יצירת שיעורים משלך:
public Cat(String name, int age) {
   this.name = name;
   this.age = age;
}

public Cat(int age, String name) {
   this.age = age;
   this.name = name;
}
אלו שני מעצבים שונים לחלוטין! אם נביע את התשובה לשאלה "למה אנחנו צריכים בנאי?" במשפט אחד, נוכל לומר: כך שאובייקטים יהיו תמיד במצב הנכון. כאשר אתה משתמש בבנאים, כל המשתנים שלך יאותחלו כהלכה, ולא יהיו מכוניות עם מהירות 0 או אובייקטים "שגויים" אחרים בתוכנית. השימוש בהם מועיל מאוד, קודם כל, למתכנת עצמו. אם אתה מאתחל את השדות בעצמך, קיים סיכון גבוה לפספס משהו ולטעות. אבל זה לא יקרה עם בנאי: אם לא העברתם אליו את כל הטיעונים הנדרשים או ערבבתם את הסוגים שלהם, המהדר יזרוק מיד שגיאה. כדאי להזכיר בנפרד שאסור לשים את ההיגיון של התוכנית שלך בתוך הבנאי. לשם כך, עומדות לרשותך שיטות בהן תוכל לתאר את כל הפונקציונליות הדרושה לך. בואו נסתכל מדוע לוגיקה של בנאים היא רעיון רע:
public class CarFactory {

   String name;
   int age;
   int carsCount;

   public CarFactory(String name, int age, int carsCount) {
   this.name = name;
   this.age = age;
   this.carsCount = carsCount;

   System.out.println("Our car factory is called" + this.name);
   System.out.println("She was founded" + this.age + " years ago" );
   System.out.println("During this time it was produced" + this.carsCount +  "cars");
   System.out.println("On average she produces" + (this.carsCount/this.age) + "cars per year");
}

   public static void main(String[] args) {

       CarFactory ford = new CarFactory("Ford", 115 , 50000000);
   }
}
יש לנו כיתת CarFactory שמתארת ​​מפעל לייצור מכוניות. בתוך הקונסטרוקטור, אנו מאתחלים את כל השדות וממקמים את ההיגיון כאן: אנו מציגים מידע על המפעל לקונסולה. נראה שאין בזה שום דבר רע, התוכנית עבדה בצורה מושלמת. פלט מסוף:
מפעל הרכב שלנו נקרא פורד, הוא נוסד לפני 115 שנים, במהלך תקופה זו ייצר 50,000,000 מכוניות, בממוצע הוא מייצר 434,782 מכוניות בשנה.
אבל למעשה, השתלנו פצצת זמן. וקוד כזה יכול בקלות רבה להוביל לשגיאות. בואו נדמיין שעכשיו אנחנו לא מדברים על פורד, אלא על המפעל החדש "אמיגו מוטורס", ​​שקיים פחות משנה וייצר 1000 מכוניות:
public class CarFactory {

   String name;
   int age;
   int carsCount;

   public CarFactory(String name, int age, int carsCount) {
   this.name = name;
   this.age = age;
   this.carsCount = carsCount;

   System.out.println("Our car factory is called" + this.name);
   System.out.println("She was founded" + this.age + " years ago" );
   System.out.println("During this time it was produced" + this.carsCount +  "cars");
   System.out.println("On average she produces" + (this.carsCount/this.age) + "cars per year");
}


   public static void main(String[] args) {

       CarFactory ford = new CarFactory("Amigo Motors", 0 , 1000);
   }
}
פלט מסוף:
מפעל הרכב שלנו נקרא Amigo Motors Exception בשרשור "ראשי" java.lang.ArithmeticException: / by zero הוא נוסד לפני 0 שנים במהלך תקופה זו, הוא ייצר 1000 מכוניות ב-CarFactory.<init>(CarFactory.java:15) ב- CarFactory.main(CarFactory.java:23) התהליך הסתיים עם קוד יציאה 1</init>
הגענו! התוכנית הסתיימה באיזו שגיאה מוזרה. האם תנסה לנחש מה הסיבה? הסיבה היא ההיגיון שהצבנו בקונסטרוקטור. ספציפית, בשורה זו:
System.out.println("On average she produces" + (this.carsCount/this.age) + "cars per year");
כאן אנחנו עושים את החישוב ומחלקים את מספר המכוניות שיוצרו לפי גיל המפעל. ומכיוון שהמפעל שלנו חדש (כלומר, הוא בן 0 שנים), התוצאה היא חלוקה ב-0, מה שאסור במתמטיקה. כתוצאה מכך, התוכנית מסתיימת עם שגיאה. מה היינו צריכים לעשות? העבר את כל ההיגיון למתודה נפרדת וקרא לה, למשל, printFactoryInfo() . אתה יכול להעביר לו אובייקט CarFactory כפרמטר . אפשר גם לשים שם את כל ההיגיון, ובמקביל - לעבד שגיאות אפשריות, כמו שלנו עם אפס שנים. על טעם ועל ריח. דרושים בנאים כדי להגדיר נכון את המצב של אובייקט. להיגיון עסקי יש לנו שיטות. לא כדאי לערבב אחד עם השני.
הערות
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION