Skip to content

Latest commit

 

History

History
195 lines (96 loc) · 17.5 KB

FAQ.md

File metadata and controls

195 lines (96 loc) · 17.5 KB

Easy Questions


  1. Which release introduced the Collections Framework, and what was there before it?

    • J2SE 1.2, in 1998. Before that there were only Vector, Hashtable, Stack and Enumeration. They had no common interface, so you could not write a method that accepted "any collection", and they synchronised every method whether you needed it or not.
  2. Why is Map not a Collection?

    • A Collection holds single elements; a Map holds pairs. Making one extend the other would have forced both into an awkward shape. Instead Map offers three views onto itself: keySet(), values() and entrySet().
  3. When should you use ArrayList and when LinkedList?

    • Use ArrayList almost always. It stores elements in one array, so reading by index is instant; inserting in the middle has to shift the rest. LinkedList is the opposite: inserting at a position you already hold is instant, but reading by index has to walk the chain. Its most useful role is as a Deque.
  4. What is the difference between HashMap, LinkedHashMap and TreeMap?

    • HashMap is the fastest and keeps no order. LinkedHashMap costs the same and remembers the order you inserted keys in. TreeMap keeps the keys sorted, and pays a small extra cost per operation for that.
  5. What is the difference between Comparable and Comparator?

    • Comparable is the class's own idea of order. You write compareTo inside the class and you get exactly one ordering. Comparator is defined outside the class, so you can write as many different orderings as you like without touching the class itself.
  6. What does the diamond operator do?

    • It saves you from repeating the type arguments. new HashMap<>() instead of new HashMap<String, List<Integer>>(). The compiler works them out from the left-hand side. Added in Java 7.
  7. What is try-with-resources?

    • A try block that closes its resources for you. Anything declared in the try (...) header that implements AutoCloseable is closed at the end of the block, whether the block finished normally, returned, or threw. Several resources are closed in reverse order.
  8. What is multi-catch?

    • Java 7 syntax for handling several unrelated exception types in one block: catch (IOException | SQLException ex). It saves duplicating the same handling code twice. The variable is implicitly final.
  9. What is autoboxing?

    • The compiler converting between primitives and their wrapper types for you, so you can write list.add(5) on a List<Integer>. Added in Java 5.
  10. Why is an enum better than a group of int constants?

    • It is type-safe, so only the enum's own constants can be passed. It prints a name instead of a number. It can carry fields and methods. And it works properly with switch, EnumMap, EnumSet and serialization.
  11. What are varargs?

    • A parameter that accepts any number of arguments: int sum(int... numbers). Inside the method it is a normal array. A method can have only one, and it must be the last parameter.
  12. What is a static import?

    • import static java.lang.Math.max; lets you write max(a, b) instead of Math.max(a, b). It is handy for Math and for constants, and confusing if you overuse it, because the reader cannot see where the name came from.
  13. StringBuilder or StringBuffer?

    • StringBuilder, unless the object is genuinely shared between threads. They have the same API, but StringBuffer synchronises every method, which you almost never need.
  14. What is the difference between Thread.start() and Thread.run()?

    • start() asks the JVM for a new thread, which then calls run(). Calling run() yourself just runs the method on the current thread — no new thread is created at all.
  15. What does Arrays.asList return?

    • A fixed-size view over the array you passed in. You can replace elements with set, but add and remove throw UnsupportedOperationException. It is not the same as Java 9's List.of, which is fully immutable.

Medium Questions


  1. What is type erasure, and name three things it makes impossible.

    • The compiler checks your type arguments and then removes them, so at run time List<String> and List<Integer> are both just List. Because of that: you cannot write new T() or new T[10]; you cannot write x instanceof List<String>; and you cannot have two overloads f(List<String>) and f(List<Integer>), because both erase to the same signature.
  2. Why was erasure chosen instead of keeping the types at run time?

    • Compatibility. Code compiled before Java 5 had to keep running on the new JVM, and new generic code had to work with old libraries. Erasing the type arguments meant the class-file format and the JVM did not have to change at all.
  3. What is a bridge method?

    • A hidden method the compiler generates so that overriding still works after erasure. If Child implements Comparable<Child> with compareTo(Child), the compiler adds a compareTo(Object) that casts its argument and calls yours, because Object is the erased parameter type the interface really declares.
  4. Explain PECS.

    • Producer extends, Consumer super. If a parameter only gives you values, declare it ? extends T — you can read, not add. If it only receives values from you, declare it ? super T — you can add, and reads only give you Object. Collections.copy(List<? super T> dest, List<? extends T> src) uses both in one signature.
  5. Why is List<Integer> not a List<Number>, when arrays allow the equivalent?

    • If it were allowed, you could assign a List<Integer> to a List<Number> variable and then add a Double to it, which would corrupt the original list. Arrays do allow this (Object[] a = new Integer[3]), and the mistake shows up only at run time as an ArrayStoreException. Generics were designed to catch the same mistake at compile time.
  6. Why does Integer a = 127, b = 127; a == b print true, but the same with 128 prints false?

    • Integer.valueOf keeps a cache of the values from −128 to 127, so both variables point at the same cached object. Above 127 each boxing creates a new object, and == compares references, not values. Use equals for boxed types.
  7. What are the four kinds of nested class?

    • Static nested, which has no link to an outer object. Inner (non-static), which holds a hidden reference to the outer object. Local, declared inside a method body. Anonymous, declared and created in a single expression. All except static nested keep the enclosing object alive, which can be a memory leak.
  8. What is a suppressed exception?

    • When a try-with-resources block throws and close() then also throws, the block's exception is the one that propagates, and close()'s exception is attached to it. You read it back with getSuppressed(). The old try/finally pattern lost the original exception instead, which made debugging much harder.
  9. What is precise rethrow?

    • Java 7 taught the compiler to look at which exceptions the try block can actually throw, rather than only at the declared type of the catch parameter. So catch (Exception ex) { throw ex; } can sit inside a method declared throws IOException, as long as ex is never reassigned.
  10. How is a switch on a String compiled?

    • Into a switch on hashCode() to find a candidate label, followed by an equals() call to confirm it. So it stays fast even with many cases. Passing null throws NullPointerException; case null only became legal with Java 21's pattern matching.
  11. What does NIO.2 give you over java.io.File?

    • Real error messages. File methods returned false on failure without saying why; Files throws a specific exception such as NoSuchFileException that names the file. On top of that you get symbolic-link support, file attributes, atomic moves, walkFileTree and WatchService.
  12. Why use an ExecutorService instead of creating threads yourself?

    • It separates the task from the thread that runs it. You get thread reuse, a fixed upper limit on how many run at once, a queue for the rest, a Future to collect the result, and an orderly shutdown. Creating one thread per task gives you none of that.
  13. What is the difference between Runnable and Callable?

    • Runnable.run() returns nothing and cannot throw a checked exception. Callable<V>.call() returns a value and may throw. Submitting a Callable to an executor gives you a Future<V>, and if the task throws, get() reports it as an ExecutionException wrapping the original.
  14. What does volatile guarantee, and what does it not?

    • It guarantees that a write is visible to other threads straight away, and that reads and writes are not reordered around it. It does not make anything atomic. volatile int i; i++; is still three steps and still loses updates. Use it for a stop flag; use AtomicInteger or a lock for a counter.
  15. Why must wait() always be called inside a loop?

    • Because a thread can wake up without anyone notifying it — the specification allows these "spurious wakeups". And with notifyAll, several threads wake up for a single item, so all but one will find nothing there. In both cases you have to re-check the condition after waking, which means while, not if.
  16. How does ConcurrentHashMap differ from Collections.synchronizedMap?

    • synchronizedMap puts one lock around the whole map and its iterators still throw ConcurrentModificationException. ConcurrentHashMap locks only the part of the map being touched, offers atomic compound operations like putIfAbsent and compute, and its iterators never throw — they just may or may not show writes made after iteration started.
  17. What is fork/join, and where is it used today?

    • A pool designed for splitting one large task into smaller ones. Each worker has its own queue and idle workers steal tasks from busy ones. You write a RecursiveTask that splits until the piece is small, then computes. Java 8's parallel streams run on the shared ForkJoinPool.commonPool(), which is why they suit CPU-bound work over easily-split sources.

Hard Questions


  1. List the real differences between a lambda and an anonymous class implementing the same interface.

    • this is the anonymous object inside an anonymous class, but the enclosing object inside a lambda, because a lambda does not create a new scope for it. An anonymous class compiles to a real class file that is loaded like any other; a lambda compiles to an invokedynamic call site that LambdaMetafactory links on first use, and a lambda that captures nothing can be reused as a single instance instead of allocated each time. An anonymous class can extend a class, hold fields and implement several methods; a lambda can only target one functional interface. Anonymous classes also introduce a new scope, so they can shadow names that a lambda cannot.
  2. Why did captured variables have to be final before Java 8, and what changed?

    • The captured value is copied into a field of the generated class. If the original local could still change, the copy would go stale, and nobody reading the code could tell which value they were looking at. Java forbids the situation rather than defining confusing behaviour. Java 8 kept the rule and relaxed only the syntax: if the compiler can prove you never reassign the variable, you no longer have to write final. The old workaround for a value that genuinely had to change was a one-element final array.
  3. State the equals/hashCode contract and describe what breaks when it is violated.

    • Equal objects must return equal hash codes, and the hash code must not change while the object is in a hash-based collection. Override hashCode but not equals, and every object stays unique, so lookups miss. Override equals but not hashCode, and two equal objects can land in different buckets — a HashSet will hold both, and map.get(key) returns null for a key that is equal to a stored one. Change a field used by hashCode after inserting the object and you lose the entry completely: it is in the old bucket, and every lookup goes to the new one.
  4. How does HashMap handle collisions, and what changed in Java 8?

    • Keys that land in the same bucket are kept in a list. Before Java 8 that list was always a linked list, so in the worst case — many keys with the same hash — lookups degraded to O(n). Java 8 converts a bucket to a red-black tree once it holds 8 entries and the table is at least 64 long, which makes the worst case O(log n). This was a security fix as much as a performance one: attackers could deliberately send colliding keys to slow a server to a halt. HashMap also spreads the hash with h ^ (h >>> 16) so the high bits influence which bucket is chosen.
  5. Why is static state shared between Box<String> and Box<Integer>?

    • Because after erasure there is only one Box class. The type arguments exist in the source and in the compiler's checks, not in the loaded class, so there is exactly one copy of each static field. The same reasoning explains why a static method cannot mention the class's type parameter: a type argument belongs to an instance's type, and a static method has no instance.
  6. What is heap pollution, and what does @SafeVarargs actually promise?

    • A T... parameter is really a T[]. Arrays know their element type at run time, generics do not, so a List<String>[] can be aliased through Object[] and have a List<Integer> stored into it. Nothing fails at that moment; the ClassCastException appears later, somewhere unrelated. That is heap pollution. @SafeVarargs checks nothing — it is the author asserting that the method never writes into the varargs array and never lets it escape. It is restricted to static, final and private methods, because an overriding method could break the promise.
  7. What does the Java Memory Model (Java 5, JSR 133) actually guarantee?

    • It defines a happens-before relation, and anything not connected by it may simply never become visible to another thread, because the compiler and the CPU are free to reorder. The main edges are: releasing a monitor happens-before any later acquisition of the same monitor; a volatile write happens-before every later read of that field; Thread.start() happens-before everything the new thread does; everything a thread does happens-before another thread's join() returns; and a final field assigned in a constructor is visible to any thread afterwards, provided this did not escape during construction.
  8. Why is double-checked locking broken without volatile?

    • instance = new Singleton() is three operations: allocate memory, run the constructor, assign the reference. The JVM is allowed to reorder the last two. Another thread can then see a non-null reference to an object whose constructor has not finished, and read uninitialised fields. Declaring the field volatile forbids that reordering — but only from Java 5, when the memory model was rewritten; before that the pattern was unfixable. In practice a static holder class or an enum singleton is simpler and correct without any of this.
  9. When does ThreadLocal leak memory?

    • The values live in a map owned by the Thread object. In a thread pool the thread outlives the request, so anything you never remove stays reachable for as long as the pool does. The map holds its keys weakly but its values strongly, so even a discarded ThreadLocal can keep its value pinned until that thread is reused. Always call remove() in a finally block. Java 21's ScopedValue exists partly to remove this whole category of bug.
  10. Explain fail-fast, fail-safe and weakly consistent iterators.

    • Fail-fast (ArrayList, HashMap): the collection counts its structural changes and the iterator checks that count on every step, throwing ConcurrentModificationException when it changes. It is best effort — a detection aid, not a guarantee. Fail-safe or snapshot (CopyOnWriteArrayList): the iterator walks a frozen copy, so it never throws and never sees later writes. Weakly consistent (ConcurrentHashMap): the iterator walks the live structure, never throws, and may or may not reflect writes made after it started.
  11. Why should compareTo never be written as a - b?

    • Because integer subtraction overflows. With a = Integer.MIN_VALUE and b = 1, the difference wraps around to a positive number, so the comparator claims the smallest possible value is the larger one. That breaks TreeMap's invariants silently, and since Java 7 replaced the sort algorithm with TimSort it can also throw IllegalArgumentException: Comparison method violates its general contract! from an apparently unrelated sort. Use Integer.compare(a, b).
  12. What is invokedynamic and why does the Java language itself depend on it now?

    • It is a call instruction whose target is not fixed in the class file. Instead it names a bootstrap method that runs once, the first time the line executes, and returns a MethodHandle for the JVM to link. After that the call is optimised like a static one. It was added in Java 7 for dynamic languages on the JVM, and then Java itself became its main user: lambdas (LambdaMetafactory, Java 8), string concatenation (StringConcatFactory, Java 9) and switch pattern matching (SwitchBootstraps, Java 21). The advantage is that these features need no new bytecode, so how they work can be improved without recompiling anyone's code.