JavaRush /Java Blog /Random-TL /Java Core. Mga tanong sa panayam, bahagi 2
Andrey
Antas

Java Core. Mga tanong sa panayam, bahagi 2

Nai-publish sa grupo
Para sa mga nakarinig ng salitang Java Core sa unang pagkakataon, ito ang mga pangunahing pundasyon ng wika. Sa kaalamang ito, maaari kang ligtas na pumunta para sa isang internship/internship.
Java Core. Вопросы к собеседованию, ч. 2 - 1
Ang mga tanong na ito ay makakatulong sa iyo na i-refresh ang iyong kaalaman bago ang interbyu, o matuto ng bago para sa iyong sarili. Upang makakuha ng mga praktikal na kasanayan, mag-aral sa JavaRush . Orihinal na artikulo Mga link sa iba pang bahagi: Java Core. Mga tanong sa panayam, bahagi 1 Java Core. Mga tanong para sa isang panayam, bahagi 3

Bakit dapat iwasan ang finalize() na pamamaraan?

Alam nating lahat ang pahayag na ang isang pamamaraan finalize()ay tinatawag ng tagakolekta ng basura bago palayain ang memorya na inookupahan ng isang bagay. Narito ang isang halimbawang programa na nagpapatunay na ang isang method call finalize()ay hindi ginagarantiyahan:
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: Sa try block Sa catch block Sa wakas block Sa try block Sa catch block Sa wakas block Sa try block Sa catch block Sa finally block Nakakagulat, ang pamamaraan finalizeay hindi naisakatuparan para sa anumang thread. Ito ay nagpapatunay sa aking mga salita. Sa tingin ko ang dahilan ay ang mga finalizer ay isinasagawa ng isang hiwalay na thread ng kolektor ng basura. Kung ang Java Virtual Machine ay masyadong maagang magwawakas, kung gayon ang tagakolekta ng basura ay walang sapat na oras upang lumikha at magsagawa ng mga finalizer. Ang iba pang mga dahilan upang hindi gamitin ang pamamaraan finalize()ay maaaring:
  1. Ang pamamaraan finalize()ay hindi gumagana sa mga kadena tulad ng mga konstruktor. Nangangahulugan ito na kapag tumawag ka ng isang class constructor, ang mga superclass constructor ay tatawagin nang walang kondisyon. Ngunit sa kaso ng pamamaraan finalize(), hindi ito mangyayari. Ang pamamaraan ng superclass finalize()ay dapat na tahasang tawagan.
  2. Ang anumang pagbubukod na itinapon ng pamamaraan finalizeay binabalewala ng thread ng kolektor ng basura at hindi na ipapalaganap pa, na nangangahulugan na ang kaganapan ay hindi itatala sa iyong mga log. Ito ay napakasama, hindi ba?
  3. Makakakuha ka rin ng makabuluhang parusa sa pagganap kung ang pamamaraan finalize()ay naroroon sa iyong klase. Sa Effective Programming (2nd ed.), sinabi ni Joshua Bloch:
    “Oo, at isa pa: may malaking parusa sa pagganap kapag gumagamit ng mga finalizer. Sa aking makina, ang oras upang lumikha at sirain ang mga simpleng bagay ay humigit-kumulang 5.6 nanosecond.
    Ang pagdaragdag ng finalizer ay nagpapataas ng oras sa 2400 nanoseconds. Sa madaling salita, humigit-kumulang 430 beses na mas mabagal ang paggawa at pagtanggal ng isang bagay na may finalizer."

Bakit hindi dapat gamitin ang HashMap sa isang multi-threaded na kapaligiran? Maaari ba itong maging sanhi ng isang walang katapusang loop?

Мы знаем, что HashMap — это не синхронизированная коллекция, синхронизированным аналогом которой является HashTable. Таким образом, когда вы обращаетесь к коллекции и многопоточном окружении, где все нити имеют доступ к одному экземпляру коллекции, тогда безопасней использовать HashTable по очевидным причинам, например во избежание грязного чтения и обеспечения согласованности данных. В худшем случае это многопоточное окружение вызовет бесконечный цикл. Да, это правда. HashMap.get() может вызвать бесконечный цикл. Давайте посмотрим How? Если вы посмотрите на исходный code метода HashMap.get(Object key), он выглядит так:
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) всегда может стать жертвой бесконечного цикла в многопоточном окружении времени исполнения, если по Howой-то причине e.next сможет указать на себя. Это станет причиной бесконечного цикла, но How e.next укажет на себя(то есть на e)? Это может произойти в методе void transfer(Entry[] newTable), который вызывается, в то время How HashMap изменяет размер.
do {
    Entry next = e.next;
    int i = indexFor(e.hash, newCapacity);
    e.next = newTable[i];
    newTable[i] = e;
    e = next;
} while (e != null);
Этот фрагмент codeа склонен к созданию бесконечного цикла, если изменение размера происходит в то же время, когда другая нить пытается изменить экземпляр карты (HashMap). Единственный способ избежать описанного сценария – использовать синхронизацию в codeе, or еще лучше, использовать синхронизированную коллекцию.

Объясните абстракцию и инкапсуляцию. Как они связаны?

Простыми словами «Абстракция отображает только те свойства an object, которые значимы для текущего ракурса». В теории an objectно-ориентированного программирования, абстракция включает возможность определения an objectов, представляющих абстрактных «действующих лиц», которые могут выполнять работу, изменять и сообщать об изменении своего состояния, и «взаимодействовать» с другими an objectми в системе. Абстракция в любом языке программирования работает во многих отношениях. Это видно начиная от создания подпрограмм для определения интерфейсов низкоуровневых языковых команд. Некоторые абстракции стараются ограничить ширину общего представления потребностей программиста, fully скрывая абстракции, на которых они построены, например шаблоны проектирования. Как правило, абстракцию можно увидеть в двух отношениях: Абстракция данных – это способ создания сложных типов данных и выставляя только значимые операции для взаимодействия с моделью данных, в то же время, скрывая все детали реализации от внешнего мира. Абстракция исполнения – это процесс выявления всех значимых операторов и выставляя их How рабочую единицу. Мы обычно используем эту особенность, когда мы создаем метод для выполнения Howой-либо работы. Заключение данных и методов внутри классов в комбинации с осуществлением сокрытия (используя контроль доступа) часто называется инкапсуляцией. Результатом является тип данных с характеристиками и поведением. Инкапсуляция, в сущности, содержит также сокрытие данных и сокрытие реализации. «Инкапсулируйте все, что может измениться». Эта цитата является известным принципом проектирования. Если на то пошло, в любом классе, изменения данных могут произойти во время исполнения и изменения в реализации может произойти в следующих versionх. Таким образом, инкапсуляция применима How к данным, так и к реализации. Итак, они могут быть связаны таким образом:
  • Абстракция по большей части является What класс может делать [Идея]
  • Инкапсуляция более является Как достигнуть данной функциональности [Реализация]

Различия между интерфейсом и абстрактным классом?

Основные различия могут быть перечислены следующим:
  • Интерфейс не может реализовать ниHowих методов, зато абстрактный класс может.
  • Класс может реализовать множество интерфейсов, но может иметь только один суперкласс (абстрактный or не абстрактный)
  • Интерфейс не является частью иерархии классов. Несвязанные классы могут реализовывать один и тот же интерфейс.
Вы должны запомнить следующее: «Когда вы можете fully описать понятие в словах «что это делает» без необходимости уточнять «How это делает», тогда вы должны использовать интерфейс. Если вам необходимо включить некоторые детали реализации, тогда вам надо представить вашу концепцию в абстрактном классе». Также, говоря другими словами: Много есть классов, которые могут быть «группированы вместе» и описаны одним существительным? Раз так, создайте абстрактный класс с именем этого существительного, и унаследуйте классы от него. К примеру, Cat и Dog могут наследоваться от абстрактного класса Animal, и этот абстрактный базовый класс будет реализовывать метод void Breathe() – дышать, который все животные будут таким образом выполнять одинаковым способом. Какие глаголы могут быть применены к моему классу и могут применяться к другим? Создайте интерфейс к каждому из этих глаголов. Например, все животные могут питаться, поэтому я создам интерфейс IFeedable и сделаю Animal реализующим этот интерфейс. Только Dog и Horse достаточно хороши для реализации интерфейса ILikeable (способны мне нравиться), но не все. Кто-то сказал: главное отличие в том, где вы хотите вашу реализацию. Создавая интерфейс, вы можете переместить реализацию в любой класс, который реализует ваш интерфейс. Создавая абстрактный класс, вы можете разделить реализацию всех производных классов в одном месте и избежать много плохих вещей, таких How дублирование codeа.

Как StringBuffer экономит память?

Класс String реализован How неизменный (immutable) an object, то есть, когда вы изначально решor положить что-то в an object String, виртуальная машина выделяет массив фиксированной длины, точно такого размера, How и ваше первоначальное meaning. В дальнейшем это будет обрабатываться How константа внутри виртуальной машины, что предоставляет значительное улучшение производительности в случае, если meaning строки не изменяется. Однако если вы решите изменить содержимое строки любым способом, на самом деле виртуальная машина копирует содержимое исходной строки во временное пространство, делает ваши изменения, затем сохраняет эти изменения в новый массив памяти. Таким образом, внесение изменений в meaning строки после инициализации является дорогостоящей операцией. StringBuffer, с другой стороны выполнен в виде динамически расширяемого массива внутри виртуальной машины, что означает, что любая операция изменения может происходить на существующей ячейке памяти, и новая память будет выделяться по мере необходимости. Однако нет ниHowой возможности виртуальной машине сделать оптимизацию StringBuffer, поскольку его содержимое считается непостоянным в каждом экземпляре.

Почему методы wait и notify объявлены у класса Object взамен Thread?

Методы wait, notify, notifyAll необходимы только тогда, когда вы хотите, чтобы ваши нити имели доступ к общим ресурсам и общий ресурс мог быть любым java an objectом в хипе(heap). Таким образом эти методы определены на базовом классе Object, так что каждый an object имеет контроль, позволяющий нитям ожидать на своем мониторе. Java не имеет Howого-либо специального an object, который используется для разделения общего ресурса. НиHowая такая структура данных не определена. Поэтому на класс Object возложена ответственность иметь возможность становиться общим ресурсом, и предоставлять вспомогательные методы, такие How wait(), notify(), notifyAll(). Java основывается на идее мониторов Чарльза Хоара (Hoare). В Java все an objectы имеют монитор. Нити ожидают на мониторах, поэтому для выполнения ожидания нам необходимо два параметра:
  • нить
  • монитор (любой an object).
В Java проектировании, нить не может быть точно определена, это всегда текущая нить, исполняющая code. Однако мы можем определить монитор (который является an objectом, у которого мы можем вызвать метод wait). Это хороший замысел, поскольку если мы можем заставить любую другую нить ожидать на определенном мониторе, это приведет к «вторжению», оказывая трудности проектирования/программирования параллельных программ. Помните, что в Java все операции, которые вторгаются в другие нити являются устаревшими(например, stop()).

Напишите программу для создания deadlock в Java и исправьте его

В Java deadlock – это ситуация, когда минимум две нити удерживают блок на разных ресурсах, и обе ожидают освобождения другого ресурса для завершения своей задачи. И ни одна не в состоянии оставить блокировку удерживаемого ресурса. Java Core. Вопросы к собеседованию, ч. 2 - 2 Пример программы:
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;
		}
	}
}
Запуск приведенного codeа приведет к deadlock по весьма очевидным причинам (объяснены выше). Теперь нам необходимо решить эту проблему. Я верю, что решение любой проблемы лежит в корне самой проблемы. В нашем случае модель доступа к А и В является главной проблемой. Поэтом для решения её, мы просто изменим порядок операторов доступа к разделяемым ресурсам. После изменения это будет выглядеть так:
// 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");
			}
		}
	}
};
Запустите снова этот класс, и теперь вы не увидите deadlock. Я надеюсь, это поможет вам избежать deadlocks и избавиться от них, если столкнетесь.

What случится, если ваш класс, реализующий Serializable интерфейс содержит несериализуемый компонент? Как исправить это?

В таком случае будет выброшено NotSerializableException в процессе выполнения. Для исправления этой проблемы, есть очень простое решение – отметить эти поля transient. Это означает, что отмеченные поля не будут сериализованы. Если вы также хотите сохранить состояние этих полей, тогда вам необходимо рассмотреть ссылочные переменные, которые уде реализуют интерфейс Serializable. Также вам может понадобиться использовать методы readResolve() и writeResolve(). Подведем итоги:
  • Во-первых, сделайте ваше несериализуемое поле transient.
  • В writeObject первым делом вызовите defaultWriteObject на потоке, для сохранения всех не transient полей, затем вызовите остальные методы для сериализации индивидуальных свойств вашего несериализуемого an object.
  • В readObject, сперва вызовите defaultReadObject на потоке для чтения всех не transient полей, затем вызовите другие методы (соответствующие тем, которые вы добавor в writeObject) для десериализации вашего не transient an object.

Объясните ключевые слова transient и volatile в Java

«Ключевое слово transient используется для обозначения полей, которые не будут сериализованы». Согласно спецификации языка Java: Переменные могут быть маркированы индикатором transient для обозначения, что они не являются частью устойчивого состояния an object. Например, вы можете содержать поля, полученные из других полей, и их предпочтительнее получать программно, чем восстанавливать их состояние через сериализацию. К примеру, в классе BankPayment.java такие поля, How principal (директор) и rate (ставка) могут быть сериализованы, а interest (начисленные проценты) могут быть вычислены в любое время, даже после десериализации. Если мы вспомним, каждая нить в Java имеет собственную локальную память и производит операции чтения/записи в эту локальную память. Когда все операции сделаны, она записывает модифицированное состояние переменной в общую память, откуда все нити получают доступ к переменной. Как правило, это обычный поток внутри виртуальной машины. Но модификатор volatile говорит виртуальной машине, что обращение нити к этой переменной всегда должно согласовывать свою собственную копию этой переменной с основной копией переменной в памяти. Это означает, что каждый раз, когда нить хочет прочитать состояние переменной, она должна очистить состояние внутренней памяти и обновить переменную из основной памяти. Volatile наиболее полезно в свободных от блокировок алгоритмах. Вы отмечаете переменную, хранящую общие данные How volatile, тогда вы не используете блокировки для доступа к этой переменной, и все изменения, сделанные одной нитью, будут видимы для других. Или если вы хотите создать отношение «случилось-после» для обеспечения того, чтобы не повторялись вычисления, опять же для обеспечения видимости изменений в реальном времени. Volatile должно использоваться для безопасной публикации неизменяемых an objectов в многопоточном окружении. Объявление поля public volatile ImmutableObject обеспечивает, что все нити всегда видят текущую доступную ссылку на экземпляр.

Difference между Iterator и ListIterator?

Мы можем использовать Iterator для перебора элементов Set, List or Map. Но ListIterator может быть применим только для перебора элементов List. Другие отличия описаны ниже. Вы можете:
  1. итерировать в обратном порядке.
  2. получить индекс в любом месте.
  3. добавить любое meaning в любом месте.
  4. установить любое meaning в текущей позиции.
Удачи в обучении!! Author статьи Lokesh Gupta Оригинал статьи Java Core. Вопросы к собеседованию, ч. 1 Java Core. Вопросы к собеседованию, ч. 3
Mga komento
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION