Java questions are the first filter. If you cannot answer OOP and Collections questions, the interview ends early — they will not even get to Selenium. Every answer here ties Java concepts to test automation. That is how you stand out.
Q: What are the 4 pillars of OOP? Explain with automation examples.
A: Abstraction — hiding implementation details. My BasePage exposes methods like click() and type() without revealing the WebDriverWait logic inside. Tests call loginPage.enterUsername("admin") without knowing the locator or wait strategy. Encapsulation — bundling data and methods together with restricted access. In my page objects, locators are private and only accessible through public action methods. If a locator changes, I update one file, not 50 test classes. Inheritance — child class reuses parent code. All my test classes extend BaseTest which handles browser launch, teardown, and screenshots. All page objects extend BasePage which has common wait and interaction methods. Polymorphism — same interface, different behavior. WebDriver driver = new ChromeDriver() — I can swap in FirefoxDriver without changing test code because both implement WebDriver. This is how my BrowserFactory works.
Q: What is the difference between an abstract class and an interface?
A: An abstract class can have both abstract methods (no body) and concrete methods (with implementation). It can have constructors, instance variables, and any access modifier. A class can extend only one abstract class. An interface (before Java 8) had only abstract methods. Since Java 8, interfaces support default and static methods. A class can implement multiple interfaces. In my framework: BasePage is an abstract class with concrete methods like waitForElement() and click(), plus an abstract method isPageLoaded() that each page must implement. I use an interface when I need a contract — like a Navigable interface with navigateTo() that multiple pages implement differently. The interview rule: use abstract class for shared code with some variation, use interface for defining a contract that unrelated classes must follow.
Q: Explain method overloading vs method overriding.
A: Overloading: same method name, different parameters, in the same class. It is compile-time polymorphism. In my utility class: click(By locator), click(WebElement element), click(By locator, int timeoutSeconds) — three versions of click for different situations. Overriding: child class replaces a parent method with the same signature. It is runtime polymorphism. My LoginPage overrides isPageLoaded() from BasePage to check for the username field, while DashboardPage overrides it to check for the welcome banner. Key rules: overloaded methods must differ in parameter types or count. Overridden methods must have the same signature and use @Override annotation. An overridden method cannot have a more restrictive access modifier than the parent.
Q: What is the static keyword? Where do you use it in your framework?
A: Static means the member belongs to the class, not to any instance. A static variable is shared across all objects. A static method can be called without creating an object. In my framework: (1) Constants — static final String BASE_URL, static final int TIMEOUT. (2) Utility methods — ScreenshotUtil.capture(driver, testName) is static because I do not need an instance. (3) BrowserFactory.createDriver("chrome") is a static factory method. (4) ThreadLocal<WebDriver> is static — shared across the class but stores a separate WebDriver per thread for parallel execution. Static methods cannot access instance variables or use "this". A common trap: using a static WebDriver without ThreadLocal causes race conditions in parallel execution.
Q: What is the difference between == and .equals()?
A: == compares references — are these two variables pointing to the exact same object in memory? .equals() compares content — do these two objects have the same value? For primitives, == compares values directly. For objects like String, you must use .equals(). The classic trap: String a = new String("hello"); String b = new String("hello"); a == b is false (different objects in heap), a.equals(b) is true (same content). In test automation, always use .equals() for String comparisons: Assert.assertEquals(actualTitle, expectedTitle) — internally this uses .equals(). One more: String a = "hello"; String b = "hello"; a == b is true here because of the String pool — Java reuses the same literal. But never rely on this. Always use .equals().
Q: String vs StringBuilder vs StringBuffer — when do you use each?
A: String is immutable — every modification creates a new object. Good for constants and comparisons, bad for repeated concatenation. StringBuilder is mutable and not thread-safe — best for building strings in a single thread. StringBuffer is mutable and thread-safe — same API as StringBuilder but slower due to synchronization. In my framework: I use String for locator values, expected messages, and assertions. I use StringBuilder when constructing dynamic XPaths or building CSV test data in loops: StringBuilder xpath = new StringBuilder("//table//tr"); for (String filter : filters) { xpath.append("[td[text()='").append(filter).append("']]"); }. I never use StringBuffer — thread safety for string building is rarely needed in test automation. Interview tip: if asked "When would you use StringBuffer?", say "almost never in modern code — StringBuilder is preferred unless multiple threads are modifying the same builder."
Q: Explain try-catch-finally with an automation example.
A: try contains code that might throw an exception. catch handles the exception — log it, take a screenshot, or rethrow it. finally always executes, whether an exception occurred or not — perfect for cleanup. In my framework: try { loginPage.enterUsername(user); loginPage.clickLogin(); Assert.assertEquals(dashboardPage.getWelcomeText(), "Welcome"); } catch (NoSuchElementException e) { ScreenshotUtil.capture(driver, "login_failure"); throw e; } finally { driver.quit(); }. The finally block ensures the browser closes even if the test crashes. You can have multiple catch blocks: catch NoSuchElementException first (specific), then catch Exception (general). A key point: if you return in both try and finally, the finally return wins — but never do this, it is confusing.
Q: What are checked vs unchecked exceptions?
A: Checked exceptions are verified at compile time — the compiler forces you to handle them with try-catch or declare them with throws. Examples: IOException, FileNotFoundException, SQLException. They represent recoverable situations. Unchecked exceptions extend RuntimeException — the compiler does not force handling. Examples: NullPointerException, ArrayIndexOutOfBoundsException. In Selenium, most exceptions are unchecked: NoSuchElementException, StaleElementReferenceException, TimeoutException. I still wrap Selenium code in try-catch for screenshot capture and better logging, even though the compiler does not require it. Interview question: "Does your test stop if you get an unchecked exception?" — Yes, unless you catch it. The "unchecked" part only means the compiler does not force you to handle it.
Q: What is the difference between ArrayList and LinkedList?
A: ArrayList uses a dynamic array internally — random access by index is O(1), but inserting or removing in the middle is O(n) because elements shift. LinkedList uses a doubly linked list — insertion and deletion at known positions are O(1), but random access is O(n) because you traverse from the start. In automation, I use ArrayList almost exclusively. driver.findElements() returns a List, and I store results in ArrayList: List<WebElement> rows = driver.findElements(By.cssSelector("tr")). I iterate sequentially, access by index, and never insert in the middle. LinkedList is rarely needed. The interview answer: "ArrayList for 99% of automation use cases because our access pattern is sequential iteration and index-based access, which is exactly what ArrayList is optimized for."
Q: What is HashMap? How do you use it in automation?
A: HashMap stores key-value pairs with O(1) average time for get and put. Internally, it uses an array of buckets — hashCode() determines the bucket, equals() resolves collisions within a bucket. In my framework: (1) Test data — Map<String, String> data = new HashMap<>(); data.put("username", "admin"); data.put("password", "pass123");. (2) Configuration — ConfigReader loads properties into a HashMap. (3) ExcelReader returns List<Map<String, String>> — each row is a map with column headers as keys. (4) Mapping environments to URLs — Map<String, String> envUrls with keys "qa", "staging", "prod". Key interview points: HashMap allows one null key. It is not ordered — use LinkedHashMap if insertion order matters. It is not thread-safe — use ConcurrentHashMap for parallel execution.
Q: List vs Set vs Map — when do you use each?
A: List: ordered, allows duplicates, access by index. Use for storing WebElements from findElements(), test data rows, dropdown options. I use ArrayList. Set: unordered, no duplicates. Use for collecting unique values — unique error messages across tests, unique tags from a page. I use HashSet. Map: key-value pairs, keys are unique. Use for test data (field name to value), configuration, environment mapping. I use HashMap. Quick decision: need to keep order and access by index? List. Need uniqueness? Set. Need to look up values by key? Map. In my framework, I use List most (80%), Map second (15%), and Set rarely (5%). A common interview follow-up: "What if you need an ordered Set?" — use LinkedHashSet or TreeSet.
Q: What is a constructor? How do you use constructors in page objects?
A: A constructor is a special method called when you create an object with the new keyword. Same name as the class, no return type. In my framework, every page object has a constructor that accepts WebDriver: public LoginPage(WebDriver driver) { super(driver); } — this calls the BasePage constructor which stores the driver and initializes WebDriverWait. Without this constructor, page objects would not have access to the browser. Constructor chaining with super() ensures the parent class is properly initialized. I also have a parameterized BasePage constructor: protected BasePage(WebDriver driver) { this.driver = driver; this.wait = new WebDriverWait(driver, Duration.ofSeconds(ConfigReader.getInstance().getTimeout())); }. Interview tip: always mention that you pass WebDriver through constructors, not static variables — this enables parallel execution.
Q: What is the final keyword?
A: final prevents modification at three levels. final variable — cannot be reassigned after initialization: private static final int TIMEOUT = 10;. I use it for constants like timeouts, URLs, and locator strings. final method — cannot be overridden by child classes: I use it on critical BasePage methods like getDriver() that should never be changed. final class — cannot be extended: Java String is a final class. In automation, I rarely make classes final because inheritance is central to the POM pattern. The common interview trio: final vs finally vs finalize. final prevents modification. finally is a try-catch block that always executes. finalize is a deprecated garbage collection method — never use it.
Q: What are access modifiers in Java?
A: Four levels of visibility. public — accessible everywhere. I use it for test methods (@Test) and page object action methods (login(), getErrorMessage()). protected — accessible within the same package and by subclasses. I use it for BasePage utility methods that child pages need (waitForVisible(), scrollTo()). default (no modifier) — accessible only within the same package. Rarely used in my framework. private — accessible only within the class. I use it for WebElement locators and internal helper methods in page objects. The encapsulation principle: locators are private, action methods are public, shared utilities are protected. This way, test classes cannot directly access locators — they must go through the page object methods.
Q: What is autoboxing? When does it matter in automation?
A: Autoboxing is automatic conversion from a primitive to its wrapper class: int to Integer, boolean to Boolean. Unboxing is the reverse. Java does this so primitives can be used with Collections (which only accept objects). It matters in automation with DataProviders — Object[][] contains objects, so int values are autoboxed to Integer. The dangerous trap: Integer a = 127; Integer b = 127; a == b is true (cached). Integer a = 128; Integer b = 128; a == b is false (different objects — Java only caches -128 to 127). Always use .equals() for wrapper comparisons. In practice, you encounter this when comparing values extracted from Excel or JSON test data — they often come as wrapper types.
Key Point: Java questions are the first gate. Nail OOP and Collections with automation examples, and you pass through to the Selenium round.