Not all interviews will focus on algorithms and data structures — often times an interview will focus solely on the language or technology that you have claimed to be an expert in. In interviews like these, there usually aren’t any “gotcha” questions, instead they require you to draw on memory and your experience with the language — in other words, they test your knowledge of the programming language.
However, it can be easy to forget all the ins and outs of a language like Java, because simply put, we don’t deal with questions like “What kind of memory does the JVM manage?” and “Describe polymorphism with an example.” on a daily basis.
Today we’ll help you brush up by reviewing interview questions on:
Compilation to bytecode is the magic behind Java’s interoperability. Different operating systems and hardware architectures have JVMs custom designed for themselves and all JVMs can run the same bytecode. Therefore, if you write a Java program on Linux, it will run seamlessly on a JVM designed for Windows operating system, making code agnostic to the underlying hardware and OS.
JRE (Java Runtime Environment) includes the Java Virtual Machine and the standard Java APIs (core classes and supporting files.). The JRE contains just enough to execute a Java application, but not enough to compile it.
JDK (Java Development Kit) is the JRE plus the Java compiler, and a set of other tools to compile and debug code. JRE consists of Java platform libraries, Java Virtual Machine (JVM), Java Plugin and Java Web Start to run Java applications. JRE as a stand-alone does not contain compilers and debugging tools. If you need to develop Java programs you need the full Java SDK. The JRE is not enough for program development. Only the full Java SDK contains the Java compiler which turns your .java
source files into bytecode .class
files.
JVM (Java Virtual Machine) is an implementation of a specification, detailing the behavior expected of a JVM. Any implementation that conforms to the JVM specification should be able to run code compiled into Java bytecode irrespective of the language in which the code was originally written. In the Java programming language, all source code is first written in plain text files ending with the .java
extension. Those source files are then compiled into .class files by the javac
compiler. A .class
file does not contain code that is native to your processor; it instead contains bytecodes — the machine language of the Java Virtual Machine. The java launcher tool then runs your application with an instance of the Java Virtual Machine.
There’s no explicit modifier for package private. In the absence of any modifier the class or member variables are package private. A member marked package private is only visible within its own package. Consider the class below.
Package private is a slightly wider form of private. One nice thing about package-private is that you can use it to give access to methods you would otherwise consider private to unit test classes.
So, if you use helper classes which have no other use but to help your public classes do something clients need, it makes sense to make them package private as you want to keep things as simple as possible for users of the library.
finalize()
method in the Object class? What are some alternatives?The Object class provides a callback method, finalize()
, that may be invoked on an object when it becomes garbage. Object’s implementation of finalize()
does nothing — you can override finalize()
to do cleanup, such as freeing up resources.
The finalize()
method may be called automatically by the system, but when it is called, or even if it is called, is uncertain. Therefore, you should not rely on this method to do your cleanup for you. For example, if you don’t close file descriptors in your code after performing I/O and you expect finalize()
to close them for you, you may run out of file descriptors.
Here are some alternatives:
AutoCloseable
interface.PhantomReference
to perform cleanup when an object is garbage collectedCleaner
class to perform cleanup actions.close()
method, which does the cleanup and document that the method be called.final int[] array = new int[5];array[0] = 1;
It may appear counterintuitive, but we can actually change the contents of the array even though it is marked as final. The array variable points to a particular start location in the memory where the contents of the array are placed.
The location or the memory address can’t be changed. For example, the following code will not compile:
final int[] array = new int [5]array = new int[10];
However, the following code will work.
public class FinalArrayExample {final int[] array = new int[5];// allowedvoid changeArrayContents(int i, int val) {array[i] = val;}// not allowed and will not compile/*void changeArray() {array = new int [10]}*/}
An abstract class can’t be instantiated, but it can be subclassed. An abstract class usually contains abstract and non-abstract methods that subclasses are forced to provide an implementation for.
An interface is a completely “abstract class” that is used to group related methods with empty bodies.
Each allow for advanced OOP constructions in Java.
Following are four main differences between abstract classes and interfaces:
An abstract class can have final variables, static variables, or class member variables whereas an interface can only have variables that are final and static by default.
An abstract class can have static, abstract, or non-abstract methods. An interface can have static, abstract, or default methods.
Members of an abstract class can have varying visibility of private, protected, or public. Whereas, in an interface all methods and constants are public.
A class can only extend another class, but it can implement multiple interfaces. Similarly, an interface can extend multiple interfaces. An interface never implements a class or an interface.
Use an abstract class when subclasses share state or use common functionality. Or you require to declare non-static, non-final fields or need access modifiers other than public.
Use an interface if you expect unrelated classes would implement your interface. For example, the interfaces Comparable and Cloneable are implemented by many unrelated classes. Interfaces are also used in instances where multiple inheritance of type is desired.
Polymorphism is the ability in object-oriented programming to present the same interface for differing underlying forms or data types. Polymorphism is when you can treat an object as a generic version of something, but when you access it, the code determines which exact type it is and calls the associated code. What this means is that polymorphism allows your code to work with different classes without needing to know which class it’s using.
Polymorphism is used to make applications more modular and extensible. Instead of messy conditional statements describing different courses of action, you create interchangeable objects that you select based on your needs. That is the basic goal of polymorphism.
The classic example of polymorphism is a Shape
class. We derive Circle
, Triangle
, and Rectangle
classes from the parent class Shape
, which exposes an abstract method draw(). The derived classes provide their custom implementations for the draw()
method. Now it is very easy to render the different types of shapes all contained within the same array by calling the draw()
method on each object. This saves us from creating separate draw methods for each shape e.g. drawTriangle()
, drawCircle()
etc.
Yes, the main
method, which is a static method, can be overloaded. But only public static void main(String[] args)
will be used when your class is launched by the JVM even if you specify one or two command-line arguments. However, programmatically one can invoke the overloaded versions of the main
method.
We can pass variable number of arguments to a method using varargs
feature. Below is an example of passing multiple arguments of the same type to a method.
public void childrenNames(string... names) {for(int i= 0; i < names.length; i++)system.out.println(names[i]);}
varargs
variable is treated like an array.varargs
variable must appear at the last in the method signature.varargs
in a method signature.The above method can be invoked as follows:
Invoking Varargs
Method
childrenNames();childrenNames("Jane");childrenNames("Jane", "Tom", "Peter");
A semaphore can potentially act as a mutex if the number of permits it can give out is set to 1. However, the most important difference between the two is that in the case of a mutex, the same thread must call acquire and subsequent release on the mutex whereas in the case of a binary semaphore, different threads can call acquire and release on the semaphore.
This leads us to the concept of “ownership”. A mutex is owned by the thread acquiring it, till the point, it releases it, whereas for a semaphore there’s no notion of ownership.
The Serializable
interface gets us automatic serialization capability for objects of our class. On the other hand the Externalizable
interface provides a way to implement a custom serialization mechanism. A class that implements the Externalizable
interface is responsible to save and restore the contents of its own instances.
The Externalizable
interface extends the Serializable
interface and provides two methods to serialize and deserialize an object, writeExternal()
and readExternal()
.
Multiple types of exceptions thrown by a snippet of code can be handled by multiple catch block clauses followed by the try block. An example snippet of exception handling appears below:
void process(int val) {try {if (val == 1)//checked exceptionthrow new FileNotFoundException();if (val == 2)// runtime exceptionthrow new NullPointerExxception();if (val == 3)// error exceptionthrow new StackOverflowError} catch (RuntimeException re) {// catches all unchecked exceptions} catch (Exception e) {// catches all checked exceptions} catch (Error err) {// catches all errors}}
Initially, you may want to use a HashSet data structure as it will give you a better time complexity, but it makes no guarantees as to the iteration order of the set; in particular, it does not guarantee that the order will remain constant over time.
So if you are wanting to maintain the order it’s best to use a TreeSet as it stores keys in ascending order rather than in their insertion order. It’s not thread-safe. However, keep in mind that TreeSet is not thread-safe whereas a HashSet is.
Here are three key steps you can take to improve the memory footprint:
null
when not needed. This will make objects eligible for garbage collection.The best way to implement a singleton as per Josh Bloch is to use an enum
type for the singleton. Because Java ensures that only a single instance of an enum
is ever created, the singleton class implemented via enums
is safe from reflection and serialization attacks.
class Demonstration {public static void main( String args[] ) {Superman superman = Superman.INSTANCE;superman.fly();}}enum Superman {INSTANCE;private final String name = "Clark Kent";private String residence = "USA";public void fly() {System.out.println("I am flyyyyinggggg ...");}}
There has been a lot covered in this post about the Java programming language, ranging from the Java ecosystem to multi-threading and exceptions. These are the types of Java interview questions you can expect.
However, the material here just scratches the surface. There are many more concepts to revisit or explore.
If you’d like to take a deep dive into Java and explore hundreds of more questions about the topics mentioned above then Grokking Coding Interview Patterns in Java is going to be a great resource to get you refreshed on the core java principles — everything from the basics to more advanced functionality.
Happy learning!
Join a community of more than 1.6 million readers. A free, bi-monthly email with a roundup of Educative's top articles and coding tips.