17. תן דוגמאות לשימוש מוצלח ולא מוצלח באופציונלי
נניח שיש לנו סדרה מסוימת של ערכים שדרכה אנו עוברים בזרם, ובסופו של דבר נקבל כמה אופציונליים כתוצאה מכך:Optional<String> stringOptional = Stream.of("a", "ab", "abc", "abcd")
.filter(str -> str.length() >= 3)
.findAny();
אנחנו, כצפוי, צריכים לקבל את הערך מהאופציונלי הזה . רק שימוש ב-get() הוא דרך גרועה:
String result = stringOptional.get();
אבל השיטה הזו אמורה לקבל את הערך מ- Optional ולהחזיר לנו אותו? זה, כמובן, נכון, אבל אם יש לזה משמעות. ובכן, אם הערכים בזרם היו שונים, ובסופו של דבר קיבלנו ריק אופציונלי , כשאנחנו מנסים לקחת ממנו ערך בשיטת get() ייזרקו הדברים הבאים: וזה לא טוב. במקרה זה, עדיף להשתמש במבנים הבאים:
-
String result = null; if (stringOptional.isPresent()) { stringOptional.get(); }
במקרה זה, אנו בודקים אם הרכיב נמצא ב- Optional . אם לא, למחרוזת המתקבלת יש את הערך הישן שלה.
-
String result = stringOptional.orElse("default value");
במקרה זה, אנו מציינים ערך ברירת מחדל כלשהו, שיינתן למחרוזת המתקבלת במקרה של אופציונלי ריק .
-
String result = stringOptional.orElseThrow(() -> new CustomException());
במקרה זה, אנו בעצמנו זורקים חריג כאשר אופציונלי ריק .
18. האם ניתן להכריז על שיטה עיקרית כסופית?
כן, כמובן, שום דבר לא מונע מאיתנו להכריז על השיטה main( ) כסופית . המהדר לא יפיק שגיאות. אבל כדאי לזכור שכל שיטה לאחר הכרזתה כסופית תהפוך לשיטה האחרונה - לא תידחה. אמנם, מי יגדיר מחדש את ראשי ???19. האם ניתן לייבא את אותה חבילה/מחלקה פעמיים? מה יכולות להיות ההשלכות?
כן אתה יכול. השלכות? יהיו לנו כמה יבוא מיותר ש-Intelijj IDEA יציג כאפור, כלומר. לא בשימוש.20. מה זה ליהוק? מתי נוכל לקבל ClassCastException?
ליהוק, או ליהוק סוג , הוא תהליך של המרת סוג נתונים אחד לסוג נתונים אחר: ידנית (הליהקה מרומזת) או אוטומטית (הליהוק מסוג מפורש). המרה אוטומטית מתבצעת על ידי המהדר, והמרה ידנית מבוצעת על ידי המפתח. ליהוק סוג עבור פרימיטיבים ומעמדות שונה במקצת, אז נשקול אותם בנפרד. טיפוסים פרימיטיביים דוגמה ליציקה אוטומטית של טיפוסים פרימיטיביים:int value = 17;
double convertedValue = value;
כפי שאתה יכול לראות, אין צורך במניפולציות נוספות מלבד הסימן = כאן. דוגמה ליציקה ידנית של טיפוסים פרימיטיביים:
double value = 17.89;
int convertedValue = (int)value;
במקרה זה, אנו יכולים לראות cast ידני, אשר מיושם באמצעות (int) , לפיו החלק שאחרי הפסיק יימחק וה- convertedValue יהיה בעל ערך של - 17. קרא עוד על ליהוק טיפוסים פרימיטיביים במאמר זה . ובכן, עכשיו בואו נעבור לחפצים. סוגי התייחסות עבור סוגי התייחסות, הליהוק אוטומטי אפשרי עבור כיתות צאצאות לכיתות אב. זה נקרא גם פולימורפיזם . נניח שיש לנו כיתת אריה שיורשת מכיתה חתול . במקרה זה, ההמרה האוטומטית תיראה כך:
Cat cat = new Lion();
אבל עם צוות מפורש , הכל קצת יותר מסובך, כי אין פונקציונליות לחתוך עודפים, כמו עם פרימיטיביים. ורק לעשות המרה מפורשת של הטופס:
Lion lion= (Lion)new Cat();
תקבל שגיאה: למעשה, אתה יכול להוסיף שיטות למחלקה צאצא אריה שלא היו במקור במחלקה Cat , ולאחר מכן לנסות לקרוא להן, כי סוג האובייקט שלך יהפוך לאריה . ובכן, אין בזה שום היגיון. לכן, צמצום הסוג אפשרי רק כאשר האובייקט המקורי היה מסוג אריה , אך מאוחר יותר יצוק למחלקת אב:
Lion lion = new Lion();
Cat cat = lion;
Lion newLion = (Lion)cat;
כמו כן, לאמינות רבה יותר, מומלץ לצמצם יציקה עבור אובייקטים באמצעות המבנה instanceOf :
if (cat instanceof Lion) {
newLion = (Lion)new Cat();
}
קרא עוד על יציאות של סוג התייחסות במאמר זה .
21. מדוע מסגרות מודרניות משתמשות בעיקר בחריגים לא מסומנים?
אני חושב שהכל בגלל שטיפול בחריגים שנבדקו הוא עדיין קוד ספגטי שחוזר על עצמו בכל מקום, אבל לא באמת נחוץ בכל המקרים. במקרים כאלה, קל יותר לבצע את העיבוד בתוך המסגרת, כדי לא להעביר זאת שוב על כתפי המפתחים. כן, כמובן, עלול להיווצר מצב חירום, אבל ניתן לטפל באותם חריגים לא מסומנים בצורה נוחה יותר, מבלי להתעסק בעיבוד ב- Try-catch ובלי להעביר אותם הלאה בשיטות. זה מספיק רק להמיר את החריג לתגובת HTTP כלשהי ב- exceptionHandler .22. מהו יבוא סטטי?
כשמשתמשים בנתונים סטטיים (שיטות, משתנים), לא ניתן ליצור את האובייקט עצמו, אלא לעשות זאת לפי שם המחלקה, אבל גם במקרה הזה אנחנו צריכים קישור למחלקה. הכל פשוט איתו: הוא מתווסף באמצעות ייבוא רגיל. אבל מה אם נלך להשתמש בשיטה סטטית בלי לכתוב את שם המחלקה, כאילו זו שיטה סטטית של המחלקה הנוכחית? זה אפשרי עם יבוא סטטי! במקרה זה, עלינו לכתוב ייבוא סטטי וקישור לשיטה זו. כמו זו, למשל, שיטה סטטית של המחלקה Math לחישוב ערך הקוסינוס:import static java.lang.Math.cos;
כתוצאה מכך, אנו יכולים להשתמש בשיטה מבלי לציין את שם המחלקה:
double result = cos(60);
אנחנו יכולים גם פשוט לטעון את כל השיטות הסטטיות של מחלקה בבת אחת באמצעות ייבוא סטטי:
import static java.lang.Math.*;
23. מה הקשר בין שיטות hashCode() ל-equals()?
לפי אורקל , הכלל הוא: אם שני אובייקטים שווים (כלומר השיטה equals() מחזירה true ), עליהם להיות בעל אותו קוד hash. יחד עם זאת, אל תשכח שלשני אובייקטים שונים יכול להיות אותו קוד hash. כדי להבין מדוע equals() ו- hashCode() נדחפים תמיד בזוגות, שקול את המקרים הבאים:-
שתי השיטות מבוטלות.
במקרה זה, שני אובייקטים שונים עם אותם מצבים פנימיים יחזירו equals() - true , בעוד hashCode() יחזירו שניהם את אותו מספר.
מסתבר שהכל בסדר, כי מקיימים את הכלל.
-
שתי השיטות אינן מבוטלות.
במקרה זה, שני אובייקטים שונים עם אותם מצבים פנימיים יחזירו false כאשר equals() , מכיוון שההשוואה היא באמצעות הפניה דרך האופרטור == .
שיטת hashCode() תחזיר גם ערכים שונים (סביר להניח) מכיוון שהיא מייצרת את הערך המומר של כתובת מיקום הזיכרון. אבל עבור אותו אובייקט הערך הזה יהיה זהה, בדיוק כפי ש- equals() במקרה זה יחזיר true רק כאשר ההפניות מצביעות על אותו אובייקט.
מסתבר שבמקרה הזה הכל בסדר והכלל מתקיים.
-
עוקף שווה() , לא נדחק hashCode() .
במקרה זה, עבור שני אובייקטים שונים עם אותם מצבים פנימיים, equals() יחזיר true , ו- hashCode() יחזיר (ככל הנראה) ערכים שונים.
זוהי עבירה על הכלל, ולכן לא מומלץ לעשות זאת.
-
equals() אינו מוחלף , hashCode() מוחלף .
במקרה זה, עבור שני אובייקטים שונים עם אותם מצבים פנימיים, equals() יחזיר false ו- hashCode() יחזיר את אותם ערכים.
יש הפרה של הכלל, ולכן הגישה לא נכונה.
24. מתי משתמשים במחלקות BufferedInputStream ו- BufferedOutputStream?
InputStream משמש לקריאת נתונים בייט אחר בייט ממשאב כלשהו, ו- OutputStream משמש לכתיבת נתונים בייט אחר בייט. אבל פעולות בייט-byte יכולות להיות מאוד לא נוחות ודורשות עיבוד נוסף (כדי לקרוא/לכתוב טקסטים כרגיל). למעשה, כדי לפשט רשומות בתים כאלה, BufferedOutputStream הוצג , ו- BufferedInputStream הוצג לקריאה . מחלקות אלו אינן אלא מאגרים שצוברים נתונים, ומאפשרים לך לעבוד עם נתונים לא בייט אחר בייט, אלא לפי חבילות נתונים שלמות (מערכים). כאשר נוצר, BufferedInputStream לוקח לבנאי שלו מופע מסוג InputStream , שממנו קוראים נתונים:BufferedInputStream bufferedInputStream = new BufferedInputStream(System.in);
byte[] arr = new byte[100];
bufferedInputStream.read(arr);
System.in הוא אובייקט InputStream שקורא נתונים מהמסוף. כלומר, באמצעות אובייקט BufferedInputStream זה , אנו יכולים לקרוא נתונים מה- InputStream על ידי כתיבתם למערך המועבר. מסתבר שזה סוג של מעטפת של מחלקה InputStream . מערך המערך מדוגמה זו הוא המערך שמקבל נתונים מ- BufferedInputStream . זה, בתורו, קורא נתונים מה- InputStream עם מערך אחר, אשר כברירת מחדל יש לו גודל של 2048 בתים. הדבר נכון גם לגבי BufferedOutputStream : יש להעביר מופע מהסוג OutputStream לבנאי , אליו נכתוב נתונים במערכים שלמים:
byte[] arr = "Hello world!!!".getBytes();
BufferedOutputStream bufferedInputStream = new BufferedOutputStream(System.out);
bufferedInputStream.write(arr);
bufferedInputStream.flush();
System.out הוא אובייקט OutputStream שכותב נתונים למסוף. השיטה flush() שולחת נתונים מה- BufferedOutputStream ל- OutputStream , תוך שטיפת ה-BufferedOutputStream בתהליך . ללא שיטה זו, שום דבר לא יירשם. ובדומה לדוגמא הקודמת: arr הוא המערך שממנו נכתבים נתונים ל- BufferedOutputStream . משם הם נכתבים ל- OutputStream במערך אחר, אשר כברירת מחדל יש לו גודל של 512 בתים. קרא עוד על שני השיעורים הללו במאמר .
25. מה ההבדל בין שיעורי java.util.Collection ל-java.util.Collections?
אוסף הוא ממשק שהוא ראש היררכיית האוסף. הוא מציג מחלקות המאפשרות לך ליצור, להכיל ולשנות קבוצות שלמות של אובייקטים. קיימות שיטות רבות לכך, כמו add() , remove() , contains() ואחרות. ממשקים עיקריים של מחלקת Collection :-
קבוצה היא ממשק המתאר קבוצה המכילה אלמנטים ייחודיים לא מסודרים (לא חוזרים).
-
רשימה היא ממשק המתאר מבנה נתונים המאחסן רצף מסודר של אובייקטים. אובייקטים אלו מקבלים אינדקס (מספר) משלהם, באמצעותו ניתן ליצור איתם אינטראקציה: קח, מחק, שנה, החלף.
-
Queue הוא ממשק המתאר מבנה נתונים עם רכיבי אחסון בצורת תור העוקב אחר הכלל - FIFO - First In First Out .
-
addAll(Collection<? super T> collection, T...element) - מוסיף את האלמנטים שעברו מסוג T לאוסף .
-
copy(List<? super T> dest, List<? extends T> src) - מעתיק את כל האלמנטים מהרשימה src לרשימה ב- dest .
-
emptyList() - מחזיר רשימה ריקה.
-
max(Collection<? extends T> collection, Comparator<? super T> comp) - מחזירה את הרכיב המקסימלי של אוסף נתון לפי הסדר שצוין על ידי המשווה שצוין.
-
unmodifiableList(List<? extends T> list) - מחזיר ייצוג בלתי ניתן לשינוי של הרשימה שעברה.
GO TO FULL VERSION