JavaRush /בלוג Java /Random-HE /מ-8 עד 13: סקירה מלאה של גרסאות Java. חלק 1

מ-8 עד 13: סקירה מלאה של גרסאות Java. חלק 1

פורסם בקבוצה
מ-8 עד 13: סקירה מלאה של גרסאות Java.  חלק 1 - 1בעבר, גרסאות חדשות שוחררו כל 3-5 שנים, אך לאחרונה אורקל נקטה בגישה אחרת - "ג'אווה חדשה כל שישה חודשים". וכך, כל שישה חודשים אנו רואים שחרור של תכונות. בין אם זה טוב או רע, כל אחד רואה את זה אחרת. לדוגמה, אני לא אוהב את זה מאוד, מכיוון שלגרסאות חדשות אין הרבה תכונות חדשות, אבל באותו זמן, גרסאות גדלות כמו פטריות אחרי גשם. מצמצתי כמה פעמים על פרויקט עם Java 8, ו-Java 16 כבר שוחרר (אבל כשהוא יוצא רק לעתים רחוקות, פיצ'רים חדשים מצטברים, ובסופו של דבר האירוע הזה מצפה, כמו חג: כולם דנים על דברים טובים חדשים ואי אפשר לעבור לידם). אז בואו נתחיל!

Java 8

ממשק פונקציונלי

מה זה? ממשק פונקציונלי הוא ממשק המכיל שיטה אחת לא מיושמת (מופשטת). @FunctionalInterface הוא הערה אופציונלית שממוקמת מעל ממשק כזה. צריך לבדוק האם הוא עומד בדרישות של ממשק פונקציונלי (בעל שיטה מופשטת אחת בלבד). אבל כמו תמיד, יש לנו כמה אזהרות: ברירת מחדל ושיטות סטטיות אינן נופלות תחת הדרישות הללו. לכן, יכולות להיות כמה שיטות כאלה + אחת מופשטת, והממשק יהיה פונקציונלי. הוא עשוי להכיל גם שיטות של מחלקת Object שאינן משפיעות על הגדרת הממשק כפונקציונלי. אוסיף כמה מילים על ברירת מחדל ושיטות סטטיות:
  1. שיטות עם שינוי ברירת המחדל מאפשרות לך להוסיף שיטות חדשות לממשקים מבלי לשבור את היישום הקיים שלהן.

    public interface Something {
      default void someMethod {
          System.out.println("Some text......");
      }
    }

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

  2. שיטות סטטיות בממשק עובדות כמו שיטות סטטיות במחלקה. אל תשכח: אתה לא יכול לרשת שיטות סטטיות , בדיוק כפי שאתה לא יכול לקרוא למתודה סטטית ממעמד צאצא.

אז, עוד כמה מילים על ממשקים פונקציונליים ובואו נמשיך הלאה. להלן הרשימות העיקריות של FIs (השאר הם הזנים שלהם):

    Predicate - לוקח ערך T כארגומנט, מחזיר בוליאני.

    דוגמא:boolean someMethod(T t);

  • צרכן - לוקח ארגומנט מסוג T, לא מחזיר כלום (בטל).

    דוגמא:void someMethod(T t);

  • ספק - לא לוקח כלום כקלט, אבל מחזיר ערך T.

    דוגמא:T someMethod();

  • פונקציה - לוקחת פרמטר מסוג T כקלט, מחזירה ערך מסוג R.

    דוגמא:R someMethod(T t);

  • UnaryOperator - לוקח ארגומנט T ומחזיר ערך מסוג T.

    דוגמא:T someMethod(T t);

זרם

זרמים הם דרך לטפל במבני נתונים בסגנון פונקציונלי. בדרך כלל מדובר באוספים (אבל אתה יכול להשתמש בהם במצבים אחרים, פחות שכיחים). בשפה מובנת יותר, Stream הוא זרם נתונים שאנו מעבדים כאילו עובדים עם כל הנתונים בו-זמנית, ולא כוח גס, כמו עם כל אחד. בואו נסתכל על דוגמה קטנה. נניח שיש לנו קבוצה של מספרים שאנו רוצים לסנן (פחות מ-50), להגדיל ב-5 ולהוציא את 4 המספרים הראשונים מהנותרים לקונסולה. איך היינו עושים את זה קודם:
List<Integer> list = Arrays.asList(46, 34, 24, 93, 91, 1, 34, 94);

int count = 0;

for (int x : list) {

  if (x >= 50) continue;

  x += 5;

  count++;

  if (count > 4) break;

  System.out.print(x);

}
נראה שאין הרבה קוד, וההיגיון כבר קצת מבלבל. בוא נראה איך זה ייראה באמצעות הזרם:
Stream.of(46, 34, 24, 93, 91, 1, 34, 94)

      .filter(x -> x < 50)

      .map(x -> x + 5)

      .limit(4)

      .forEach(System.out::print);
זרמים מפשטים מאוד את החיים על ידי הפחתת כמות הקוד והפיכתו לקריאה יותר. למי שרוצה להתעמק בנושא זה ביתר פירוט, הנה מאמר טוב (אפילו הייתי אומר מצוין) בנושא זה .

למדה

אולי התכונה החשובה והמיוחלת ביותר היא המראה של הלמבדות. מהי למבדה? זהו גוש קוד שניתן להעביר למקומות שונים כך שניתן להפעיל אותו מאוחר יותר כמה פעמים לפי הצורך. נשמע די מבלבל, לא? במילים פשוטות, באמצעות lambdas, אתה יכול ליישם שיטה של ​​ממשק פונקציונלי (מעין יישום של מחלקה אנונימית):
Runnable runnable = () -> { System.out.println("I'm running !");};

new Thread(runnable).start();
הטמענו את שיטת run() במהירות וללא ביורוקרטיה מיותרת. וכן: Runnable הוא ממשק פונקציונלי. אני משתמש גם בלמבדה כשעובדים עם זרמים (כמו בדוגמאות עם זרמים למעלה). לא נעמיק יותר מדי, מכיוון שאנו יכולים לצלול די עמוק, אשאיר כמה קישורים כדי שחבר'ה שעדיין חופרים בנשמה יוכלו לחפור עמוק יותר:

לכל אחד

ל-Java 8 יש foreach חדש שעובד עם זרם נתונים כמו זרם. הנה דוגמה:
List<Integer> someList = Arrays.asList(1, 3, 5, 7, 9);

someList.forEach(x -> System.out.println(x));
(אנלוגי ל-someList.stream().foreach(...))

התייחסות לשיטה

שיטות התייחסות הן תחביר חדש ושימושי שנועד להתייחס לשיטות קיימות או בנאים של מחלקות או אובייקטים של Java באמצעות :: הפניות לשיטות מגיעות בארבעה סוגים:
  1. קישור למעצב:

    SomeObject obj = SomeObject::new

  2. התייחסות לשיטה סטטית:

    SomeObject::someStaticMethod

  3. התייחסות לשיטה לא סטטית של אובייקט מסוג מסוים:

    SomeObject::someMethod

  4. הפניה לשיטה רגילה (לא סטטית) של אובייקט ספציפי

    obj::someMethod

לעתים קרובות, נעשה שימוש בהפניות שיטות בזרמים במקום בלמבדות (שיטות התייחסות מהירות יותר ממבדות, אך הן נחותות בקריאות).
someList.stream()

        .map(String::toUpperCase)

      .forEach(System.out::println);
למי שרוצה מידע נוסף על שיטות התייחסות:

זמן API

יש ספרייה חדשה לעבודה עם תאריכים ושעות - java.time. מ-8 עד 13: סקירה מלאה של גרסאות Java.  חלק 1 - 2ה-API החדש דומה לכל Joda-Time. החלקים המשמעותיים ביותר של ממשק API זה הם:
  • LocalDate הוא תאריך ספציפי, כדוגמה - 2010-01-09;
  • LocalTime - זמן תוך התחשבות באזור הזמן - 19:45:55 (בדומה ל-LocalDate);
  • LocalDateTime - משולבת LocalDate + LocalTime - 2020-01-04 15:37:47;
  • ZoneId - מייצג אזורי זמן;
  • שעון - באמצעות סוג זה ניתן לגשת לשעה ולתאריך הנוכחיים.
הנה כמה מאמרים מעניינים באמת בנושא זה:

אופציונאלי

זהו מחלקה חדשה בחבילת java.util , מעטפת ערך שהטריק שלה הוא שהוא יכול להכיל בבטחה גם null . קבלה אופציונלית: אם נעביר את nullOptional<String> someOptional = Optional.of("Something"); ב- Optional.of , נקבל את NullPointerException המועדף עלינו . עבור מקרים כאלה הם משתמשים ב: - בשיטה זו אתה לא צריך לפחד מבטל. לאחר מכן, צור ריק בהתחלה אופציונלי: כדי לבדוק אם הוא ריק, השתמש ב: יחזיר לנו true או false. בצע פעולה מסוימת אם יש ערך, ואל תעשה כלום אם אין ערך: שיטה הפוכה שמחזירה את הערך שעבר אם אופציונלי ריק (מעין תוכנית גיבוי): אתה יכול להמשיך הרבה מאוד זמן ( למרבה המזל, אופציונלי הוסיפה שיטות בשתי ידיים נדיבות), אבל לא נתעכב על זה. עדיף לי להשאיר כמה קישורים בתור התחלה: Optional<String> someOptional = Optional.ofNullable("Something");Optional<String> someOptional = Optional.empty();someOptional.isPresent();someOptional.ifPresent(System.out::println);System.out.println(someOptional.orElse("Some default content")); עברנו על החידושים המפורסמים ביותר ב-Java 8 - זה לא הכל. אם אתה רוצה לדעת יותר, השארתי לך את זה:

Java 9

אז, ב-21 בספטמבר 2017, העולם ראה את JDK 9. Java 9 זה מגיע עם סט עשיר של תכונות. למרות שאין מושגי שפה חדשים, ממשקי ה-API החדשים ופקודות האבחון בהחלט יעניינו מפתחים. מ-8 עד 13: סקירה מלאה של גרסאות Java.  חלק 1 - 4

JShell (REPL - read-eval-print loop)

זוהי יישום Java של קונסולה אינטראקטיבית המשמשת לבדיקת פונקציונליות ושימוש במבנים שונים במסוף, כגון ממשקים, מחלקות, רשימות, אופרטורים וכו'. כדי להפעיל את JShell, אתה רק צריך לכתוב jshell בטרמינל. אז נוכל לכתוב כל מה שהדמיון שלנו מאפשר: מ-8 עד 13: סקירה מלאה של גרסאות Java.  חלק 1 - 5באמצעות JShell, אתה יכול ליצור שיטות ברמה העליונה ולהשתמש בהן באותה סשן. השיטות יעבדו בדיוק כמו שיטות סטטיות, אלא שניתן להשמיט את מילת המפתח הסטטית . קרא עוד במדריך Java 9 REPL (JShell) .

פְּרָטִי

החל מגרסה 9 של Java, יש לנו הזדמנות להשתמש בשיטות פרטיות בממשקים (ברירת מחדל ושיטות סטטיות, מכיוון שאנחנו פשוט לא יכולים לעקוף אחרים בגלל גישה לא מספקת). private static void someMethod(){} try-with-resources שודרגה היכולת לטפל בחריגים ב-Try-With-Resources:
BufferedReader reader = new BufferedReader(new FileReader("....."));
  try (reader2) {
  ....
}

מודולריות ( פאזל )

מודול הוא קבוצה של חבילות ומשאבים קשורים יחד עם קובץ מתאר מודול חדש. גישה זו משמשת כדי לשחרר את הצימוד של הקוד. צימוד רופף הוא גורם מפתח לתחזוקה והרחבה של קוד. מודולריות מיושמת ברמות שונות:
  1. שפת תכנות.
  2. מכונה וירטואלית.
  3. API רגיל של Java.
JDK 9 מגיע עם 92 מודולים: אנחנו יכולים להשתמש בהם או ליצור משלנו. הנה כמה קישורים למבט מעמיק יותר:

אוסף בלתי משתנה

ב-Java 9, אפשר היה ליצור ולמלא אוסף בשורה אחת, תוך הפיכתו לבלתי ניתן לשינוי (בעבר, כדי ליצור אוסף בלתי ניתן לשינוי, היינו צריכים ליצור אוסף, למלא אותו בנתונים ולקרוא למתודה, למשל, Collections.unmodifiableList). דוגמה ליצירה כזו: List someList = List.of("first","second","third");

חידושים נוספים:

  • מורחב אופציונלי (נוספו שיטות חדשות);
  • נראה כי ממשקי ProcessHandle ו-ProcessHandle שולטים בפעולות מערכת ההפעלה;
  • G1 - אספן אשפה ברירת מחדל;
  • לקוח HTTP עם תמיכה גם בפרוטוקולי HTTP/2 וגם ב-WebSocket;
  • נחל מורחב;
  • נוספה מסגרת API של Reactive Streams (לתכנות ריאקטיבי);
להתעמקות מלאה יותר ב-Java 9, אני ממליץ לך לקרוא:

Java 10

אז, שישה חודשים לאחר שחרורו של Java 9, במרץ 2018 (אני זוכר את זה כמו אתמול), Java 10 הגיע למקום. מ-8 עד 13: סקירה מלאה של גרסאות Java.  חלק 1 - 6

var

כעת איננו צריכים לספק סוג נתונים. אנו מסמנים את ההודעה כ-var והמהדר קובע את סוג ההודעה לפי סוג האתחול הקיים בצד ימין. תכונה זו זמינה רק למשתנים מקומיים עם אתחול: לא ניתן להשתמש בה עבור ארגומנטים של מתודה, סוגי החזרה וכו', מכיוון שאין אתחול שיוכל להגדיר את הסוג. var דוגמה (עבור סוג מחרוזת):
var message = "Some message…..";
System.out.println(message);
var אינו מילת מפתח: הוא בעצם שם סוג שמור, בדיוק כמו int . היתרון של var הוא גדול: הצהרות סוג תופסות תשומת לב רבה מבלי להביא שום תועלת, ותכונה זו תחסוך זמן. אבל יחד עם זאת, אם משתנה מתקבל משרשרת ארוכה של שיטות, הקוד הופך פחות קריא, מכיוון שמיד לא ברור איזה סוג של אובייקט נמצא שם. מוקדש למי שרוצה להכיר יותר את הפונקציונליות הזו:

מהדר JIT (GraalVM)

בלי להכביר מילים, הרשו לי להזכיר לכם שכאשר אתם מפעילים את פקודת javac, אפליקציית Java מורכבת מקוד Java לתוך JVM bytecode, שהוא הייצוג הבינארי של האפליקציה. אבל מעבד מחשב רגיל לא יכול פשוט להפעיל את קוד הבתים של JVM. כדי שתוכנית ה-JVM שלך תעבוד, אתה צריך מהדר נוסף עבור קוד הבתים הזה, המומר לקוד מכונה שהמעבד כבר מסוגל להשתמש בו. בהשוואה ל-javac, מהדר זה מורכב הרבה יותר, אך גם מייצר קוד מכונה באיכות גבוהה יותר. נכון לעכשיו, OpenJDK מכיל את המכונה הוירטואלית HotSpot, אשר בתורה כוללת שני מהדרים עיקריים של JIT. הראשון, C1 ( מהדר לקוח ), מיועד לפעולה במהירות גבוהה יותר, אך מיטוב הקוד סובל. השני הוא C2 (מהדר שרת). מהירות הביצוע סובלת, אבל הקוד מותאם יותר. מתי משתמשים באיזה? C1 נהדר עבור יישומי שולחן עבודה שבהם הפסקות JIT ארוכות אינן רצויות, ו-C2 מצוין עבור תוכניות שרת ארוכות טווח שבהן הוצאות יותר זמן על קומפילציה הן די נסבלות. קומפילציה מרובת רמות היא כאשר ההידור עובר לראשונה דרך C1, והתוצאה עוברת דרך C2 (המשמש לאופטימיזציה גדולה יותר). GraalVM הוא פרויקט שנוצר כדי להחליף לחלוטין את HotSpot. אנחנו יכולים לחשוב על Graal כעל כמה פרויקטים קשורים: מהדר JIT חדש עבור HotSpot ומכונה וירטואלית פוליגלוטית חדשה. הייחודיות של מהדר JIT זה היא שהוא כתוב בג'אווה. היתרון של מהדר Graal הוא בטיחות, כלומר לא קריסות, אלא חריגים, לא דליפות זיכרון. תהיה לנו גם תמיכת IDE טובה, ונוכל להשתמש באגרים, פרופילים או כלים נוחים אחרים. בנוסף, המהדר עשוי בהחלט להיות בלתי תלוי ב-HotSpot, והוא יוכל ליצור גרסה מהירה יותר של הידור JIT של עצמו. לחופרים:

מקביל G1

אספן האשפה G1 בהחלט מגניב, אין ספק בכך, אבל יש לו גם נקודת תורפה: הוא מבצע מחזור GC מלא עם חוט יחיד. בזמן שאתה צריך את כל הכוח של החומרה שאתה יכול לגייס כדי למצוא חפצים שאינם בשימוש, אנו מוגבלים לחוט בודד. ג'אווה 10 תיקנה את זה. כעת ה-GC עובד כעת עם כל המשאבים שאנו מוסיפים לו (כלומר, הוא הופך לרב-פתיל). כדי להשיג זאת, מפתחי השפה שיפרו את הבידוד של המקורות העיקריים מ-GC, ויצרו ממשק נקי ויפה עבור GC. המפתחים של החמודות הזו, OpenJDK, היו צריכים לנקות באופן ספציפי את ה-dump בקוד כדי לא רק לפשט את היצירה של GCs חדשים ככל האפשר, אלא גם כדי לאפשר במהירות להשבית GCs מיותרים מהמכלול. אחד הקריטריונים העיקריים להצלחה הוא היעדר ירידה במהירות ההפעלה לאחר כל השיפורים הללו. בואו נסתכל גם: חידושים נוספים:
  1. מוצג ממשק אשפה נקי. זה משפר את הבידוד של קוד המקור מאוספני אשפה שונים, ומאפשר לשלב אספנים חלופיים במהירות וללא כאבים;
  2. שילוב מקורות JDK למאגר אחד;
  3. אוספים קיבלו שיטה חדשה - copyOf (Collection) , המחזירה עותק בלתי ניתן לשינוי של אוסף זה;
  4. לאופציונלי (וגרסאותיו) יש שיטה חדשה .orElseThrow() ;
  5. מעתה ואילך, JVMs מודעים לכך שהם פועלים בקונטיינר Docker ויאחזרו תצורה ספציפית לקונטיינר במקום לבצע שאילתות על מערכת ההפעלה עצמה.
הנה כמה חומרים נוספים למבוא מפורט יותר ל-Java 10: פעם הייתי מאוד מבולבל מהעובדה שחלק מהגרסאות של Java נקראו 1.x. ברצוני להבהיר: לגרסאות Java לפני 9 פשוט הייתה ערכת שמות שונה. לדוגמה, ניתן לקרוא ל-Java 8 גם 1.8 , Java 5 - 1.5 וכו'. ועכשיו אנו רואים שעם המעבר למהדורות מ-Java 9, גם סכימת השמות השתנתה, וגרסאות Java כבר אינן קידומת 1.x . זהו הסוף של החלק הראשון: עברנו על המאפיינים המעניינים החדשים של Java 8-10. בואו נמשיך את ההיכרות שלנו עם האחרון בפוסט הבא .
הערות
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION