CodeGym /Java Blog /Acak /50 pertanyaan dan jawaban wawancara kerja teratas untuk J...
John Squirrels
Level 41
San Francisco

50 pertanyaan dan jawaban wawancara kerja teratas untuk Java Core. Bagian 2

Dipublikasikan di grup Acak
50 pertanyaan dan jawaban wawancara kerja teratas untuk Java Core. Bagian 150 pertanyaan dan jawaban wawancara kerja teratas untuk Java Core.  Bagian 2 - 1

Multithreading

24. Bagaimana cara membuat thread baru di Java?

Dengan satu atau lain cara, utas dibuat menggunakan kelas Utas. Tetapi ada berbagai cara untuk melakukan ini…
  1. Mewarisi java.lang.Thread .
  2. Terapkan antarmuka java.lang.Runnable — konstruktor kelas Thread mengambil objek Runnable.
Mari kita bicara tentang mereka masing-masing.

Mewarisi kelas Thread

Dalam hal ini, kita membuat kelas kita mewarisi java.lang.Thread . Ini memiliki metode run() , dan itulah yang kita butuhkan. Semua kehidupan dan logika utas baru akan ada dalam metode ini. Ini seperti metode utama untuk utas baru. Setelah itu, yang tersisa hanyalah membuat objek kelas kita dan memanggil metode start() . Ini akan membuat utas baru dan mulai menjalankan logikanya. Mari lihat:

/**
* 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();
   }
}
Output konsol akan menjadi seperti ini:
Utas-1 Utas-0 Utas-2
Artinya, bahkan di sini kita melihat bahwa utas dieksekusi tidak berurutan, melainkan sesuai keinginan JVM untuk menjalankannya :)

Implementasikan antarmuka Runnable

Jika Anda menentang pewarisan dan/atau sudah mewarisi beberapa kelas lain, Anda dapat menggunakan antarmuka java.lang.Runnable . Di sini, kita membuat kelas kita mengimplementasikan antarmuka ini dengan mengimplementasikan metode run() , seperti pada contoh di atas. Yang tersisa hanyalah membuat objek Thread . Tampaknya lebih banyak baris kode lebih buruk. Tapi kita tahu betapa merusak warisan itu dan lebih baik menghindarinya dengan segala cara ;) Coba lihat:

/**
* 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();
   }
}
Dan inilah hasilnya:
Utas-0 Utas-1 Utas-2

25. Apa perbedaan antara proses dan utas?

50 pertanyaan dan jawaban wawancara kerja teratas untuk Java Core.  Bagian 2 - 2Proses dan utas berbeda dengan cara berikut:
  1. Program yang sedang berjalan disebut proses, tetapi utas adalah bagian dari proses.
  2. Proses bersifat independen, tetapi utas adalah bagian dari proses.
  3. Proses memiliki ruang alamat yang berbeda di memori, tetapi utas berbagi ruang alamat yang sama.
  4. Pergantian konteks antar utas lebih cepat daripada peralihan antar proses.
  5. Komunikasi antar proses lebih lambat dan lebih mahal daripada komunikasi antar utas.
  6. Setiap perubahan dalam proses induk tidak memengaruhi proses anak, tetapi perubahan dalam utas induk dapat memengaruhi utas anak.

26. Apa manfaat multithreading?

  1. Multithreading memungkinkan sebuah aplikasi/program untuk selalu responsif terhadap masukan, meskipun sudah menjalankan beberapa tugas latar belakang;
  2. Multithreading memungkinkan untuk menyelesaikan tugas lebih cepat, karena utas berjalan secara independen;
  3. Multithreading menyediakan penggunaan memori cache yang lebih baik, karena utas dapat mengakses sumber daya memori bersama;
  4. Multithreading mengurangi jumlah server yang dibutuhkan, karena satu server dapat menjalankan banyak thread secara bersamaan.

27. Apa status dalam siklus hidup utas?

50 pertanyaan dan jawaban wawancara kerja teratas untuk Java Core.  Bagian 2 - 3
  1. Baru: Dalam keadaan ini, objek Utas dibuat menggunakan operator baru, tetapi utas baru belum ada. Utas tidak dimulai sampai kita memanggil metode start() .
  2. Dapat dijalankan: Dalam keadaan ini, utas siap dijalankan setelah start() metode disebut. Namun, itu belum dipilih oleh penjadwal utas.
  3. Berjalan: Dalam keadaan ini, penjadwal utas mengambil utas dari keadaan siap, dan berjalan.
  4. Menunggu/Diblokir: dalam keadaan ini, utas tidak berjalan, tetapi masih hidup atau menunggu utas lainnya selesai.
  5. Mati/Dihentikan: saat utas keluar dari metode run() , utas dalam keadaan mati atau dihentikan.

28. Apakah mungkin menjalankan utas dua kali?

Tidak, kami tidak dapat memulai ulang utas, karena setelah utas dimulai dan dijalankan, utas akan masuk ke keadaan Mati. Jika kami mencoba memulai utas dua kali, java.lang.IllegalThreadStateException akan dilempar. Mari lihat:

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();
   }
}
Akan ada pengecualian segera setelah eksekusi sampai pada awal kedua dari utas yang sama. Cobalah sendiri ;) Lebih baik melihat ini sekali daripada mendengarnya ratusan kali.

29. Bagaimana jika Anda memanggil run() secara langsung tanpa memanggil start()?

Ya, Anda pasti dapat memanggil metode run() , tetapi utas baru tidak akan dibuat, dan metode tersebut tidak akan berjalan pada utas terpisah. Dalam hal ini, kami memiliki objek biasa yang memanggil metode biasa. Jika kita berbicara tentang metode start() , maka itu soal lain. Saat metode ini dipanggil, JVM memulai utas baru. Utas ini, pada gilirannya, memanggil metode kami;) Tidak percaya? Di sini, cobalah:

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();
   }
}
Dan keluaran konsol akan terlihat seperti ini:
0123401234
Seperti yang Anda lihat, tidak ada utas yang dibuat. Semuanya bekerja seperti di kelas biasa. Pertama, metode objek pertama dieksekusi, lalu yang kedua.

30. Apa itu utas daemon?

Utas daemon adalah utas yang melakukan tugas dengan prioritas lebih rendah daripada utas lainnya. Dengan kata lain, tugasnya adalah melakukan tugas tambahan yang perlu dilakukan hanya dalam hubungannya dengan utas (utama) lainnya. Ada banyak utas daemon yang berjalan secara otomatis, seperti pengumpulan sampah, finalizer, dll.

Mengapa Java mengakhiri utas daemon?

Satu-satunya tujuan utas daemon adalah untuk memberikan dukungan latar belakang ke utas pengguna. Dengan demikian, jika utas utama diakhiri, JVM secara otomatis mengakhiri semua utas daemonnya.

Metode kelas Thread

Kelas java.lang.Thread menyediakan dua metode untuk bekerja dengan daemon thread:
  1. public void setDaemon(boolean status) — Metode ini menunjukkan apakah ini akan menjadi utas daemon. Standarnya salah . Ini berarti bahwa tidak ada utas daemon yang akan dibuat kecuali Anda secara khusus mengatakannya.
  2. public boolean isDaemon() — Metode ini pada dasarnya adalah getter untuk variabel daemon , yang kita atur menggunakan metode sebelumnya.
Contoh:

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();
   }
}
Keluaran konsol:
setan? daemon sejati? daemon palsu? utas pengguna utas daemon palsu utas pengguna
Dari output, kita melihat bahwa di dalam thread itu sendiri, kita dapat menggunakan metode static currentThread() untuk mengetahui thread mana itu. Alternatifnya, jika kita memiliki referensi ke objek thread, kita juga bisa mencari tahu langsung darinya. Ini memberikan tingkat konfigurasi yang diperlukan.

31. Apakah mungkin membuat utas sebagai daemon setelah dibuat?

Tidak. Jika Anda mencoba melakukan ini, Anda akan mendapatkan IllegalThreadStateException . Ini berarti kita hanya dapat membuat utas daemon sebelum dimulai. Contoh:

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);
   }
}
Keluaran konsol:
Bekerja... Pengecualian di utas "utama" java.lang.IllegalThreadStateException di java.lang.Thread.setDaemon(Thread.java:1359) di SetDaemonAfterStartExample.main(SetDaemonAfterStartExample.java:14)

32. Apa yang dimaksud dengan shutdown hook?

Shutdown hook adalah utas yang secara implisit dipanggil sebelum mesin virtual Java (JVM) dimatikan. Dengan demikian, kita dapat menggunakannya untuk melepaskan sumber daya atau menyimpan keadaan saat mesin virtual Java dimatikan secara normal atau tidak normal. Kita dapat menambahkan hook shutdown menggunakan metode berikut:

Runtime.getRuntime().addShutdownHook(new ShutdownHookThreadExample());
Seperti yang ditunjukkan pada contoh:

/**
* 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();
       }
   }
}
Keluaran konsol:
Sekarang program akan tertidur. Tekan Ctrl+C untuk menghentikannya. hook shutdown dieksekusi

33. Apa itu sinkronisasi?

Di Jawa, sinkronisasi adalah kemampuan untuk mengontrol akses beberapa utas ke sumber daya bersama apa pun. Saat beberapa utas mencoba melakukan tugas yang sama secara bersamaan, Anda bisa mendapatkan hasil yang salah. Untuk memperbaiki masalah ini, Java menggunakan sinkronisasi, yang memungkinkan hanya satu utas yang berjalan pada satu waktu. Sinkronisasi dapat dicapai dengan tiga cara:
  • Menyinkronkan metode
  • Menyinkronkan blok tertentu
  • Sinkronisasi statis

Menyinkronkan metode

Metode yang disinkronkan digunakan untuk mengunci objek untuk setiap sumber daya bersama. Saat utas memanggil metode yang disinkronkan, ia secara otomatis memperoleh kunci objek dan melepaskannya saat utas menyelesaikan tugasnya. Untuk membuatnya berfungsi, Anda perlu menambahkan kata kunci yang disinkronkan . Kita dapat melihat cara kerjanya dengan melihat contoh:

/**
* 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);
   }
}
Dan keluaran konsolnya adalah ini:
I Utas-0 Tulis Surat I Jangan Utas-1 Tidak Tulis Surat

Blok sinkronisasi

Blok yang disinkronkan dapat digunakan untuk melakukan sinkronisasi pada sumber daya tertentu dalam suatu metode. Katakanlah dalam metode yang besar (ya, Anda tidak boleh menulisnya, tetapi terkadang itu terjadi) Anda hanya perlu menyinkronkan sebagian kecil karena alasan tertentu. Jika Anda meletakkan semua kode metode di blok yang disinkronkan, itu akan bekerja sama dengan metode yang disinkronkan. Sintaksnya terlihat seperti ini:

synchronized ("object to be locked") {
   // The code that must be protected
}
Untuk menghindari pengulangan contoh sebelumnya, kami akan membuat utas menggunakan kelas anonim, yaitu kami akan segera mengimplementasikan antarmuka 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();
   }
}

}
Dan keluaran konsolnya adalah ini:
Saya Penulis1 Menulis Surat Saya Tidak Menulis Surat2 Tidak Menulis

Sinkronisasi statis

Jika Anda membuat metode statis disinkronkan, maka penguncian akan terjadi pada kelas, bukan pada objek. Dalam contoh ini, kami melakukan sinkronisasi statis dengan menerapkan kata kunci yang disinkronkan ke metode statis:

/**
* 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();
   }
}
Dan keluaran konsolnya adalah ini:
Saya Tidak Penulis2 Tidak Menulis Tidak Ada Surat Saya Penulis1 Menulis Surat

34. Apa itu variabel volatil?

Dalam pemrograman multithreaded, kata kunci volatile digunakan untuk keamanan thread. Saat variabel yang dapat berubah dimodifikasi, perubahan tersebut terlihat oleh semua utas lainnya, sehingga variabel dapat digunakan oleh satu utas dalam satu waktu. Dengan menggunakan kata kunci volatil , Anda dapat menjamin bahwa suatu variabel aman untuk thread dan disimpan dalam memori bersama, dan bahwa thread tidak akan menyimpannya dalam cache mereka. Ini kelihatannya seperti apa?

private volatile AtomicInteger count;
Kami hanya menambahkan volatile ke variabel. Namun perlu diingat bahwa ini tidak berarti keamanan utas sepenuhnya. Bagaimanapun, operasi pada variabel mungkin tidak bersifat atomik. Meskipun demikian, Anda dapat menggunakan kelas Atomic yang melakukan operasi secara atomik, yaitu dalam satu instruksi CPU. Ada banyak kelas seperti itu dalam paket java.util.concurrent.atomic .

35. Apa itu kebuntuan?

Di Jawa, kebuntuan adalah sesuatu yang bisa terjadi sebagai bagian dari multithreading. Kebuntuan dapat terjadi ketika utas sedang menunggu kunci objek diperoleh oleh utas lain, dan utas kedua menunggu kunci objek diperoleh oleh utas pertama. Ini berarti kedua utas sedang menunggu satu sama lain, dan eksekusi kodenya tidak dapat dilanjutkan. 50 pertanyaan dan jawaban wawancara kerja teratas untuk Java Core.  Bagian 2 - 4Mari pertimbangkan contoh yang memiliki kelas yang mengimplementasikan Runnable. Konstruktornya membutuhkan dua sumber daya. Metode run() memperoleh kunci untuk mereka secara berurutan. Jika Anda membuat dua objek dari kelas ini, dan meneruskan sumber daya dalam urutan yang berbeda, maka Anda dapat dengan mudah mengalami kebuntuan:

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);
           }
       }
   }
}
Keluaran konsol:
Utas pertama memperoleh sumber daya pertama Utas kedua memperoleh sumber daya kedua

36. Bagaimana Anda menghindari kebuntuan?

Karena kita tahu bagaimana kebuntuan terjadi, kita bisa menarik beberapa kesimpulan...
  • Pada contoh di atas, kebuntuan terjadi karena kita memiliki penguncian bersarang. Yaitu, kami memiliki blok yang disinkronkan di dalam blok yang disinkronkan. Untuk menghindari hal ini, alih-alih bersarang, Anda perlu membuat lapisan abstraksi baru yang lebih tinggi, memindahkan sinkronisasi ke tingkat yang lebih tinggi, dan menghilangkan penguncian bersarang.
  • Semakin banyak penguncian yang Anda lakukan, semakin besar kemungkinan akan terjadi kebuntuan. Oleh karena itu, setiap kali Anda menambahkan blok yang disinkronkan, Anda perlu memikirkan apakah Anda benar-benar membutuhkannya dan apakah Anda dapat menghindari penambahan yang baru.
  • Menggunakan Thread.join() . Anda juga dapat mengalami kebuntuan saat satu utas menunggu utas lainnya. Untuk menghindari masalah ini, Anda dapat mempertimbangkan menyetel batas waktu untuk metode join() .
  • Jika kita memiliki satu utas, maka tidak akan ada jalan buntu ;)

37. Apa itu kondisi balapan?

Jika balapan kehidupan nyata melibatkan mobil, maka balapan dalam multithreading melibatkan utas. Tapi kenapa? :/ Ada dua utas yang sedang berjalan dan dapat mengakses objek yang sama. Dan mereka mungkin mencoba memperbarui status objek yang dibagikan pada saat yang sama. Semuanya jelas sejauh ini, kan? Utas dieksekusi baik secara paralel (jika prosesor memiliki lebih dari satu inti) atau secara berurutan, dengan prosesor mengalokasikan irisan waktu yang disisipkan. Kami tidak dapat mengelola proses ini. Ini berarti bahwa ketika satu utas membaca data dari suatu objek, kami tidak dapat menjamin bahwa utas tersebut akan memiliki waktu untuk mengubah objek SEBELUM beberapa utas lainnya melakukannya. Masalah seperti itu muncul ketika kita memiliki kombo "periksa dan bertindak" ini. Maksudnya itu apa? Misalkan kita memiliki pernyataan if yang badannya mengubah kondisi if itu sendiri, misalnya:

int z = 0;

// Check
if (z < 5) {
// Act
   z = z + 5;
}
Dua utas dapat secara bersamaan memasukkan blok kode ini ketika z masih nol dan kemudian kedua utas dapat mengubah nilainya. Akibatnya, kami tidak akan mendapatkan nilai yang diharapkan dari 5. Sebaliknya, kami akan mendapatkan 10. Bagaimana cara menghindarinya? Anda perlu mendapatkan kunci sebelum memeriksa dan bertindak, dan kemudian melepaskan kunci sesudahnya. Artinya, Anda harus memasukkan utas pertama ke blok if , melakukan semua tindakan, mengubah z , dan baru kemudian memberikan utas berikutnya kesempatan untuk melakukan hal yang sama. Tapi utas berikutnya tidak akan masuk ke blok if , karena z sekarang akan menjadi 5:

// Acquire the lock for z
if (z < 5) {
   z = z + 5;
}
// Release z's lock
===================================================

Alih-alih sebuah kesimpulan

Saya ingin mengucapkan terima kasih kepada semua orang yang membaca sampai akhir. Itu jauh, tetapi Anda bertahan! Mungkin tidak semuanya jelas. Ini normal. Ketika saya pertama kali mulai mempelajari Java, saya tidak dapat memahami apa itu variabel statis. Tapi bukan masalah besar. Saya tidur di atasnya, membaca beberapa sumber lagi, dan kemudian pemahaman datang. Mempersiapkan wawancara lebih merupakan pertanyaan akademis daripada pertanyaan praktis. Akibatnya, sebelum setiap wawancara, Anda harus meninjau dan menyegarkan ingatan Anda tentang hal-hal yang mungkin jarang Anda gunakan.

Dan seperti biasa, berikut adalah beberapa tautan yang berguna:

Terima kasih semuanya telah membaca. Sampai jumpa lagi :) Profil GitHub saya50 pertanyaan dan jawaban wawancara kerja teratas untuk Java Core.  Bagian 2 - 5
Komentar
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION