JavaRush /Blog Java /Random-MS /Teras Java. Soalan temu bual, bahagian 2
Andrey
Tahap

Teras Java. Soalan temu bual, bahagian 2

Diterbitkan dalam kumpulan
Bagi mereka yang pertama kali mendengar perkataan Java Core, ini adalah asas asas bahasa. Dengan pengetahuan ini, anda boleh pergi ke latihan / latihan dengan selamat.
Teras Java.  Soalan untuk temuduga, bahagian 2 - 1
Soalan-soalan ini akan membantu anda menyegarkan pengetahuan anda sebelum temu duga, atau mempelajari sesuatu yang baharu untuk diri sendiri. Untuk mendapatkan kemahiran praktikal, belajar di JavaRush . Artikel asal Pautan ke bahagian lain: Java Core. Soalan temu bual, bahagian 1 Java Core. Soalan untuk temuduga, bahagian 3

Mengapa kaedah finalize() harus dielakkan?

Kita semua tahu kenyataan bahawa kaedah finalize()dipanggil oleh pemungut sampah sebelum membebaskan memori yang diduduki oleh objek. Berikut ialah contoh program yang membuktikan bahawa panggilan kaedah 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();
	}
	}
}
Output: Dalam blok cuba Dalam blok tangkap Dalam blok akhirnya Dalam blok cuba Dalam blok tangkap Dalam blok akhirnya Dalam blok cuba Dalam blok tangkap Dalam blok akhirnya Secara mengejutkan, kaedah itu finalizetidak dilaksanakan untuk mana-mana benang. Ini membuktikan kata-kata saya. Saya rasa sebabnya ialah pemuktamad dilaksanakan oleh benang pemungut sampah yang berasingan. Jika Mesin Maya Java ditamatkan terlalu awal, maka pengumpul sampah tidak mempunyai masa yang mencukupi untuk membuat dan melaksanakan pemuktamad. Sebab lain untuk tidak menggunakan kaedah ini finalize()mungkin:
  1. Kaedah ini finalize()tidak berfungsi dengan rantai seperti pembina. Ini bermakna apabila anda memanggil pembina kelas, pembina kelas super akan dipanggil tanpa syarat. Tetapi dalam kes kaedah finalize(), ini tidak akan berlaku. Kaedah superclass finalize()mesti dipanggil secara eksplisit.
  2. Sebarang pengecualian yang dilemparkan oleh kaedah finalizediabaikan oleh benang pemungut sampah dan tidak akan disebarkan lebih lanjut, yang bermaksud bahawa peristiwa itu tidak akan direkodkan dalam log anda. Ini sangat teruk, bukan?
  3. Anda juga mendapat penalti prestasi yang ketara jika kaedah itu finalize()ada dalam kelas anda. Dalam Pengaturcaraan Berkesan (edisi ke-2), Joshua Bloch berkata:
    “Ya, dan satu perkara lagi: terdapat penalti prestasi yang besar apabila menggunakan pemuktamad. Pada mesin saya, masa untuk mencipta dan memusnahkan objek mudah ialah kira-kira 5.6 nanosaat.
    Menambah pemuktamad meningkatkan masa kepada 2400 nanosaat. Dalam erti kata lain, ia adalah kira-kira 430 kali lebih perlahan untuk mencipta dan memadam objek dengan pemuktamad."

Mengapa HashMap tidak boleh digunakan dalam persekitaran berbilang benang? Bolehkah ini menyebabkan gelung tak terhingga?

Kami tahu bahawa HashMapini ialah koleksi tidak disegerakkan, rakan sejawat yang disegerakkan ialah HashTable. Jadi, apabila anda mengakses koleksi dan dalam persekitaran berbilang benang di mana semua utas mempunyai akses kepada satu contoh koleksi, maka lebih selamat untuk digunakan HashTableatas sebab yang jelas, seperti mengelakkan bacaan kotor dan memastikan konsistensi data. Dalam kes yang paling teruk, persekitaran berbilang benang ini akan menyebabkan gelung tak terhingga. Ya memang benar. HashMap.get()boleh menyebabkan gelung tak terhingga. Mari kita lihat bagaimana? Jika anda melihat kod sumber kaedah HashMap.get(Object key), ia kelihatan 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)sentiasa boleh menjadi mangsa gelung tak terhingga dalam persekitaran masa jalan berbilang benang jika atas sebab tertentu e.nextia boleh menunjuk kepada dirinya sendiri. Ini akan menyebabkan gelung yang tidak berkesudahan, tetapi bagaimanakah e.nextia akan menunjuk kepada dirinya sendiri (iaitu, kepada e)? Ini boleh berlaku dalam kaedah void transfer(Entry[] newTable)yang dipanggil semasa ia HashMapdiubah saiz.
do {
    Entry next = e.next;
    int i = indexFor(e.hash, newCapacity);
    e.next = newTable[i];
    newTable[i] = e;
    e = next;
} while (e != null);
Sekeping kod ini cenderung untuk mencipta gelung tak terhingga jika saiz semula berlaku pada masa yang sama bahawa utas lain cuba menukar contoh peta ( HashMap). Satu-satunya cara untuk mengelakkan senario ini ialah menggunakan penyegerakan dalam kod anda, atau lebih baik lagi, gunakan koleksi yang disegerakkan.

Terangkan abstraksi dan enkapsulasi. Bagaimana mereka disambungkan?

Dalam perkataan mudah , " Abstraksi hanya memaparkan sifat objek yang penting untuk paparan semasa . " Dalam teori pengaturcaraan berorientasikan objek, abstraksi melibatkan keupayaan untuk mentakrifkan objek yang mewakili "aktor" abstrak yang boleh melakukan kerja, mengubah dan melaporkan perubahan dalam keadaan mereka, dan "berinteraksi" dengan objek lain dalam sistem. Abstraksi dalam mana-mana bahasa pengaturcaraan berfungsi dalam pelbagai cara. Ini dapat dilihat daripada penciptaan rutin untuk menentukan antara muka untuk arahan bahasa peringkat rendah. Beberapa abstraksi cuba mengehadkan keluasan perwakilan keseluruhan keperluan pengaturcara dengan menyembunyikan sepenuhnya abstraksi yang dibina, seperti corak reka bentuk. Biasanya, abstraksi boleh dilihat dalam dua cara: Abstraksi data ialah cara mencipta jenis data yang kompleks dan mendedahkan hanya operasi yang bermakna untuk berinteraksi dengan model data, sementara pada masa yang sama menyembunyikan semua butiran pelaksanaan dari dunia luar. Abstraksi pelaksanaan ialah proses mengenal pasti semua pernyataan penting dan mendedahkannya sebagai unit kerja. Kami biasanya menggunakan ciri ini apabila kami membuat kaedah untuk melakukan sesuatu kerja. Mengehadkan data dan kaedah dalam kelas dalam kombinasi dengan melakukan penyembunyian (menggunakan kawalan akses) sering dipanggil enkapsulasi. Hasilnya ialah jenis data dengan ciri dan tingkah laku. Enkapsulasi pada dasarnya juga melibatkan penyembunyian data dan penyembunyian pelaksanaan. "Merangkum semua yang boleh berubah" . Petikan ini adalah prinsip reka bentuk yang terkenal. Untuk itu, dalam mana-mana kelas, perubahan data boleh berlaku pada masa jalan dan perubahan pelaksanaan boleh berlaku dalam versi akan datang. Oleh itu, enkapsulasi digunakan untuk kedua-dua data dan pelaksanaan. Jadi mereka boleh disambungkan seperti ini:
  • Abstraksi kebanyakannya ialah Perkara yang boleh dilakukan oleh kelas [Idea]
  • Enkapsulasi lebih banyak Bagaimana untuk mencapai fungsi ini [Pelaksanaan]

Perbezaan antara antara muka dan kelas abstrak?

Perbezaan utama boleh disenaraikan seperti berikut:
  • Antara muka tidak boleh melaksanakan sebarang kaedah, tetapi kelas abstrak boleh.
  • Kelas boleh melaksanakan banyak antara muka, tetapi hanya boleh mempunyai satu superclass (abstrak atau bukan abstrak)
  • Antara muka bukan sebahagian daripada hierarki kelas. Kelas yang tidak berkaitan boleh melaksanakan antara muka yang sama.
Apa yang anda perlu ingat ialah: "Apabila anda boleh menerangkan sepenuhnya konsep dari segi "apa yang dilakukannya" tanpa perlu menentukan "bagaimana ia melakukannya", maka anda harus menggunakan antara muka. Jika anda perlu memasukkan beberapa butiran pelaksanaan, maka anda perlu mewakili konsep anda dalam kelas abstrak." Juga, dengan cara lain: Adakah terdapat banyak kelas yang boleh "dihimpunkan bersama" dan diterangkan oleh kata nama tunggal? Jika ya, buat kelas abstrak dengan nama kata nama ini dan warisi kelas daripadanya. Sebagai contoh, Catdan Dogboleh mewarisi daripada kelas abstrak Animal, dan kelas asas abstrak ini akan melaksanakan kaedah void Breathe()- bernafas, yang oleh itu semua haiwan akan melakukan dengan cara yang sama. Apakah kata kerja yang boleh digunakan pada kelas saya dan boleh digunakan untuk orang lain? Buat antara muka untuk setiap kata kerja ini. Sebagai contoh, semua haiwan boleh makan, jadi saya akan mencipta antara muka IFeedabledan menjadikannya Animalmelaksanakan antara muka itu. Hanya cukup baik untuk melaksanakan antara muka Dog( mampu menyukai saya), tetapi bukan semua. Seseorang berkata: perbezaan utama adalah di mana anda mahu pelaksanaan anda. Apabila anda membuat antara muka, anda boleh mengalihkan pelaksanaan ke mana-mana kelas yang melaksanakan antara muka anda. Dengan mencipta kelas abstrak, anda boleh berkongsi pelaksanaan semua kelas terbitan di satu tempat dan mengelakkan banyak perkara buruk seperti menduplikasi kod. HorseILikeable

Bagaimanakah StringBuffer menjimatkan memori?

Kelas Stringdilaksanakan sebagai objek tidak berubah, bermakna apabila anda pada mulanya memutuskan untuk meletakkan sesuatu ke dalam object String, mesin maya memperuntukkan tatasusunan panjang tetap sama persis dengan saiz nilai asal anda. Ini kemudiannya akan dianggap sebagai pemalar di dalam mesin maya, yang memberikan peningkatan prestasi yang ketara jika nilai rentetan tidak berubah. Walau bagaimanapun, jika anda memutuskan untuk menukar kandungan rentetan dalam apa jua cara, apa yang sebenarnya dilakukan oleh mesin maya ialah menyalin kandungan rentetan asal ke dalam ruang sementara, buat perubahan anda, kemudian simpan perubahan tersebut ke tatasusunan memori baharu. Oleh itu, membuat perubahan kepada nilai rentetan selepas permulaan adalah operasi yang mahal. StringBuffer, sebaliknya, dilaksanakan sebagai tatasusunan yang berkembang secara dinamik di dalam mesin maya, yang bermaksud bahawa sebarang operasi pengubahsuaian boleh berlaku pada sel memori sedia ada dan memori baharu akan diperuntukkan mengikut keperluan. Walau bagaimanapun, tiada cara untuk mesin maya melakukan pengoptimuman StringBufferkerana kandungannya dianggap tidak konsisten pada setiap kejadian.

Mengapa kaedah tunggu dan pemberitahuan diisytiharkan dalam kelas Objek dan bukannya Benang?

Kaedah wait, notify, notifyAllhanya diperlukan apabila anda mahu urutan anda mempunyai akses kepada sumber yang dikongsi dan sumber yang dikongsi boleh menjadi sebarang objek java dalam timbunan. Oleh itu, kaedah ini ditakrifkan pada kelas asas Objectsupaya setiap objek mempunyai kawalan yang membolehkan benang menunggu pada monitor mereka. Java tidak mempunyai sebarang objek khas yang digunakan untuk berkongsi sumber yang dikongsi. Tiada struktur data sedemikian ditakrifkan. Oleh itu, adalah menjadi tanggungjawab kelas Objectuntuk menjadi sumber yang dikongsi, dan menyediakan kaedah pembantu seperti wait(), notify(), notifyAll(). Java didasarkan pada idea monitor Charles Hoare. Di Java, semua objek mempunyai monitor. Benang menunggu pada monitor, jadi untuk melakukan penantian kita memerlukan dua parameter:
  • seutas benang
  • memantau (sebarang objek).
Dalam reka bentuk Java, benang tidak boleh ditakrifkan dengan tepat; ia sentiasa menjadi utas semasa yang melaksanakan kod. Walau bagaimanapun, kita boleh mentakrifkan monitor (yang merupakan objek yang boleh kita panggil kaedah wait). Ini adalah reka bentuk yang baik kerana jika kita boleh memaksa mana-mana benang lain untuk menunggu pada monitor tertentu, ia akan mengakibatkan "pencerobohan", menyukarkan mereka bentuk/pengaturcaraan program selari. Ingat bahawa dalam Java, semua operasi yang mengganggu urutan lain ditamatkan (contohnya, stop()).

Tulis program untuk mencipta kebuntuan dalam Java dan perbaikinya

Di Java deadlock, ini ialah situasi di mana sekurang-kurangnya dua utas memegang blok pada sumber yang berbeza, dan kedua-duanya sedang menunggu sumber lain tersedia untuk menyelesaikan tugas mereka. Dan tiada seorang pun daripada mereka dapat meninggalkan kunci pada sumber yang dipegang. Teras Java.  Soalan untuk temuduga, bahagian 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 kod di atas akan mengakibatkan kebuntuan atas sebab yang sangat jelas (diterangkan di atas). Sekarang kita perlu menyelesaikan masalah ini. Saya percaya bahawa penyelesaian kepada sebarang masalah terletak pada punca masalah itu sendiri. Dalam kes kami, model akses kepada A dan B adalah masalah utama. Oleh itu, untuk menyelesaikannya, kami hanya menukar susunan pengendali akses kepada sumber yang dikongsi. Selepas perubahan ia akan kelihatan 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 sekali lagi dan kini anda tidak akan melihat jalan buntu. Saya harap ini membantu anda mengelakkan kebuntuan dan menyingkirkannya jika anda menemuinya.

Apakah yang berlaku jika kelas anda yang melaksanakan antara muka Serializable mengandungi komponen tidak boleh bersiri? Bagaimana untuk membetulkannya?

Dalam kes ini, ia akan dilemparkan NotSerializableExceptionsemasa pelaksanaan. Untuk menyelesaikan masalah ini, terdapat penyelesaian yang sangat mudah - tandai kotak ini transient. Ini bermakna medan yang ditandakan tidak akan bersiri. Jika anda juga ingin menyimpan keadaan medan ini, maka anda perlu mempertimbangkan pembolehubah rujukan, yang sudah melaksanakan Serializable. Anda juga mungkin perlu menggunakan kaedah readResolve()dan writeResolve(). Mari kita ringkaskan:
  • Mula-mula, jadikan medan anda tidak boleh bersiri transient.
  • Mula-mula writeObject, panggil defaultWriteObjectpada urutan untuk menyimpan semua bukan transientmedan, kemudian panggil kaedah yang selebihnya untuk mensiri sifat individu objek tidak boleh bersiri anda.
  • Dalam readObject, mula-mula panggil defaultReadObjectstrim untuk membaca semua bukan transientmedan, kemudian panggil kaedah lain (sepadan dengan kaedah yang anda tambahkan dalam writeObject) untuk menyahsiri bukan transientobjek anda.

Terangkan kata kunci sementara dan tidak menentu dalam Java

"Kata kunci transientdigunakan untuk menunjukkan medan yang tidak akan bersiri." Menurut Spesifikasi Bahasa Java: Pembolehubah boleh ditandakan dengan penunjuk sementara untuk menunjukkan bahawa ia bukan sebahagian daripada keadaan berterusan objek. Sebagai contoh, anda mungkin mengandungi medan yang diperoleh daripada medan lain, dan adalah lebih baik untuk mendapatkannya secara pengaturcaraan daripada memulihkan keadaannya melalui bersiri. Contohnya, dalam kelas, BankPayment.javamedan seperti principal(pengarah) dan rate(kadar) boleh bersiri dan interest(faedah terakru) boleh dikira pada bila-bila masa, walaupun selepas penyahserikatan. Jika kita ingat, setiap thread dalam Java mempunyai memori tempatan sendiri dan melakukan operasi baca/tulis pada memori tempatan ini. Apabila semua operasi selesai, ia menulis keadaan diubah suai pembolehubah ke dalam memori kongsi, dari mana semua utas mengakses pembolehubah. Biasanya, ini adalah benang biasa di dalam mesin maya. Tetapi pengubah suai yang tidak menentu memberitahu mesin maya bahawa akses benang kepada pembolehubah itu mesti sentiasa sepadan dengan salinan pembolehubah itu sendiri dengan salinan induk pembolehubah itu dalam ingatan. Ini bermakna bahawa setiap kali benang ingin membaca keadaan pembolehubah, ia mesti mengosongkan keadaan memori dalaman dan mengemas kini pembolehubah daripada memori utama. Volatilepaling berguna dalam algoritma tanpa kunci. Anda menandakan pembolehubah yang menyimpan data kongsi sebagai tidak menentu, kemudian anda tidak menggunakan kunci untuk mengakses pembolehubah itu dan semua perubahan yang dibuat oleh satu urutan akan kelihatan kepada orang lain. Atau jika anda ingin mencipta hubungan "berlaku selepas" untuk memastikan pengiraan tidak diulang, sekali lagi untuk memastikan perubahan dapat dilihat dalam masa nyata. Meruap harus digunakan untuk menerbitkan objek tidak berubah dengan selamat dalam persekitaran berbilang benang. Pengisytiharan medan public volatile ImmutableObjectmemastikan bahawa semua urutan sentiasa melihat rujukan yang tersedia pada masa ini kepada contoh.

Perbezaan antara Iterator dan ListIterator?

Kita boleh menggunakan , atau Iteratoruntuk mengulangi elemen . Tetapi ia hanya boleh digunakan untuk mengulangi elemen . Perbezaan lain diterangkan di bawah. Awak boleh: SetListMapListIteratorList
  1. lelaran dalam susunan terbalik.
  2. dapatkan indeks di mana-mana sahaja.
  3. menambah apa-apa nilai di mana-mana sahaja.
  4. tetapkan sebarang nilai pada kedudukan semasa.
Semoga berjaya dalam pelajaran!! Pengarang artikel Lokesh Gupta Artikel asal Java Core. Soalan temu bual, bahagian 1 Java Core. Soalan untuk temuduga, bahagian 3
Komen
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION