Toptal Com Java Interview-Questions
Toptal Com Java Interview-Questions
Give
examples.
Hide answer
inserting or removing an element only requires updating the links that immediately
precede and follow the element being inserted or removed.
However, it is worth noting that if performance is that critical, it’s better to just use an
array and manage it yourself, or use one of the high performance 3rd party packages
such as Trove or HPPC.
In Java, Strings are immutable and are stored in the String pool. What this means is
that, once a String is created, it stays in the pool in memory until being garbage
collected. Therefore, even after you’re done processing the string value (e.g., the
password), it remains available in memory for an indeterminate period of time thereafter
(again, until being garbage collected) which you have no real control over. Therefore,
anyone having access to a memory dump can potentially extract the sensitive data and
exploit it.
In contrast, if you use a mutable object like a character array, for example, to store the
value, you can set it to blank once you are done with it with confidence that it will no
longer be retained in memory.
A single ThreadLocal instance can store different values for each thread independently.
Each thread that accesses the get() or set() method of a ThreadLocal instance is
accessing its own, independently initialized copy of the variable. ThreadLocal instances
are typically private static fields in classes that wish to associate state with a thread
(e.g., a user ID or transaction ID). The example below, from the ThreadLocal Javadoc,
generates unique identifiers local to each thread. A thread’s id is assigned the first time
it invokes ThreadId.get() and remains unchanged on subsequent calls.
public class ThreadId {
// Next thread ID to be assigned
private static final AtomicInteger nextId = new AtomicInteger(0);
Each thread holds an implicit reference to its copy of a thread-local variable as long as
the thread is alive and the ThreadLocal instance is accessible; after a thread goes
away, all of its copies of thread-local instances are subject to garbage collection (unless
other references to these copies exist).
One common example for using volatile is for a flag to terminate a thread. If you’ve
started a thread, and you want to be able to safely interrupt it from a different thread,
you can have the thread periodically check a flag (i.e., to stop it, set the flag to true ).
By making the flag volatile, you can ensure that the thread that is checking its value will
see that it has been set to true without even having to use a synchronized block. For
example:
public class Foo extends Thread {
private volatile boolean close = false;
public void run() {
while(!close) {
// do work
}
}
public void close() {
close = true;
// interrupt here if needed
}
}
Compare the sleep() and wait() methods in Java,
including when and why you would use one vs. the other.
Hide answer
sleep() is a blocking operation that keeps a hold on the monitor / lock of the shared
object for the specified number of milliseconds.
wait() , on the other hand, simply pauses the thread until either (a) the specified
Tail recursion occurs when the recursive call is in the tail position within its enclosing
context - after the function calls itself, it performs no additional work. That is, once the
base case is complete, the solution is apparent. For example:
while(n > 0) {
a += n--;
}
return a;
}
Many functional languages natively support tail call optimization, however the JVM does
not. In order to implement recursive functions in Java, we need to be aware of this
limitation to avoid StackOverflowError s. In Java, iteration is almost universally preferred
to recursion.
// set our uncaught exception handler as the one to be used when the new
thread
// throws an uncaught exception
otherThread.setUncaughtExceptionHandler(handler);
// start the other thread - our uncaught exception handler will be invoked
when
// the other thread throws an uncaught exception
otherThread.start();
finally of divide
finally in main
Exception in thread "main" java.lang.ArithmeticException: / by zero
at exceptions.FinallyExecution.divide(FinallyExecution.java:20)
at exceptions.FinallyExecution.main(FinallyExecution.java:9)
…or…
public Widget() {
this.cachedWidth = width();
this.cachedHeight = height();
}
This seems like a good start for an abstract Widget: it allows subclasses to fill
in width and height , and caches their initial values. However, look when you spec out
a typical subclass implementation like so:
public class SquareWidget extends Widget {
private final int size;
@Override
protected int width() {
return size;
}
@Override
protected int height() {
return size;
}
}
Now we’ve introduced a subtle bug: Widget.cachedWidth and Widget.cachedHeight will
always be zero for SquareWidget instances! This is because the this.size =
size assignment occurs after the Widget constructor runs.
Avoid calling abstract methods in your abstract classes’ constructors, as it restricts how
those abstract methods can be implemented.
Java’s generic type parameters are invariant. This means for any distinct
types A and B , G<A> is not a subtype or supertype of G<B> . As a real world
example, List<String> is not a supertype or subtype of List<Object> . So even
though String extends (i.e. is a subtype of) Object , both of the following assignments
will fail to compile:
List<String> strings = Arrays.<Object>asList("hi there");
List<Object> objects = Arrays.<String>asList("hi there");
Java does give you some control over this in the form of use-site variance. On individual
methods, we can use ? extends Type to create a covariant parameter. Here’s an
example:
public double sum(List<? extends Number> numbers) {
double sum = 0;
for (Number number : numbers) {
sum += number.doubleValue();
}
return sum;
}
Even though longs is a List<Long> and not List<Number> , it can be passed to sum .
Similarly, ? super Type lets a method parameter be contravariant. Consider a function
with a callback parameter:
public void forEachNumber(Callback<? super Number> callback) {
callback.call(50.0f);
callback.call(123123);
callback.call((short) 99);
}
forEachNumber allows Callback<Object> to
be a subtype of Callback <Number> , which
means any callback that handles a supertype of Number will do:
forEachNumber(new Callback<Object>() {
@Override public void call(Object value) {
System.out.println(value);
}
});
Note, however, that attempting to provide a callback that handles only Long (a subtype
of Number ) will rightly fail:
// fails to compile!
forEachNumber(new Callback<Long>() { ... });
Liberal application of use-site variance can prevent many of the unsafe casts that often
appear in Java code and is crucial when designing interfaces used by multiple
developers.
If one needs a Set , how do you choose
between HashSet vs. TreeSet ?
Hide answer
Method references were introduced in Java 8 and allow constructors and methods
(static or otherwise) to be used as lambdas. They allow one to discard the boilerplate of
a lambda when the method reference matches an expected signature.
For example, suppose we have a service that must be stopped by a shutdown hook.
Before Java 8, we would have code like this:
onShutdown(new Runnable() {
@Override
public void run() {
service.stop();
}
});
This is terse (as opposed to verbose code) and clearly communicates what is going on.
Method references don’t need to be tied to a specific instance, either; one can also use
a method reference to an arbitrary object, which is useful in Stream operations. For
example, suppose we have a Person class and want just the lowercase names of a
collection of people:
List<Person> people = ...
A complex lambda can also be pushed into a static or instance method and then used
via a method reference instead. This makes the code more reusable and testable than if
it were “trapped” in the lambda.
So we can see that method references are mainly used to improve code organization,
clarity and terseness.
How are Java enums more powerful than integer
constants? How can this capability be used?
Hide answer
Enums are essentially final classes with a fixed number of instances. They can
implement interfaces but cannot extend another class.
This flexibility is useful in implementing the strategy pattern, for example, when the
number of strategies is fixed. Consider an address book that records multiple methods
of contact. We can represent these methods as an enum and attach fields, like the
filename of the icon to display in the UI, and any corresponding behaviour, like how to
initiate contact via that method:
ContactMethod(String icon) {
this.icon = icon;
}
This is just the beginning of what can be done with enums. Generally, the safety and
flexibility of enums means they should be used in place of integer constants, and switch
statements can be eliminated with liberal use of abstract methods.
For example, suppose we wanted to create a whitelist function that removes invalid
keys from a Map . This is made far easier with Map.keySet , which returns a set of keys
that is backed by the original map. When we remove keys from the key set, they are
also removed from the backing map:
public static <K, V> Map<K, V> whitelist(Map<K, V> map, K...
allowedKeys) {
Map<K, V> copy = new HashMap<>(map);
copy.keySet().retainAll(asList(allowedKeys));
return copy;
}
retainAll writes through to the backing map, and allows us to easily implement
something that would otherwise require iterating over the entries in the input map,
comparing them against allowedKey , etcetera.
Note, it is important to consult the documentation of the backing collection to see which
modifications will successfully write through. In the example
above, map.keySet().add(value) would fail, because we cannot add a key to the
backing map without a value.
A concrete code example could be something simple, like copying an object’s fields into
a map:
Such tricks can be useful for debugging, or for utility methods such as
a toString method that works on any class.
Aside from implementing generic libraries, direct use of reflection is rare but it is still a
handy tool to have. Knowledge of reflection is also useful for when these mechanisms
fail.
What are static initializers and when would you use them?
Hide answer
A static initializer gives you the opportunity to run code during the initial loading of a
class and it guarantees that this code will only run once and will finish running before
your class can be accessed in any way.
They are useful for performing initialization of complex static objects or to register a type
with a static registry, as JDBC drivers do.
Suppose you want to create a static, immutable Map containing some feature flags.
Java doesn’t have a good one-liner for initializing maps, so you can use static initializers
instead:
public static final Map<String, Boolean> FEATURE_FLAGS;
static {
Map<String, Boolean> flags = new HashMap<>();
flags.put("frustrate-users", false);
flags.put("reticulate-splines", true);
flags.put(...);
FEATURE_FLAGS = Collections.unmodifiableMap(flags);
}
Within the same class, you can repeat this pattern of declaring a static field and
immediately initializing it, since multiple static initializers are allowed.
The key difference between is that inner classes have full access to the fields and
methods of the enclosing class. This can be convenient for event handlers, but comes
at a cost: every instance of an inner class retains and requires a reference to its
enclosing class.
With this cost in mind, there are many situations where we should prefer static nested
classes. When instances of the nested class will outlive instances of the enclosing
class, the nested class should be static to prevent memory leaks. Consider this
implementation of the factory pattern:
public interface WidgetParser {
Widget parse(String str);
}
In general, String s = "Test" is more efficient to use than String s = new
String("Test") .
In the case of String s = "Test" , a String with the value “Test” will be created in the
String pool. If another String with the same value is then created (e.g., String s2 =
"Test" ), it will reference this same object in the String pool.
However, if you use String s = new String("Test") , in addition to creating a String
with the value “Test” in the String pool, that String object will then be passed to the
constructor of the String Object (i.e., new String("Test") ) and will create another String
object (not in the String pool) with that value. Each such call will therefore create an
additional String object (e.g., String s2 = new String("Test") would create an addition
String object, rather than just reusing the same String object from the String pool).
How can you swap the values of two numeric
variables without using any other variables?
Hide answer
You can swap two values a and b without using any other variables as follows:
a = a + b;
b = a - b;
a = a - b;