JavaRush /Java блогу /Random-KY /Java Core. Интервью суроолору, 2-бөлүк
Andrey
Деңгээл

Java Core. Интервью суроолору, 2-бөлүк

Группада жарыяланган
Java Core деген сөздү биринчи жолу уккандар үчүн булар тилдин негизги негиздери. Бул бorм менен, сиз аман-эсен стажировкага/интернатурага бара аласыз.
Java Core.  Маектешүү үчүн суроолор, 2-1-бөлүк
Бул суроолор интервью алдында бorмиңизди жаңыртууга же өзүңүз үчүн жаңы нерсени үйрөнүүгө жардам берет. Практикалык көндүмдөрдү алуу үчүн JavaRush программасында оку . Түпнуска макала Башка бөлүктөргө шилтемелер: Java Core. Интервью суроолору, 1-бөлүк Java Core. Интервью үчүн суроолор, 3-бөлүк

Эмне үчүн finalize() ыкмасынан качуу керек?

finalize()Объект ээлеген эстутумду бошотконго чейин таштанды жыйноочу тарабынан ыкма чакырылат деген сөз баарыбызга маалым . Бул жерде ыкма чалуу finalize()кепилдик жок экенин далилдеген мисал программасы болуп саналат:
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();
	}
	}
}
Чыгуу: try block In catch block In sonunda block In try block In catch block In sonunda block In try block In catch block In sonunda блок Таң калыштуусу, метод finalizeэч бир жип үчүн аткарылган эмес. Бул менин сөздөрүмдү далилдейт. Менин оюмча, жыйынтыктоочулар өзүнчө таштанды жыйноочу жип менен аткарылат. Эгерде Java Virtual Machine өтө эле эрте бүтсө, анда таштанды жыйноочунун жыйынтыктоочуларды түзүү жана ишке ашыруу үчүн жетиштүү убактысы жок. Бул ыкманы колдонбоо үчүн башка себептер finalize()болушу мүмкүн:
  1. Метод finalize()конструкторлор сыяктуу чынжырлар менен иштебейт. Бул класстын конструкторун чакырганда, суперкласстын конструкторлору шартсыз чакырылат дегенди билдирет. Бирок ыкмада finalize()бул ээрчибейт. Superclass ыкмасы finalize()ачык аталууга тийиш.
  2. Метод менен ыргытылган бардык өзгөчөлүктөр finalizeташтанды жыйноочу жип тарабынан этибарга алынbyte жана андан ары жайылтылbyte, бул окуя журналдарыңызга жазылbyte дегенди билдирет. Бул абдан жаман, туурабы?
  3. finalize()Эгер бул ыкма сиздин классыңызда болсо, сиз дагы олуттуу жаза аласыз . Натыйжалуу программалоодо (2-бас.) Жошуа Блох мындай деди:
    «Ооба, жана дагы бир нерсе: жыйынтыктоочуларды колдонууда чоң аткаруу жазасы бар. Менин машинамда жөнөкөй an objectтерди түзүү жана жок кылуу убактысы болжол менен 5,6 наносекундду түзөт.
    Аяктоочуну кошуу убакытты 2400 наносекундка чейин көбөйтөт. Башкача айтканда, жыйынтыктоочу менен an objectти түзүү жана жок кылуу болжол менен 430 эсе жайыраак».

Эмне үчүн HashMap көп жиптүү чөйрөдө колдонулбашы керек? Бул чексиз циклге алып келиши мүмкүнбү?

Бул синхрондоштурулган эмес жыйнак экенин билебиз HashMap, анын синхрондоштурулган кесиптеши HashTable. Ошентип, сиз коллекцияга кирип жатканыңызда жана бардык жиптер коллекциянын бир инстанциясына кире алган көп жиптүү чөйрөдө, анда HashTableкир окуудан качуу жана маалыматтардын ырааттуулугун камсыз кылуу сыяктуу айкын себептерден улам колдонуу коопсуз. Эң начар учурда, бул көп жиптүү чөйрө чексиз циклди пайда кылат. Ооба бул чындык. HashMap.get()чексиз циклге алып келиши мүмкүн. Келгиле, кантип көрөлү? Эгер сиз методдун баштапкы 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)e.nextкандайдыр бир себептерден улам өзүнө ишарат кыла турган болсо, ар дайым көп агымдуу иштөө чөйрөсүндө чексиз циклдин курмандыгы болуп калышы мүмкүн . Бул чексиз циклди пайда кылат, бирок e.nextал өзүн (б.а. e) кантип көрсөтөт? Бул анын өлчөмү өзгөртүлүп void transfer(Entry[] newTable)жатканда чакырылган ыкмада болушу мүмкүн .HashMap
do {
    Entry next = e.next;
    int i = indexFor(e.hash, newCapacity);
    e.next = newTable[i];
    newTable[i] = e;
    e = next;
} while (e != null);
Коддун бул бөлүгү чексиз циклди түзүүгө жакын, эгерде өлчөмдү өзгөртүү ошол эле учурда башка жип картанын инстанциясын өзгөртүүгө аракет кылып жатканда пайда болсо ( HashMap). Бул сценарийди болтурбоонун бирден-бир жолу - codeуңузда синхрондоштурууну колдонуу, же андан да жакшысы, синхрондоштурулган коллекцияны колдонуу.

Абстракцияны жана инкапсуляцияны түшүндүрүңүз. Алар кантип байланышкан?

Жөнөкөй сөз менен айтканда , " Абстракция учурдагы көрүнүш үчүн маанилүү болгон an objectтин касиеттерин гана көрсөтөт . " Объектке багытталган программалоо теориясында абстракция жумушту аткара ала турган, өз абалынын өзгөрүшүн жана өзгөрүшүн билдире ала турган абстракттуу “актёрлорду” чагылдырган an objectтерди аныктоо мүмкүнчүлүгүн камтыйт жана системанын башка an objectтери менен “өз ара аракеттенет”. Ар кандай программалоо тorндеги абстракция ар кандай жолдор менен иштейт. Муну төмөнкү деңгээлдеги тил буйруктары үчүн интерфейстерди аныктоо тартибин түзүүдөн көрүүгө болот. Кээ бир абстракциялар долбоорлоо үлгүлөрү сыяктуу, алар курулган абстракцияларды толугу менен жашыруу менен программисттин керектөөлөрүнүн жалпы өкүлчүлүгүнүн кеңдигин чектөөгө аракет кылышат. Адатта, абстракцияны эки жол менен көрүүгө болот: Берorштерди абстракциялоо – бул маалыматтардын татаал түрлөрүн түзүү жана маалымат модели менен өз ара аракеттенүү үчүн маанилүү операцияларды гана көрсөтүү, ошол эле учурда ишке ашыруунун бардык деталдарын тышкы дүйнөдөн жашыруу. Аткаруу абстракциясы - бул бардык маанилүү билдирүүлөрдү аныктоо жана аларды иш бирдиги катары көрсөтүү процесси. Биз, адатта, бул функцияны кандайдыр бир ишти аткаруу үчүн ыкманы түзүп жатканда колдонобуз. Жашырууну аткаруу менен бирге класстар ичиндеги маалыматтарды жана методдорду чектөө (жетүүнү башкарууну колдонуу) көбүнчө инкапсуляция деп аталат. Натыйжада мүнөздөмөлөрү жана жүрүм-туруму бар маалымат түрү. Инкапсуляция ошондой эле маалыматтарды жашырууну жана ишке ашырууну жашырууну камтыйт. "Өзгөрө турган нерселердин баарын капсулдаңыз" . Бул цитата белгилүү дизайн принцип болуп саналат. Ушуга байланыштуу, кайсы класста болбосун, берorштердин өзгөрүшү аткаруу убагында болушу мүмкүн жана ишке ашыруу өзгөрүүлөрү келечектеги versionларда болушу мүмкүн. Ошентип, инкапсуляция маалыматтарга да, ишке ашырууга да тиешелүү. Ошентип, аларды төмөнкүдөй туташтырууга болот:
  • Абстракция көбүнчө класс эмне кыла алат [Идея]
  • Инкапсуляция көбүрөөк Бул функцияга кантип жетсе болот [Ишке ашыруу]

Интерфейс менен абстракттуу класстын айырмасы барбы?

Негизги айырмачылыктарды төмөндөгүдөй санаса болот:
  • Интерфейс эч кандай ыкмаларды ишке ашыра алbyte, бирок абстракттуу класс жасай алат.
  • Класс көптөгөн интерфейстерди ишке ашыра алат, бирок бир гана суперкласс болушу мүмкүн (абстрактуу же абстракттуу эмес)
  • Интерфейс класс иерархиясынын бөлүгү эмес. Байланышсыз класстар бир эле интерфейсти ишке ашыра алат.
Эсиңизде болсун, бул: "Качан сиз концепцияны "бул муну кантип кылат" деп көрсөтпөстөн, "эмне кылат" деген термин менен толук сүрөттөп бере алсаңыз, анда интерфейсти колдонушуңуз керек. Эгер сиз ишке ашыруунун айрым деталдарын камтышыңыз керек болсо, анда сиз концепцияңызды абстракттуу класста көрсөтүшүңүз керек." Ошондой эле, башкача айтканда: "Бирге топтолгон" жана бир зат атооч менен сүрөттөлүүчү көп класстар барбы? Андай болсо, бул зат атоочтун аты менен абстракттуу класс түзүңүз жана андан класстарды мурастаңыз. Мисалы, Catжана Dogабстракттуу класстан мураска алат Animalжана бул абстракттуу базалык класс - дем алуу ыкмасын ишке ашырат void Breathe(), муну менен бардык жаныбарлар бирдей аткарышат. Кандай этиштер менин классыма колдонулушу мүмкүн жана башкаларга колдонулушу мүмкүн? Бул этиштердин ар бири үчүн интерфейс түзүңүз. Мисалы, бардык жаныбарлар жей алышат, ошондуктан мен интерфейс түзүп IFeedable, аны Animalошол интерфейсти ишке ашырам. Интерфейсти ишке ашыруу үчүн гана жетиштүү Dog( мени жактыра алат), бирок баары эмес. Кимдир бирөө мындай деди: негизги айырмачылык, сиз ишке ашырууну каалаган жерде. Интерфейс түзгөндө, сиз ишке ашырууну интерфейсиңизди ишке ашырган каалаган класска жылдыра аласыз. Абстракттуу классты түзүү менен, сиз бардык алынган класстардын ишке ашырылышын бир жерде бөлүшө аласыз жана codeду кайталоо сыяктуу көптөгөн жаман нерселерден качсаңыз болот. HorseILikeable

StringBuffer эстутумду кантип сактайт?

Класс Stringөзгөрүлбөс an object катары ишке ашырылат, демек, сиз алгач an objectке бир нерсе коюуну чечкенде String, виртуалдык машина сиздин баштапкы маанисиңиздин так өлчөмүндө белгиленген узундуктагы массивди бөлүп берет. Андан кийин бул виртуалдык машинанын ичиндеги константа катары каралат, ал саптын мааниси өзгөрбөсө, иштин олуттуу жакшыруусун камсыз кылат. Бирок, эгер сиз саптын мазмунун кандайдыр бир жол менен өзгөртүүнү чечсеңиз, виртуалдык машина түпнуска саптын мазмунун убактылуу мейкиндикке көчүрүп, өзгөртүүлөрүңүздү киргизип, анан ал өзгөртүүлөрдү жаңы эс тутум массивине сактап коюңуз. Ошентип, инициализациядан кийин саптын маанисине өзгөртүү киргизүү кымбат операция болуп саналат. StringBuffer, экинчи жагынан, виртуалдык машинанын ичинде динамикалык түрдө кеңейүүчү массив катары ишке ашырылат, демек, учурдагы эс тутум уячасында каалаган өзгөртүү операциясы болушу мүмкүн жана керек болсо жаңы эс бөлүштүрүлөт. Бирок, виртуалдык машинанын оптималдаштырууга эч кандай жолу жок, StringBufferанткени анын мазмуну ар бир инстанцияда дал келбейт деп эсептелет.

Эмне үчүн күтүү жана кабарлоо ыкмалары Threadдин ордуна Object классында жарыяланган?

wait, notify, методдору notifyAllсиздин жиптериңиздин жалпы ресурстарга кирүү мүмкүнчүлүгүн кааласаңыз гана керек болот жана жалпы ресурс үймөктөгү каалаган Java an objectиси болушу мүмкүн. Ошентип, бул ыкмалар базалык класста аныкталат, Objectандыктан ар бир an object жиптерге монитордо күтүүгө мүмкүндүк берген башкарууга ээ. Java'да жалпы ресурсту бөлүшүү үчүн колдонулган атайын an object жок. Мындай маалымат структурасы аныкталган эмес. Демек, бул класстын милдети Objectжалпы ресурс болуп, wait(), notify(), сыяктуу жардамчы ыкмаларды камсыз кылуу notifyAll(). Java Чарльз Хоардын мониторлор идеясына негизделген. Java тorнде бардык an objectтерде монитор бар. Жиптер мониторлордо күтөт, ошондуктан күтүүнү аткаруу үчүн бизге эки параметр керек:
  • жип
  • монитор (ар кандай an object).
Java дизайнында жипти так аныктоо мүмкүн эмес; ал ар дайым codeду аткаруучу учурдагы жип болуп саналат. Бирок, биз мониторду аныктай алабыз (ал методду чакыра турган an object wait). Бул жакшы дизайн, анткени биз кандайдыр бир башка жипти белгилүү бир монитордо күтүүгө мажбурлай алсак, ал "баскынчылыкка" алып келип, параллелдүү программаларды долбоорлоо/программалоону кыйындатат. Javaда башка жиптерге тоскоол болгон бардык операциялар эскирилгенин унутпаңыз (мисалы, stop()).

Java'да туюкту түзүү жана аны оңдоо үчүн программа жазыңыз

Java-да deadlockбул, жок эле дегенде, эки жип ар кандай ресурстарда блокту кармап турган кырдаал жана экөө тең башка ресурстун тапшырмасын аткаруу үчүн жеткorктүү болушун күтүп жатышат. Жана алардын бири да кармалып турган ресурста кулпу калтыра алbyte. 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ду иштетүү абдан ачык себептерден улам туюкка алып келет (жогоруда түшүндүрүлгөн). Эми бул маселени чечишибиз керек. Кандай гана маселенин чечorшин ошол маселенин өзүндө жатат деп эсептейм. Биздин учурда, А жана В кирүү модели негизги көйгөй болуп саналат. Ошондуктан, аны чечүү үчүн, биз жөн гана жалпы ресурстарга кирүү операторлорунун тартибин өзгөртүү. Өзгөртүүдөн кийин ал төмөнкүдөй болот:
// 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");
			}
		}
	}
};
Бул классты кайра иштетиңиз, эми туюкту көрбөйсүз. Бул сизге туюктан качууга жана аларга туш болсоңуз, алардан арылууга жардам берет деп үмүттөнөм.

Сериялаштырылуучу интерфейсти ишке ашырган классыңыз серияланбай турган компонентти камтыса эмне болот? Муну кантип оңдоо керек?

NotSerializableExceptionБул учурда, ал аткаруу учурунда ыргытылат . Бул көйгөйдү чечүү үчүн абдан жөнөкөй чечим бар - бул кутучаларды белгилеңиз transient. Бул белгиленген талаалар серияланbyte дегенди билдирет. Эгер сиз дагы бул талаалардын абалын сактагыңыз келсе, анда Serializable. readResolve()Сиз жана ыкмаларын колдонушуңуз керек болушу мүмкүн writeResolve(). Жыйынтыктап көрөлү:
  • Биринчиден, талааңызды сериалдаштырууга болбойт transient.
  • Биринчиден writeObject, defaultWriteObjectбардык талааларды сактоо үчүн жипти чакырыңыз transient, андан кийин сериялаштырылбаган an objectиңиздин жеке касиеттерин сериялаштыруу үчүн калган ыкмаларды чакырыңыз.
  • ичинде readObject, адегенде defaultReadObjectбардык эмес талааларды окуу үчүн агымга чалыңыз transient, андан кийин an objectиңизди сериядан чыгаруу үчүн башка ыкмаларды (сиз кошкондорго туура келет writeObject) чакырыңыз transient.

Javaдагы убактылуу жана туруксуз ачкыч сөздөрдү түшүндүрүңүз

"Ачкыч сөз transientсерияланбай турган талааларды көрсөтүү үчүн колдонулат." Java тorнин спецификациясына ылайык: Өзгөрмөлөр an objectтин туруктуу абалынын бир бөлүгү эмес экенин көрсөтүү үчүн убактылуу индикатор менен белгилениши мүмкүн. Мисалы, сиз башка талаалардан алынган талааларды камтышы мүмкүн жана алардын абалын сериялаштыруу аркылуу калыбына келтиргенден көрө, программалык түрдө алуу артык. Мисалы, класста (директор) жана (ставка) BankPayment.javaсыяктуу талаалар сериялаштырылышы мүмкүн жана (чогултулган пайыздар) сериядан ажыратылгандан кийин да каалаган убакта эсептелиши мүмкүн. Эсибизде болсок, Javaдагы ар бир жип өзүнүн локалдык эсине ээ жана бул локалдык эстутумда окуу/жазуу операцияларын аткарат. Бардык операциялар аткарылгандан кийин, ал өзгөрмөнүн өзгөртүлгөн абалын жалпы эс тутумга жазат, ал жерден бардык жиптер өзгөрмөгө кире алат. Адатта, бул виртуалдык машинанын ичиндеги кадимки жип. Бирок туруксуз модификатор виртуалдык машинага жиптин ошол өзгөрмөгө кирүү мүмкүнчүлүгү ар дайым ошол өзгөрмөнүн өзүнүн көчүрмөсү менен эстутумдагы өзгөрмөнүн башкы көчүрмөсү менен дал келиши керектигин айтат. Бул жип өзгөрмөнүн абалын окугусу келген сайын ички эс тутумдун абалын тазалап, өзгөрмөнү негизги эстутумдан жаңыртуу керек дегенди билдирет. кулпусуз алгоритмдерде эң пайдалуу. Бөлүшүлгөн дайындарды сактаган өзгөрмөнү туруксуз деп белгилейсиз, андан кийин ал өзгөрмөгө кирүү үчүн кулпуларды колдонбойсуз жана бир жип тарабынан жасалган бардык өзгөртүүлөр башкаларга көрүнүп калат. Же болбосо, эсептөөлөр кайталанбашы үчүн, "кийин болгон" мамилесин түзгүңүз келсе, өзгөртүүлөр реалдуу убакытта көрүнүп турушун камсыз кылуу үчүн. Учуучу көп жиптүү чөйрөдө өзгөрүлгүс an objectтерди коопсуз жарыялоо үчүн колдонулушу керек. Талаа декларациясы бардык жиптер инстанцияга учурда жеткorктүү шилтемени дайыма көрүшүн камсыздайт. principalrateinterestVolatilepublic volatile ImmutableObject

Iterator жана ListIterator ортосундагы айырма?

Биз колдоно алабыз , же Iteratorэлементтерди кайталоо үчүн . Бирок аны элементтердин үстүнөн кайталоо үчүн гана колдонсо болот . Башка айырмачылыктар төмөндө сүрөттөлөт. Сенин колуңдан келет: SetListMapListIteratorList
  1. тескери тартипте кайталоо.
  2. каалаган жерден индексти алуу.
  3. каалаган жерде кандайдыр бир маанини кошуу.
  4. учурдагы абалына каалаган маанини коюу.
Сиздин изилдөө менен ийгorк коштосун!! Макаланын автору Локеш Гупта Оригиналдуу макала Java Core. Интервью суроолору, 1-бөлүк Java Core. Интервью үчүн суроолор, 3-бөлүк
Комментарийлер
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION