Java Tricky Interview Questions and Answers

AlishaS
The Fresh Writes
Published in
16 min readMar 13, 2023
Image Reference: Pixab

We know that the Java world is changing fast. To remain relevant, it’s important for developers to keep their knowledge up-to-date with new trends and technologies. However, most people find it hard to ace their interviews because they lack hands-on experience.

If you are one of them, don’t worry! We have a perfect solution — This list of java tricky interview questions and answers to help you ace your next interview. There are many great resources out there for preparing for a job interview, including videos, articles, and books.

However, some of the best insights come from real people who have been in your shoes at some point in their careers. In this article, we will cover a variety of tricky java interview questions with detailed explanations.

1. Is there any difference between Methods and Functions in Java?

Java is a method-oriented language, meaning that the method you call determines what happens when you call a certain function. Most of the time, you will want to call a method because you want a result from that method. This is different from a function, which doesn’t return anything. A function is used to perform a certain task. The result is that the task gets done. When a method gets called, the method does the task and then returns a result. This is why you call a method and not a function. A method is essentially a function that has been given an identifier (name) and has been placed inside a class.

2. How does a Thread operate in Java?

A thread is a path of execution that happens within a process at the same time. When you create a new thread, you’re basically creating a new path of execution. Threads are separate from each other and can run at the same time. The operating system manages the threads and decides which thread gets executed when. A thread can read and write data from the memory of the process it’s in and it can access local variables and read them. But, a thread can’t change the value of a variable. It can only read a variable. A thread can read and write data from the memory of the process it’s in and it can access local variables and read them. But, a thread can’t change the value of a variable. It can only read a variable.

3. Why use immutability in our code?

The main reason why you should use immutability in your code is that it makes your code scale. Let’s say you are creating a huge application that has millions of users. If you have one variable that stores an image and the image is mutable, each user can change the value of that variable and the image will be different for each user. With an immutable variable, all the users will see the same image because the value of the variable can’t be changed. So, you have to write your code in a way that it can handle millions of users. This can only be done if you write your code immutably. In general, immutable code is easier to reason about because it’s obvious what its state is at any point in time.

4. How do you use immutability in your code?

Let’s say you have a class that represents a user and you want to store the user’s name. The naive way to do it is to create an instance of String and store it in the constructor of the class. The problem with this approach is that the value of the variable can be changed by any client, so if you have millions of users, each user will see a different name on his/her screen. What we really want here is to store a constant value that never changes. This can be done by using an immutable data type like StringBuilder or StringBuffer.

In Java, these data types are thread-safe and they are backed by an internal mutable string that can be modified only by their methods. If we use them, our class will look like this:

These two classes have only one method called append(), which takes one parameter as input and returns nothing as output. They also have another method called toString(), which returns nothing as output too but it’s not mandatory because Java automatically calls it when you call getter methods like getName(). So, with this approach, all clients will see the same value for userName at all times because we are storing a constant value in an immutable data type behind the scenes, which means that we don’t need to worry about changing values during runtime anymore. We should always use immutable data types to store constants.

5. Explain the Differences Between Synchronized and Synchronized blocks?

Synchronized methods are used to control access to a block of code by one thread at a time. When you mark a method as synchronized, you are telling the Java Virtual Machine (JVM) that only one thread can execute that method at a time. Synchronized blocks are similar to synchronized methods, but they are used to control access to a block of code. A block of code can be any method or a code block within a method. When you mark a block of code as synchronized, you are telling the JVM that only one thread can execute that code at a time.

6. How does a HashMap work?

A HashMap is a data structure that stores values of any type, indexed by a key of any type. The key must be unique for each value, but the key doesn’t have to be a stable identifier for the value. A HashMap uses a hashing function to compute an index into an internal array that holds the values. The index is the smaller hash code of the key and the size of the HashMap divided by the size of the bucket array. When you add a value to a HashMap, it figures out where to put it. It first computes the hash code of the key, then figures out the correct bucket by dividing the size of the array by the hash code. It then uses the same hash code to determine where in the bucket, or array, to store it. You can also use a HashMap to store a set of values, one per key. For example, you might want to store a set of words and their definitions.

7. How can nested classes be used in Java?

A nested class is a class defined inside another class. The nested class may be declared as public, private, or protected, just as if it were declared outside the containing class. A nested class is useful when you want to define a class that has some connection with a surrounding class, but that doesn’t fit into the normal organization of the program. The main purpose of a nested class is to minimize the amount of code that you need to write. Normally, if you want to write a class that performs a certain task, you have to write a new file and put the code for the class in that file. A nested class lets you put the code for that class inside the file containing the code for the surrounding class.

8. How do you test whether a stream is dry or deserted?

A stream is dry if no data flows through it. Deserted means that data could flow through it but doesn’t. The difference between the two is important because code may depend on the presence or absence of data in the stream. A dry stream may still contain data. It may be waiting for a connection to open or a request to come in. A deserted stream has data but it is not waiting for any connection or request.

For example — You might use a stream to read data from a file and store it in a database. In this situation, you want to know when the stream is dry. You don’t want to continue reading the file when the database already has all the data it needs.

9. Do you explain the process for casting objects?

Casting lets you treat one object as if it were another type of object. For example, you might cast a number to a character, or vice versa. This is a useful feature, but you have to be careful because it can cause unexpected results. Casting is possible because Java is a type-safe language. This means that Java knows the type of every object and doesn’t let you treat a number as a character or vice versa. Casting has two steps. First, you ask the Java Virtual Machine to check whether the object is of the desired type. Then the Java Virtual Machine checks to see if the object is really of that type.

10. Describe the JVM process.

The JVM loads your code, followed by the byte codes that make up your program. The JVM then breaks the byte codes into instructions, loads the corresponding classes, and executes the instructions. The JVM keeps track of memory by keeping track of what variables and objects exist. When the JVM sees a new variable, it allocates memory for that variable and puts the variable’s value there. A new object is a different story. When the JVM sees a new object, it doesn’t put the object’s value in memory. Instead, the JVM allocates memory for the object and puts an offset, or address, in the object. The JVM later puts the object’s value at that address.

11. Define class loader? And how does it work?

A class loader loads the byte codes for a class from a source, such as a file. A class loader can also load the byte codes for a class from another class loader. The Java Virtual Machine (JVM) uses class loaders to find the byte codes for a class. The JVM uses a class loader to find the byte codes for each class in your program. Each running program has at least one class loader. However, if you run a program that uses another program, the Java Virtual Machine uses the class loader of the program that runs first

12. Explain Assertion API in JVM?

The Java Assertion Framework and the Java Debugging Architecture (JDA) allow you to add assertions to your code. An assertion is a Boolean expression that is evaluated at runtime and is considered false only if there is an error, such as a system failure or invalid input data. The JVM lets you put an assertion in your code as part of the debugging process. The JVM then checks the assertion for every execution of the code and throws an exception if the assertion is false. The JVM also lets you add assertions to your production code. The JVM checks the assertions only when the program is running in debug mode.

13. How to explain the difference between implicit and explicit interfaces?

An interface is a contract between a class and its clients. If a class implements an interface, it must abide by the contract outlined in the interface. An interface may have methods, constants, and/or fields. An interface is explicit if a class must specify the interface in the class declaration. In other words, the class must include the interface as part of its definition. A class is implicit if a class may receive an instance of the class from a variable, method, or another class.

14. Explain why the method in the parent class calls in the case of Runtime Polymorphism, which is only overridden in the child class. Why not the methods that are directly available in the child’s class?

Due to the functionality of the JVM, the method that is present in the parent class or overridden methods in the parent class are only called in dynamic or runtime polymorphism. Let’s examine the fundamentals of Java’s object creation in order to better appreciate this idea.

According to the codes accessible in the object class, when we create an object using the new Keyword, the memory assigned to the object and its methods are not allocated to it. However, it is based on the citation made about it.

When the compiler notices the new Keyword, it recognizes that an object will need to be constructed during program execution. The compiler just checks the Reference and the object to which it will refer.

The methods available in the parent class are now automatically available in the child class because the object is now constructed based on the reference class. Furthermore, the base class or parent class, which is referencing the objects, is unaware of what is available in the child class. So the underlying cause of Dynamic Polymorphism is all of this.

Java Tricky Programming Questions

1. Predict the output of the following java code. Also, state the reason for your answer.

public class JavaTrickyInterviewQuestions{
public static void main(String[] str){
Integer number_1 = 50;
Integer number_2 = 50;
Integer number_3 = 150;
Integer number_4 = 150;

if(number_1 == number_2)
System.out.println(“Number 1 and Number 2 are Equal”);
else
System.out.println(“Number 1 and Number 2 are not Equal”);

if(number_3 == number_4)
System.out.println(“Number 3 and Number 4 are Equal”);
else
System.out.println(“Number 3 and Number 4 are not Equal”);
}
}
Options — 
Number 1 and Number 2 are Equal
Number 3 and Number 4 are Equal
Number 1 and Number 2 are Equal 
Number 3 and Number 4 are not Equal
Number 1 and Number 2 are not Equal 
Number 3 and Number 4 are Equal
Number 1 and Number 2 are not Equal 
Number 3 and Number 4 are not Equal

Reason-

We used to believe that when two object references were compared with “==”, the result was always “false.” However, in this case, integer caching alters the outcome. The integer class has a caching range of -128 to 127. When a number falls within this range and autoboxing is utilized, it allocates the same reference. As a result, for the number 50, both number 1 and number 2 will have the same reference, but for the value 150 (not in the range of -128 to 127), number 3 and number 4 will have distinct references.

2. State the output with reason.

public class JavaTrickyInterviewQuestions{
public static void main(String[] args){

}
public static void main(String args){

}
}

Options -

  • Nothing.
  • Error.

Reason — We may also overload main(). However, the JVM will always call main() with the String[] parameter.

3. Predict the output with the reason of your answer.

public class JavaTrickyInterviewQuestion{
public static void main(String[] args){
System.out.println(‘j’ + ‘a’ + ‘v’ + ‘a’);
}
}

Reason — “java” would be written if String literals (in double quotes) were used, but because character literals were used in the query, these would not be concatenated. As a result, after running the program, the sum of each equivalent ASCII (Unicode) value of the character will be obtained.

As a result, the result is 106 + 97 + 118 + 97 = 418.

4. Can you Implement the custom Java Collection of the Integer ArrayList class with the basic methods of —

1.Adding into the List.
2.Removing the element from the List.
3.Get the element at a particular index.
Note — This must be dynamic and the operations must be done in the Amortized constant time. O(1).

Solution —

import java.util.*;
class CustomArrayList{
int[] array;
int size, lastIndex;

//Constructor marking initial values of the ArrayList.
CustomArrayList(){
lastIndex = 0;
size = 2;
array = new int[2];
}

//Private method that makes in dynamic growing nature of the ArrayList.
private void dynamic(){
size *= 2;
int[] newArray = new int[size];
for(int i = 0; i < lastIndex; i++)
newArray[i] = array[i];
array = newArray;
}

//Method used to add the element in the list.
public void add(int value){
//If the size of the ArrayList exceeded with the current then call the dynamic method.
if(lastIndex == size) dynamic();
array[lastIndex++] = value;
}

//Method that removes the element at particular index.
public void remove(int index){
for(int i = index; i < lastIndex; i++){
array[i] = array[i+1];
}
lastIndex — ;
}

//Method that returns the element at particular index.
public int get(int index){
return array[index];
}

//Overriden toString method used to print the elements of the ArrayList
@Override
public String toString() {
StringBuffer temp = new StringBuffer();
temp.append(“[“);
for(int i = 0; i < lastIndex; i++) temp.append(array[i]+”,”);
temp.deleteCharAt(temp.length() — 1);
temp.append(“]”);
return temp.toString();
}
}
public class Solution {
public static void main(String[] args) {

//Testing Methods
CustomArrayList al = new CustomArrayList();
for(int i = 0; i < 20; i++){
al.add((int)(Math.random()*9000));
}
System.out.println(al);
al.remove(9);
System.out.println(al.get(6));
System.out.println(al);
}
}

5. Create a custom Java Collections Arrays.sort() method and Arrays.toString() method, that deals with the integer value.

Solution -

public class Arrays {
//Sort Method that sorts the given array in place by internally using Merge Sort algorithm
public static void Sort(int[] arr){
m_sort(arr, 0, arr.length-1);
}
private static void m_sort(int A[], int l, int h) {
if (l < h) {
int mid = (l + h) / 2;
m_sort(A, l, mid);
m_sort(A, mid + 1, h);
merge(A, l, mid, h);
}
}
private static void merge(int A[], int l, int mid, int h) {

// Pointing the Elements index and performing the merge to the original array
// with the help of an Auxiliary Array.
int i = l, j = mid + 1, k = 0;
int B[] = new int[(h — l) + 1];
while (i <= mid && j <= h) {
if (A[i] < A[j])
B[k++] = A[i++];
else
B[k++] = A[j++];
}

// Merging the Remaining items.
for (; i <= mid; i++)
B[k++] = A[i];
for (; j <= h; j++)
B[k++] = A[j];

// Copying the Merge item from the Auxiliary Array to the Main Array from the
// location it is being called.
for (i = 0; i < B.length; i++)
A[l++] = B[i];
}
//to String method that prints the array
public static String toString(int[] arr) {
StringBuffer temp = new StringBuffer();
temp.append(“[“);
for(int i = 0; i < arr.length; i++) temp.append(arr[i]+”,”);
temp.deleteCharAt(temp.length() — 1);
temp.append(“]”);
return temp.toString();
}
public static void main(String[] args) {
int[] arr = new int[]{4,6,8,2,7,2,1,0,3,6,98,2,1,5};
Arrays.Sort(arr);
System.out.println(Arrays.toString(arr));
}
}

6. Implement LRU Cache with the basic functionalities with the methods-

  • LRUCache (int capacity) — Set the LRU cache to a positive size capacity.
  • int get (int key) — If the key exists, return its value; else, return -1.
  • void put (int key, int value) — If the key exists, update the value of the key. If not, save the key-value pair to the cache. If the number of keys exceeds the operation’s limit, evict the most recently used key.

Solution -


import java.util.*;
class LRUCache {
//Map that stores the key and the value to the node
HashMap<Integer, Node> map;

//Pointers that keep track of the things.
int cap, size;
Node front, rear;

//Constructor that initialized the cache of capacity.
public LRUCache(int capacity) {
cap = capacity;
size = 0;
front = rear = null;
map = new HashMap<>();
}

//Metho that return the value related to the key.
public int get(int key) {
if(!map.containsKey(key))
return -1;

Node temp = map.get(key);
if(temp == rear)
return temp.value;

else if(temp == front){
removeFromFront();
insertAtEnd(temp);
}else{
removeFromMiddle(temp);
insertAtEnd(temp);
}
return temp.value;
}

//This method put the Key with the value.
public void put(int key, int value) {
Node node = new Node(key, value);

if(map.containsKey(key)){
Node temp = map.get(key);
map.remove(key);
//Adjust
if(temp == front){
removeFromFront();
insertAtEnd(node);
}else if(temp == rear){
if(front == rear)
front = rear = node;
else{
removeFromRear();
insertAtEnd(node);
}
}else{
removeFromMiddle(temp);
insertAtEnd(node);
}
map.put(key, node);
}
else if(front == null){
insertAtEnd(node);
map.put(key, node);
size++;
}else if(size < cap){
insertAtEnd(node);
map.put(key, node);
size++;
}else if(size == cap){
map.remove(front.key);
removeFromFront();
insertAtEnd(node);
map.put(key, node);
}
}

//LinkedList that handles internally for managing the Time Complexities.
private void insertAtEnd(Node node){
if(rear == null){
rear = front = node;
return;
}
rear.next = node;
node.prev = rear;
rear = node;
}
private void removeFromFront(){
front = front.next;
if(front != null){
front.prev.next = null;
front.prev = null;
}else
rear = null;
}
private void removeFromRear(){
rear = rear.prev;
if(rear != null){
rear.next.prev = null;
rear.next = null;
}else
front = null;
}
private void removeFromMiddle(Node temp){
temp.prev.next = temp.next;
temp.next.prev = temp.prev;
temp.prev = temp.next = null;
}
private class Node{
int value, key;
Node prev, next;
Node(int key, int value){
this.value = value;
this.key = key;
}
}

public static void main(String[] args) {
LRUCache cache = new LRUCache(4);
cache.put(1, 3);
cache.put(8, 4);
cache.put(1, 7);
System.out.println(cache.get(8));
}
}

7. Create a parking lot parking system. The parking lot is divided into three sections: large, medium, and tiny, each with a certain number of places.
Implement the ParkingSystem class with the method as follows:

  • ParkingSystem(int big, int medium, int tiny) — It creates a ParkingSystem object. The function Object() { [native code] } specifies the number of slots for each parking space.
  • boolean addCar(int carType) — Determines whether there is a parking spot of carType available for the car that wishes to enter the parking lot. carType can be of three sizes: large, medium, or tiny, denoted by the numbers 1, 2, and 3 correspondingly. An automobile can only park in the parking place that corresponds to its carType. If no space is available, return false; otherwise, park the automobile in that size spot and return true.

Solution -


class ParkingSystem {
//Parking Array that stores 3 types of vehicle size
int[] parking;
public ParkingSystem(int big, int medium, int small) {
parking = new int[]{0, big, medium, small};
}

// Method that add the car to the parking.
public boolean addCar(int carType) {
if(parking[carType] > 0){
parking[carType]--;
return true;
}
return false;
}
}

Conclusion

Java is a widely used programming language. In this article, we have covered some of the tricky questions that the interviewer might ask you during your java interview. This set of questions is not only the questions, there are a lot more.

Thanks for reading.Happy learning 😄

Do support our publication by following it

--

--

AlishaS
The Fresh Writes

I am enthusiastic about programming, and marketing, and constantly seeking new experiences.