JavaRush /Java Blog /Random EN /java core. Questions for the interview, part 2
Andrey
Level 26

java core. Questions for the interview, part 2

Published in the Random EN group
For those who hear the word Java Core for the first time, these are the fundamental foundations of the language. With this knowledge, you can already safely go on an internship / internship.
java core.  Questions for the interview, part 2 - 1
These questions will help you refresh your knowledge before the interview, or learn something new for yourself. For practical skills, study on CodeGym . Original article Links to other parts: Java Core. Interview Questions Part 1 Java Core. Questions for the interview, part 3

Why should the finalize() method be avoided?

We all know the statement that a method finalize()is called by the garbage collector before the memory occupied by an object is deallocated. Here is an example program that proves that a method call finalize()is not guaranteed:
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: In try block In catch block In finally block In try block In catch block In finally block In try block In catch block In finally block Surprisingly, the method finalizewas not executed for any thread. This proves my words. I think the reason is that the finalizers are executed by a separate garbage collector thread. If the JVM terminates too early, then the garbage collector does not have enough time to create and execute the finalizers. Other reasons not to use the method finalize()might be:
  1. The method finalize()does not work with chains like constructors do. This means that when you call a class constructor, the superclass constructors will be called unconditionally. But in the case of the method finalize(), this will not follow. The superclass method finalize()must be called explicitly.
  2. Any exception thrown by the method finalizeis ignored by the garbage collector thread and will not be propagated further, which means the event will not be logged to your logs. It's very bad, isn't it?
  3. You also get a significant performance penalty if the method finalize()is present in your class. In Effective Programming (2nd ed.), Joshua Bloch said,
    “Yes, and one more thing: there is a big performance penalty when using finalizers. On my machine, the creation and destruction of simple objects takes about 5.6 nanoseconds.
    Adding a finalizer increases the time to 2400 nanoseconds. In other words, creating and deleting an object with a finalizer is about 430 times slower.”

Why shouldn't HashMap be used in a multi-threaded environment? Can this cause an infinite loop?

We know that HashMapis an unsynchronized collection, of which HashTable. Thus, when you are accessing a collection in a multi-threaded environment where all threads have access to the same instance of the collection, then it is safer to use HashTablefor obvious reasons, such as avoiding dirty reading and maintaining data consistency. In the worst case, this multi-threaded environment will cause an infinite loop. Yes it's true. HashMap.get()can cause an infinite loop. Let's see how? If you look at the method's source code HashMap.get(Object key), it looks like this:
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)can always fall prey to an infinite loop in a multi-threaded runtime environment if for some reason e.nextit can point to itself. This will cause an infinite loop, but how e.nextwill it point to itself (that is, to e)? This can happen in a method void transfer(Entry[] newTable)that is being called while HashMapresizing.
do {
    Entry next = e.next;
    int i = indexFor(e.hash, newCapacity);
    e.next = newTable[i];
    newTable[i] = e;
    e = next;
} while (e != null);
This piece of code is prone to creating an infinite loop if the resize happens at the same time another thread tries to resize the map instance ( HashMap). The only way to avoid this scenario is to use synchronization in code, or better yet, use a synchronized collection.

Explain abstraction and encapsulation. How are they related?

In simple words , " The abstraction displays only those properties of the object that are significant for the current view". In object-oriented programming theory, abstraction includes the ability to define objects that represent abstract "actors" that can do work, change and report changes in their state, and "interact" with other objects in the system. Abstraction in any programming language works in many ways. This can be seen from the creation of routines to define interfaces for low-level language commands. Some abstractions try to limit the breadth of the overall view of the programmer's needs by completely hiding the abstractions they are built on, such as design patterns. Generally, abstraction can be seen in two ways: Data abstractionis a way of creating complex data types and exposing only meaningful operations to interact with the data model, while at the same time hiding all implementation details from the outside world. Execution abstraction is the process of identifying all relevant statements and exposing them as a unit of work. We usually use this feature when we create a method to do some work. The encapsulation of data and methods within classes, combined with the implementation of hiding (using access control), is often referred to as encapsulation. The result is a data type with characteristics and behavior. Encapsulation, in essence, also contains data hiding and implementation hiding. "Encapsulate everything that can change". This quote is a well-known design principle. For that matter, in any class, data changes can happen at runtime and implementation changes can happen in future versions. Thus, encapsulation applies to both data and implementation. So they can be linked like this:
  • Abstraction for the most part is What a class can do [Idea]
  • Encapsulation is more How to achieve this functionality [Implementation]

Differences between interface and abstract class?

The main differences can be listed as follows:
  • An interface cannot implement any methods, but an abstract class can.
  • A class can implement many interfaces, but can only have one superclass (abstract or non-abstract)
  • An interface is not part of a class hierarchy. Unrelated classes can implement the same interface.
You must remember the following: “When you can fully describe the concept in terms of “what it does” without having to specify “how it does it,” then you should use an interface. If you need to include some implementation details, then you need to represent your concept in an abstract class." Also, in other words: Are there many classes that can be "grouped together" and described by a single noun? If so, create an abstract class named after this noun, and derive classes from it. For example, Catand Dogcan be inherited from an abstract class Animal, and this abstract base class will implement the methodvoid Breathe()- to breathe, which all animals will thus perform in the same way. What verbs can be applied to my class and can be applied to others? Create an interface to each of these verbs. For example, all animals can eat, so I'll create an interface IFeedableand make it Animalimplement that interface. Only Dogand Horsegood enough to implement the interface ILikeable(capable of liking me), but not all. Someone said: the main difference is where you want your implementation. When creating an interface, you can move the implementation to any class that implements your interface. By creating an abstract class, you can separate the implementation of all derived classes in one place and avoid a lot of bad things like code duplication.

How does StringBuffer save memory?

The class Stringis implemented as an immutable object, meaning that when you initially decide to put something into an object String, the virtual machine allocates a fixed-length array, exactly the size of your original value. In the future, this will be treated as a constant inside the virtual machine, which provides a significant performance improvement in case the value of the string does not change. However, if you decide to change the contents of the string in any way, what the virtual machine is really doing is copying the contents of the original string into temporary space, making your changes, then saving those changes to a new memory array. Thus, making changes to the string value after initialization is an expensive operation. StringBuffer, on the other hand, is implemented as a dynamically expandable array inside the virtual machine, which means that any change operation can take place on an existing memory location, and new memory will be allocated as needed. However, there is no way for the virtual machine to make optimizations StringBuffer, since its contents are considered volatile in each instance.

Why are the wait and notify methods declared on the Object class instead of Thread?

waitThe , notify, methods notifyAllare only needed when you want your threads to have access to shared resources and the shared resource can be any java object in the heap. Thus, these methods are defined on the base class Objectso that each object has the control to allow threads to wait on its monitor. Java does not have any special object that is used to share a shared resource. No such data structure is defined. Therefore, it is the responsibility of the class Objectto be able to become a shared resource, and to provide helper methods such as wait(), notify(),notifyAll(). Java is based on the idea of ​​monitors by Charles Hoare. In Java, all objects have a monitor. Threads are waiting on monitors, so we need two parameters to execute the wait:
  • a thread
  • monitor (any object).
In Java design, a thread cannot be precisely defined, it is always the current thread executing the code. However, we can define a monitor (which is an object on which we can call a method wait). This is a good idea, because if we can make any other thread wait on a certain monitor, it will lead to "intrusion", making it difficult to design/program parallel programs. Remember that in Java, all operations that interfere with other threads are deprecated (for example, stop()).

Write a program to create a deadlock in Java and fix it

In Java deadlock, this is a situation where at least two threads hold a block on different resources, and both are waiting for another resource to be freed to complete their task. And neither is able to leave the lock on the held resource. java core.  Questions for the interview, part 2 - 2 Program example:
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;
		}
	}
}
Running the above code will result in a deadlock for very obvious reasons (explained above). Now we need to solve this problem. I believe that the solution to any problem lies at the root of the problem itself. In our case, the access model to A and B is the main problem. Therefore, to solve it, we simply change the order of access operators to shared resources. After the change it will look like this:
// 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");
			}
		}
	}
};
Run this class again and now you won't see the deadlock. I hope this helps you avoid deadlocks and get rid of them if you encounter them.

What happens if your class that implements the Serializable interface contains a non-serializable component? How to fix it?

In this case, it will be thrown NotSerializableExceptionduring execution. To fix this problem, there is a very simple solution - check these boxes transient. This means that the checked fields will not be serialized. If you also want to store the state of these fields, then you need to consider reference variables that already implement the Serializable. You may also need to use the readResolve()and methods writeResolve(). Let's summarize:
  • First, make your non-serializable transient.
  • First writeObjectcall defaultWriteObjecton the thread to save all non- transientfields, then call the rest of the methods to serialize the individual properties of your non-serializable object.
  • In readObject, first call defaultReadObjecton the stream to read all non- transientfields, then call other methods (corresponding to the ones you added in writeObject) to deserialize your non- transientobject.

Explain transient and volatile keywords in Java

"The keyword transientis used to designate fields that will not be serialized." According to the Java Language Specification: Variables can be marked with the transient indicator to indicate that they are not part of the stable state of an object. For example, you can contain fields derived from other fields, and it is preferable to get them programmatically rather than restoring their state through serialization. For example, in a class, BankPayment.javafields such as principal(director) and rate(rate) can be serialized, andinterest(interest accrued) can be calculated at any time, even after deserialization. If we recall, each thread in Java has its own local memory and performs read/write operations on this local memory. When all operations are done, it writes the modified state of the variable to shared memory, from where all threads can access the variable. As a rule, this is a normal thread inside the virtual machine. But the volatile modifier tells the virtual machine that a thread's access to this variable must always match its own copy of this variable with the main copy of the variable in memory. This means that every time a thread wants to read the state of a variable, it must clear the state of internal memory and update the variable from main memory. Volatilemost useful in lock-free algorithms. You mark a variable holding shared data as volatile, then you don't use locks to access that variable, and any changes made by one thread will be visible to others. Or if you want to create an happened-after relationship to ensure that calculations are not repeated, again to provide real-time visibility into changes. Volatile should be used to securely publish immutable objects in a multi-threaded environment. The field declaration public volatile ImmutableObjectensures that all threads always see the currently available instance reference.

Difference between Iterator and ListIterator?

We can use , or Iteratorto iterate over the elements . But it can only be used for iterating over elements . Other differences are described below. You can: SetListMapListIteratorList
  1. iterate in reverse order.
  2. get index anywhere.
  3. add any value anywhere.
  4. set any value at the current position.
Good luck with your studies!! Article by Lokesh Gupta Original Java Core article. Interview Questions Part 1 Java Core. Questions for the interview, part 3
Comments
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION