Java
Core Java for backend interviews: OOP, equals/hashCode, collections internals, the JVM and GC, concurrency, streams and modern Java features.
Top 20 Java interview questions most asked first
1.What are the four pillars of object-oriented programming, and how does Java support each one?easy
The four pillars are encapsulation, inheritance, polymorphism and abstraction.
- Encapsulation: bundle state with the behaviour that works on it, and hide the state behind an API. In Java that means
privatefields exposed through methods that can enforce invariants. - Inheritance: a class reuses and extends another with
extends. Java allows single inheritance of classes, but a class can implement many interfaces. - Polymorphism: one reference type, many behaviours. Compile-time polymorphism is method overloading; runtime polymorphism is overriding, where the JVM picks the method from the object's actual class (dynamic dispatch).
- Abstraction: expose what something does, not how. Interfaces and abstract classes let callers depend on a contract such as
Listrather than onArrayList.
I'd also mention that composition is often preferred over inheritance, because deep class hierarchies couple classes tightly.
What interviewers listen for- Encapsulation: private state behind methods
- Inheritance:
extends, single class inheritance only - Polymorphism: overloading (compile time) vs overriding (runtime)
- Abstraction via interfaces and abstract classes
- Favour composition over inheritance
Likely follow-up: Why does Java not support multiple inheritance of classes? · Is encapsulation the same as abstraction?
- Encapsulation: bundle state with the behaviour that works on it, and hide the state behind an API. In Java that means
2.What is the difference between the JDK, the JRE and the JVM?easy
They nest inside each other.
- The JVM (Java Virtual Machine) runs bytecode. It loads and verifies classes, manages memory with a garbage collector, and executes code with an interpreter plus a JIT compiler. Each platform has its own JVM, which is exactly what makes
.classfiles portable: "write once, run anywhere". - The JRE (Java Runtime Environment) is the JVM plus the standard class libraries (
java.lang,java.utiland so on): everything needed to run a program. - The JDK (Java Development Kit) is the JRE plus development tools:
javac,jar,javadoc,jshell,jlink, and diagnostic tools likejcmd.
One modern nuance: since JDK 11, Oracle no longer ships a separate JRE. Instead,
jlink(Java 9+) builds a trimmed runtime image containing only the modules your application needs.What interviewers listen for- JVM executes bytecode and is platform-specific
- JRE = JVM + standard class libraries
- JDK = JRE + tools like
javacandjar - Bytecode is portable: write once, run anywhere
jlinkbuilds custom runtime images
Likely follow-up: Is the JVM itself platform-independent? · What does the JIT compiler do?
- The JVM (Java Virtual Machine) runs bytecode. It loads and verifies classes, manages memory with a garbage collector, and executes code with an interpreter plus a JIT compiler. Each platform has its own JVM, which is exactly what makes
3.What is the difference between
==andequals()? What does this code print?easy==compares values for primitives but references for objects: it's true only if both sides point to the very same object.equals()compares logical equality, and what that means is up to the class.Object.equalsis just==, so a class only gets value semantics if it overrides it.String, the wrapper types, collections and records all do.So the snippet prints
false,true,false,true.s2is a new object, so==is false whileequalsis true. TheIntegercase is the classic trap: autoboxing uses a cache that's only guaranteed for -128 to 127, so with default JVM settings1000produces two different boxes.Rules of thumb: use
==for primitives, enums and deliberate identity checks; useequalsfor everything else, orObjects.equals(a, b)when either side may benull.String s1 = "java"; String s2 = new String("java"); System.out.println(s1 == s2); // ? System.out.println(s1.equals(s2)); // ? Integer x = 1000, y = 1000; System.out.println(x == y); // ? System.out.println(x.equals(y)); // ?What interviewers listen for==compares references for objects, values for primitivesequalscompares logical content when overridden- Default
Object.equalsis identity - Integer cache only guaranteed for -128 to 127
- Use
Objects.equalsfor null-safe comparison
Likely follow-up: Why is
==safe for enums? · What happens if you overrideequalsbut nothashCode?4.Why is
Stringimmutable in Java, and what is the string pool?easyA
Stringcan't change once created: methods liketoUpperCase()orconcat()return new strings, and the class isfinal, so no subclass can add mutability. The main reasons:- String pool: literals are interned, so every
"hello"in a program can share one instance. Sharing is only safe because nobody can modify it. - Security: strings hold class names, file paths, URLs and credentials. If they were mutable, a value could change after it had been validated.
- Thread safety: immutable objects can be shared across threads without synchronization.
- Hashing: the hash code is cached after first use, which makes strings fast, reliable
HashMapkeys.
The pool is a JVM-managed table of canonical strings. String literals and compile-time constants go there automatically, and
intern()looks up or adds others. Since Java 7 it lives on the main heap rather than in PermGen, so unused pooled strings can be garbage-collected.What interviewers listen for- Methods return new strings; the class is
final - Immutability makes pooling and sharing safe
- Security: values cannot change after validation
- Thread-safe, and the hash code is cached
- The pool lives on the heap since Java 7
Likely follow-up: Why is a
char[]sometimes preferred over aStringfor passwords? · What doesintern()do?- String pool: literals are interned, so every
5.How does
HashMapwork internally? Walk me throughputandget.midA
HashMapis an array of buckets. Each bucket holds a chain of nodes with the key, value, hash and anextpointer.- Hashing:
putcallskey.hashCode()and spreads it by XOR-ing the high 16 bits into the low 16. The index is(n - 1) & hash, which works because the capacitynis always a power of two. - Collisions: an empty bucket takes the node directly. Otherwise the chain is walked, comparing hash and then
equals(): a match replaces the value, else a node is appended. - Treeification (Java 8+): when a chain grows past 8 nodes and the table has at least 64 buckets, it becomes a red-black tree, so a bad hash degrades lookups to O(log n) rather than O(n). A smaller table is resized instead.
- Resizing: default capacity is 16 with load factor 0.75, so once size exceeds 12 the table doubles. Each entry either stays at index
ior moves toi + oldCapacity.
getrepeats the hash and index steps, then matches withequals(). Onenullkey is allowed.What interviewers listen for- Array of buckets; index is
(n - 1) & hash - Collisions chained, matched by hash then
equals - Chains past 8 nodes treeify (table size at least 64)
- Load factor 0.75; the table doubles on resize
- One
nullkey allowed; not thread-safe
Likely follow-up: Why must the capacity be a power of two? · What happens if a key is mutated after insertion?
- Hashing:
6.What is the contract between
equals()andhashCode(), and what breaks if you override only one of them?midequalsmust be reflexive, symmetric, transitive and consistent, andx.equals(null)must return false. The link tohashCode:- If two objects are equal, they must return the same hash code.
- Unequal objects may share a hash code (a collision), but fewer collisions mean faster hash tables.
- The hash code must stay stable while the fields used by
equalsdon't change.
Hash-based collections depend on this: they use the hash to find the bucket and only then call
equals. In the snippet,Pointoverridesequalsbut inherits the identity-basedhashCode, so two equal points almost certainly have different hash codes andcontainsreturns false.The fix is to override both from the same fields, for example
Objects.hash(x, y), or to use a record, which generates both. And keep keys immutable: mutating a field after insertion leaves the entry filed under its old hash, where lookups can't find it.class Point { final int x, y; Point(int x, int y) { this.x = x; this.y = y; } @Override public boolean equals(Object o) { return o instanceof Point p && x == p.x && y == p.y; } // hashCode() not overridden! } Set<Point> set = new HashSet<>(); set.add(new Point(1, 2)); System.out.println(set.contains(new Point(1, 2))); // almost always falseWhat interviewers listen for- Equal objects must have equal hash codes
- Equal hash codes do not imply equality
- equals: reflexive, symmetric, transitive, consistent, null-safe
- Override both from the same fields
- Mutable keys break hash-based collections
Likely follow-up: Why is using
getClass()versusinstanceofinequalsdebated? · What does a record generate for you?7.What is the difference between an abstract class and an interface, now that interfaces can have
default,staticandprivatemethods?easySince Java 8 the line is blurrier, but the core differences remain:
- State: an abstract class can have instance fields and constructors. An interface can only have constants (implicitly
public static final), never instance state. - Inheritance: a class extends one abstract class but can implement many interfaces.
- Access: abstract class members can use any access level. Interface methods are public, except
privatehelper methods, allowed since Java 9. - Evolution: Java 8 added
defaultmethods, so an interface can grow without breaking implementers, andstaticmethods for utilities, which are called on the interface and not inherited by implementing classes.
I use an interface for a capability or contract that unrelated classes share, like
ComparableorRunnable. I use an abstract class when related classes share state and a partly implemented template, such as a base class that manages a connection and lets subclasses fill in one step.What interviewers listen for- Abstract classes can hold state and constructors
- One superclass, many interfaces
defaultandstaticinterface methods since Java 8privateinterface methods since Java 9- Interface = capability; abstract class = shared base
Likely follow-up: What happens if a class inherits the same default method from two interfaces? · Can an interface have a constructor?
- State: an abstract class can have instance fields and constructors. An interface can only have constants (implicitly
8.What is the difference between method overloading and method overriding? What does this print?easy
Overloading means several methods with the same name but different parameter lists. The compiler picks one at compile time from the static types of the arguments. The return type alone can't distinguish overloads.
Overriding means a subclass redefines an inherited instance method with the same signature. The JVM picks the implementation at runtime from the object's actual class: dynamic dispatch.
So the snippet prints
Woof, thenanimal.a.speak()dispatches on the runtime type,Dog, butgreet(a)was resolved at compile time, when the type ofaisAnimal.Overriding rules: the return type may be covariant (a subtype), access can't be narrowed, the method can't throw broader checked exceptions, and
static,privateandfinalmethods can't be overridden. A static method with the same signature hides the parent's instead. Always add@Overrideso the compiler catches signature mistakes.class Animal { void speak() { System.out.println("..."); } } class Dog extends Animal { @Override void speak() { System.out.println("Woof"); } } static void greet(Animal a) { System.out.println("animal"); } static void greet(Dog d) { System.out.println("dog"); } Animal a = new Dog(); a.speak(); // ? greet(a); // ?What interviewers listen for- Overloading: same name, different parameters, compile time
- Overriding: same signature in a subclass, runtime dispatch
- Overload choice uses the static argument types
- Covariant returns; no narrower access or broader checked exceptions
- Static methods are hidden, not overridden
Likely follow-up: Can you overload
main? · Why are fields not polymorphic?9.When would you use
ArrayListversusLinkedList?easyArrayListis backed by a resizable array.get(i)is O(1), appending is amortized O(1), and when the array is full it grows by about 50% and copies the elements over. Inserting or removing in the middle is O(n) because later elements shift, but that shift is a fast bulk copy over contiguous memory.LinkedListis a doubly linked list that also implementsDeque. Adding or removing at either end, or at an iterator's position, is O(1), butget(i)is O(n) because it walks from the nearer end. Every element costs a separate node object with two extra pointers, and nodes are scattered in memory, which hurts CPU cache performance.In practice
ArrayListwins almost every real workload, even ones that look insert-heavy, because finding the position in a linked list is itself O(n). For a queue or stack I'd chooseArrayDequerather thanLinkedList.What interviewers listen for- ArrayList: O(1) random access, amortized O(1) append
- ArrayList grows by about 50% when full
- LinkedList: O(1) at the ends, O(n) indexed access
- LinkedList has per-node overhead and poor locality
- Prefer ArrayList; use ArrayDeque for queues and stacks
Likely follow-up: When would
LinkedListactually be the better choice? · What is the default capacity of anArrayList?10.What is the difference between checked and unchecked exceptions?easy
Checked exceptions are subclasses of
Exceptionthat aren'tRuntimeExceptions, such asIOExceptionorSQLException. The compiler forces you to handle them: catch them or declare them withthrows. They model recoverable conditions outside the program's control.Unchecked exceptions are
RuntimeExceptionand its subclasses, likeNullPointerException,IllegalArgumentExceptionorIndexOutOfBoundsException. They usually signal programming bugs, and the compiler doesn't require handling.Errors such asOutOfMemoryErrorandStackOverflowErrorare unchecked too; they signal serious problems that applications generally shouldn't catch.The hierarchy has
Throwableat the top, withExceptionandErrorunder it. One rule worth knowing: an overriding method can't declare broader checked exceptions than the method it overrides. Many modern APIs and frameworks, Spring included, favour unchecked exceptions, because checked ones add boilerplate and don't compose well with lambdas.What interviewers listen for- Checked: compiler enforces catch or
throws - Unchecked:
RuntimeExceptionand its subclasses Erroris unchecked and usually not caught- Hierarchy:
Throwable→Exception/Error - Overrides cannot throw broader checked exceptions
Likely follow-up: When would you create a custom checked exception? · How do you handle a checked exception inside a lambda?
- Checked: compiler enforces catch or
11.What is the difference between
final,finallyandfinalize()?easyThey're unrelated apart from the spelling.
finalis a modifier. Afinalvariable can be assigned only once; for a reference, the reference is fixed but the object it points to can still change. Afinalmethod can't be overridden, and afinalclass, likeString, can't be subclassed.finallyis a block aftertry/catchthat runs whether the try block completes normally, returns or throws. It's meant for cleanup, though try-with-resources is usually better. It's skipped only in extreme cases, such asSystem.exit(), a JVM crash, or a try block that never finishes.finalize()is a method onObjectthat the garbage collector might call before reclaiming an object. There's no guarantee when, or even whether, it runs; it slows GC and can resurrect objects. It was deprecated in Java 9 and deprecated for removal in Java 18. Use try-with-resources orjava.lang.ref.Cleanerinstead.
What interviewers listen forfinal: no reassignment, overriding or subclassing- A final reference can still point to a mutable object
finallyruns after try/catch, for cleanupfinalizeis unreliable and deprecated for removal- Prefer try-with-resources or
Cleaner
Likely follow-up: What happens if both the
tryblock and thefinallyblock contain areturn?12.What is the difference between
String,StringBuilderandStringBuffer?easyStringis immutable, so every "modification" creates a new object. Building a string with+=in a loop creates many intermediate strings and copies the characters again each time, which is O(n²) overall.StringBuilderandStringBufferare both mutable character buffers with the same API:append,insert,reverse,deleteCharAtand so on. The difference is synchronization:StringBuffer(Java 1.0) hassynchronizedmethods, so it's thread-safe but pays for locking on every call.StringBuilder(Java 5) is the unsynchronized equivalent and the right default.
You almost never share a string buffer between threads, so
StringBufferis mostly legacy. For simple one-line concatenation, plain+is fine: the compiler optimizes it, and since Java 9 it compiles to aninvokedynamiccall backed byStringConcatFactory. It's loops where you should reach forStringBuilderexplicitly.What interviewers listen forStringis immutable;+=in a loop is wasteful- Both builders are mutable with the same API
StringBufferis synchronized,StringBuilderis not- Default to
StringBuilder - Simple
+concatenation is optimized by the compiler
Likely follow-up: Why does
sb1.equals(sb2)return false for two builders with the same content?13.What were the most important features introduced in Java 8?easy
Java 8 (2014) was the biggest update since generics, and it brought a functional style to Java:
- Lambda expressions and functional interfaces, with
Function,Predicate,Supplier,Consumerand friends injava.util.function. - Method references such as
String::length. - The Streams API for declarative, lazily evaluated, optionally parallel processing of collections.
- Default and static methods in interfaces, which let the JDK add methods like
forEachandstream()to existing interfaces without breaking every implementation. Optionalfor return values that may be absent.- The
java.timeAPI: immutable, thread-safe dates and times replacingDateandCalendar. CompletableFuturefor composing asynchronous work.
Under the hood, PermGen was replaced by Metaspace, and
HashMapbuckets with many collisions started converting into balanced trees.What interviewers listen for- Lambdas and functional interfaces
- Streams API and method references
- Default and static interface methods
Optionaland thejava.timeAPI- Metaspace replaced PermGen
Likely follow-up: Why were default methods needed to add streams? · What was wrong with
java.util.Date?- Lambda expressions and functional interfaces, with
14.Compare
HashMap,HashtableandConcurrentHashMap. How doesConcurrentHashMapachieve thread safety?hardHashMapisn't thread-safe: concurrent writes can lose updates or corrupt its internal structure. It allows onenullkey andnullvalues.Hashtableis a legacy Java 1.0 class whose methods are allsynchronizedon the table, so only one thread can use it at a time, even for reads. It rejectsnullkeys and values.Collections.synchronizedMaphas the same single-lock design.ConcurrentHashMapis built for concurrency. Since Java 8 it no longer uses segments: reads don't lock, inserting into an empty bucket uses a CAS, and other updates lock only the first node of that bucket. Threads even help each other with resizing. Its iterators are weakly consistent and never throwConcurrentModificationException.
ConcurrentHashMapalso rejectsnull: agetreturningnullwould be ambiguous between "absent" and "mapped to null", and you can't lock the map to check. For compound updates, use its atomic methods likecomputeIfAbsentormergeinstead of get-then-put.What interviewers listen for- HashMap: fast, not thread-safe, allows
null - Hashtable: every method synchronized, legacy, no
null - CHM: CAS plus per-bucket locking, reads without locks
- CHM iterators are weakly consistent
- Use
computeormergefor atomic compound updates
Likely follow-up: How did
ConcurrentHashMapwork in Java 7? · Issize()on aConcurrentHashMapexact?15.What does the
statickeyword mean, and where can you use it?easystaticmeans the member belongs to the class rather than to any instance.- Static fields: one copy shared by all instances, such as a counter or a
static finalconstant. - Static methods: called on the class, like
Math.max. They have nothis, so they can't use instance fields or methods directly. They can't be overridden, only hidden, because the call is bound at compile time. - Static blocks: run once, when the class is initialized, to set up complex static state.
- Static nested classes: unlike inner classes, they hold no reference to an enclosing instance, which saves memory and avoids accidental leaks.
- Static imports:
import static java.lang.Math.max;lets you writemax(a, b).
Pitfalls: mutable static state is effectively a global variable, which makes code hard to test and needs synchronization when threads share it. And a static collection that only grows is a classic memory leak, since it lives as long as the class itself.
What interviewers listen for- Belongs to the class, not an instance
- Static methods have no
this - Static methods are hidden, not overridden
- Static blocks run once at class initialization
- Static nested classes hold no outer reference
Likely follow-up: Why is
mainstatic? · Can you call a static method through an instance reference?- Static fields: one copy shared by all instances, such as a counter or a
16.Is Java pass-by-value or pass-by-reference? What does this print?easy
Java is always pass-by-value. For primitives, the value is copied. For objects, the value that gets copied is the reference, so the method receives its own copy of a pointer to the same object.
That explains both lines. In
reassign, the parametersbis pointed at a brand-new object, but that only changes the method's local copy; the caller'ssstill refers to the original, so it printshello. Inmutate, the copy still points at the same object, soappendchanges the object the caller sees, and it printshello world.A handy test: if Java were pass-by-reference, you could write a
swap(a, b)method that swaps two of the caller's variables. You can't. People loosely say "objects are passed by reference", but the precise phrasing is "object references are passed by value".static void reassign(StringBuilder sb) { sb = new StringBuilder("new"); } static void mutate(StringBuilder sb) { sb.append(" world"); } StringBuilder s = new StringBuilder("hello"); reassign(s); System.out.println(s); // ? mutate(s); System.out.println(s); // ?What interviewers listen for- Java is always pass-by-value
- For objects, the reference is copied
- Mutating the object is visible to the caller
- Reassigning the parameter is not
- A
swap(a, b)method cannot work
Likely follow-up: How would you return two values from a method?
17.Explain the Java Collections Framework hierarchy.easy
At the root is
Iterable, extended byCollection, which has three main sub-interfaces:List: ordered, index-based, allows duplicates.ArrayList,LinkedList.Set: no duplicates.HashSet(unordered),LinkedHashSet(insertion order), andTreeSet, which implementsSortedSetandNavigableSet.Queue: holds elements for processing, usually FIFO.PriorityQueueorders by priority, and theDequesub-interface, implemented byArrayDequeandLinkedList, works at both ends.
Mapis a separate hierarchy: it stores key-value pairs and doesn't extendCollection. Implementations includeHashMap,LinkedHashMap, andTreeMapthroughSortedMapandNavigableMap.Java 21 added sequenced collections:
SequencedCollection,SequencedSetandSequencedMapgive ordered collections uniform methods such asgetFirst(),getLast()andreversed(). Also, don't confuseCollection, the interface, withCollections, the utility class of static helpers likesortandunmodifiableList.What interviewers listen forIterable→Collection→List,Set,QueueMapis separate; it does not extendCollection- Sorted variants:
TreeSetandTreeMap - Java 21 added the sequenced collection interfaces
Collectionsis a utility class
Likely follow-up: Why does
Mapnot extendCollection? · Which collections allownullelements?18.Explain the four access levels in Java.easy
From most to least restrictive:
private: visible only inside the top-level class that declares it, including its nested classes.- package-private (no modifier): visible to any class in the same package.
protected: the same package, plus subclasses in other packages, which can reach it through inheritance.public: visible everywhere, subject to module exports since Java 9.
Top-level classes can only be
publicor package-private, and a source file can have at most one public top-level class, whose name must match the file name. Interface methods are implicitlypublicunless declaredprivate.Two interview favourites: an overriding method can't reduce visibility, so you can't override a
publicmethod asprotected; andprotectedis wider than package-private, not narrower.Good practice is to start with the most restrictive level that works:
privatefields, and package-private classes unless they're part of your public API.What interviewers listen for- private → package-private → protected → public
- No modifier means package-private
protectedadds subclasses in other packages- Overrides cannot reduce visibility
- Start with the most restrictive level
Likely follow-up: Can a subclass in another package access a protected member through a parent-type reference?
19.What is the difference between
ComparableandComparator?easyComparable(injava.lang) defines a class's natural ordering through a single method,compareTo(T other), implemented by the class itself.String,IntegerandLocalDateimplement it, and it's whatCollections.sort(list),TreeMapandTreeSetuse by default.Comparator(injava.util) is a separate object withcompare(a, b). It lets you define any number of orderings, for classes you don't control or when there's no single natural order. Since Java 8 it has factories and combinators:comparing,comparingInt,thenComparing,reversed,nullsFirst.Both return a negative number, zero, or a positive number. Two tips: use
Integer.compare(a, b)rather thana - b, which can overflow; and keepcompareToconsistent withequals, because sorted collections use the comparison, notequals, to decide whether two elements are duplicates.record Person(String name, int age) implements Comparable<Person> { public int compareTo(Person o) { return Integer.compare(age, o.age); } } List<Person> people = new ArrayList<>(List.of(new Person("Ann", 30), new Person("Bob", 25))); Collections.sort(people); // natural order: by age people.sort(Comparator.comparing(Person::name)); // by name people.sort(Comparator.comparingInt(Person::age) .reversed() .thenComparing(Person::name)); // age desc, then nameWhat interviewers listen for- Comparable: natural order via
compareTo, inside the class - Comparator: external, many orderings,
compare(a, b) - Chain with
comparing,thenComparing,reversed - Avoid subtraction; use
Integer.compare - Sorted collections treat a comparison of 0 as a duplicate
Likely follow-up: What happens if
compareTois inconsistent withequalsin aTreeSet?- Comparable: natural order via
20.What is the difference between fail-fast and fail-safe iterators?mid
Fail-fast iterators, used by
ArrayList,HashMapand mostjava.utilcollections, throwConcurrentModificationExceptionif the collection is structurally modified after the iterator was created, by anything other than the iterator itself. They track amodCountcounter and check it on eachnext(). The check is best-effort: it exists to catch bugs, not to provide thread safety.The classic bug is removing inside a for-each loop, as in the snippet. The fixes are
Iterator.remove()orremoveIf.Fail-safe iterators (the JDK docs say snapshot or weakly consistent) never throw.
CopyOnWriteArrayListiterates over a snapshot of the array taken when the iterator was created, so it doesn't see later changes.ConcurrentHashMapiterators are weakly consistent: they may or may not reflect updates made during iteration. The costs are copying on every write for copy-on-write lists, and possibly stale views.List<String> names = new ArrayList<>(List.of("a", "b", "c")); for (String n : names) { if (n.equals("a")) names.remove(n); // throws ConcurrentModificationException } names.removeIf(n -> n.equals("a")); // fix 1 (Java 8+) for (Iterator<String> it = names.iterator(); it.hasNext(); ) { if (it.next().equals("a")) it.remove(); // fix 2 } List<String> cow = new CopyOnWriteArrayList<>(List.of("a", "b", "c")); for (String n : cow) cow.remove(n); // no exception: iterates a snapshotWhat interviewers listen for- Fail-fast throws
ConcurrentModificationException - Detected through a
modCountcheck, best-effort only - Remove via
Iterator.remove()orremoveIf - Snapshot iterators:
CopyOnWriteArrayList - Weakly consistent iterators:
ConcurrentHashMap
Likely follow-up: Does removing the second-to-last element in a for-each loop throw? · When is
CopyOnWriteArrayLista good fit?- Fail-fast throws
No questions match that filter.