diff --git a/README.md b/README.md index 6e4956b..cf7bf34 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,7 @@ One module per article. This repository currently holds: | [`binary-tree-traversals`](binary-tree-traversals/) | Binary Tree Traversals in Java: Inorder, Preorder, Postorder and Level Order (Recursive and Iterative) | this commit | | [`sliding-window`](sliding-window/) | Sliding Window and Two Pointers in Java: 12 Interview Problems Solved | this commit | | [`dynamic-programming`](dynamic-programming/) | Dynamic Programming in Java for Beginners: Memoization vs Tabulation (10 Classic Problems) | this commit | +| [`fresher-qa`](fresher-qa/) | Top 50 Java Interview Questions for Freshers (2026) with Simple Answers | this commit | ## `collections` module diff --git a/fresher-qa/README.md b/fresher-qa/README.md new file mode 100644 index 0000000..157fb67 --- /dev/null +++ b/fresher-qa/README.md @@ -0,0 +1,32 @@ +# fresher-qa + +Runnable companion for the ankurm.com article +[Top 50 Java Interview Questions for Freshers (2026) with Simple Answers](https://ankurm.com/top-50-java-interview-questions-for-freshers/). + +One small demo per question, grouped in six classes. Tests assert what each demo prints and write the +transcripts the article quotes. There is no `docs/` folder on purpose: the explanations live in the article. + +| Component | Version | Verified against | +|---|---|---| +| JDK | 25.0.5+8 (Temurin, LTS) | `java -version` | +| JUnit | 6.1.3 | `maven-metadata.xml` on Maven Central | +| Maven | 3.9.11 | `mvn -version` | + +```bash +export JAVA_HOME=/path/to/jdk-25 && export PATH="$JAVA_HOME/bin:$PATH" +cd fresher-qa +mvn test # 6 tests; rewrites output/01-06 +./scripts/run-all.sh # also regenerates output/07 (javac's own error messages for src/broken/) +``` + +| Class | Questions | Transcript | +|---|---|---| +| `BasicsQa` | 1-9 | `output/01-basics.txt` | +| `OopQa` | 10-20 | `output/02-oop.txt` | +| `StringsQa` | 21-27 | `output/03-strings.txt` | +| `CollectionsQa` | 28-38 | `output/04-collections.txt` | +| `ExceptionsQa` | 39-43 | `output/05-exceptions.txt` | +| `ModernJavaQa` | 44-50 | `output/06-modern-java.txt` | + +`src/broken/` holds five files that must NOT compile (one of them only fails below Java 25); `scripts/compile-errors.sh` runs `javac` on each and +commits the real message to `output/07-compile-errors.txt`. They are excluded from the Maven build. diff --git a/fresher-qa/output/01-basics.txt b/fresher-qa/output/01-basics.txt new file mode 100644 index 0000000..00632fe --- /dev/null +++ b/fresher-qa/output/01-basics.txt @@ -0,0 +1,43 @@ +=== Q01 Why is Java platform independent? === +magic = CAFEBABE +class file major version = 69 +=== Q02 JDK vs JRE vs JVM === +running on Java 25 +JVM: OpenJDK 64-Bit Server VM +compiler available: true +=== Q03 Primitive types and their sizes === +byte 1 byte, -128 to 127 +short 2 bytes, -32768 to 32767 +int 4 bytes, -2147483648 to 2147483647 +long 8 bytes, -9223372036854775808 to 9223372036854775807 +float 4 bytes, double 8 bytes, char 2 bytes +field defaults: 0, false, null +=== Q04 Type casting and overflow === +7 / 2 = 3, (double) 7 / 2 = 3.5 +(byte) 130 = -126 +Integer.MAX_VALUE + 1 = -2147483648 +c + 1 = 66, (char) (c + 1) = B +0.1 + 0.2 = 0.30000000000000004 +Math.addExact: integer overflow +=== Q05 Autoboxing and the Integer cache === +127 == 127 -> true +128 == 128 -> false +128 equals 128 -> true +Long 127 equals Integer 127 -> false +=== Q06 Is Java pass by value or pass by reference? === +after reassign: original +after mutate: original + mutated +=== Q07 static variables, static blocks and initialisation order === +static block runs once, when the class loads +instance block runs before each constructor body +constructor #1 +instance block runs before each constructor body +constructor #2 +=== Q08 final, finally and finalize === +final array contents changed: 99 +in try +finally always runs +Object.finalize forRemoval = true, since 9 +=== Q09 Stack vs heap === +heap array length = 1000000 +StackOverflowError after many frames: true diff --git a/fresher-qa/output/02-oop.txt b/fresher-qa/output/02-oop.txt new file mode 100644 index 0000000..466a009 --- /dev/null +++ b/fresher-qa/output/02-oop.txt @@ -0,0 +1,36 @@ +=== Q10 The four pillars of OOP === +Circle area = 3.14 +Rectangle area = 6.00 +rejected: amount must be positive, balance still 500.0 +=== Q11 Inheritance and constructor chaining === +Animal constructor +Dog constructor +Animal constructor +Puppy constructor for Bruno +=== Q12 Method overloading vs overriding === +FancyPrinter.show(Object) +show(String) +FancyPrinter.show(Object) +=== Q13 Abstract class vs interface === +Scooter has 2 wheels, charging, plug Type 2 +=== Q14 Polymorphism: what is and is not dynamic === +Child.who() | Parent.field | Parent.static | Child.static +=== Q15 Can you override static, private or final methods? === +Base.secret | Base.locked +=== Q16 How do you create an immutable class? === +members: [Asha, Ravi] +add blocked: UnsupportedOperationException +=== Q17 Composition vs inheritance (is-a vs has-a) === +stack = [99, 1, 2], pop gives 2 +=== Q18 Access modifiers === +a -> public +b -> protected +c -> (package-private) +d -> private +=== Q19 Object class methods: toString, equals, hashCode === +default toString looks like ClassName@hex: true +p1.equals(p2) = true +set contains an equal point: false +=== Q20 Constructors: default, chained and private === +default config: dev:8080 +same Registry instance: true diff --git a/fresher-qa/output/03-strings.txt b/fresher-qa/output/03-strings.txt new file mode 100644 index 0000000..b82726b --- /dev/null +++ b/fresher-qa/output/03-strings.txt @@ -0,0 +1,31 @@ +=== Q21 == vs equals() on Strings === +a == b -> true +a == c -> false +a.equals(c) -> true +=== Q22 Why is String immutable? === +s = java +upper = JAVA +same object returned when nothing changes: true +=== Q23 String vs StringBuilder vs StringBuffer === +builder result: cba- +append is synchronized: StringBuffer=true, StringBuilder=false +=== Q24 The string pool, compile-time constants and intern() === +"hel" + "lo" == "hello" -> true +"hel" + lo == "hello" -> false +"hel" + finalLo == "hello" -> true +("hel" + lo).intern() == "hello" -> true +=== Q25 Reverse a string and check for a palindrome === +weivretni +true +false +=== Q26 Useful String methods and the split() surprises === +trim().length() = 6 +strip().length() = 2 +" ".isBlank() = true, "ab".repeat(3) = ababab +"a,b,,".split(",").length = 2 +"a,b,,".split(",", -1).length = 4 +String.join: 2026-11-11 +=== Q27 Count characters, find the first non-repeating one, check anagrams === +frequency: {s=3, w=1, i=1} +first non-repeating: w +listen/silent anagrams: true diff --git a/fresher-qa/output/04-collections.txt b/fresher-qa/output/04-collections.txt new file mode 100644 index 0000000..2e2ad15 --- /dev/null +++ b/fresher-qa/output/04-collections.txt @@ -0,0 +1,49 @@ +=== Q28 The Collections Framework hierarchy === +ArrayList extends AbstractList implements [List, RandomAccess, Cloneable, Serializable] +LinkedList extends AbstractSequentialList implements [List, Deque, Cloneable, Serializable] +HashSet extends AbstractSet implements [Set, Cloneable, Serializable] +TreeSet extends AbstractSet implements [NavigableSet, Cloneable, Serializable] +ArrayDeque extends AbstractCollection implements [Deque, Cloneable, Serializable] +HashMap extends AbstractMap implements [Map, Cloneable, Serializable] +TreeMap extends AbstractMap implements [NavigableMap, Cloneable, Serializable] +=== Q29 ArrayList vs LinkedList === +ArrayList is RandomAccess: true +LinkedList is RandomAccess: false +LinkedList is also a Deque: true +after add(0, 5): [5, 10, 20, 30], get(2) = 20 +=== Q30 How HashMap works internally === +Aa: hashCode=2112, bucket (16 slots)=0 +BB: hashCode=2112, bucket (16 slots)=0 +Ankur: hashCode=63412443, bucket (16 slots)=12 +both survive a collision: first, second, size 2 +=== Q31 HashMap vs Hashtable vs ConcurrentHashMap vs TreeMap === +HashMap: null key accepted +Hashtable: null key throws NullPointerException +ConcurrentHashMap: null key throws NullPointerException +TreeMap: null key throws NullPointerException +=== Q32 HashSet vs LinkedHashSet vs TreeSet === +HashSet [banana, apple, cherry, pear, mango] +LinkedHashSet [pear, apple, mango, banana, cherry] +TreeSet [apple, banana, cherry, mango, pear] +=== Q33 Comparable vs Comparator === +natural (Comparable): [Student[name=Asha, marks=88], Student[name=Meena, marks=88], Student[name=Ravi, marks=90]] +marks desc, then name: [Student[name=Ravi, marks=90], Student[name=Asha, marks=88], Student[name=Meena, marks=88]] +=== Q34 Fail-fast iterators and ConcurrentModificationException === +removing 2 inside for-each -> ConcurrentModificationException +removing the second-to-last element threw nothing, list = [1, 2, 4] +removeIf even -> [1, 3, 5] +=== Q35 Arrays.asList() and List.of() pitfalls === +write-through to the array: changed +Arrays.asList().add -> UnsupportedOperationException +Arrays.asList(int[]) has 1 element +List.of(.., null) -> NullPointerException +=== Q36 Queue, Deque and PriorityQueue === +PriorityQueue polls: 1 2 3 4 5 +ArrayDeque as a stack pops: cba +=== Q37 Mutable keys and the equals()/hashCode() contract in a HashMap === +get with the mutated key: null +get with a fresh "A1" key: null +size is still 1 +=== Q38 unmodifiableList() view vs List.copyOf() snapshot === +view sees the change: [a, b, c] +copy does not: [a, b] diff --git a/fresher-qa/output/05-exceptions.txt b/fresher-qa/output/05-exceptions.txt new file mode 100644 index 0000000..874a9cd --- /dev/null +++ b/fresher-qa/output/05-exceptions.txt @@ -0,0 +1,21 @@ +=== Q39 Checked vs unchecked exceptions === +checked: NoSuchFileException (is an IOException: true) +unchecked: ArithmeticException: / by zero +NoSuchFileException is checked: true +=== Q40 try, catch, finally and return === +finally runs before the method actually returns +returned 1 +primitiveSnapshot = 10 +finallyOverrides = 7 +=== Q41 throw vs throws, and custom exceptions === +withdrawal refused <- cause: balance 100 < 500 +=== Q42 try-with-resources and suppressed exceptions === +open first +open second +close second +close first +caught: body failed +suppressed: close failed: second +=== Q43 Error vs Exception, and multi-catch === +Error, not Exception: Requested array size exceeds VM limit +multi-catch caught ClassCastException diff --git a/fresher-qa/output/06-modern-java.txt b/fresher-qa/output/06-modern-java.txt new file mode 100644 index 0000000..02e378a --- /dev/null +++ b/fresher-qa/output/06-modern-java.txt @@ -0,0 +1,27 @@ +=== Q44 Lambdas and functional interfaces === +180.0 true 6 hello 5 +=== Q45 Streams: map, filter, collect, laziness === +[ASHA, ARJUN] +{4=2, 5=2} +nothing visited yet: no terminal operation +visited asha +first = asha +reuse -> stream has already been operated upon or closed +=== Q46 Optional === +expensive() called +orElse : value +orElseGet : value +nothing here +=== Q47 Records === +Point[x=1, y=2] equals Point[x=1, y=2]: true, x() = 1 +rejected: negative coordinate +=== Q48 var, switch expressions and text blocks === +{"name": "asha", + "grade": "A"} +=== Q49 Method references and default methods === +abc +walk and swim +=== Q50 Threads: start() vs run(), and virtual threads === +running on: main +running on: worker-1 +virtual: true diff --git a/fresher-qa/output/07-compile-errors.txt b/fresher-qa/output/07-compile-errors.txt new file mode 100644 index 0000000..c42e3bd --- /dev/null +++ b/fresher-qa/output/07-compile-errors.txt @@ -0,0 +1,38 @@ +=== javac FinalOverride.java === +FinalOverride.java:6: error: locked() in Derived cannot override locked() in Base + String locked() { return "Derived.locked"; } + ^ + overridden method is final +1 error + +=== javac CheckedNotHandled.java === +CheckedNotHandled.java:6: error: unreported exception IOException; must be caught or declared to be thrown + String s = Files.readString(Path.of("missing.txt")); + ^ +1 error + +=== javac LossyCast.java === +LossyCast.java:3: error: incompatible types: possible lossy conversion from double to int + int x = 3.5; + ^ +LossyCast.java:4: error: incompatible types: possible lossy conversion from int to byte + byte b = 200; + ^ +2 errors + +=== javac MissingReturnAndUnreachable.java === +MissingReturnAndUnreachable.java:4: error: missing return statement + } + ^ +1 error + +=== javac --release 21 PreSuper.java === +PreSuper.java:8: error: flexible constructors is not supported in -source 21 + super(); + ^ + (use -source 25 or higher to enable flexible constructors) +1 error + +=== javac --release 25 PreSuper.java === +compiled without errors + diff --git a/fresher-qa/pom.xml b/fresher-qa/pom.xml new file mode 100644 index 0000000..35832e8 --- /dev/null +++ b/fresher-qa/pom.xml @@ -0,0 +1,64 @@ + + + 4.0.0 + com.ankurm.interviewlab + fresher-qa + 1.0.0 + jar + + java-interview-lab :: fresher-qa + + Runnable companion code for ankurm.com's "Top 50 Java Interview Questions for Freshers" article: + one small demo per question, with tests that assert what each demo prints and write the transcripts the article quotes. + + + + 25 + UTF-8 + 6.1.3 + + + + + + org.junit + junit-bom + ${junit.version} + pom + import + + + + + + + org.junit.jupiter + junit-jupiter + test + + + org.junit.platform + junit-platform-launcher + test + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.14.0 + + + org.apache.maven.plugins + maven-surefire-plugin + 3.6.0 + + -Xmx512m -Xss1m -Duser.timezone=UTC -Dfile.encoding=UTF-8 + + + + + diff --git a/fresher-qa/scripts/compile-errors.sh b/fresher-qa/scripts/compile-errors.sh new file mode 100755 index 0000000..399b5f2 --- /dev/null +++ b/fresher-qa/scripts/compile-errors.sh @@ -0,0 +1,20 @@ +#!/usr/bin/env bash +# Compiles each file in src/broken/ (they are NOT part of the Maven build) and captures javac's own message. +set -uo pipefail +cd "$(dirname "$0")/../src/broken" +out=../../output/07-compile-errors.txt +: > "$out" +for f in FinalOverride CheckedNotHandled LossyCast MissingReturnAndUnreachable; do + echo "=== javac $f.java ===" >> "$out" + javac --release 25 -d /tmp/fresher-broken "$f.java" 2>&1 | grep -v '^Picked up JAVA_TOOL_OPTIONS' >> "$out" + echo >> "$out" +done +# The same file under both language levels: an error before Java 25, fine from Java 25 (JEP 513). +for rel in 21 25; do + echo "=== javac --release $rel PreSuper.java ===" >> "$out" + if javac --release "$rel" -d /tmp/fresher-broken "PreSuper.java" 2>&1 | grep -v '^Picked up JAVA_TOOL_OPTIONS' >> "$out"; [ "${PIPESTATUS[0]}" -eq 0 ]; then + echo "compiled without errors" >> "$out" + fi + echo >> "$out" +done +cat "$out" diff --git a/fresher-qa/scripts/run-all.sh b/fresher-qa/scripts/run-all.sh new file mode 100755 index 0000000..293ebfb --- /dev/null +++ b/fresher-qa/scripts/run-all.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +# Regenerates every file in output/. Needs JDK 25 and Maven (first Maven run online). +# 01-06 are written by JUnit tests that assert the lines they write; 07 is javac's own output for src/broken/. +set -euo pipefail +cd "$(dirname "$0")/.." +mvn -B -q -T1 test +./scripts/compile-errors.sh > /dev/null +echo "done:"; ls -1 output diff --git a/fresher-qa/src/broken/CheckedNotHandled.java b/fresher-qa/src/broken/CheckedNotHandled.java new file mode 100644 index 0000000..76a424c --- /dev/null +++ b/fresher-qa/src/broken/CheckedNotHandled.java @@ -0,0 +1,8 @@ +import java.nio.file.Files; +import java.nio.file.Path; + +class CheckedNotHandled { + public static void main(String[] args) { + String s = Files.readString(Path.of("missing.txt")); + } +} diff --git a/fresher-qa/src/broken/FinalOverride.java b/fresher-qa/src/broken/FinalOverride.java new file mode 100644 index 0000000..84ad0d9 --- /dev/null +++ b/fresher-qa/src/broken/FinalOverride.java @@ -0,0 +1,7 @@ +class Base { + final String locked() { return "Base.locked"; } +} + +class Derived extends Base { + String locked() { return "Derived.locked"; } +} diff --git a/fresher-qa/src/broken/LossyCast.java b/fresher-qa/src/broken/LossyCast.java new file mode 100644 index 0000000..637f73d --- /dev/null +++ b/fresher-qa/src/broken/LossyCast.java @@ -0,0 +1,6 @@ +class LossyCast { + public static void main(String[] args) { + int x = 3.5; + byte b = 200; + } +} diff --git a/fresher-qa/src/broken/MissingReturnAndUnreachable.java b/fresher-qa/src/broken/MissingReturnAndUnreachable.java new file mode 100644 index 0000000..d614280 --- /dev/null +++ b/fresher-qa/src/broken/MissingReturnAndUnreachable.java @@ -0,0 +1,5 @@ +class MissingReturnAndUnreachable { + static int sign(int n) { + if (n > 0) return 1; + } +} diff --git a/fresher-qa/src/broken/PreSuper.java b/fresher-qa/src/broken/PreSuper.java new file mode 100644 index 0000000..42dfaac --- /dev/null +++ b/fresher-qa/src/broken/PreSuper.java @@ -0,0 +1,10 @@ +class Animal { + Animal() { } +} + +class Puppy extends Animal { + Puppy(String name) { + if (name.isBlank()) throw new IllegalArgumentException("name required"); + super(); + } +} diff --git a/fresher-qa/src/main/java/com/ankurm/interviewlab/fresherqa/BasicsQa.java b/fresher-qa/src/main/java/com/ankurm/interviewlab/fresherqa/BasicsQa.java new file mode 100644 index 0000000..9437d7d --- /dev/null +++ b/fresher-qa/src/main/java/com/ankurm/interviewlab/fresherqa/BasicsQa.java @@ -0,0 +1,167 @@ +package com.ankurm.interviewlab.fresherqa; + +import java.io.DataInputStream; +import java.io.IOException; +import java.io.InputStream; + +/** Questions 1-9: how Java runs, primitives, casting, boxing, pass-by-value, static, final, stack and heap. */ +public final class BasicsQa { + private BasicsQa() {} + + public static void main(String[] args) throws Exception { + run(); + } + + public static void run() throws Exception { + q01(); q02(); q03(); q04(); q05(); q06(); q07(); q08(); q09(); + } + + static void q01() throws IOException { + Qa.header(1, "Why is Java platform independent?"); + // >>> q01 + try (InputStream in = BasicsQa.class.getResourceAsStream("BasicsQa.class"); + DataInputStream data = new DataInputStream(in)) { + int magic = data.readInt(); // first four bytes of every .class file + data.readUnsignedShort(); // minor version + int major = data.readUnsignedShort(); // class-file format version + System.out.println("magic = " + Integer.toHexString(magic).toUpperCase()); + System.out.println("class file major version = " + major); + } + // <<< q01 + } + + static void q02() { + Qa.header(2, "JDK vs JRE vs JVM"); + // >>> q02 + System.out.println("running on Java " + Runtime.version().feature()); + System.out.println("JVM: " + System.getProperty("java.vm.name")); + System.out.println("compiler available: " + (javax.tools.ToolProvider.getSystemJavaCompiler() != null)); + // <<< q02 + } + + static int defaultInt; + static boolean defaultBoolean; + static String defaultString; + + static void q03() { + Qa.header(3, "Primitive types and their sizes"); + // >>> q03 + System.out.println("byte " + Byte.BYTES + " byte, " + Byte.MIN_VALUE + " to " + Byte.MAX_VALUE); + System.out.println("short " + Short.BYTES + " bytes, " + Short.MIN_VALUE + " to " + Short.MAX_VALUE); + System.out.println("int " + Integer.BYTES + " bytes, " + Integer.MIN_VALUE + " to " + Integer.MAX_VALUE); + System.out.println("long " + Long.BYTES + " bytes, " + Long.MIN_VALUE + " to " + Long.MAX_VALUE); + System.out.println("float " + Float.BYTES + " bytes, double " + Double.BYTES + " bytes, char " + Character.BYTES + " bytes"); + System.out.println("field defaults: " + defaultInt + ", " + defaultBoolean + ", " + defaultString); + // <<< q03 + } + + static void q04() { + Qa.header(4, "Type casting and overflow"); + // >>> q04 + System.out.println("7 / 2 = " + (7 / 2) + ", (double) 7 / 2 = " + ((double) 7 / 2)); + System.out.println("(byte) 130 = " + (byte) 130); + System.out.println("Integer.MAX_VALUE + 1 = " + (Integer.MAX_VALUE + 1)); + char c = 'A'; + System.out.println("c + 1 = " + (c + 1) + ", (char) (c + 1) = " + (char) (c + 1)); + System.out.println("0.1 + 0.2 = " + (0.1 + 0.2)); + try { + Math.addExact(Integer.MAX_VALUE, 1); + } catch (ArithmeticException e) { + System.out.println("Math.addExact: " + e.getMessage()); + } + // <<< q04 + } + + static void q05() { + Qa.header(5, "Autoboxing and the Integer cache"); + // >>> q05 + Integer a = 127, b = 127; + Integer c = 128, d = 128; + System.out.println("127 == 127 -> " + (a == b)); + System.out.println("128 == 128 -> " + (c == d)); + System.out.println("128 equals 128 -> " + c.equals(d)); + Long boxed = 127L; + System.out.println("Long 127 equals Integer 127 -> " + boxed.equals(127)); + // <<< q05 + } + + // >>> q06 + static void reassign(StringBuilder sb) { + sb = new StringBuilder("new object"); // changes only the local copy of the reference + } + + static void mutate(StringBuilder sb) { + sb.append(" + mutated"); // follows the reference to the same object + } + // <<< q06 + + static void q06() { + Qa.header(6, "Is Java pass by value or pass by reference?"); + // >>> q06b + StringBuilder sb = new StringBuilder("original"); + reassign(sb); + System.out.println("after reassign: " + sb); + mutate(sb); + System.out.println("after mutate: " + sb); + // <<< q06b + } + + // >>> q07 + static class Order { + static int created; + static { System.out.println("static block runs once, when the class loads"); } + { System.out.println("instance block runs before each constructor body"); } + + Order() { + created++; + System.out.println("constructor #" + created); + } + } + // <<< q07 + + static void q07() { + Qa.header(7, "static variables, static blocks and initialisation order"); + // >>> q07b + new Order(); + new Order(); + // <<< q07b + } + + static void q08() throws Exception { + Qa.header(8, "final, finally and finalize"); + // >>> q08 + final int[] numbers = {1, 2, 3}; + numbers[0] = 99; // legal: final fixes the reference, not the contents + System.out.println("final array contents changed: " + numbers[0]); + try { + System.out.println("in try"); + } finally { + System.out.println("finally always runs"); + } + Deprecated d = Object.class.getDeclaredMethod("finalize").getAnnotation(Deprecated.class); + System.out.println("Object.finalize forRemoval = " + d.forRemoval() + ", since " + d.since()); + // <<< q08 + } + + // >>> q09 + static int depth; + + static void recurseForever() { + depth++; + recurseForever(); + } + // <<< q09 + + static void q09() { + Qa.header(9, "Stack vs heap"); + // >>> q09b + int[] onHeap = new int[1_000_000]; // the array lives on the heap; only the reference sits on the stack + System.out.println("heap array length = " + onHeap.length); + try { + recurseForever(); + } catch (StackOverflowError e) { + System.out.println("StackOverflowError after many frames: " + (depth > 1000)); + } + // <<< q09b + } +} diff --git a/fresher-qa/src/main/java/com/ankurm/interviewlab/fresherqa/CollectionsQa.java b/fresher-qa/src/main/java/com/ankurm/interviewlab/fresherqa/CollectionsQa.java new file mode 100644 index 0000000..3b8699b --- /dev/null +++ b/fresher-qa/src/main/java/com/ankurm/interviewlab/fresherqa/CollectionsQa.java @@ -0,0 +1,213 @@ +package com.ankurm.interviewlab.fresherqa; + +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.Deque; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.PriorityQueue; +import java.util.Queue; +import java.util.RandomAccess; +import java.util.Set; +import java.util.TreeMap; +import java.util.TreeSet; +import java.util.concurrent.ConcurrentHashMap; + +/** Questions 28-38: the framework, ArrayList vs LinkedList, HashMap internals, sets, sorting, iterators, pitfalls. */ +public final class CollectionsQa { + private CollectionsQa() {} + + public static void main(String[] args) { + run(); + } + + public static void run() { + q28(); q29(); q30(); q31(); q32(); q33(); q34(); q35(); q36(); q37(); q38(); + } + + static void q28() { + Qa.header(28, "The Collections Framework hierarchy"); + // >>> q28 + for (Class c : List.of(ArrayList.class, LinkedList.class, HashSet.class, TreeSet.class, ArrayDeque.class, HashMap.class, TreeMap.class)) { + String parents = Arrays.stream(c.getInterfaces()).map(Class::getSimpleName).toList().toString(); + System.out.println(c.getSimpleName() + " extends " + c.getSuperclass().getSimpleName() + " implements " + parents); + } + // <<< q28 + } + + static void q29() { + Qa.header(29, "ArrayList vs LinkedList"); + // >>> q29 + System.out.println("ArrayList is RandomAccess: " + (new ArrayList<>() instanceof RandomAccess)); + System.out.println("LinkedList is RandomAccess: " + (new LinkedList<>() instanceof RandomAccess)); + System.out.println("LinkedList is also a Deque: " + (new LinkedList<>() instanceof Deque)); + List list = new ArrayList<>(List.of(10, 20, 30)); + list.add(0, 5); // ArrayList shifts every later element one slot right + System.out.println("after add(0, 5): " + list + ", get(2) = " + list.get(2)); + // <<< q29 + } + + static void q30() { + Qa.header(30, "How HashMap works internally"); + // >>> q30 + // Same formula as java.util.HashMap.hash() followed by the (table.length - 1) mask + for (String key : new String[] {"Aa", "BB", "Ankur"}) { + int h = key.hashCode(); + int spread = h ^ (h >>> 16); + System.out.println(key + ": hashCode=" + h + ", bucket (16 slots)=" + (spread & 15)); + } + Map map = new HashMap<>(); + map.put("Aa", "first"); + map.put("BB", "second"); // same hashCode, so same bucket, but not equals() + System.out.println("both survive a collision: " + map.get("Aa") + ", " + map.get("BB") + ", size " + map.size()); + // <<< q30 + } + + static void q31() { + Qa.header(31, "HashMap vs Hashtable vs ConcurrentHashMap vs TreeMap"); + // >>> q31 + Map> maps = new LinkedHashMap<>(); + maps.put("HashMap", new HashMap<>()); + maps.put("Hashtable", new Hashtable<>()); + maps.put("ConcurrentHashMap", new ConcurrentHashMap<>()); + maps.put("TreeMap", new TreeMap<>()); + for (var e : maps.entrySet()) { + try { + e.getValue().put(null, 1); + System.out.println(e.getKey() + ": null key accepted"); + } catch (NullPointerException ex) { + System.out.println(e.getKey() + ": null key throws NullPointerException"); + } + } + // <<< q31 + } + + static void q32() { + Qa.header(32, "HashSet vs LinkedHashSet vs TreeSet"); + // >>> q32 + List fruit = List.of("pear", "apple", "mango", "banana", "cherry"); + System.out.println("HashSet " + new HashSet<>(fruit)); + System.out.println("LinkedHashSet " + new LinkedHashSet<>(fruit)); + System.out.println("TreeSet " + new TreeSet<>(fruit)); + // <<< q32 + } + + // >>> q33 + record Student(String name, int marks) implements Comparable { + @Override + public int compareTo(Student other) { // the "natural" order: by name + return name.compareTo(other.name); + } + } + // <<< q33 + + static void q33() { + Qa.header(33, "Comparable vs Comparator"); + // >>> q33b + List students = new ArrayList<>(List.of(new Student("Ravi", 90), new Student("Asha", 88), new Student("Meena", 88))); + Collections.sort(students); + System.out.println("natural (Comparable): " + students); + students.sort(Comparator.comparingInt(Student::marks).reversed().thenComparing(Student::name)); + System.out.println("marks desc, then name: " + students); + // <<< q33b + } + + static void q34() { + Qa.header(34, "Fail-fast iterators and ConcurrentModificationException"); + // >>> q34 + List list = new ArrayList<>(List.of(1, 2, 3, 4, 5)); + try { + for (Integer n : list) { + if (n == 2) list.remove(n); // structural change while the for-each iterator is live + } + } catch (java.util.ConcurrentModificationException e) { + System.out.println("removing 2 inside for-each -> ConcurrentModificationException"); + } + List second = new ArrayList<>(List.of(1, 2, 3, 4)); + for (Integer n : second) { + if (n == 3) second.remove(n); // removing the second-to-last element: no exception, loop just stops + } + System.out.println("removing the second-to-last element threw nothing, list = " + second); + list.removeIf(n -> n % 2 == 0); // the safe way + System.out.println("removeIf even -> " + list); + // <<< q34 + } + + static void q35() { + Qa.header(35, "Arrays.asList() and List.of() pitfalls"); + // >>> q35 + String[] array = {"x", "y"}; + List view = Arrays.asList(array); + view.set(0, "changed"); + System.out.println("write-through to the array: " + array[0]); + try { + view.add("z"); + } catch (UnsupportedOperationException e) { + System.out.println("Arrays.asList().add -> UnsupportedOperationException"); + } + System.out.println("Arrays.asList(int[]) has " + Arrays.asList(new int[] {1, 2, 3}).size() + " element"); + try { + List.of("a", null); + } catch (NullPointerException e) { + System.out.println("List.of(.., null) -> NullPointerException"); + } + // <<< q35 + } + + static void q36() { + Qa.header(36, "Queue, Deque and PriorityQueue"); + // >>> q36 + Queue pq = new PriorityQueue<>(List.of(5, 1, 4, 2, 3)); + StringBuilder order = new StringBuilder(); + while (!pq.isEmpty()) order.append(pq.poll()).append(' '); + System.out.println("PriorityQueue polls: " + order.toString().trim()); + Deque stack = new ArrayDeque<>(); + stack.push("a"); stack.push("b"); stack.push("c"); + System.out.println("ArrayDeque as a stack pops: " + stack.pop() + stack.pop() + stack.pop()); + // <<< q36 + } + + // >>> q37 + static class Key { + String id; + Key(String id) { this.id = id; } + @Override public boolean equals(Object o) { return o instanceof Key k && k.id.equals(id); } + @Override public int hashCode() { return id.hashCode(); } + } + // <<< q37 + + static void q37() { + Qa.header(37, "Mutable keys and the equals()/hashCode() contract in a HashMap"); + // >>> q37b + Map map = new HashMap<>(); + Key k = new Key("A1"); + map.put(k, "value"); + k.id = "B2"; // the hashCode changes after the entry was filed + System.out.println("get with the mutated key: " + map.get(k)); + System.out.println("get with a fresh \"A1\" key: " + map.get(new Key("A1"))); + System.out.println("size is still " + map.size()); + // <<< q37b + } + + static void q38() { + Qa.header(38, "unmodifiableList() view vs List.copyOf() snapshot"); + // >>> q38 + List original = new ArrayList<>(List.of("a", "b")); + List view = Collections.unmodifiableList(original); + List copy = List.copyOf(original); + original.add("c"); + System.out.println("view sees the change: " + view); + System.out.println("copy does not: " + copy); + // <<< q38 + } +} diff --git a/fresher-qa/src/main/java/com/ankurm/interviewlab/fresherqa/ExceptionsQa.java b/fresher-qa/src/main/java/com/ankurm/interviewlab/fresherqa/ExceptionsQa.java new file mode 100644 index 0000000..ecb4bcd --- /dev/null +++ b/fresher-qa/src/main/java/com/ankurm/interviewlab/fresherqa/ExceptionsQa.java @@ -0,0 +1,149 @@ +package com.ankurm.interviewlab.fresherqa; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.NoSuchFileException; +import java.nio.file.Path; + +/** Questions 39-43: checked vs unchecked, finally and return, custom exceptions, try-with-resources, Error vs Exception. */ +public final class ExceptionsQa { + private ExceptionsQa() {} + + public static void main(String[] args) { + run(); + } + + public static void run() { + q39(); q40(); q41(); q42(); q43(); + } + + static void q39() { + Qa.header(39, "Checked vs unchecked exceptions"); + // >>> q39 + try { + Files.readString(Path.of("missing.txt")); // checked: the compiler forces you to deal with IOException + } catch (IOException e) { + System.out.println("checked: " + e.getClass().getSimpleName() + " (is an IOException: " + (e instanceof IOException) + ")"); + } + try { + int zero = 0; + System.out.println(10 / zero); // unchecked: no try/catch required + } catch (ArithmeticException e) { + System.out.println("unchecked: ArithmeticException: " + e.getMessage()); + } + System.out.println("NoSuchFileException is checked: " + !RuntimeException.class.isAssignableFrom(NoSuchFileException.class)); + // <<< q39 + } + + // >>> q40 + @SuppressWarnings("finally") + static int tryOrFinally() { + try { + return 1; + } finally { + System.out.println("finally runs before the method actually returns"); + } + } + + static int primitiveSnapshot() { + int x = 10; + try { + return x; // value 10 is copied aside here + } finally { + x = 99; // too late to change what is returned + } + } + + @SuppressWarnings("finally") + static int finallyOverrides() { + try { + throw new IllegalStateException("lost"); + } finally { + return 7; // swallows the exception: never do this + } + } + // <<< q40 + + static void q40() { + Qa.header(40, "try, catch, finally and return"); + // >>> q40b + System.out.println("returned " + tryOrFinally()); + System.out.println("primitiveSnapshot = " + primitiveSnapshot()); + System.out.println("finallyOverrides = " + finallyOverrides()); + // <<< q40b + } + + // >>> q41 + static class InsufficientFundsException extends Exception { + InsufficientFundsException(String message, Throwable cause) { super(message, cause); } + } + + static void withdraw(int balance, int amount) throws InsufficientFundsException { + try { + if (amount > balance) throw new IllegalArgumentException("balance " + balance + " < " + amount); + } catch (IllegalArgumentException e) { + throw new InsufficientFundsException("withdrawal refused", e); // wrap, keep the cause + } + } + // <<< q41 + + static void q41() { + Qa.header(41, "throw vs throws, and custom exceptions"); + // >>> q41b + try { + withdraw(100, 500); + } catch (InsufficientFundsException e) { + System.out.println(e.getMessage() + " <- cause: " + e.getCause().getMessage()); + } + // <<< q41b + } + + // >>> q42 + static class Res implements AutoCloseable { + private final String name; + private final boolean failOnClose; + + Res(String name, boolean failOnClose) { + this.name = name; + this.failOnClose = failOnClose; + System.out.println("open " + name); + } + + @Override + public void close() { + System.out.println("close " + name); + if (failOnClose) throw new IllegalStateException("close failed: " + name); + } + } + // <<< q42 + + static void q42() { + Qa.header(42, "try-with-resources and suppressed exceptions"); + // >>> q42b + try (Res first = new Res("first", false); Res second = new Res("second", true)) { + throw new RuntimeException("body failed"); + } catch (RuntimeException e) { + System.out.println("caught: " + e.getMessage()); + System.out.println("suppressed: " + e.getSuppressed()[0].getMessage()); + } + // <<< q42b + } + + static void q43() { + Qa.header(43, "Error vs Exception, and multi-catch"); + // >>> q43 + try { + long[] tooBig = new long[Integer.MAX_VALUE]; + System.out.println(tooBig.length); + } catch (OutOfMemoryError e) { + System.out.println("Error, not Exception: " + e.getMessage()); + } + try { + Object o = "text"; + Integer n = (Integer) o; + } catch (ClassCastException | NullPointerException e) { // multi-catch: one handler, several types + System.out.println("multi-catch caught " + e.getClass().getSimpleName()); + } + // <<< q43 + } +} diff --git a/fresher-qa/src/main/java/com/ankurm/interviewlab/fresherqa/ModernJavaQa.java b/fresher-qa/src/main/java/com/ankurm/interviewlab/fresherqa/ModernJavaQa.java new file mode 100644 index 0000000..e3d8735 --- /dev/null +++ b/fresher-qa/src/main/java/com/ankurm/interviewlab/fresherqa/ModernJavaQa.java @@ -0,0 +1,151 @@ +package com.ankurm.interviewlab.fresherqa; + +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.function.BiFunction; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.function.Supplier; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** Questions 44-50: lambdas, streams, Optional, records, var/switch/text blocks, method references, threads. */ +public final class ModernJavaQa { + private ModernJavaQa() {} + + public static void main(String[] args) throws Exception { + run(); + } + + public static void run() throws Exception { + q44(); q45(); q46(); q47(); q48(); q49(); q50(); + } + + // >>> q44 + @FunctionalInterface + interface Discount { + double apply(double price); + } + // <<< q44 + + static void q44() { + Qa.header(44, "Lambdas and functional interfaces"); + // >>> q44b + Discount tenOff = price -> price * 0.9; + Predicate isEmpty = String::isEmpty; + Function length = String::length; + Supplier greeting = () -> "hello"; + BiFunction add = (x, y) -> x + y; + System.out.println(tenOff.apply(200) + " " + isEmpty.test("") + " " + length.apply("lambda") + + " " + greeting.get() + " " + add.apply(2, 3)); + // <<< q44b + } + + static void q45() { + Qa.header(45, "Streams: map, filter, collect, laziness"); + // >>> q45 + List names = List.of("asha", "ravi", "meena", "arjun"); + System.out.println(names.stream().filter(n -> n.startsWith("a")).map(String::toUpperCase).collect(Collectors.toList())); + System.out.println(names.stream().collect(Collectors.groupingBy(String::length, Collectors.counting()))); + Stream lazy = names.stream().peek(n -> System.out.println("visited " + n)); + System.out.println("nothing visited yet: no terminal operation"); + System.out.println("first = " + lazy.findFirst().get()); // stops after the first element + Stream used = names.stream(); + used.count(); + try { + used.count(); + } catch (IllegalStateException e) { + System.out.println("reuse -> " + e.getMessage()); + } + // <<< q45 + } + + static String expensive() { + System.out.println("expensive() called"); + return "default"; + } + + static void q46() { + Qa.header(46, "Optional"); + // >>> q46 + Optional present = Optional.of("value"); + System.out.println("orElse : " + present.orElse(expensive())); // argument is evaluated even though unused + System.out.println("orElseGet : " + present.orElseGet(() -> expensive())); // lambda is never run + Optional empty = Optional.empty(); + System.out.println(empty.map(String::toUpperCase).orElse("nothing here")); + // <<< q46 + } + + // >>> q47 + record Point(int x, int y) { + Point { // compact constructor: validation + if (x < 0 || y < 0) throw new IllegalArgumentException("negative coordinate"); + } + } + // <<< q47 + + static void q47() { + Qa.header(47, "Records"); + // >>> q47b + Point p = new Point(1, 2); + System.out.println(p + " equals " + new Point(1, 2) + ": " + p.equals(new Point(1, 2)) + ", x() = " + p.x()); + try { + new Point(-1, 0); + } catch (IllegalArgumentException e) { + System.out.println("rejected: " + e.getMessage()); + } + // <<< q47b + } + + static void q48() { + Qa.header(48, "var, switch expressions and text blocks"); + // >>> q48 + var scores = Map.of("asha", 91); + int marks = scores.get("asha"); + String grade = switch (marks / 10) { + case 10, 9 -> "A"; + case 8 -> "B"; + default -> "C"; + }; + String json = """ + {"name": "asha", + "grade": "%s"} + """.formatted(grade); + System.out.print(json); + // <<< q48 + } + + // >>> q49 + interface Walker { default String move() { return "walk"; } } + interface Swimmer { default String move() { return "swim"; } } + + static class Duck implements Walker, Swimmer { + @Override public String move() { return Walker.super.move() + " and " + Swimmer.super.move(); } + } + // <<< q49 + + static void q49() { + Qa.header(49, "Method references and default methods"); + // >>> q49b + List items = List.of("b", "a", "c"); + items.stream().sorted(String::compareTo).forEach(System.out::print); // static-style and bound references + System.out.println(); + System.out.println(new Duck().move()); + // <<< q49b + } + + static void q50() throws Exception { + Qa.header(50, "Threads: start() vs run(), and virtual threads"); + // >>> q50 + Runnable task = () -> System.out.println("running on: " + Thread.currentThread().getName()); + Thread t = new Thread(task, "worker-1"); + t.run(); // plain method call, still on main + t.start(); // new thread + t.join(); + Thread v = Thread.ofVirtual().name("virtual-1").start(() -> + System.out.println("virtual: " + Thread.currentThread().isVirtual())); + v.join(); + // <<< q50 + } +} diff --git a/fresher-qa/src/main/java/com/ankurm/interviewlab/fresherqa/OopQa.java b/fresher-qa/src/main/java/com/ankurm/interviewlab/fresherqa/OopQa.java new file mode 100644 index 0000000..b3a52a5 --- /dev/null +++ b/fresher-qa/src/main/java/com/ankurm/interviewlab/fresherqa/OopQa.java @@ -0,0 +1,309 @@ +package com.ankurm.interviewlab.fresherqa; + +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.Stack; + +/** Questions 10-20: the four pillars, constructors, overloading vs overriding, abstract vs interface, immutability. */ +public final class OopQa { + private OopQa() {} + + public static void main(String[] args) { + run(); + } + + public static void run() { + q10(); q11(); q12(); q13(); q14(); q15(); q16(); q17(); q18(); q19(); q20(); + } + + // ---------------------------------------------------------------- Q10 + // >>> q10 + interface Shape { // abstraction: callers only know "area()" + double area(); + } + + record Circle(double radius) implements Shape { + public double area() { return Math.PI * radius * radius; } + } + + record Rectangle(double w, double h) implements Shape { + public double area() { return w * h; } + } + + static class Account { + private double balance; // encapsulation: no outside code can touch this directly + + void deposit(double amount) { + if (amount <= 0) throw new IllegalArgumentException("amount must be positive"); + balance += amount; + } + + double balance() { return balance; } + } + // <<< q10 + + static void q10() { + Qa.header(10, "The four pillars of OOP"); + // >>> q10b + List shapes = List.of(new Circle(1), new Rectangle(2, 3)); + for (Shape s : shapes) { // polymorphism: one call, different behaviour + System.out.printf("%s area = %.2f%n", s.getClass().getSimpleName(), s.area()); + } + Account account = new Account(); + account.deposit(500); + try { + account.deposit(-1); + } catch (IllegalArgumentException e) { + System.out.println("rejected: " + e.getMessage() + ", balance still " + account.balance()); + } + // <<< q10b + } + + // ---------------------------------------------------------------- Q11 + // >>> q11 + static class Animal { + Animal() { System.out.println("Animal constructor"); } + } + + static class Dog extends Animal { + Dog() { + super(); // implicit if you leave it out + System.out.println("Dog constructor"); + } + } + + static class Puppy extends Animal { + Puppy(String name) { + if (name.isBlank()) throw new IllegalArgumentException("name required"); // before super(): legal since Java 25 + super(); + System.out.println("Puppy constructor for " + name); + } + } + // <<< q11 + + static void q11() { + Qa.header(11, "Inheritance and constructor chaining"); + // >>> q11b + new Dog(); + new Puppy("Bruno"); + // <<< q11b + } + + // ---------------------------------------------------------------- Q12 + // >>> q12 + static class Printer { + String show(Object o) { return "show(Object)"; } + String show(String s) { return "show(String)"; } + } + + static class FancyPrinter extends Printer { + @Override + String show(Object o) { return "FancyPrinter.show(Object)"; } + } + // <<< q12 + + static void q12() { + Qa.header(12, "Method overloading vs overriding"); + // >>> q12b + Printer p = new FancyPrinter(); + Object text = "hello"; + System.out.println(p.show(text)); // compile time picks show(Object) from the declared type; run time picks FancyPrinter's override + System.out.println(p.show("hello")); // compile time picks show(String); FancyPrinter never overrode it + System.out.println(p.show((Object) 42)); // same as the first line: overload by declared type, body by real class + // <<< q12b + } + + // ---------------------------------------------------------------- Q13 + // >>> q13 + abstract static class Vehicle { // abstract class: state + constructor + partial code + protected final String name; + Vehicle(String name) { this.name = name; } + abstract int wheels(); + String describe() { return name + " has " + wheels() + " wheels"; } + } + + interface Electric { // interface: a capability, many allowed + default String charge() { return "charging"; } + static String standard() { return "Type 2"; } + } + + static class Scooter extends Vehicle implements Electric { + Scooter() { super("Scooter"); } + int wheels() { return 2; } + } + // <<< q13 + + static void q13() { + Qa.header(13, "Abstract class vs interface"); + // >>> q13b + Scooter s = new Scooter(); + System.out.println(s.describe() + ", " + s.charge() + ", plug " + Electric.standard()); + // <<< q13b + } + + // ---------------------------------------------------------------- Q14 + // >>> q14 + static class Parent { + String name = "Parent.field"; + static String kind() { return "Parent.static"; } + String who() { return "Parent.who()"; } + } + + static class Child extends Parent { + String name = "Child.field"; // hides, does not override + static String kind() { return "Child.static"; } // hides, does not override + @Override String who() { return "Child.who()"; } + } + // <<< q14 + + static void q14() { + Qa.header(14, "Polymorphism: what is and is not dynamic"); + // >>> q14b + Parent p = new Child(); + System.out.println(p.who() + " | " + p.name + " | " + Parent.kind() + " | " + Child.kind()); + // <<< q14b + } + + // ---------------------------------------------------------------- Q15 + // >>> q15 + static class Base { + static String hello() { return "Base.hello"; } + private String secret() { return "Base.secret"; } + final String locked() { return "Base.locked"; } + String callSecret() { return secret(); } + } + + static class Derived extends Base { + static String hello() { return "Derived.hello"; } // legal: hides + private String secret() { return "Derived.secret"; } // legal: a new, unrelated method + // final String locked() { ... } // compile error: cannot override final + } + // <<< q15 + + static void q15() { + Qa.header(15, "Can you override static, private or final methods?"); + // >>> q15b + Base b = new Derived(); + System.out.println(b.callSecret() + " | " + b.locked()); + // <<< q15b + } + + // ---------------------------------------------------------------- Q16 + // >>> q16 + static final class Team { // final class: nobody can subclass and add mutators + private final String name; + private final List members; + + Team(String name, List members) { + this.name = name; + this.members = List.copyOf(members); // defensive copy in + } + + String name() { return name; } + List members() { return members; } // already unmodifiable, safe to return + } + // <<< q16 + + static void q16() { + Qa.header(16, "How do you create an immutable class?"); + // >>> q16b + List input = new ArrayList<>(List.of("Asha", "Ravi")); + Team team = new Team("Core", input); + input.add("Intruder"); // does not leak into the Team + System.out.println("members: " + team.members()); + try { + team.members().add("Hacker"); + } catch (UnsupportedOperationException e) { + System.out.println("add blocked: UnsupportedOperationException"); + } + // <<< q16b + } + + // ---------------------------------------------------------------- Q17 + static void q17() { + Qa.header(17, "Composition vs inheritance (is-a vs has-a)"); + // >>> q17 + Stack stack = new Stack<>(); // java.util.Stack EXTENDS Vector, so it inherits all of Vector + stack.push(1); + stack.push(2); + stack.add(0, 99); // legal, and it breaks "last in, first out" + System.out.println("stack = " + stack + ", pop gives " + stack.pop()); + // <<< q17 + } + + // ---------------------------------------------------------------- Q18 + // >>> q18 + static class Modifiers { + public int a; + protected int b; + int c; // package-private: no keyword at all + private int d; + } + // <<< q18 + + static void q18() { + Qa.header(18, "Access modifiers"); + // >>> q18b + for (Field f : Modifiers.class.getDeclaredFields()) { + String m = Modifier.toString(f.getModifiers()); + System.out.println(f.getName() + " -> " + (m.isEmpty() ? "(package-private)" : m)); + } + // <<< q18b + } + + // ---------------------------------------------------------------- Q19 + // >>> q19 + static 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 && p.x == x && p.y == y; + } + // hashCode() deliberately NOT overridden + } + // <<< q19 + + static void q19() { + Qa.header(19, "Object class methods: toString, equals, hashCode"); + // >>> q19b + Object plain = new Object(); + System.out.println("default toString looks like ClassName@hex: " + plain.toString().matches("java\\.lang\\.Object@[0-9a-f]+")); + Point p1 = new Point(1, 2), p2 = new Point(1, 2); + System.out.println("p1.equals(p2) = " + p1.equals(p2)); + Set set = new HashSet<>(List.of(p1)); + System.out.println("set contains an equal point: " + set.contains(p2)); + // <<< q19b + } + + // ---------------------------------------------------------------- Q20 + // >>> q20 + static class Config { + final String env; + final int port; + + Config() { this("dev", 8080); } // this(...) chains to another constructor + Config(String env, int port) { this.env = env; this.port = port; } + // The moment you write ANY constructor, the free no-argument one disappears. + // So Config() above had to be written by hand. + } + + static class Registry { + private static final Registry INSTANCE = new Registry(); + private Registry() {} // private constructor: nobody else can call new + static Registry get() { return INSTANCE; } + } + // <<< q20 + + static void q20() { + Qa.header(20, "Constructors: default, chained and private"); + // >>> q20b + Config c = new Config(); + System.out.println("default config: " + c.env + ":" + c.port); + System.out.println("same Registry instance: " + (Registry.get() == Registry.get())); + // <<< q20b + } +} diff --git a/fresher-qa/src/main/java/com/ankurm/interviewlab/fresherqa/Qa.java b/fresher-qa/src/main/java/com/ankurm/interviewlab/fresherqa/Qa.java new file mode 100644 index 0000000..dc7299d --- /dev/null +++ b/fresher-qa/src/main/java/com/ankurm/interviewlab/fresherqa/Qa.java @@ -0,0 +1,14 @@ +package com.ankurm.interviewlab.fresherqa; + +/** Tiny helper shared by the six question classes: prints the section header the transcripts use. */ +final class Qa { + private Qa() {} + + static void header(int n, String title) { + System.out.println("=== Q" + (n < 10 ? "0" : "") + n + " " + title + " ==="); + } + + static void blank() { + System.out.println(); + } +} diff --git a/fresher-qa/src/main/java/com/ankurm/interviewlab/fresherqa/StringsQa.java b/fresher-qa/src/main/java/com/ankurm/interviewlab/fresherqa/StringsQa.java new file mode 100644 index 0000000..ef0c843 --- /dev/null +++ b/fresher-qa/src/main/java/com/ankurm/interviewlab/fresherqa/StringsQa.java @@ -0,0 +1,127 @@ +package com.ankurm.interviewlab.fresherqa; + +import java.lang.reflect.Modifier; +import java.util.Arrays; +import java.util.LinkedHashMap; +import java.util.Map; + +/** Questions 21-27: equality and the string pool, immutability, StringBuilder, intern, reverse, methods, frequency. */ +public final class StringsQa { + private StringsQa() {} + + public static void main(String[] args) throws Exception { + run(); + } + + public static void run() throws Exception { + q21(); q22(); q23(); q24(); q25(); q26(); q27(); + } + + static void q21() { + Qa.header(21, "== vs equals() on Strings"); + // >>> q21 + String a = "java"; + String b = "java"; + String c = new String("java"); + System.out.println("a == b -> " + (a == b)); // two literals share one pooled object + System.out.println("a == c -> " + (a == c)); // new always builds a separate object + System.out.println("a.equals(c) -> " + a.equals(c)); // equals compares the characters + // <<< q21 + } + + static void q22() { + Qa.header(22, "Why is String immutable?"); + // >>> q22 + String s = "java"; + String upper = s.toUpperCase(); + System.out.println("s = " + s); + System.out.println("upper = " + upper); + System.out.println("same object returned when nothing changes: " + (s.toLowerCase() == s)); + // <<< q22 + } + + static void q23() throws Exception { + Qa.header(23, "String vs StringBuilder vs StringBuffer"); + // >>> q23 + StringBuilder sb = new StringBuilder("a"); + sb.append("b").append("c").insert(0, "-").reverse(); + System.out.println("builder result: " + sb); + boolean buf = Modifier.isSynchronized(StringBuffer.class.getMethod("append", String.class).getModifiers()); + boolean bld = Modifier.isSynchronized(StringBuilder.class.getMethod("append", String.class).getModifiers()); + System.out.println("append is synchronized: StringBuffer=" + buf + ", StringBuilder=" + bld); + // <<< q23 + } + + static void q24() { + Qa.header(24, "The string pool, compile-time constants and intern()"); + // >>> q24 + String lo = "lo"; + final String finalLo = "lo"; + System.out.println("\"hel\" + \"lo\" == \"hello\" -> " + ("hel" + "lo" == "hello")); + System.out.println("\"hel\" + lo == \"hello\" -> " + ("hel" + lo == "hello")); + System.out.println("\"hel\" + finalLo == \"hello\" -> " + ("hel" + finalLo == "hello")); + System.out.println("(\"hel\" + lo).intern() == \"hello\" -> " + (("hel" + lo).intern() == "hello")); + // <<< q24 + } + + // >>> q25 + static String reverse(String s) { + return new StringBuilder(s).reverse().toString(); + } + + static boolean isPalindrome(String s) { + String cleaned = s.toLowerCase().replaceAll("[^a-z0-9]", ""); + return cleaned.equals(reverse(cleaned)); + } + // <<< q25 + + static void q25() { + Qa.header(25, "Reverse a string and check for a palindrome"); + // >>> q25b + System.out.println(reverse("interview")); + System.out.println(isPalindrome("A man, a plan, a canal: Panama")); + System.out.println(isPalindrome("hello")); + // <<< q25b + } + + static void q26() { + Qa.header(26, "Useful String methods and the split() surprises"); + // >>> q26 + String spaced = "\u2003 hi \u2003"; // an em space on each side + System.out.println("trim().length() = " + spaced.trim().length()); + System.out.println("strip().length() = " + spaced.strip().length()); + System.out.println("\" \".isBlank() = " + " ".isBlank() + ", \"ab\".repeat(3) = " + "ab".repeat(3)); + System.out.println("\"a,b,,\".split(\",\").length = " + "a,b,,".split(",").length); + System.out.println("\"a,b,,\".split(\",\", -1).length = " + "a,b,,".split(",", -1).length); + System.out.println("String.join: " + String.join("-", "2026", "11", "11")); + // <<< q26 + } + + // >>> q27 + static Map frequency(String s) { + Map counts = new LinkedHashMap<>(); // keeps first-seen order + for (char ch : s.toCharArray()) { + counts.merge(ch, 1, Integer::sum); + } + return counts; + } + + static boolean areAnagrams(String x, String y) { + char[] a = x.toCharArray(), b = y.toCharArray(); + Arrays.sort(a); + Arrays.sort(b); + return Arrays.equals(a, b); + } + // <<< q27 + + static void q27() { + Qa.header(27, "Count characters, find the first non-repeating one, check anagrams"); + // >>> q27b + Map counts = frequency("swiss"); + System.out.println("frequency: " + counts); + char first = counts.entrySet().stream().filter(e -> e.getValue() == 1).findFirst().get().getKey(); + System.out.println("first non-repeating: " + first); + System.out.println("listen/silent anagrams: " + areAnagrams("listen", "silent")); + // <<< q27b + } +} diff --git a/fresher-qa/src/test/java/com/ankurm/interviewlab/fresherqa/TranscriptsTest.java b/fresher-qa/src/test/java/com/ankurm/interviewlab/fresherqa/TranscriptsTest.java new file mode 100644 index 0000000..7445a6c --- /dev/null +++ b/fresher-qa/src/test/java/com/ankurm/interviewlab/fresherqa/TranscriptsTest.java @@ -0,0 +1,94 @@ +package com.ankurm.interviewlab.fresherqa; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.concurrent.Callable; +import org.junit.jupiter.api.Test; + +/** Runs each question class, asserts the claims the article makes, and writes output/NN-*.txt. */ +class TranscriptsTest { + + private static String capture(String file, Callable body) throws Exception { + PrintStream original = System.out; + ByteArrayOutputStream buffer = new ByteArrayOutputStream(); + System.setOut(new PrintStream(buffer, true, StandardCharsets.UTF_8)); + try { + body.call(); + } finally { + System.setOut(original); + } + String text = buffer.toString(StandardCharsets.UTF_8); + Files.createDirectories(Path.of("output")); + Files.writeString(Path.of("output", file), text); + return text; + } + + private static void has(String text, String... expected) { + for (String e : expected) assertTrue(text.contains(e), "missing: " + e + "\n" + text); + } + + @Test + void basics() throws Exception { + String t = capture("01-basics.txt", () -> { BasicsQa.run(); return null; }); + has(t, "magic = CAFEBABE", "class file major version = 69", "running on Java 25", + "(byte) 130 = -126", "Integer.MAX_VALUE + 1 = -2147483648", "0.1 + 0.2 = 0.30000000000000004", + "127 == 127 -> true", "128 == 128 -> false", "Long 127 equals Integer 127 -> false", + "after reassign: original", "after mutate: original + mutated", + "Object.finalize forRemoval = true", "StackOverflowError after many frames: true"); + assertTrue(t.indexOf("static block") < t.indexOf("instance block")); + assertTrue(t.indexOf("instance block") < t.indexOf("constructor #1")); + } + + @Test + void oop() throws Exception { + String t = capture("02-oop.txt", () -> { OopQa.run(); return null; }); + has(t, "rejected: amount must be positive, balance still 500.0", + "Animal constructor\nDog constructor", "Puppy constructor for Bruno", + "FancyPrinter.show(Object)\nshow(String)\nFancyPrinter.show(Object)", + "Child.who() | Parent.field | Parent.static | Child.static", + "Base.secret | Base.locked", "members: [Asha, Ravi]", "add blocked: UnsupportedOperationException", + "stack = [99, 1, 2], pop gives 2", "p1.equals(p2) = true", "set contains an equal point: false", + "same Registry instance: true", "d -> private", "c -> (package-private)"); + } + + @Test + void strings() throws Exception { + String t = capture("03-strings.txt", () -> { StringsQa.run(); return null; }); + has(t, "a == b -> true", "a == c -> false", "a.equals(c) -> true", + "append is synchronized: StringBuffer=true, StringBuilder=false", + "trim().length() = 6", "strip().length() = 2", + "\"a,b,,\".split(\",\").length = 2", "\"a,b,,\".split(\",\", -1).length = 4", + "first non-repeating: w", "listen/silent anagrams: true"); + } + + @Test + void collections() throws Exception { + String t = capture("04-collections.txt", () -> { CollectionsQa.run(); return null; }); + has(t, "Aa: hashCode=2112", "BB: hashCode=2112", "both survive a collision: first, second, size 2", + "HashMap: null key accepted", "Hashtable: null key throws NullPointerException", + "TreeMap: null key throws NullPointerException", + "LinkedHashSet [pear, apple, mango, banana, cherry]", "TreeSet [apple, banana, cherry, mango, pear]", + "removing 2 inside for-each -> ConcurrentModificationException", + "get with the mutated key: null", "get with a fresh \"A1\" key: null", "size is still 1", + "view sees the change: [a, b, c]", "copy does not: [a, b]"); + } + + @Test + void exceptions() throws Exception { + String t = capture("05-exceptions.txt", () -> { ExceptionsQa.run(); return null; }); + has(t, "primitiveSnapshot = 10", "finallyOverrides = 7", "withdrawal refused <- cause: balance 100 < 500", + "close second\nclose first", "suppressed: close failed: second"); + } + + @Test + void modern() throws Exception { + String t = capture("06-modern-java.txt", () -> { ModernJavaQa.run(); return null; }); + has(t, "180.0 true 6 hello 5", "[ASHA, ARJUN]", "reuse -> stream has already been operated upon or closed", + "running on: main", "running on: worker-1", "virtual: true", "walk and swim"); + } +}