JavaRush /Java Blog /Random-ID /Inti Java. Pertanyaan wawancara, bagian 2
Andrey
Level 26

Inti Java. Pertanyaan wawancara, bagian 2

Dipublikasikan di grup Random-ID
Bagi mereka yang pertama kali mendengar kata Java Core, inilah dasar dasar bahasa tersebut. Dengan pengetahuan ini, Anda dapat dengan aman pergi magang/magang.
Inti Java.  Pertanyaan untuk wawancara, bagian 2 - 1
Pertanyaan-pertanyaan ini akan membantu Anda menyegarkan kembali pengetahuan Anda sebelum wawancara, atau mempelajari sesuatu yang baru untuk diri Anda sendiri. Untuk mendapatkan keterampilan praktis, belajarlah di JavaRush . Artikel asli Tautan ke bagian lain: Java Core. Pertanyaan wawancara, bagian 1 Java Core. Pertanyaan untuk wawancara, bagian 3

Mengapa metode finalize() harus dihindari?

Kita semua mengetahui pernyataan bahwa suatu metode finalize()dipanggil oleh pengumpul sampah sebelum mengosongkan memori yang ditempati oleh suatu objek. Berikut adalah contoh program yang membuktikan bahwa pemanggilan metode finalize()tidak dijamin:
public class TryCatchFinallyTest implements Runnable {

	private void testMethod() throws InterruptedException
	{
		try
		{
			System.out.println("In try block");
			throw new NullPointerException();
		}
		catch(NullPointerException npe)
		{
			System.out.println("In catch block");
		}
		finally
		{
			System.out.println("In finally block");
		}
	}

	@Override
	protected void finalize() throws Throwable {
		System.out.println("In finalize block");
		super.finalize();
	}

	@Override
	public void run() {
		try {
			testMethod();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}
public class TestMain
{
	@SuppressWarnings("deprecation")
	public static void main(String[] args) {
	for(int i=1;i< =3;i++)
	{
		new Thread(new TryCatchFinallyTest()).start();
	}
	}
}
Keluaran: Di blok coba Di blok tangkap Di blok akhirnya Di blok coba Di blok tangkap Di blok akhirnya Di blok coba Di blok tangkap Di blok akhirnya Anehnya, metode ini finalizetidak dieksekusi untuk thread mana pun. Ini membuktikan kata-kataku. Saya rasa alasannya adalah finalizer dijalankan oleh thread pengumpul sampah yang terpisah. Jika Mesin Virtual Java dihentikan terlalu dini, maka pengumpul sampah tidak memiliki cukup waktu untuk membuat dan menjalankan finalisator. Alasan lain untuk tidak menggunakan metode ini finalize()mungkin:
  1. Metode ini finalize()tidak bekerja dengan rantai seperti konstruktor. Artinya ketika Anda memanggil konstruktor kelas, konstruktor superkelas akan dipanggil tanpa syarat. Namun dalam kasus metode ini finalize(), hal ini tidak akan terjadi. Metode superclass finalize()harus dipanggil secara eksplisit.
  2. Pengecualian apa pun yang diberikan oleh metode ini finalizeakan diabaikan oleh thread pengumpul sampah dan tidak akan disebarkan lebih lanjut, yang berarti peristiwa tersebut tidak akan dicatat di log Anda. Ini sangat buruk, bukan?
  3. Anda juga mendapatkan penalti kinerja yang signifikan jika metode tersebut finalize()ada di kelas Anda. Dalam Pemrograman Efektif (edisi ke-2), Joshua Bloch berkata:
    “Ya, dan satu hal lagi: ada penalti kinerja yang besar saat menggunakan finalizer. Di mesin saya, waktu untuk membuat dan menghancurkan objek sederhana kira-kira 5,6 nanodetik.
    Menambahkan finalizer meningkatkan waktu menjadi 2400 nanodetik. Dengan kata lain, pembuatan dan penghapusan objek dengan finalizer kira-kira 430 kali lebih lambat.”

Mengapa HashMap tidak digunakan di lingkungan multi-utas? Mungkinkah ini menyebabkan putaran tak terbatas?

Kita tahu bahwa HashMapini adalah koleksi yang tidak disinkronkan, padanannya yang disinkronkan adalah HashTable. Jadi, ketika Anda mengakses koleksi dan berada dalam lingkungan multi-utas di mana semua thread memiliki akses ke satu contoh koleksi, maka lebih aman digunakan HashTableuntuk alasan yang jelas, seperti menghindari pembacaan kotor dan memastikan konsistensi data. Dalam kasus terburuk, lingkungan multi-thread ini akan menyebabkan loop tak terbatas. Ya itu benar. HashMap.get()dapat menyebabkan loop tak terbatas. Mari kita lihat bagaimana caranya? Jika Anda melihat kode sumber metodenya HashMap.get(Object key), tampilannya seperti ini:
public Object get(Object key) {
    Object k = maskNull(key);
    int hash = hash(k);
    int i = indexFor(hash, table.length);
    Entry e = table[i];
    while (true) {
        if (e == null)
            return e;
        if (e.hash == hash && eq(k, e.key))
            return e.value;
        e = e.next;
    }
}
while(true)selalu dapat menjadi korban loop tak terbatas dalam lingkungan runtime multi-thread jika karena alasan tertentu e.nextia dapat mengarah ke dirinya sendiri. Ini akan menyebabkan perulangan tanpa akhir, tapi bagaimana cara e.nextmenunjuk ke dirinya sendiri (yaitu, ke e)? Hal ini dapat terjadi pada metode void transfer(Entry[] newTable)yang dipanggil saat HashMapsedang diubah ukurannya.
do {
    Entry next = e.next;
    int i = indexFor(e.hash, newCapacity);
    e.next = newTable[i];
    newTable[i] = e;
    e = next;
} while (e != null);
Potongan kode ini cenderung membuat loop tak terbatas jika pengubahan ukuran terjadi pada saat yang sama ketika thread lain mencoba mengubah instance peta ( HashMap). Satu-satunya cara untuk menghindari skenario ini adalah dengan menggunakan sinkronisasi dalam kode Anda, atau lebih baik lagi, menggunakan koleksi yang disinkronkan.

Menjelaskan abstraksi dan enkapsulasi. Bagaimana mereka terhubung?

Dengan kata sederhana , “ Abstraksi hanya menampilkan properti objek yang penting untuk tampilan saat ini . ” Dalam teori pemrograman berorientasi objek, abstraksi melibatkan kemampuan untuk mendefinisikan objek yang mewakili "aktor" abstrak yang dapat melakukan pekerjaan, mengubah dan melaporkan perubahan keadaannya, dan "berinteraksi" dengan objek lain dalam sistem. Abstraksi dalam bahasa pemrograman apa pun bekerja dalam banyak cara. Hal ini terlihat dari pembuatan rutin untuk mendefinisikan antarmuka perintah bahasa tingkat rendah. Beberapa abstraksi mencoba membatasi luasnya representasi keseluruhan kebutuhan programmer dengan sepenuhnya menyembunyikan abstraksi yang menjadi dasar pembuatannya, seperti pola desain. Biasanya, abstraksi dapat dilihat dalam dua cara: Abstraksi data adalah cara membuat tipe data yang kompleks dan hanya menampilkan operasi yang bermakna untuk berinteraksi dengan model data, sekaligus menyembunyikan semua detail implementasi dari dunia luar. Abstraksi eksekusi adalah proses mengidentifikasi semua pernyataan penting dan memaparkannya sebagai satu unit kerja. Kami biasanya menggunakan fitur ini ketika kami membuat metode untuk melakukan suatu pekerjaan. Membatasi data dan metode di dalam kelas yang dikombinasikan dengan melakukan penyembunyian (menggunakan kontrol akses) sering disebut enkapsulasi. Hasilnya adalah tipe data dengan karakteristik dan perilaku. Enkapsulasi pada dasarnya juga melibatkan penyembunyian data dan penyembunyian implementasi. "Rangkum segala sesuatu yang bisa berubah" . Kutipan ini adalah prinsip desain yang terkenal. Oleh karena itu, di kelas mana pun, perubahan data bisa terjadi saat runtime dan perubahan implementasi bisa terjadi di versi mendatang. Dengan demikian, enkapsulasi berlaku untuk data dan implementasinya. Jadi mereka bisa dihubungkan seperti ini:
  • Abstraksi sebagian besar adalah apa yang dapat dilakukan suatu kelas [Ide]
  • Enkapsulasi lebih lanjut Bagaimana mencapai fungsi ini [Implementasi]

Perbedaan antara antarmuka dan kelas abstrak?

Perbedaan utamanya dapat dicantumkan sebagai berikut:
  • Antarmuka tidak dapat mengimplementasikan metode apa pun, tetapi kelas abstrak bisa.
  • Sebuah kelas dapat mengimplementasikan banyak antarmuka, tetapi hanya dapat memiliki satu superkelas (abstrak atau non-abstrak)
  • Antarmuka bukan bagian dari hierarki kelas. Kelas yang tidak berhubungan dapat mengimplementasikan antarmuka yang sama.
Apa yang harus Anda ingat adalah ini: “Ketika Anda dapat sepenuhnya mendeskripsikan sebuah konsep dalam istilah “apa yang dilakukannya” tanpa harus menentukan “bagaimana cara kerjanya”, maka Anda harus menggunakan antarmuka. Jika Anda perlu menyertakan beberapa detail implementasi, maka Anda perlu merepresentasikan konsep Anda di kelas abstrak." Juga, dengan kata lain: Apakah ada banyak kelas yang dapat "dikelompokkan" dan dijelaskan oleh satu kata benda? Jika demikian, buatlah kelas abstrak dengan nama kata benda ini, dan warisi kelas darinya. Misalnya, Catdan Dogdapat mewarisi dari kelas abstrak Animal, dan kelas dasar abstrak ini akan mengimplementasikan metode void Breathe()- bernapas, yang akan dilakukan semua hewan dengan cara yang sama. Kata kerja apa yang bisa diterapkan di kelas saya dan bisa diterapkan di kelas lain? Buat antarmuka untuk masing-masing kata kerja ini. Misalnya, semua hewan bisa makan, jadi saya akan membuat antarmuka IFeedabledan membuatnya Animalmengimplementasikan antarmuka tersebut. Hanya cukup baik untuk mengimplementasikan antarmuka Dog( mampu menyukai saya), tetapi tidak semua. Seseorang berkata: perbedaan utamanya adalah di mana Anda ingin penerapannya. Saat Anda membuat antarmuka, Anda dapat memindahkan implementasinya ke kelas mana pun yang mengimplementasikan antarmuka Anda. Dengan membuat kelas abstrak, Anda dapat berbagi implementasi semua kelas turunan di satu tempat dan menghindari banyak hal buruk seperti duplikasi kode. HorseILikeable

Bagaimana cara StringBuffer menghemat memori?

Kelas Stringdiimplementasikan sebagai objek yang tidak dapat diubah, artinya ketika Anda pertama kali memutuskan untuk memasukkan sesuatu ke dalam objek String, mesin virtual akan mengalokasikan array dengan panjang tetap persis dengan ukuran nilai asli Anda. Ini kemudian akan diperlakukan sebagai konstanta di dalam mesin virtual, yang memberikan peningkatan kinerja yang signifikan jika nilai string tidak berubah. Namun, jika Anda memutuskan untuk mengubah konten string dengan cara apa pun, yang sebenarnya dilakukan mesin virtual adalah menyalin konten string asli ke ruang sementara, membuat perubahan, lalu menyimpan perubahan tersebut ke array memori baru. Jadi, membuat perubahan pada nilai string setelah inisialisasi adalah operasi yang mahal. StringBuffer, di sisi lain, diimplementasikan sebagai array yang berkembang secara dinamis di dalam mesin virtual, yang berarti bahwa setiap operasi modifikasi dapat terjadi pada sel memori yang ada dan memori baru akan dialokasikan sesuai kebutuhan. Namun, mesin virtual tidak dapat melakukan optimasi StringBufferkarena isinya dianggap tidak konsisten di setiap instance.

Mengapa metode wait dan notify dideklarasikan di kelas Object dan bukan di Thread?

Metode wait, notify, notifyAllhanya diperlukan ketika Anda ingin thread Anda memiliki akses ke sumber daya bersama dan sumber daya bersama dapat berupa objek java apa pun di heap. Dengan demikian, metode ini didefinisikan pada kelas dasar Objectsehingga setiap objek memiliki kontrol yang memungkinkan thread menunggu di monitornya. Java tidak memiliki objek khusus apa pun yang digunakan untuk berbagi sumber daya bersama. Tidak ada struktur data yang ditentukan. Oleh karena itu, merupakan tanggung jawab kelas Objectuntuk dapat menjadi sumber daya bersama, dan menyediakan metode pembantu seperti wait(), notify(), notifyAll(). Java didasarkan pada ide monitor Charles Hoare. Di Java, semua objek memiliki monitor. Thread menunggu di monitor, jadi untuk melakukan menunggu kita memerlukan dua parameter:
  • sebuah benang
  • monitor (objek apa pun).
Dalam desain Java, sebuah thread tidak dapat didefinisikan secara tepat; thread tersebut selalu merupakan thread saat ini yang mengeksekusi kode. Namun, kita dapat mendefinisikan monitor (yang merupakan objek yang dapat kita panggil metodenya wait). Ini adalah desain yang bagus karena jika kita dapat memaksa thread lain untuk menunggu pada monitor tertentu, hal ini akan mengakibatkan "invasi", membuat perancangan/pemrograman program paralel menjadi sulit. Ingatlah bahwa di Java, semua operasi yang mengganggu thread lain tidak digunakan lagi (misalnya, stop()).

Tulis program untuk membuat kebuntuan di Java dan perbaiki

Di Java deadlock, ini adalah situasi di mana setidaknya dua thread menahan satu blok pada sumber daya yang berbeda, dan keduanya menunggu sumber daya lain tersedia untuk menyelesaikan tugasnya. Dan tidak satu pun dari mereka yang mampu mengunci sumber daya yang dimilikinya. Inti Java.  Pertanyaan untuk wawancara, bagian 2 - 2 Contoh program:
package thread;

public class ResolveDeadLockTest {

	public static void main(String[] args) {
		ResolveDeadLockTest test = new ResolveDeadLockTest();

		final A a = test.new A();
		final B b = test.new B();

		// Thread-1
		Runnable block1 = new Runnable() {
			public void run() {
				synchronized (a) {
					try {
					// Добавляем задержку, чтобы обе нити могли начать попытки
					// блокирования ресурсов
						Thread.sleep(100);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					// Thread-1 заняла A но также нуждается в B
					synchronized (b) {
						System.out.println("In block 1");
					}
				}
			}
		};

		// Thread-2
		Runnable block2 = new Runnable() {
			public void run() {
				synchronized (b) {
					// Thread-2 заняла B но также нуждается в A
					synchronized (a) {
						System.out.println("In block 2");
					}
				}
			}
		};

		new Thread(block1).start();
		new Thread(block2).start();
	}

	// Resource A
	private class A {
		private int i = 10;

		public int getI() {
			return i;
		}

		public void setI(int i) {
			this.i = i;
		}
	}

	// Resource B
	private class B {
		private int i = 20;

		public int getI() {
			return i;
		}

		public void setI(int i) {
			this.i = i;
		}
	}
}
Menjalankan kode di atas akan mengakibatkan kebuntuan karena alasan yang sangat jelas (dijelaskan di atas). Sekarang kita perlu menyelesaikan masalah ini. Saya percaya bahwa solusi terhadap suatu permasalahan terletak pada akar permasalahan itu sendiri. Dalam kasus kami, model akses ke A dan B adalah masalah utama. Oleh karena itu, untuk mengatasinya, kita cukup mengubah urutan operator akses menjadi sumber daya bersama. Setelah diubah tampilannya akan seperti ini:
// Thread-1
Runnable block1 = new Runnable() {
	public void run() {
		synchronized (b) {
			try {
				// Добавляем задержку, чтобы обе нити могли начать попытки
				// блокирования ресурсов
				Thread.sleep(100);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			// Thread-1 заняла B но также нуждается в А
			synchronized (a) {
				System.out.println("In block 1");
			}
		}
	}
};

// Thread-2
Runnable block2 = new Runnable() {
	public void run() {
		synchronized (b) {
			// Thread-2 заняла B но также нуждается в А
			synchronized (a) {
				System.out.println("In block 2");
			}
		}
	}
};
Jalankan kelas ini lagi dan sekarang Anda tidak akan melihat kebuntuan. Saya harap ini membantu Anda menghindari kebuntuan dan menghilangkannya jika Anda menemuinya.

Apa yang terjadi jika kelas Anda yang mengimplementasikan antarmuka Serializable berisi komponen yang tidak dapat diserialisasi? Bagaimana cara memperbaikinya?

Dalam hal ini, itu akan dilempar NotSerializableExceptionselama eksekusi. Untuk memperbaiki masalah ini, ada solusi yang sangat sederhana - centang kotak ini transient. Ini berarti bidang yang dicentang tidak akan diserialkan. Jika Anda juga ingin menyimpan status bidang ini, maka Anda perlu mempertimbangkan variabel referensi, yang sudah mengimplementasikan Serializable. Anda mungkin juga perlu menggunakan metode readResolve()dan writeResolve(). Mari kita rangkum:
  • Pertama, jadikan bidang Anda non-serializable transient.
  • Pertama writeObject, panggil defaultWriteObjectthread untuk menyimpan semua non- transientfield, lalu panggil metode yang tersisa untuk membuat serial properti individual dari objek non-serializable Anda.
  • Di readObject, pertama-tama panggil defaultReadObjectaliran untuk membaca semua non- transientbidang, lalu panggil metode lain (sesuai dengan yang Anda tambahkan writeObject) untuk membatalkan serialisasi non- transientobjek Anda.

Jelaskan kata kunci sementara dan mudah berubah di Java

"Kata kunci transientdigunakan untuk menunjukkan bidang yang tidak akan diserialkan." Menurut Spesifikasi Bahasa Java: Variabel dapat ditandai dengan indikator sementara untuk menunjukkan bahwa variabel tersebut bukan bagian dari status persisten suatu objek. Misalnya, Anda mungkin berisi bidang yang berasal dari bidang lain, dan lebih baik mendapatkannya secara terprogram daripada memulihkan statusnya melalui serialisasi. Misalnya, dalam sebuah kelas, BankPayment.javabidang seperti principal(direktur) dan rate(tarif) dapat diurutkan, dan interest(bunga yang masih harus dibayar) dapat dihitung kapan saja, bahkan setelah deserialisasi. Jika kita ingat, setiap thread di Java memiliki memori lokalnya sendiri dan melakukan operasi baca/tulis pada memori lokal ini. Ketika semua operasi selesai, ia menulis status variabel yang dimodifikasi ke dalam memori bersama, tempat semua thread mengakses variabel tersebut. Biasanya, ini adalah thread normal di dalam mesin virtual. Namun pengubah volatil memberi tahu mesin virtual bahwa akses thread ke variabel tersebut harus selalu mencocokkan salinan variabel tersebut dengan salinan master variabel di memori. Artinya setiap kali thread ingin membaca status suatu variabel, thread harus menghapus status memori internal dan memperbarui variabel dari memori utama. Volatilepaling berguna dalam algoritma bebas kunci. Anda menandai variabel yang menyimpan data bersama sebagai variabel yang mudah menguap, lalu Anda tidak menggunakan kunci untuk mengakses variabel tersebut, dan semua perubahan yang dilakukan oleh satu thread akan terlihat oleh orang lain. Atau jika Anda ingin membuat hubungan "yang terjadi setelah" untuk memastikan bahwa perhitungan tidak terulang, sekali lagi untuk memastikan perubahan terlihat secara real-time. Volatile harus digunakan untuk mempublikasikan objek yang tidak dapat diubah dengan aman di lingkungan multi-thread. Deklarasi bidang public volatile ImmutableObjectmemastikan bahwa semua thread selalu melihat referensi yang tersedia saat ini ke instance.

Perbedaan antara Iterator dan ListIterator?

Kita dapat menggunakan , atau Iteratormengulangi elemen . Tapi itu hanya bisa digunakan untuk mengulangi elemen . Perbedaan lainnya dijelaskan di bawah ini. Kamu bisa: SetListMapListIteratorList
  1. ulangi dalam urutan terbalik.
  2. dapatkan indeks di mana saja.
  3. menambahkan nilai apa pun di mana pun.
  4. tetapkan nilai apa pun pada posisi saat ini.
Semoga sukses dengan studimu!! Penulis artikel Lokesh Gupta Artikel asli Java Core. Pertanyaan wawancara, bagian 1 Java Core. Pertanyaan untuk wawancara, bagian 3
Komentar
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION