CodeGym /בלוג Java /Random-HE /50 השאלות והתשובות המובילות לראיונות עבודה עבור Java Core...
John Squirrels
רָמָה
San Francisco

50 השאלות והתשובות המובילות לראיונות עבודה עבור Java Core. חלק 2

פורסם בקבוצה
50 השאלות והתשובות המובילות לראיונות עבודה עבור Java Core. חלק 1 50 השאלות והתשובות המובילות לראיונות עבודה עבור Java Core.  חלק 2 - 1

ריבוי השרשורים

24. איך אני יוצר שרשור חדש ב-Java?

בדרך זו או אחרת, שרשור נוצר באמצעות המחלקה Thread. אבל ישנן דרכים שונות לעשות זאת...
  1. ירש את java.lang.Thread .
  2. הטמיע את ממשק java.lang.Runnable - הבנאי של המחלקה Thread לוקח אובייקט Runnable.
בואו נדבר על כל אחד מהם.

ירשו את הכיתה Thread

במקרה זה, אנו גורמים לכיתה שלנו לרשת את java.lang.Thread . יש לו שיטת run() וזה בדיוק מה שאנחנו צריכים. כל החיים וההיגיון של החוט החדש יהיו בשיטה זו. זה כמו שיטה עיקרית לשרשור החדש. לאחר מכן, כל מה שנותר הוא ליצור אובייקט של המחלקה שלנו ולקרוא למתודה start() . זה יצור שרשור חדש ויתחיל לבצע את ההיגיון שלו. בואו נסתכל:
/**
* An example of how to create threads by inheriting the {@link Thread} class.
*/
class ThreadInheritance extends Thread {

   @Override
   public void run() {
       System.out.println(Thread.currentThread().getName());
   }

   public static void main(String[] args) {
       ThreadInheritance threadInheritance1 = new ThreadInheritance();
       ThreadInheritance threadInheritance2 = new ThreadInheritance();
       ThreadInheritance threadInheritance3 = new ThreadInheritance();
       threadInheritance1.start();
       threadInheritance2.start();
       threadInheritance3.start();
   }
}
פלט הקונסולה יהיה בערך כך:
שרשור-1 חוט-0 חוט-2
כלומר, גם כאן אנו רואים שהשרשורים מבוצעים לא לפי הסדר, אלא כפי שה-JVM מוצא לנכון להפעיל אותם :)

הטמע את ממשק Runnable

אם אתה נגד ירושה ו/או כבר יורשת מחלקה אחרת, אתה יכול להשתמש בממשק java.lang.Runnable . כאן, אנו גורמים לכיתה שלנו ליישם את הממשק הזה על ידי הטמעת שיטת run() בדיוק כמו בדוגמה למעלה. כל מה שנותר הוא ליצור אובייקטי Thread . נראה שיותר שורות קוד גרועות יותר. אבל אנחנו יודעים כמה תורשה מזיקה ושעדיף להימנע ממנה בכל האמצעים ;) תסתכל:
/**
* An example of how to create threads from the {@link Runnable} interface.
* It's easier than easy — we implement this interface and then pass an instance of our object
* to the constructor.
*/
class ThreadInheritance implements Runnable {

   @Override
   public void run() {
       System.out.println(Thread.currentThread().getName());
   }

   public static void main(String[] args) {
       ThreadInheritance runnable1 = new ThreadInheritance();
       ThreadInheritance runnable2 = new ThreadInheritance();
       ThreadInheritance runnable3 = new ThreadInheritance();

       Thread threadRunnable1 = new Thread(runnable1);
       Thread threadRunnable2 = new Thread(runnable2);
       Thread threadRunnable3 = new Thread(runnable3);

       threadRunnable1.start();
       threadRunnable2.start();
       threadRunnable3.start();
   }
}
והנה התוצאה:
שרשור-0 חוט-1 חוט-2

25. מה ההבדל בין תהליך לשרשור?

50 השאלות והתשובות המובילות לראיונות עבודה עבור Java Core.  חלק 2 - 2תהליך וחוט שונים באופנים הבאים:
  1. תוכנית פועלת נקראת תהליך, אבל חוט הוא חלק מתהליך.
  2. תהליכים הם עצמאיים, אבל חוטים הם חלקים של תהליך.
  3. לתהליכים יש מרחבי כתובות שונים בזיכרון, אך שרשורים חולקים מרחב כתובות משותף.
  4. מעבר הקשר בין שרשורים מהיר יותר ממעבר בין תהליכים.
  5. תקשורת בין תהליכים איטית ויקרה יותר מתקשורת בין חוטים.
  6. כל שינוי בתהליך אב אינו משפיע על תהליך צאצא, אך שינויים בשרשור אב יכולים להשפיע על שרשור צאצא.

26. מהם היתרונות של ריבוי הליכים?

  1. ריבוי השרשורים מאפשר לאפליקציה/תוכנית להגיב תמיד לקלט, גם אם הוא כבר מפעיל כמה משימות רקע;
  2. ריבוי השחלות מאפשר להשלים משימות מהר יותר, מכיוון שרשורים פועלים באופן עצמאי;
  3. ריבוי השחלות מספק שימוש טוב יותר בזיכרון המטמון, מכיוון שהשרשורים יכולים לגשת למשאבי זיכרון משותפים;
  4. ריבוי השרשורים מפחית את מספר השרתים הנדרשים, מכיוון ששרת אחד יכול להריץ שרשורים מרובים בו זמנית.

27. מהם המצבים במחזור החיים של חוט?

50 השאלות והתשובות המובילות לראיונות עבודה עבור Java Core.  חלק 2 - 3
  1. חדש: במצב זה, אובייקט Thread נוצר באמצעות האופרטור החדש, אך שרשור חדש אינו קיים עדיין. השרשור לא מתחיל עד שנקרא לשיטת start() .
  2. ניתן להרצה: במצב זה, השרשור מוכן להפעלה לאחר ההתחלה() שיטה נקראת. עם זאת, הוא עדיין לא נבחר על ידי מתזמן השרשור.
  3. פועל: במצב זה, מתזמן השרשור בוחר שרשור ממצב מוכן, והוא פועל.
  4. ממתין/חסום: במצב זה שרשור אינו פועל, אך הוא עדיין חי או ממתין להשלמת שרשור אחר.
  5. Dead/Terminated: כאשר שרשור יוצא משיטת run() הוא במצב מת או הסתיים.

28. האם אפשר להריץ שרשור פעמיים?

לא, אנחנו לא יכולים להפעיל מחדש שרשור, כי לאחר שרשור מתחיל ופועל, הוא עובר למצב מת. אם אכן ננסה לפתוח שרשור פעמיים, ייזרק java.lang.IllegalThreadStateException . בואו נסתכל:
class DoubleStartThreadExample extends Thread {

   /**
    * Simulate the work of a thread
    */
   public void run() {
	// Something happens. At this state, this is not essential.
   }

   /**
    * Start the thread twice
    */
   public static void main(String[] args) {
       DoubleStartThreadExample doubleStartThreadExample = new DoubleStartThreadExample();
       doubleStartThreadExample.start();
       doubleStartThreadExample.start();
   }
}
יהיה חריג ברגע שהביצוע יגיע להתחלה השנייה של אותו שרשור. נסה את זה בעצמך ;) עדיף לראות את זה פעם אחת מאשר לשמוע על זה מאה פעמים.

29. מה אם אתה קורא ל-run() ישירות מבלי לקרוא ל-start()?

כן, אתה בהחלט יכול לקרוא למתודה run() אבל לא ייווצר שרשור חדש, והשיטה לא תפעל על שרשור נפרד. במקרה זה, יש לנו אובייקט רגיל הקורא שיטה רגילה. אם אנחנו מדברים על שיטת start() אז זה כבר עניין אחר. כאשר קוראים לשיטה זו, ה- JVM מתחיל שרשור חדש. השרשור הזה, בתורו, קורא לשיטה שלנו ;) לא מאמין? הנה, נסה את זה:
class ThreadCallRunExample extends Thread {

   public void run() {
       for (int i = 0; i < 5; i++) {
           System.out.print(i);
       }
   }

   public static void main(String args[]) {
       ThreadCallRunExample runExample1 = new ThreadCallRunExample();
       ThreadCallRunExample runExample2 = new ThreadCallRunExample();

       // Two ordinary methods will be called in the main thread, one after the other.
       runExample1.run();
       runExample2.run();
   }
}
ופלט הקונסולה ייראה כך:
0123401234
כפי שאתה יכול לראות, לא נוצר שרשור. הכל עבד בדיוק כמו בכיתה רגילה. ראשית, השיטה של ​​האובייקט הראשון בוצעה, ולאחר מכן השניה.

30. מהו שרשור דמון?

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

מדוע Java מפסיקה שרשור דמון?

מטרתו היחידה של שרשור הדמון היא לספק תמיכה ברקע לשרשור של משתמש. בהתאם לכך, אם השרשור הראשי מסתיים, אז ה-JVM מסיים אוטומטית את כל שרשורי הדמון שלו.

שיטות של כיתת Thread

המחלקה java.lang.Thread מספקת שתי שיטות לעבודה עם שרשור דמון:
  1. public void setDaemon(סטטוס בוליאני) - שיטה זו מציינת אם זה יהיה שרשור דמון. ברירת המחדל היא שקר . זה אומר שלא ייווצרו שרשורי דמון אלא אם כן תאמר זאת במפורש.
  2. public boolean isDaemon() - שיטה זו היא בעצם משבר עבור משתנה daemon , אותו הגדרנו באמצעות השיטה הקודמת.
דוגמא:
class DaemonThreadExample extends Thread {

   public void run() {
       // Checks whether this thread is a daemon
       if (Thread.currentThread().isDaemon()) {
           System.out.println("daemon thread");
       } else {
           System.out.println("user thread");
       }
   }

   public static void main(String[] args) {
       DaemonThreadExample thread1 = new DaemonThreadExample();
       DaemonThreadExample thread2 = new DaemonThreadExample();
       DaemonThreadExample thread3 = new DaemonThreadExample();

       // Make thread1 a daemon thread.
       thread1.setDaemon(true);

       System.out.println("daemon? " + thread1.isDaemon());
       System.out.println("daemon? " + thread2.isDaemon());
       System.out.println("daemon? " + thread3.isDaemon());

       thread1.start();
       thread2.start();
       thread3.start();
   }
}
פלט מסוף:
דמון? דמון אמיתי? דמון שקר? Fase daemon שרשור משתמש שרשור משתמש
מהפלט, אנו רואים שבתוך השרשור עצמו, אנו יכולים להשתמש בשיטת currentThread() הסטטית כדי לגלות באיזה חוט מדובר. לחילופין, אם יש לנו הפניה לאובייקט השרשור, נוכל גם לברר ממנו ישירות. זה מספק את רמת התצורה הדרושה.

31. האם ניתן להפוך שרשור לדמון לאחר יצירתו?

לא. אם תנסה לעשות זאת, תקבל הודעה מסוג IllegalThreadStateException . זה אומר שאנחנו יכולים ליצור שרשור דמון רק לפני שהוא מתחיל. דוגמא:
class SetDaemonAfterStartExample extends Thread {

   public void run() {
       System.out.println("Working...");
   }

   public static void main(String[] args) {
       SetDaemonAfterStartExample afterStartExample = new SetDaemonAfterStartExample();
       afterStartExample.start();

       // An exception will be thrown here
       afterStartExample.setDaemon(true);
   }
}
פלט מסוף:
עובד... חריגה בשרשור "main" java.lang.IllegalThreadStateException ב-java.lang.Thread.setDaemon(Thread.java:1359) ב-SetDaemonAfterStartExample.main(SetDaemonAfterStartExample.java:14)

32. מהו וו כיבוי?

וו כיבוי הוא חוט שנקרא באופן מרומז לפני כיבוי המחשב הווירטואלי של Java (JVM). לפיכך, אנו יכולים להשתמש בו כדי לשחרר משאב או מצב שמירה כאשר המחשב הוירטואלי של Java נכבה באופן רגיל או חריג. אנו יכולים להוסיף וו כיבוי בשיטה הבאה:
Runtime.getRuntime().addShutdownHook(new ShutdownHookThreadExample());
כפי שמוצג בדוגמה:
/**
* A program that shows how to start a shutdown hook thread,
* which will be executed right before the JVM shuts down
*/
class ShutdownHookThreadExample extends Thread {

   public void run() {
       System.out.println("shutdown hook executed");
   }

   public static void main(String[] args) {

       Runtime.getRuntime().addShutdownHook(new ShutdownHookThreadExample());

       System.out.println("Now the program is going to fall asleep. Press Ctrl+C to terminate it.");
       try {
           Thread.sleep(60000);
       } catch (InterruptedException e) {
           e.printStackTrace();
       }
   }
}
פלט מסוף:
עכשיו התוכנית הולכת להירדם. הקש Ctrl+C כדי לסיים אותו. וו כיבוי בוצע

33. מהו סנכרון?

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

סנכרון שיטה

שיטה מסונכרנת משמשת לנעילת אובייקט עבור כל משאב משותף. כאשר שרשור קורא לשיטה מסונכרנת, הוא רוכש אוטומטית את נעילת האובייקט ומשחרר אותה כאשר השרשור משלים את המשימה שלו. כדי לגרום לזה לעבוד, עליך להוסיף את מילת המפתח המסונכרנת . אנחנו יכולים לראות איך זה עובד על ידי הסתכלות על דוגמה:
/**
* An example where we synchronize a method. That is, we add the synchronized keyword to it.
* There are two authors who want to use one printer. Each of them has composed their own poems
* And of course they don’t want their poems mixed up. Instead, they want work to be performed in * * * order for each of them
*/
class Printer {

   synchronized void print(List<String> wordsToPrint) {
       wordsToPrint.forEach(System.out::print);
       System.out.println();
   }

   public static void main(String args[]) {
       // One object for two threads
       Printer printer  = new Printer();

       // Create two threads
       Writer1 writer1 = new Writer1(printer);
       Writer2 writer2 = new Writer2(printer);

       // Start them
       writer1.start();
       writer2.start();
   }
}

/**
* Author No. 1, who writes an original poem.
*/
class Writer1 extends Thread {
   Printer printer;

   Writer1(Printer printer) {
       this.printer = printer;
   }

   public void run() {
       List<string> poem = Arrays.asList("I ", this.getName(), " Write", " A Letter");
       printer.print(poem);
   }

}

/**
* Author No. 2, who writes an original poem.
*/
class Writer2 extends Thread {
   Printer printer;

   Writer2(Printer printer) {
       this.printer = printer;
   }

   public void run() {
       List<String> poem = Arrays.asList("I Do Not ", this.getName(), " Not Write", " No Letter");
       printer.print(poem);
   }
}
ופלט הקונסולה הוא זה:
אני שרשור-0 כותב מכתב אני לא חוט-1 לא כותב שום אות

בלוק סנכרון

ניתן להשתמש בבלוק מסונכרן לביצוע סנכרון על כל משאב מסוים בשיטה. נניח שבשיטה גדולה (כן, לא כדאי לכתוב אותם, אבל לפעמים הם קורים) צריך מסיבה כלשהי לסנכרן רק קטע קטן. אם תשים את כל הקוד של השיטה בבלוק מסונכרן, זה יעבוד כמו שיטה מסונכרנת. התחביר נראה כך:
synchronized ("object to be locked") {
   // The code that must be protected
}
כדי להימנע מלחזור על הדוגמה הקודמת, ניצור שרשורים באמצעות מחלקות אנונימיות, כלומר ניישם מיד את ממשק Runnable.
/**
* This is how a synchronization block is added.
* Inside the block, you need to specify which object's mutex will be acquired.
*/
class Printer {

   void print(List<String> wordsToPrint) {
       synchronized (this) {
           wordsToPrint.forEach(System.out::print);
       }
       System.out.println();
   }

   public static void main(String args[]) {
       // One object for two threads
       Printer printer = new Printer();

       // Create two threads
       Thread writer1 = new Thread(new Runnable() {
           @Override
           public void run() {
               List<String> poem = Arrays.asList("I ", "Writer1", " Write", " A Letter");
               printer.print(poem);
           }
       });
       Thread writer2 = new Thread(new Runnable() {
           @Override
           public void run() {
               List<String> poem = Arrays.asList("I Do Not ", "Writer2", " Not Write", " No Letter");
               printer.print(poem);
           }
       });

       // Start them
       writer1.start();
       writer2.start();
   }
}

}
ופלט הקונסולה הוא זה:
אני כותב1 כותב מכתב אני לא כותב2 לא כותב שום מכתב

סנכרון סטטי

אם תבצע סנכרון של שיטה סטטית, הנעילה תתרחש על המחלקה, לא על האובייקט. בדוגמה זו, אנו מבצעים סנכרון סטטי על ידי החלת מילת המפתח המסונכרנת על שיטה סטטית:
/**
* This is how a synchronization block is added.
* Inside the block, you need to specify which object's mutex will be acquired.
*/
class Printer {

   static synchronized void print(List<String> wordsToPrint) {
       wordsToPrint.forEach(System.out::print);
       System.out.println();
   }

   public static void main(String args[]) {

       // Create two threads
       Thread writer1 = new Thread(new Runnable() {
           @Override
           public void run() {
               List<String> poem = Arrays.asList("I ", "Writer1", " Write", " A Letter");
               Printer.print(poem);
           }
       });
       Thread writer2 = new Thread(new Runnable() {
           @Override
           public void run() {
               List<String> poem = Arrays.asList("I Do Not ", "Writer2", " Not Write", " No Letter");
               Printer.print(poem);
           }
       });

       // Start them
       writer1.start();
       writer2.start();
   }
}
ופלט הקונסולה הוא זה:
אני לא כותב2 לא כותב שום מכתב אני כותב1 כותב מכתב

34. מהו משתנה נדיף?

בתכנות מרובה הליכי, מילת המפתח נדיפה משמשת לבטיחות חוטים. כאשר משתנה ניתן לשינוי, השינוי גלוי לכל שאר השרשורים, כך שניתן להשתמש במשתנה על ידי שרשור אחד בכל פעם. על ידי שימוש במילת המפתח הפכפכה , אתה יכול להבטיח שמשתנה בטוח בשרשור ומאוחסן בזיכרון משותף, וששרשורים לא יאחסנו אותו במטמונים שלהם. איך זה נראה?
private volatile AtomicInteger count;
אנחנו פשוט מוסיפים נדיף למשתנה. אבל קחו בחשבון שזה לא אומר בטיחות חוט מלאה... אחרי הכל, ייתכן שפעולות על המשתנה אינן אטומיות. עם זאת, אתה יכול להשתמש במחלקות Atomic שעושות פעולות בצורה אטומית, כלומר בהוראה יחידה של CPU. יש הרבה מחלקות כאלה בחבילת java.util.concurrent.atomic .

35. מהו מבוי סתום?

ב-Java, מבוי סתום הוא משהו שיכול לקרות כחלק מ-multithreading. מבוי סתום יכול להתרחש כאשר חוט ממתין למנעול אובייקט שנרכש על ידי חוט אחר, והחוט השני ממתין לנעילת האובייקט שנרכש על ידי החוט הראשון. המשמעות היא ששני השרשורים ממתינים זה לזה, וביצוע הקוד שלהם לא יכול להמשיך. 50 השאלות והתשובות המובילות לראיונות עבודה עבור Java Core.  חלק 2 - 4בואו ניקח בחשבון דוגמה שיש לה מחלקה המיישמת Runnable. הקונסטרוקטור שלו לוקח שני משאבים. שיטת run() רוכשת עבורם את המנעול לפי הסדר. אם תיצור שני אובייקטים מהמחלקה הזו, ותעביר את המשאבים בסדר אחר, תוכל להיתקל בקלות במבוי סתום:
class DeadLock {

   public static void main(String[] args) {
       final Integer r1 = 10;
       final Integer r2 = 15;

       DeadlockThread threadR1R2 = new DeadlockThread(r1, r2);
       DeadlockThread threadR2R1 = new DeadlockThread(r2, r1);

       new Thread(threadR1R2).start();
       new Thread(threadR2R1).start();
   }
}

/**
* A class that accepts two resources.
*/
class DeadlockThread implements Runnable {

   private final Integer r1;
   private final Integer r2;

   public DeadlockThread(Integer r1, Integer r2) {
       this.r1 = r1;
       this.r2 = r2;
   }

   @Override
   public void run() {
       synchronized (r1) {
           System.out.println(Thread.currentThread().getName() + " acquired resource: " + r1);

           try {
               Thread.sleep(1000);
           } catch (InterruptedException e) {
               e.printStackTrace();
           }

           synchronized (r2) {
               System.out.println(Thread.currentThread().getName() + " acquired resource: " + r2);
           }
       }
   }
}
פלט מסוף:
השרשור הראשון רכש את המשאב הראשון השרשור השני רכש את המשאב השני

36. איך נמנעים ממבוי סתום?

מכיוון שאנו יודעים כיצד מתרחש מבוי סתום, אנו יכולים להסיק כמה מסקנות...
  • בדוגמה שלמעלה, המבוי סתום מתרחש עקב העובדה שיש לנו נעילה מקוננת. כלומר, יש לנו בלוק מסונכרן בתוך בלוק מסונכרן. כדי להימנע מכך, במקום קינון, עליך ליצור שכבת הפשטה גבוהה יותר, להעביר את הסנכרון לרמה הגבוהה יותר ולבטל את הנעילה המקוננת.
  • ככל שתבצע יותר נעילה, כך גדל הסיכוי שתהיה מבוי סתום. לכן, בכל פעם שאתה מוסיף בלוק מסונכרן, אתה צריך לחשוב אם אתה באמת צריך אותו והאם אתה יכול להימנע מהוספת אחד חדש.
  • שימוש ב-Thread.join() . אתה יכול גם להיתקל במבוי סתום בזמן שרשור אחד מחכה למשנהו. כדי למנוע בעיה זו, ייתכן שתשקול להגדיר פסק זמן עבור שיטת join() .
  • אם יש לנו שרשור אחד, אז לא יהיה מבוי סתום ;)

37. מהו תנאי גזע?

אם מירוצים מהחיים האמיתיים כוללים מכוניות, אז מירוצים בריבוי השרשורים כוללים חוטים. אבל למה? :/ ישנם שני שרשורים שפועלים ויכולים לגשת לאותו אובייקט. והם עשויים לנסות לעדכן את מצב האובייקט המשותף בו-זמנית. הכל ברור עד כה, נכון? שרשורים מבוצעים באופן מילולי במקביל (אם למעבד יש יותר מליבה אחת) או ברצף, כאשר המעבד מקצה פרוסות זמן משולבות. אנחנו לא יכולים לנהל את התהליכים האלה. משמעות הדבר היא שכאשר שרשור אחד קורא נתונים מאובייקט איננו יכולים להבטיח שיהיה לו זמן לשנות את האובייקט לפני שרשור אחר יעשה זאת. בעיות כאלה מתעוררות כאשר יש לנו את השילובים האלה של "בדוק-ופעול". מה זה אומר? נניח שיש לנו הצהרת if שהגוף שלה משנה את התנאי אם עצמו, למשל:
int z = 0;

// Check
if (z < 5) {
// Act
   z = z + 5;
}
שני שרשורים יכולים להיכנס בו-זמנית לגוש הקוד הזה כאשר z עדיין אפס ואז שני השרשורים יכולים לשנות את ערכו. כתוצאה מכך, לא נקבל את הערך הצפוי של 5. במקום זאת, נקבל 10. איך נמנעים מכך? עליך לרכוש מנעול לפני בדיקה ופעולה, ולאחר מכן לשחרר את המנעול לאחר מכן. כלומר, אתה צריך שהשרשור הראשון יכנס לבלוק if , תבצע את כל הפעולות, תשנה את z , ורק אז תיתן לשרשור הבא את ההזדמנות לעשות את אותו הדבר. אבל השרשור הבא לא ייכנס לבלוק if , מכיוון ש- z יהיה כעת 5:
// Acquire the lock for z
if (z < 5) {
   z = z + 5;
}
// Release z's lock
===================================================

במקום מסקנה

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

וכמו תמיד, הנה כמה קישורים שימושיים:

תודה לכולכם שקראתם. נתראה בקרוב :) פרופיל GitHub שלי 50 השאלות והתשובות המובילות לראיונות עבודה עבור Java Core.  חלק 2 - 5
הערות
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION