Do you have what it takes to ace a Java Interview? We are here to help you in consolidating your knowledge and concepts in Java. Before we begin, let's understand what Java is all about.
Join our community and share your recent Java interview experiences.
Java language was developed so that it does not depend on any hardware or software because the compiler compiles the code and then converts it to platform-independent byte code which can be run on multiple systems.
Java supports primitive data types - byte, boolean, char, short, int, float, long, and double and hence it is not a pure object oriented language.
Stack memory is the portion of memory that was assigned to every individual program. And it was fixed. On the other hand, Heap memory is the portion that was not allocated to the java program but it will be available for use by the java program when it is required, mostly during the runtime of the program.
Java Utilizes this memory as -
Example- Consider the below java program:
class Main < public void printArray(int[] array)< for(int i : array) System.out.println(i); > public static void main(String args[]) < int[] array = new int[10]; printArray(array); > >
For this java program. The stack and heap memory occupied by java is -
Main and PrintArray is the method that will be available in the stack area and as well as the variables declared that will also be in the stack area.
And the Object (Integer Array of size 10) we have created, will be available in the Heap area because that space will be allocated to the program during runtime.
You can download a PDF version of Java Interview Questions. Download PDF Download PDFIt is not wrong if we claim that Java is the complete object-oriented programming language because everything in Java is under the classes and we can access them by creating the objects.
But we can even say that Java is not a completely object-oriented programming language because it has the support of primitive data types like int, float, char, boolean, double, etc.
Now for the question: Is Java a completely object-oriented programming language? We can say that - Java is not a pure object-oriented programming language, because it has direct access to primitive data types. And these primitive data types don't directly belong to the Integer classes.
Pointers are quite complicated and unsafe to use by beginner programmers. Java focuses on code simplicity, and the usage of pointers can make it challenging. Pointer utilization can also cause potential errors. Moreover, security is also compromised if pointers are used because the users can directly access memory with the help of pointers.
Thus, a certain level of abstraction is furnished by not including pointers in Java. Moreover, the usage of pointers can make the procedure of garbage collection quite slow and erroneous. Java makes use of references as these cannot be manipulated, unlike pointers.
Instance variables are those variables that are accessible by all the methods in the class. They are declared outside the methods and inside the class. These variables describe the properties of an object and remain bound to it at any cost.
All the objects of the class will have their copy of the variables for utilization. If any modification is done on these variables, then only that instance will be impacted by it, and all other class instances continue to remain unaffected.
Example:
class Athlete < public String athleteName; public double athleteSpeed; public int athleteAge; >
Local variables are those variables present within a block, function, or constructor and can be accessed only inside them. The utilization of the variable is restricted to the block scope. Whenever a local variable is declared inside a method, the other class methods don’t have any knowledge about the local variable.
Example:
public void athlete() < String athleteName; double athleteSpeed; int athleteAge; >
We are already aware of the (==) equals operator. That we have used this to compare the equality of the values. But when we talk about the terms of object-oriented programming, we deal with the values in the form of objects. And this object may contain multiple types of data. So using the (==) operator does not work in this case. So we need to go with the .equals() method.
Both [(==) and .equals()] primary functionalities are to compare the values, but the secondary functionality is different.
So in order to understand this better, let’s consider this with the example -
String str1 = "InterviewBit"; String str2 = "InterviewBit"; System.out.println(str1 == str2);
This code will print true. We know that both strings are equals so it will print true. But here (==) Operators don’t compare each character in this case. It compares the memory location. And because the string uses the constant pool for storing the values in the memory, both str1 and str2 are stored at the same memory location. See the detailed Explanation in Question no 73: Link.
Now, if we modify the program a little bit with -
String str1 = new String("InterviewBit"); String str2 = "InterviewBit"; System.out.println(str1 == str2);
Then in this case, it will print false. Because here no longer the constant pool concepts are used. Here, new memory is allocated. So here the memory address is different, therefore ( == ) Operator returns false. But the twist is that the values are the same in both strings. So how to compare the values? Here the .equals() method is used.
.equals() method compares the values and returns the result accordingly. If we modify the above code with -
System.out.println(str1.equals(str2));
Then it returns true.
equals() | == |
---|---|
This is a method defined in the Object class. | It is a binary operator in Java. |
The .equals() Method is present in the Object class, so we can override our custom .equals() method in the custom class, for objects comparison. | It cannot be modified. They always compare the HashCode. |
This method is used for checking the equality of contents between two objects as per the specified business logic. | This operator is used for comparing addresses (or references), i.e checks if both the objects are pointing to the same memory location. |
Note:
Infinite loops are those loops that run infinitely without any breaking conditions. Some examples of consciously declaring infinite loop is:
for (;;) < // Business logic // Any break logic >
while(true)< // Business logic // Any break logic >
do< // Business logic // Any break logic >while(true);
Constructor overloading is the process of creating multiple constructors in the class consisting of the same name with a difference in the constructor parameters. Depending upon the number of parameters and their corresponding types, distinguishing of the different types of constructors is done by the compiler.
class Hospital < int variable1, variable2; double variable3; public Hospital(int doctors, int nurses) < variable1 = doctors; variable2 = nurses; >public Hospital(int doctors) < variable1 = doctors; >public Hospital(double salaries) < variable3 = salaries >>
Three constructors are defined here but they differ on the basis of parameter type and their numbers.
Copy Constructor is the constructor used when we want to initialize the value to the new object from the old object of the same class.
class InterviewBit< String department; String service; InterviewBit(InterviewBit ib)< this.departments = ib.departments; this.services = ib.services; > >
Here we are initializing the new object value from the old object value in the constructor. Although, this can also be achieved with the help of object cloning.
Yes, It is possible to overload the main method. We can create as many overloaded main methods we want. However, JVM has a predefined calling method that JVM will only call the main method with the definition of -
public static void main(string[] args)
Consider the below code snippets:
class Main < public static void main(String args[]) < System.out.println(" Main Method"); > public static void main(int[] args)< System.out.println("Overloaded Integer array Main Method"); > public static void main(char[] args)< System.out.println("Overloaded Character array Main Method"); > public static void main(double[] args)< System.out.println("Overloaded Double array Main Method"); > public static void main(float args)< System.out.println("Overloaded float Main Method"); > >
In Java, method overloading is made possible by introducing different methods in the same class consisting of the same name. Still, all the functions differ in the number or type of parameters. It takes place inside a class and enhances program readability. The only difference in the return type of the method does not promote method overloading. The following example will furnish you with a clear picture of it.
class OverloadingHelp < public int findarea (int l, int b) < int var1; var1 = l * b; return var1; > public int findarea (int l, int b, int h) < int var2; var2 = l * b * h; return var2; > >
Both the functions have the same name but differ in the number of arguments. The first method calculates the area of the rectangle, whereas the second method calculates the area of a cuboid. Method overriding is the concept in which two methods having the same method signature are present in two different classes in which an inheritance relationship is present. A particular method implementation (already present in the base class) is possible for the derived class by using method overriding.
Let’s give a look at this example:
class HumanBeing < public int walk (int distance, int time) < int speed = distance / time; return speed; > > class Athlete extends HumanBeing < public int walk(int distance, int time) < int speed = distance / time; speed = speed * 2; return speed; > >
Both class methods have the name walk and the same parameters, distance, and time. If the derived class method is called, then the base class method walk gets overridden by that of the derived class.
Yes, multiple catch blocks can exist but specific approaches should come prior to the general approach because only the first catch block satisfying the catch condition is executed. The given code illustrates the same:
public class MultipleCatch < public static void main(String args[]) < try < int n = 1000, x = 0; int arr[] = new int[n]; for (int i = 0; i > catch (ArrayIndexOutOfBoundsException exception) < System.out.println("1st block = ArrayIndexOutOfBoundsException"); > catch (ArithmeticException exception) < System.out.println("2nd block = ArithmeticException"); > catch (Exception exception) < System.out.println("3rd block = Exception"); > > >
Here, the second catch block will be executed because of division by 0 (i / x). In case x was greater than 0 then the first catch block will execute because for loop runs till i = n and array index are till n-1.
All three keywords have their own utility while programming.
Final: If any restriction is required for classes, variables, or methods, the final keyword comes in handy. Inheritance of a final class and overriding of a final method is restricted by the use of the final keyword. The variable value becomes fixed after incorporating the final keyword. Example:
final int a=100; a = 0; // error
The second statement will throw an error.
Finally: It is the block present in a program where all the codes written inside it get executed irrespective of handling of exceptions. Example:
try < int variable = 5; > catch (Exception exception) < System.out.println("Exception occurred"); > finally < System.out.println("Execution of finally block"); >
Finalize: Prior to the garbage collection of an object, the finalize method is called so that the clean-up activity is implemented. Example:
public static void main(String[] args) < String example = new String("InterviewBit"); example = null; System.gc(); // Garbage collector called > public void finalize() < // Finalize called >
Yes. It is possible that the ‘finally’ block will not be executed. The cases are-
1. public class InterviewBit 2. < 3. public static void main(String[] args) < 4. final int i; 5. i = 20; 6. int j = i+20; 7. i = j+30; 8. System.out.println(i + " " + j); 9. > 10. >
The above code will generate a compile-time error at Line 7 saying - [error: variable i might already have been initialized]. It is because variable ‘i’ is the final variable. And final variables are allowed to be initialized only once, and that was already done on line no 5.
class Parent< protected int num = 1; Parent()< System.out.println("Parent class default constructor."); > Parent(String x)< System.out.println("Parent class parameterised constructor."); > public void foo()< System.out.println("Parent class foo!"); > > class Child extends Parent< private int num = 2; Child()< //super constructor call should always be in the first line // super(); // Either call default super() to call default parent constructor OR super("Call Parent"); // call parameterised super to call parameterised parent constructor. System.out.println("Child class default Constructor"); > void printNum()< System.out.println(num); System.out.println(super.num); //prints the value of num of parent class > @Override public void foo()< System.out.println("Child class foo!"); super.foo(); //Calls foo method of Parent class inside the Overriden foo method of Child class. > > public class DemoClass < public static void main(String args[]) < Child demoObject=new Child(); demoObject.foo(); /* This would print - Parent class parameterised constructor. Child class default Constructor Child class foo! Parent class foo! */ > >
Yes! There can be two or more static methods in a class with the same name but differing input parameters.
The main method is always static because static members are those methods that belong to the classes, not to an individual object. So if the main method will not be static then for every object, It is available. And that is not acceptable by JVM. JVM calls the main method based on the class name itself. Not by creating the object.
Because there must be only 1 main method in the java program as the execution starts from the main method. So for this reason the main method is static.
The main objective of this process is to free up the memory space occupied by the unnecessary and unreachable objects during the Java program execution by deleting those unreachable objects.
To copy the object's data, we have several methods like deep copy and shallow copy.
Example -
class Rectangle< int length = 5; int breadth = 3; >
Object for this Rectangle class - Rectangle obj1 = new Rectangle();
Rectangle obj2 = obj1;
Now by doing this what will happen is the new reference is created with the name obj2 and that will point to the same memory location.
Rectangle obj3 = new Rectangle(); Obj3.length = obj1.length; Obj3.breadth = obj1.breadth;
Both these objects will point to the memory location as stated below -
Now, if we change the values in shallow copy then they affect the other reference as well. Let's see with the help of an example -
class Rectangle < int length = 5; int breadth = 3; > public class Main < public static void main(String[] args) < Rectangle obj1 = new Rectangle(); //Shallow Copy Rectangle obj2 = obj1; System.out.println(" Before Changing the value of object 1, the object2 will be - "); System.out.println(" Object2 Length hljs-string">", Object2 Breadth hljs-comment">//Changing the values for object1. obj1.length = 10; obj1.breadth = 20; System.out.println("\n After Changing the value of object 1, the object2 will be - "); System.out.println(" Object2 Length hljs-string">", Object2 Breadth language-java hljs">Before Changing the value of object 1, the object2 will be - Object2 Length = 5, Object2 Breadth = 3 After Changing the value of object 1, the object2 will be - Object2 Length = 10, Object2 Breadth = 20
We can see that in the above code, if we change the values of object1, then the object2 values also get changed. It is because of the reference.
Now, if we change the code to deep copy, then there will be no effect on object2 if it is of type deep copy. Consider some snippets to be added in the above code.
class Rectangle < int length = 5; int breadth = 3; > public class Main < public static void main(String[] args) < Rectangle obj1 = new Rectangle(); //Shallow Copy Rectangle obj2 = new Rectangle(); obj2.length = obj1.length; obj2.breadth = obj1.breadth; System.out.println(" Before Changing the value of object 1, the object2 will be - "); System.out.println(" Object2 Length hljs-string">", Object2 Breadth hljs-comment">//Changing the values for object1. obj1.length = 10; obj1.breadth = 20; System.out.println("\n After Changing the value of object 1, the object2 will be - "); System.out.println(" Object2 Length hljs-string">", Object2 Breadth language-java hljs">Before Changing the value of object 1, the object2 will be - Object2 Length = 5, Object2 Breadth = 3 After Changing the value of object 1, the object2 will be - Object2 Length = 5, Object2 Breadth = 3
Now we see that we need to write the number of codes for this deep copy. So to reduce this, In java, there is a method called clone().
The clone() will do this deep copy internally and return a new object. And to do this we need to write only 1 line of code. That is - Rectangle obj2 = obj1.clone();
A String is made immutable due to the following reasons:
Singleton classes are those classes, whose objects are created only once. And with only that object the class members can be accessed.
Understand this with the help of an example-:
Consider the water jug in the office and if every employee wants that water then they will not create a new water jug for drinking water. They will use the existing one with their own reference as a glass. So programmatically it should be implemented as -
class WaterJug< private int waterQuantity = 500; private WaterJug()<> private WaterJug object = null; // Method to provide the service of Giving Water. public int getWater(int quantity)< waterQuantity -= quantity; return quantity; > // Method to return the object to the user. public static Waterjug getInstance()< // Will Create a new object if the object is not already created and return the object. if(object == null)< object = new WaterJug(); > return object; > >
In the above class, the Constructor is private so we cannot create the object of the class. But we can get the object by calling the method getInstance(). And the getInstance is static so it can be called without creating the object. And it returns the object. Now with that object, we can call getWater() to get the water.
Waterjug glass1 = WaterJug.getInstance(); glass1.getWater(1);
We can get the single object using this getInstance(). And it is static, so it is a thread-safe singleton class. Although there are many ways to create a thread-safe singleton class. So thread-safe classes can also be:
We get a compile-time error in line 3. The error we will get in Line 3 is - integer number too large. It is because the array requires size as an integer. And Integer takes 4 Bytes in the memory. And the number (2241423798) is beyond the capacity of the integer. The maximum array size we can declare is - (2147483647).
Because the array requires the size in integer, none of the lines (1, 2, and 4) will give a compile-time error. The program will compile fine. But we get the runtime exception in line 2. The exception is - NegativeArraySizeException.
Here what will happen is - At the time when JVM will allocate the required memory during runtime then it will find that the size is negative. And the array size can’t be negative. So the JVM will throw the exception.
// String String first = "InterviewBit"; String second = new String("InterviewBit"); // StringBuffer StringBuffer third = new StringBuffer("InterviewBit"); // StringBuilder StringBuilder fourth = new StringBuilder("InterviewBit");
Abstract class example:
public abstract class Athlete < public abstract void walk(); >
Interface example:
public interface Walkable < void walk(); >
abstract final class InterviewBit< 2. public abstract void printMessage(); 3. > 4. class ScalarAcademy extends InterviewBit< 5. public void printMessage()< 6. System.out.println("Welcome to Scalar Academy By InterviewBit"); 7. > 8. > 9. class ScalarTopics extends ScalarAcademy< 10. public void printMessage()< 11. System.out.println("Welcome to Scalar Topics By Scalar Academy"); 12. > 13. > public class Main< public static void main(String[] args) < InterviewBit ib = new ScalarTopics(); ib.printMessage(); > >
The above program will give a compile-time error. The compiler will throw 2 errors in this.
It is because abstract classes are incomplete classes that need to be inherited for making their concrete classes. And on the other hand, the final keywords in class are used for avoiding inheritance. So these combinations are not allowed in java.
Consider the example where we have an ArrayList of employees like( EId, Ename, Salary), etc. Now if we want to sort this list of employees based on the names of employees. Then that is not possible to sort using the Collections.sort() method. We need to provide something to the sort() function depending on what values we have to perform sorting. Then in that case a comparator is used.
Comparator is the interface in java that contains the compare method. And by overloading the compare method, we can define that on what basis we need to compare the values.
The statement in the context is completely False. The static methods have no relevance with the objects, and these methods are of the class level. In the case of a child class, a static method with a method signature exactly like that of the parent class can exist without even throwing any compilation error.
The phenomenon mentioned here is popularly known as method hiding, and overriding is certainly not possible. Private method overriding is unimaginable because the visibility of the private method is restricted to the parent class only. As a result, only hiding can be facilitated and not overriding.
Although both HashSet and TreeSet are not synchronized and ensure that duplicates are not present, there are certain properties that distinguish a HashSet from a TreeSet.
In Java, a string is basically immutable i.e. it cannot be modified. After its declaration, it continues to stay in the string pool as long as it is not removed in the form of garbage. In other words, a string resides in the heap section of the memory for an unregulated and unspecified time interval after string value processing is executed.
As a result, vital information can be stolen for pursuing harmful activities by hackers if a memory dump is illegally accessed by them. Such risks can be eliminated by using mutable objects or structures like character arrays for storing any variable. After the work of the character array variable is done, the variable can be configured to blank at the same instant. Consequently, it helps in saving heap memory and also gives no chance to the hackers to extract vital data.
Criteria | JDK | JRE | JVM |
---|---|---|---|
Abbreviation | Java Development Kit | Java Runtime Environment | Java Virtual Machine |
Definition | JDK is a complete software development kit for developing Java applications. It comprises JRE, JavaDoc, compiler, debuggers, etc. | JRE is a software package providing Java class libraries, JVM and all the required components to run the Java applications. | JVM is a platform-dependent, abstract machine comprising of 3 specifications - document describing the JVM implementation requirements, computer program meeting the JVM requirements and instance object for executing the Java byte code and provide the runtime environment for execution. |
Main Purpose | JDK is mainly used for code development and execution. | JRE is mainly used for environment creation to execute the code. | JVM provides specifications for all the implementations to JRE. |
Tools provided | JDK provides tools like compiler, debuggers, etc for code development | JRE provides libraries and classes required by JVM to run the program. | JVM does not include any tools, but instead, it provides the specification for implementation. |
Summary | JDK = (JRE) + Development tools | JRE = (JVM) + Libraries to execute the application | JVM = Runtime environment to execute Java byte code. |
HashMap | HashTable |
---|---|
HashMap is not synchronized thereby making it better for non-threaded applications. | HashTable is synchronized and hence it is suitable for threaded applications. |
Allows only one null key but any number of null in the values. | This does not allow null in both keys or values. |
Supports order of insertion by making use of its subclass LinkedHashMap. | Order of insertion is not guaranteed in HashTable. |
Method methodOfFoo = fooObject.getClass().getMethod("fooBar", null); methodOfFoo.invoke(fooObject, null);
class InterviewBitThreadExample extends Thread< public void run()< System.out.println("Thread runs. "); > public static void main(String args[])< InterviewBitThreadExample ib = new InterviewBitThreadExample(); ib.start(); > >
class InterviewBitThreadExample implements Runnable< public void run()< System.out.println("Thread runs. "); > public static void main(String args[])< Thread ib = new Thread(new InterviewBitThreadExample()); ib.start(); > >
There are a total of 3 different types of priority available in Java.
MIN_PRIORITY: It has an integer value assigned with 1.
MAX_PRIORITY: It has an integer value assigned with 10.
NORM_PRIORITY: It has an integer value assigned with 5.
In Java, Thread with MAX_PRIORITY gets the first chance to execute. But the default priority for any thread is NORM_PRIORITY assigned by JVM.
Example -
class Main < public static int testExceptionDivide(int a, int b) throws ArithmeticException< if(a == 0 || b == 0) throw new ArithmeticException(); return a/b; > public static void main(String args[]) < try< testExceptionDivide(10, 0); > catch(ArithmeticException e)< //Handle the exception > > >
Here in the above snippet, the method testExceptionDivide throws an exception. So if the main method is calling it then it must have handled the exception. Otherwise, the main method can also throw the exception to JVM.
And the method testExceptionDivide 'throws’ the exception based on the condition.
Constructor | Method |
---|---|
Constructor is used for initializing the object state. | Method is used for exposing the object's behavior. |
Constructor has no return type. | Method should have a return type. Even if it does not return anything, return type is void. |
Constructor gets invoked implicitly. | Method has to be invoked on the object explicitly. |
If the constructor is not defined, then a default constructor is provided by the java compiler. | If a method is not defined, then the compiler does not provide it. |
The constructor name should be equal to the class name. | The name of the method can have any name or have a class name too. |
A constructor cannot be marked as final because whenever a class is inherited, the constructors are not inherited. Hence, marking it final doesn't make sense. Java throws compilation error saying - modifier final not allowed here | A method can be defined as final but it cannot be overridden in its subclasses. |
Final variable instantiations are possible inside a constructor and the scope of this applies to the whole class and its objects. | A final variable if initialised inside a method ensures that the variable cant be changed only within the scope of that method. |
class Main < public static void main(String args[]) < Scaler s = new Scaler(5); > > class InterviewBit< InterviewBit()< System.out.println(" Welcome to InterviewBit "); > > class Scaler extends InterviewBit< Scaler()< System.out.println(" Welcome to Scaler Academy "); > Scaler(int x)< this(); super(); System.out.println(" Welcome to Scaler Academy 2"); > >
The above code will throw the compilation error. It is because the super() is used to call the parent class constructor. But there is the condition that super() must be the first statement in the block. Now in this case, if we replace this() with super() then also it will throw the compilation error. Because this() also has to be the first statement in the block. So in conclusion, we can say that we cannot use this() and super() keywords in the same block.
Java always works as a “pass by value”. There is nothing called a “pass by reference” in Java. However, when the object is passed in any method, the address of the value is passed due to the nature of object handling in Java. When an object is passed, a copy of the reference is created by Java and that is passed to the method. The objects point to the same memory location. 2 cases might happen inside the method:
class InterviewBitTest< int num; InterviewBitTest(int x) < num = x; >InterviewBitTest()< num = 0; > > class Driver < public static void main(String[] args) < //create a reference InterviewBitTest ibTestObj = new InterviewBitTest(20); //Pass the reference to updateObject Method updateObject(ibTestObj); //After the updateObject is executed, check for the value of num in the object. System.out.println(ibTestObj.num); > public static void updateObject(InterviewBitTest ibObj) < // Point the object to new reference ibObj = new InterviewBitTest(); // Update the value ibObj.num = 50; > > Output: 20
class InterviewBitTest< int num; InterviewBitTest(int x) < num = x; >InterviewBitTest()< num = 0; > > class Driver< public static void main(String[] args) < //create a reference InterviewBitTest ibTestObj = new InterviewBitTest(20); //Pass the reference to updateObject Method updateObject(ibTestObj); //After the updateObject is executed, check for the value of num in the object. System.out.println(ibTestObj.num); > public static void updateObject(InterviewBitTest ibObj) < // no changes are made to point the ibObj to new location // Update the value of num ibObj.num = 50; > > Output: 50
‘IS-A’ relationship is another name for inheritance. When we inherit the base class from the derived class, then it forms a relationship between the classes. So that relationship is termed an ‘IS-A’ Relationship.
Example - Consider a Television (Typical CRT TV). Now another Smart TV that is inherited from television class. So we can say that the Smart iv is also a TV. Because CRT TV things can also be done in the Smart TV.
So here ‘IS-A’ Relationship formed. [ SmartTV ‘IS-A’ TV ].
StringBuffer is mutable and dynamic in nature whereas String is immutable. Every updation / modification of String creates a new String thereby overloading the string pool with unnecessary objects. Hence, in the cases of a lot of updates, it is always preferred to use StringBuffer as it will reduce the overhead of the creation of multiple String objects in the string pool.
public class InterviewBitExample < private transient String someInfo; private String name; private int id; // : // Getters setters // : >
There wouldn't be any compilation error. But then the program is run, since the JVM cant map the main method signature, the code throws “NoSuchMethodError” error at the runtime.
public class Main< public static void main(String[] args) < System.out.println(" Hello. Main Method. "); > public static void main(int[] args) < System.out.println(" Hello. Main Method2. "); > >
The output of the above program will be Hello. Main Method. This is because JVM will always call the main method based on the definition it already has. Doesn't matter how many main methods we overload it will only execute one main method based on its declaration in JVM.
In java multithreading, the main() threads are always non-daemon threads. And there is no way we can change the nature of the non-daemon thread to the daemon thread.
The program can't compile as the compiler says that the method has been already defined inside the class.
protected Object clone() throws CloneNotSupportedException< return (Object)super.clone(); >
When an exception occurs, first it searches to locate the matching catch block. In case, the matching catch block is located, then that block would be executed. Else, the exception propagates through the method call stack and goes into the caller method where the process of matching the catch block is performed. This propagation happens until the matching catch block is found. If the match is not found, then the program gets terminated in the main method.
Exceptions are runtime errors. Suppose we are making an android application with java. And it all works fine but there is an exceptional case when the application tries to get the file from storage and the file doesn’t exist (This is the case of exception in java). And if this case is not handled properly then the application will crash. This will be a bad experience for users. This is the type of error that cannot be controlled by the programmer. But programmers can take some steps to avoid this so that the application won’t crash. The proper action can be taken at this step.
No, it is not necessary for a catch block to be present after a try block. - A try block should be followed either by a catch block or by a finally block. If the exceptions likelihood is more, then they should be declared using the throws clause of the method.
public int someMethod(int i)< try< //some statement return 1; >catch(Exception e)< //some statement return 999; >finally< //finally block statements > >
finally block will be executed irrespective of the exception or not. The only case where finally block is not executed is when it encounters ‘System.exit()’ method anywhere in try/catch block.
Yes, the concept can be termed as constructor chaining and can be achieved using this() .
In the case of ArrayList, data storing in the form of primitive data types (like int, float, etc.) is not possible. The data members/objects present in the ArrayList have references to the objects which are located at various sites in the memory. Thus, storing of actual objects or non-primitive data types (like Integer, Double, etc.) takes place in various memory locations.
However, the same does not apply to the arrays. Object or primitive type values can be stored in arrays in contiguous memory locations, hence every element does not require any reference to the next element.
It is because the 0 index array avoids the extra arithmetic operation to calculate the memory address.
Example - Consider the array and assume each element takes 4-byte memory space. Then the address will be like this -
Now if we want to access index 4. Then internally java calculates the address using the formula-
[Base Address + (index * no_of_bytes)]. So according to this. The starting address of the index 4 will be - [100 + (4*4)] = 116. And exactly that's what the address is calculated.
Now consider the same with 1 index Array -
Now if we apply the same formula here. Then we get - 116 as the starting address of the 4th index. Which is wrong. Then we need to apply formula - [Base Address + ((index-1) * no_of_bytes)].
And for calculating this, an extra arithmetic operation has to be performed. And consider the case where millions of addresses need to be calculated, this causes complexity. So to avoid this, ) the index array is supported by java.
In the linked list, we only need to adjust the references when we want to delete the element from either end or the front of the linked list. But in the array, indexes are used. So to manage proper indexing, we need to adjust the values from the array So this adjustment of value is costlier than the adjustment of references.
Example - To Delete from the front of the linked list, internally the references adjustments happened like this.
The only thing that will change is that the head pointer will point to the head’s next node. And delete the previous node. That is the constant time operation.
Whereas in the ArrayList, internally it should work like this-
For deletion of the first element, all the next element has to move to one place ahead. So this copying value takes time. So that is the reason why removing in ArrayList is slower than LinkedList.
There are a total of 4 overloaded methods for add() and addAll() methods available in List Interface. The below table states the description of all.
Return Type | Method Description |
---|---|
boolean | add(Element e): This method is used for adding the element at the end of the List. The Datatype of the element is of any type it has been initially assigned with. It returns the boolean indicating successfully inserted or not. |
void | add(int index, Element e): This method is the overloaded version of add() method. In this, along with the element, the index is also passed to the method for the specific index the value needs to be inserted. |
boolean | addAll(Collection c): This method helps to add all elements at the end of collections from the list received in the parameter. It contains an iterator that helps to iterate the list and add the elements to the collection. |
boolean | addAll(int index, Collection c): This is the overloaded method for addAll() method. In this along with the list, we can pass the specified index from which the list elements need to be added. |
ArrayList is implemented in such a way that it can grow dynamically. We don't need to specify the size of ArrayList. For adding the values in it, the methodology it uses is -
1. Consider initially that there are 2 elements in the ArrayList. [2, 3].
2. If we need to add the element into this. Then internally what will happen is-
3. This process continues and the time taken to perform all of these is considered as the amortized constant time.
This is how the ArrayList grows dynamically. And when we delete any entry from the ArrayList then the following steps are performed -
1. It searches for the element index in the array. Searching takes some time. Typically it’s O(n) because it needs to search for the element in the entire array.
2. After searching the element, it needs to shift the element from the right side to fill the index.
So this is how the elements are deleted from the ArrayList internally. Similarly, the search operations are also implemented internally as defined in removing elements from the list (searching for elements to delete).
Inheritance lags behind composition in the following scenarios:
Let’s take an example:
package comparison; public class Top < public int start() < return 0; > > class Bottom extends Top < public int stop() < return 0; > >
In the above example, inheritance is followed. Now, some modifications are done to the Top class like this:
public class Top < public int start() < return 0; > public void stop() < >>
If the new implementation of the Top class is followed, a compile-time error is bound to occur in the Bottom class. Incompatible return type is there for the Top.stop() function. Changes have to be made to either the Top or the Bottom class to ensure compatibility. However, the composition technique can be utilized to solve the given problem:
class Bottom < Top par = new Top(); public int stop() < par.start(); par.stop(); return 0; > >
These 2 are the bitwise right shift operators. Although both operators look similar. But there is a minimal difference between these two right shift operators.
Example- Num1 = 8, Num2 = -8.
So the binary form of these numbers are -
Num1 = 00000000 00000000 00000000 00001000
Num2 = 11111111 11111111 11111111 11111000
‘>>’ Operator : 8 >> 1 (Shift by one bit) :
Num1 = 00000000 00000000 00000000 00000100
Num2 = 11111111 11111111 11111111 11111100
‘>>>’ Operator : 8 >>> 1 (Shift by one bit) =
Num1 = 00000000 00000000 00000000 00000100
Num2 = 01111111 11111111 11111111 11111100
Composition, and Aggregation help to build (Has - A - Relationship) between classes and objects. But both are not the same in the end. Let’s understand with the help of an example.
When a String is formed as a literal with the assistance of an assignment operator, it makes its way into the String constant pool so that String Interning can take place. This same object in the heap will be referenced by a different String if the content is the same for both of them.
public bool checking() < String first = "InterviewBit"; String second = "InterviewBit"; if (first == second) return true; else return false; >
The checking() function will return true as the same content is referenced by both the variables.
Conversely, when a String formation takes place with the help of a new() operator, interning does not take place. The object gets created in the heap memory even if the same content object is present.
public bool checking() < String first = new String("InterviewBit"); String second = new String("InterviewBit"); if (first == second) return true; else return false; >
The checking() function will return false as the same content is not referenced by both the variables.
Both ‘new’ and ‘newInstance()’ operators are used to creating objects. The difference is- that when we already know the class name for which we have to create the object then we use a new operator. But suppose we don’t know the class name for which we need to create the object, Or we get the class name from the command line argument, or the database, or the file. Then in that case we use the ‘newInstance()’ operator.
The ‘newInstance()’ keyword throws an exception that we need to handle. It is because there are chances that the class definition doesn’t exist, and we get the class name from runtime. So it will throw an exception.
Yes, it is possible for the program to go out of memory in spite of the presence of a garbage collector. Garbage collection assists in recognizing and eliminating those objects which are not required in the program anymore, in order to free up the resources used by them.
In a program, if an object is unreachable, then the execution of garbage collection takes place with respect to that object. If the amount of memory required for creating a new object is not sufficient, then memory is released for those objects which are no longer in the scope with the help of a garbage collector. The memory limit is exceeded for the program when the memory released is not enough for creating new objects.
Moreover, exhaustion of the heap memory takes place if objects are created in such a manner that they remain in the scope and consume memory. The developer should make sure to dereference the object after its work is accomplished. Although the garbage collector endeavors its level best to reclaim memory as much as possible, memory limits can still be exceeded.
Let’s take a look at the following example:
List example = new LinkedList(); while(true)< example.add(new String("Memory Limit Exceeded")); >
Concurrent execution of different processes is made possible by synchronization. When a particular resource is shared between many threads, situations may arise in which multiple threads require the same shared resource.
Synchronization assists in resolving the issue and the resource is shared by a single thread at a time. Let’s take an example to understand it more clearly. For example, you have a URL and you have to find out the number of requests made to it. Two simultaneous requests can make the count erratic.
No synchronization:
package anonymous; public class Counting < private int increase_counter; public int increase() < increase_counter = increase_counter + 1; return increase_counter; > >
If a thread Thread1 views the count as 10, it will be increased by 1 to 11. Simultaneously, if another thread Thread2 views the count as 10, it will be increased by 1 to 11. Thus, inconsistency in count values takes place because the expected final value is 12 but the actual final value we get will be 11.
Now, the function increase() is made synchronized so that simultaneous accessing cannot take place.
With synchronization:
package anonymous; public class Counting < private int increase_counter; public synchronized int increase() < increase_counter = increase_counter + 1; return increase_counter; > >
If a thread Thread1 views the count as 10, it will be increased by 1 to 11, then the thread Thread2 will view the count as 11, it will be increased by 1 to 12. Thus, consistency in count values takes place.
public void fooBarMethod(String. variables)< // method code >
fooBarMethod("foo", "bar"); fooBarMethod("foo", "bar", "boo"); fooBarMethod(new String[]"foo", "var", "boo">); public void myMethod(String. variables)< for(String variable : variables)< // business logic > >
class InterviewBit< int i; static int j; < System.out.println(" Instance Block 1. Value of i hljs-keyword">static< System.out.println(" Static Block 1. Value of j hljs-number">5; > static< j = 10; > InterviewBit()< System.out.println(" Welcome to InterviewBit "); > public static void main(String[] args)< InterviewBit ib = new InterviewBit(); > public void method_1()< System.out.println(" Instance method. "); > static< System.out.println(" Static Block 2. Value of j hljs-string">" Instance Block 2. Value of i hljs-function">public static void method_2()< System.out.println(" Static method. "); > >
The Output we get by executing this program will be
Static Block 1. Value of j = 0
Static method.
Static Block 2. Value of j = 10
Instance Block 1. Value of i = 0
Instance Block 2. Value of i = 5
Instance method.
Welcome to InterviewBit
This is a java tricky interview question frequently asked in java interviews for the experienced. The output will be like this because, when the java program is compiled and gets executed, then there are various steps followed for execution. And the steps are -
In above steps from 4 to 6, will be executed for every object creation. If we create multiple objects then for every object these steps will be performed.
Now from the above code, the execution will happen like this -
1. In the step of identification of static members. It is found that -
During identification, the JVM will assign the default value in the static int j variable. Then it is currently in the state of reading and indirectly writing. Because the original value is not assigned.
2. In the next step, it will execute the static block and assign the value in static variables.
3. Now it will execute the main method. In which it will create an object for the class InterviewBit. And then the execution of instances will happen.
4. Identify the instance variables and blocks from top to bottom.
Like a static variable, the instance variable also has been initialized with the default value 0 and will be in the state of reading and writing indirectly.
5. It will execute the instance methods and assign the original value to the instance variable.
6. And at the last step, the constructor will be invoked and the lines will be executed in the constructor.
This is how the java program gets executed.
System.out.println() is used to print the message on the console. System - It is a class present in java.lang package. Out is the static variable of type PrintStream class present in the System class. println() is the method present in the PrintStream class.
So if we justify the statement, then we can say that if we want to print anything on the console then we need to call the println() method that was present in PrintStream class. And we can call this using the output object that is present in the System class.
Java thread life cycle is as follows:
The following flowchart clearly explains the lifecycle of the thread in Java.
It is possible to import a class or package more than once, however, it is redundant because the JVM internally loads the package or class only once.
This is a big NO. We need to understand that the importing of the sub-packages of a package needs to be done explicitly. Importing the parent package only results in the import of the classes within it and not the contents of its child/sub-packages.
NO. The control of the program post System.exit(0) is immediately gone and the program gets terminated which is why the finally block never gets executed.
Marker interfaces, also known as tagging interfaces are those interfaces that have no methods and constants defined in them. They are there for helping the compiler and JVM to get run time-related information regarding the objects.
This is a convenient means of initializing any collections in Java. Consider the below example.
import java.util.HashSet; import java.util.Set; public class IBDoubleBraceDemo< public static void main(String[] args) < SetstringSets = new HashSet() < < add("set1"); add("set2"); add("set3"); > >; doSomething(stringSets); > private static void doSomething(Set stringSets) < System.out.println(stringSets); >>
In the above example, we see that the stringSets were initialized by using double braces.
Care should be taken while initializing through this method as the method involves the creation of anonymous inner classes which can cause problems during the garbage collection or serialization processes and may also result in memory leaks.
Now if a string contained supplementary characters, the length function would count that as 2 units and the result of the length() function would not be as per what is expected.
In other words, if there is 1 supplementary character of 2 units, the length of that SINGLE character is considered to be TWO - Notice the inaccuracy here? As per the java documentation, it is expected, but as per the real logic, it is inaccurate.
public class InterviewBit< public static void main(String[] args) < System.out.println('b' + 'i' + 't'); > >
“bit” would have been the result printed if the letters were used in double-quotes (or the string literals). But the question has the character literals (single quotes) being used which is why concatenation wouldn't occur. The corresponding ASCII values of each character would be added and the result of that sum would be printed.
The ASCII values of ‘b’, ‘i’, ‘t’ are:
98 + 105 + 116 = 319
Hence 319 would be printed.
First Approach: Set the object references to null once the object creation purpose is served.
public class IBGarbageCollect < public static void main (String [] args)< String s1 = "Some String"; // s1 referencing String object - not yet eligible for GC s1 = null; // now s1 is eligible for GC > >
Second Approach: Point the reference variable to another object. Doing this, the object which the reference variable was referencing before becomes eligible for GC.
public class IBGarbageCollect < public static void main(String [] args)< String s1 = "To Garbage Collect"; String s2 = "Another Object"; System.out.println(s1); // s1 is not yet eligible for GC s1 = s2; // Point s1 to other object pointed by s2 /* Here, the string object having the content "To Garbage Collect" is not referred by any reference variable. Therefore, it is eligible for GC */ > >
Third Approach: Island of Isolation Approach: When 2 reference variables pointing to instances of the same class, and these variables refer to only each other and the objects pointed by these 2 variables don't have any other references, then it is said to have formed an “Island of Isolation” and these 2 objects are eligible for GC.
public class IBGarbageCollect < IBGarbageCollect ib; public static void main(String [] str)< IBGarbageCollect ibgc1 = new IBGarbageCollect(); IBGarbageCollect ibgc2 = new IBGarbageCollect(); ibgc1.ib = ibgc2; //ibgc1 points to ibgc2 ibgc2.ib = ibgc1; //ibgc2 points to ibgc1 ibgc1 = null; ibgc2 = null; /* * We see that ibgc1 and ibgc2 objects refer * to only each other and have no valid * references- these 2 objects for island of isolcation - eligible for GC */ > >
class Main< public static void main(String[] args)< int[][] num = new int[3][]; num[0] = new int[5]; num[1] = new int[2]; num[2] = new int[3]; num[2] = new int[5]; num[0] = new int[4]; num[1] = new int[3]; num = new int[2][]; > >
In the above program, a total of 7 objects will be eligible for garbage collection. Let’s visually understand what's happening in the code.
In the above figure on line 3, we can see that on each array index we are declaring a new array so the reference will be of that new array on all the 3 indexes. So the old array will be pointed to by none. So these three are eligible for garbage collection. And on line 4, we are creating a new array object on the older reference. So that will point to a new array and older multidimensional objects will become eligible for garbage collection.
There is no boundation for using a particular dependency injection. But the recommended approach is -
Setters are mostly recommended for optional dependencies injection, and constructor arguments are recommended for mandatory ones. This is because constructor injection enables the injection of values into immutable fields and enables reading them more easily.
A scope can be set by an annotation such as the @Scope annotation or the "scope" attribute in an XML configuration file. Spring Bean supports the following five scopes:
Java Design patterns are categorized into the following different types. And those are also further categorized as
Structural patterns:
Behavioral patterns:
J2EE patterns:
Creational patterns:
The Java Garbage Collector (GC) typically removes unused objects when they are no longer required, but when they are still referenced, the unused objects cannot be removed. So this causes the memory leak problem. Example - Consider a linked list like the structure below -
In the above image, there are unused objects that are not referenced. But then also Garbage collection will not free it. Because it is referencing some existing referenced object. So this can be the situation of memory leak.
Some common causes of Memory leaks are -
A thread that has a lock won't be released even after it calls sleep(). Despite the thread sleeping for a specified period of time, the lock will not be released.
/* * Java program to check if a given inputted string is palindrome or not using recursion. */ import java.util.*; public class InterviewBit < public static void main(String args[]) < Scanner s = new Scanner(System.in); String word = s.nextLine(); System.out.println("Is "+word+" palindrome? - "+isWordPalindrome(word)); > public static boolean isWordPalindrome(String word)< String reverseWord = getReverseWord(word); //if word equals its reverse, then it is a palindrome if(word.equals(reverseWord))< return true; > return false; > public static String getReverseWord(String word)< if(word == null || word.isEmpty())< return word; > return word.charAt(word.length()- 1) + getReverseWord(word.substring(0, word.length() - 1)); > >
class InterviewBit < public static void printFibonacci(int val_1, int val_2, int num)< //Base Case if(num == 0) return; //Printing the next Fibonacci number System.out.print( val_1 + val_2 + " "); //Recursively calling for printing Fibonacci for remaining length printFibonacci(val_2, val_1+val_2, --num); > public static void main(String args[]) < System.out.println(" *** Fibonacci Series *** "); //Printing the first two values System.out.print("0 1 "); //Calling Method to print the fibonacci for length 10 printFibonacci(0, 1, 10); > >
In the above code, we are printing the base 2 Fibonacci values 0 and 1. And then based on the length of Fibonacci to be printed, we are using the helper function to print that.
The main idea is to validate the length of strings and then if found equal, convert the string to char array and then sort the arrays and check if both are equal.
import java.util.Arrays; import java.util.Scanner; public class InterviewBit < public static void main(String[] args) < Scanner s = new Scanner(System.in); //Input from two strings System.out.print("First String: "); String string1 = s.nextLine(); System.out.print("Second String: "); String string2 = s.nextLine(); // check for the length if(string1.length() == string2.length()) < // convert strings to char array char[] characterArray1 = string1.toCharArray(); char[] characterArray2 = string2.toCharArray(); // sort the arrays Arrays.sort(characterArray1); Arrays.sort(characterArray2); // check for equality, if found equal then anagram, else not an anagram boolean isAnagram = Arrays.equals(characterArray1, characterArray2); System.out.println("Anagram: "+ isAnagram); > >
public class FindFactorial < public static void main(String[] args) < int num = 10; long factorialResult = 1l; for(int i = 1; i System.out.println("Factorial: "+factorialResult); > >
Idea is to find the sum of n natural numbers using the formula and then finding the sum of numbers in the given array. Subtracting these two sums results in the number that is the actual missing number. This results in O(n) time complexity and O(1) space complexity.
public class IBMissingNumberProblem < public static void main(String[] args) < int[] array=4,3,8,7,5,2,6>; int missingNumber = findMissingNum(array); System.out.println("Missing Number is "+ missingNumber); > public static int findMissingNum(int[] array) < int n=array.length+1; int sumOfFirstNNums=n*(n+1)/2; int actualSumOfArr=0; for (int i = 0; i < array.length; i++) < actualSumOfArr+=array[i]; >return sumOfFirstNNums-actualSumOfArr; > >
Example, consider the number:
public class IBMagicNumber< public static void main(String[] args) < int num = 163; int sumOfDigits = 0; while (num > 0 || sumOfDigits > 9) < if (num == 0) < num = sumOfDigits; sumOfDigits = 0; > sumOfDigits += num % 10; num /= 10; > // If sum is 1, original number is magic number if(sumOfDigits == 1) < System.out.println("Magic number"); >else < System.out.println("Not magic number"); > > >
class InterviewBit < public static void main(String args[]) throws CustomException < // Throwing the custom exception be passing the message throw new CustomException(" This is my custom Exception "); > > //Creating Custom Exception Class class CustomException extends Exception< //Defining Constructor to throw exception message public CustomException(String message)< super(message); > >
We have created the exception class named with CustomException and called the base exception constructor with the error message that we want to print. And to avoid handling exceptions in the main method, we have used the throws keyword in the method declaration.
class InterviewBit< public static void main(String[] args)< //Input String String str = "Welcome to InterviewBit"; //Pointers. int i = 0, j = str.length()-1; //Result character array to store the reversed string. char[] revString = new char[j+1]; //Looping and reversing the string. while(i < j)< revString[j] = str.charAt(i); revString[i] = str.charAt(j); i++; j--; >//Printing the reversed String. System.out.println("Reversed String ibpage-article-header" > 9. Write a Java program to rotate arrays 90 degree clockwise by taking matrices from user input.
mport java.util.Scanner; public class InterviewBit < public static void main(String[] args) < Scanner sc = new Scanner(System.in); int no; System.out.print("Enter size of Array : "); no = sc.nextInt(); int[][] a = new int[no][no]; System.out.print("Enter "+ no*no+" Element Array : "); for(int i = 0; ifor
(int j = 0; j > System.out.print("\nArray Before Rotation\n\n"); for(int i = 0; ifor(int j = 0; j" "); > System.out.println(); > System.out.println("\n"); //Rotation //Transpose for(int i = 0; i < no; i++)< for(int j = i; j < no; j++)< int temp = a[i][j]; a[i][j] = a[j][i]; a[j][i] = temp; > > //Reverse Each row for(int i = 0; i < no; i++)< int l, j; for(j = 0, l = no -1; j < l; j++)< int temp = a[i][j]; a[i][j] = a[i][l]; a[i][l] = temp; l--; > > System.out.println("Array After Rotation - \n"); for(int i = 0; ifor(int j = 0; j" "); > System.out.println(); > > >
In the above code, for rotating the matrix to 90 degrees we are first transposing the matrix so the row becomes the column. And after that, we are reversing each row in the matrix. So this is how the matrix got rotated.
Example :
Input - 18
Output -
18 = 13 + 5
18 = 11 + 7
public class InterviewBit < // Method to Check Prime Number private static int check_prime(int num)< int flag = 0; for(int i = 2; i2; i++)< if(num%i == 0)< flag = 1; return 1; > > if(flag == 0) return 0; return 1; > // Method to get print the prime sum private static void find(int num)< for(int i = 2; i 2; i++)< if(check_prime(i) == 0)< if(check_prime(num-i) == 0) System.out.println(num + " hljs-string">" "+ i); > > > public static void main(String[] args) < find(18); > >
In the above code, for any number n, we find all the 2 pairs of numbers that are added together resulting in n. And each checking number if it is prime. If it is prime then we are printing that.
public class InterviewBit < //Recursive Method for Solving the Tower of hanoi. private static void TOH(char source, char auxiliary, char destination, int numOfDisk)< if (numOfDisk > 0)< TOH(source, destination, auxiliary, numOfDisk-1); System.out.println("Move 1 disk from "+source+" to "+destination+" using "+auxiliary+"."); TOH(auxiliary, source, destination, numOfDisk-1); > > public static void main(String[] args) < TOH('A','B','C', 3); > >
In the above code we are first moving the n-1 disk from Tower A to Tower B, then moving that nth disk from Tower A to Tower C, and finally, the remaining n-1 disk from Tower B to Tower C. And we are doing this recursively for the n-1 disk.
public class Main < //Recursive method for binary search private static boolean binarySearch(int[] arr, int low, int high, int key)< //Calculating Mid. int mid = (low + high)/2; //Base Case. if(low > high) return false; //Checking if the key is found in the middle. if(arr[mid] == key) return true; //Searching on the left half if a key exists there. if(key < arr[mid]) return binarySearch(arr, low, mid-1, key); //Searching on the other half otherwise. return binarySearch(arr, mid+1, high, key); > public static void main(String[] args) < int[] arr = 2, 5, 9, 13, 17, 21, 30>; if(binarySearch(arr, 0, (arr.length-1), 30)) System.out.println(" Element Found. "); else System.out.println(" Element not Found."); > >
In the above code, we are finding the middle element each time and checking if the element is in the middle or not. If it is not, then we check on which side from the middle it exists. And Recursively searching on the particular subarray. So this way we are reducing the search space by 2 every time. So the search time is very low.
Java is one of the simple high-level languages that provides powerful tools and impressive standards required for application development. It was also one of the first languages to provide amazing threading support for tackling concurrency-based problems. The easy-to-use syntax and the built-in features of Java combined with the stability it provides to applications are the main reasons for this language has ever-growing usage in the software community.