Fiserv Java Developer Interview Question for Experienced Professionals 2024

Ajay Rathod
12 min readMar 10, 2024

--

Hello folks, I would like to share my recent Java interview experience with one of the fintech companies called Fiserv. Banking and finance companies have a very tough interview process. This is for Java developers who have 3–8 years of experience and can go through this to crack their interviews. I hope this actual transcript will help you. Let's dive into the actual interview.

Photo by Christopher Gower on Unsplash

Note- If you love to watch on YouTube below is the link

Are you preparing for a job interview as a Java developer?

Find my book Guide To Clear Java Developer Interview here Gumroad (PDFFormat) and Amazon (Kindle eBook).

Guide To Clear Spring-Boot Microservice Interview here Gumroad (PDFFormat) and Amazon (Kindle eBook).

Download the sample copy here: Guide To Clear Java Developer Interview[Free Sample Copy]

Guide To Clear Spring-Boot Microservice Interview[Free Sample Copy]

Scenario-based question

Is it possible to add three objects of the same Employee class (e1, e2, e3) and two objects of the Department class (d1, d2) to a TreeSet? What will happen if I attempt to print those objects using System.out.println?

Below is the program

package com.test.springdataflowtest;

import java.util.*;


public class TreeSetExample {
public static void main(String[] args) {
TreeSet<Object> treeSet = new TreeSet<>();

Employee e1 = new Employee(101, "John");
Employee e2 = new Employee(102, "Alice");
Employee e3 = new Employee(103, "Bob");

Department d1 = new Department(201, "HR");
Department d2 = new Department(202, "IT");

treeSet.add(e1);
treeSet.add(e2);
treeSet.add(e3);
treeSet.add(d1);
treeSet.add(d2);

System.out.println("Elements in TreeSet:");
for (Object obj : treeSet) {
System.out.println(obj);
}
}
}

class Employee implements Comparable<Employee> {
private int id;
private String name;

public Employee(int id, String name) {
this.id = id;
this.name = name;
}

@Override
public int compareTo(Employee other) {
return Integer.compare(this.id, other.id);
}

@Override
public String toString() {
return "Employee{id=" + id + ", name='" + name + "'}";
}
}

class Department {
private int deptId;
private String deptName;

public Department(int deptId, String deptName) {
this.deptId = deptId;
this.deptName = deptName;
}

@Override
public String toString() {
return "Department{deptId=" + deptId + ", deptName='" + deptName + "'}";
}
}

It will throws the exception

Exception in thread “main” java.lang.ClassCastException: class Java8Features.Department cannot be cast to class java.lang.Comparable (Java8Features.Department is in unnamed module of loader ‘app’; java.lang.Comparable is in module java.base of loader ‘bootstrap’)
at java.base/java.util.TreeMap.put(TreeMap.java:811)
at java.base/java.util.TreeMap.put(TreeMap.java:534)
at java.base/java.util.TreeSet.add(TreeSet.java:255)
at Java8Features.OptionalClassDemoTest.main(OptionalClassDemoTest.java:20)

The program as provided will indeed fail, and the primary reason is related to the mixing of Employee and Department objects in the same TreeSet. Since Employee objects are comparable (they implement the Comparable interface), they can be added to a TreeSet without issue, allowing the TreeSet to maintain its elements in a sorted order based on the comparison logic provided in the compareTo method of the Employee class.

However, Department objects do not implement the Comparable interface, and thus the TreeSet cannot determine a natural ordering for them. When you attempt to add a Department object to a TreeSet that already contains Employee objects (or vice versa), the TreeSet will attempt to compare these objects to maintain order. Since the Department lacks a compareTo method (or another means of comparison, such as a Comparator provided to the TreeSet at creation), this operation will fail, throwing a ClassCastException, indicating that the objects cannot be compared for ordering.

To fix this issue, you have a few options:

Make Department Comparable: Implement the Comparable interface in the Department class as well, providing a comparison logic similar to what’s done in the Employee class.

Use a Comparator: Create the TreeSet with a custom Comparator that knows how to compare both Employee and Department objects. This approach allows for greater flexibility, especially when the classes themselves cannot be modified or when they come from external libraries.

Separate TreeSet Instances: Maintain separate collections for Employee and Department objects if there is no logical way to compare them or if you do not need them sorted in the same collection.

When to use comparable and comparator in Java?

In Java, both Comparable and Comparator interfaces are used for sorting elements in collections like TreeSet, TreeMap, Collections.sort(), etc. However, they serve different purposes and are used in different scenarios:

Comparable Interface
Natural Sorting Order: Comparable is used when you want to define a natural ordering for objects of a class. This means that the class itself defines how its instances should be compared.

Single Sorting Order: With Comparable, there is only one way to compare objects. The sorting logic is baked into the class itself via the compareTo() method.

Intrinsic Implementation: You implement Comparable in the class whose instances you want to sort. This allows for consistent sorting behavior across all collections that use natural ordering.

Example: Sorting String, Integer, or custom classes like Employee based on their ID, name, or any other intrinsic property.

Comparator Interface
Custom Sorting Order: Comparator is used when you want to define multiple sorting orders for objects of a class or when you don’t have access to the class’s source code to implement Comparable.

External Sorting Logic: With Comparator, you can define sorting logic externally to the class being sorted. This is useful when you want to sort objects based on different criteria without modifying their original class.

Multiple Sorting Orders: You can define multiple Comparator implementations to sort objects differently as needed, without modifying the class itself.

Example: Sorting Employee objects based on their salary, age, or any other criteria that may vary depending on the context.

When to Use Which?
Use Comparable when you have a natural sorting order that’s intrinsic to the class itself and you want all instances of the class to be sorted in the same way.

Use Comparator when you need custom sorting logic, or when you want to sort objects in multiple ways without altering their original class, or when you’re dealing with classes from external libraries that you can’t modify.

How to check the time complexity of the program during code review?

Evaluating the time complexity of a program during a code review involves understanding the algorithms used, the data structures involved, and how they scale with input size. While you cannot directly measure time complexity by running the code (as that gives you runtime, which varies with hardware, input, etc.), you can analyze the code to determine its theoretical time complexity. Here are steps and considerations to guide you:

1. Identify the Basic Operations
Determine what constitutes a basic operation in the algorithm — the operation that most affects performance. This could be a comparison in a sorting algorithm, an addition in a numerical computation, or a data retrieval operation.

2. Analyze Loop Complexity
For Loops: Consider how many iterations a loop will run in terms of input size (N). For example, a loop running from 1 to N has a complexity of O(N).
Nested Loops: For nested loops, multiply the complexity of the inner and outer loops. A loop of N within a loop of N has a complexity of O(N²).
While Loops: Determine what part of the input the loop conditions are processing and how many iterations would occur in the worst case.
3. Consider Recursion
For recursive functions, determine the recurrence relation. The number of recursive calls and how the problem size decreases with each call will help you identify the complexity. Tools like the Master Theorem can then be used to solve these relations.

4. Factor in Data Structures
The operations on data structures (like adding or removing elements from arrays, trees, or linked lists) have their own complexities. For example:

Array indexing is O(1), but searching could be O(N) or O(log N) if the array is sorted and binary search is used.
Tree operations (insertion, search) depend on the type of tree (e.g., binary search tree, balanced tree like AVL, or Red-Black Tree).
5. Account for Library Functions and API Calls
Understand the complexity of any library function or external API call used within the code. For example, sorting functions (Arrays.sort, Collections.sort) typically have O(N log N) complexity.

6. Worst, Best, and Average Case Scenarios
Remember that complexity can vary:

Worst-case complexity (Big O) is what you usually evaluate, representing the upper limit on the time.
Best-case complexity (Big Omega Ω) can be relevant for algorithms that have significantly different behavior on the best inputs.
Average-case complexity (Big Theta Θ) requires probabilistic analysis of the input.
7. Space Complexity
Although the focus might be on time complexity, consider also the space complexity — how much additional memory the program needs as it scales. Some algorithms trade time efficiency for memory (e.g., caching, dynamic programming).

During Code Review
Ask for Complexity Justifications: The developer should ideally comment on or document the expected time and space complexities of their functions or algorithms.
Check Against Requirements: Verify that the complexity aligns with the performance requirements or constraints of the project.
Optimization Opportunities: Identify any obvious inefficiencies, like unnecessary nested loops, repeated calculations, or suboptimal data structure usage.

“How can we determine if the current code incorporates aggregation, composition, and inheritance, and what practical purposes do these concepts serve?”

To determine if the current code incorporates aggregation, composition, and inheritance, and to understand their practical applications, you’ll need to review the relationships between objects and classes within the codebase. Here’s how to approach this:

1. Identify Inheritance:
Inheritance is a “is-a” relationship between a base class (parent) and a derived class (child), where the child class inherits properties and behaviors (methods) from the parent class and may add or override them.

How to Check:Look for class definitions to see if any class is extending another class.
Keywords to spot: In Java and similar languages, extends for class inheritance and implements for interface implementation.
Practical Use:Inheritance is used for code reuse and polymorphism. It allows derived classes to reuse code from their base classes, reducing redundancy. For example, a Vehicle class could be a base class, and Car and Motorcycle could be derived classes, inheriting properties like speed and methods like move().
2. Spot Aggregation:
Aggregation implies a “has-a” relationship where one class contains references to others but does not exclusively control their lifecycle.

How to Check:Look for classes that hold references to other class instances as member variables but don’t instantiate them within their own constructors.
The contained objects can be passed to the aggregating object’s constructor, or set via setters.
Practical Use:Aggregation is useful for representing relationships where components can belong to multiple containers. For instance, a Professor and Student class might aggregate a Department class, indicating both can be associated with the same department.
3. Determine Composition:
Composition is a stronger form of aggregation with ownership, implying a “part-of” relationship. The lifecycle of the contained objects depends on the lifecycle of the container object.

How to Check: Classes that create instances of other classes within their own constructors or methods, and manage their lifetime, exhibit composition.
If the container class is destroyed, the contained objects are also destroyed.
Practical Use:
Composition is used to model relationships where components shouldn’t exist without their container. For instance, a Page class might be a component of a Book class; if the book is destroyed, so are its pages.

What is a Memory leak in Java?

A memory leak in Java occurs when objects that are no longer needed by the application still occupy memory because the garbage collector cannot remove them from working memory. This situation typically happens when references to these objects remain in the code, preventing the garbage collector from deallocating the memory they occupy, despite them being effectively useless for the application’s further operations.

What is substring and its uses what’s wrong with it?

A substring in programming refers to a smaller part or segment of a string. In many programming languages, including Java, there are methods available to extract a substring from a given string. These methods typically allow you to specify the starting position of the substring and, optionally, the ending position. The result is a new string containing the characters from the specified range.

public class SubstringExample {
public static void main(String[] args) {
String originalString = "Hello, World!";

// Extracting a substring from index 7 to the end
String substring1 = originalString.substring(7);
System.out.println("Substring 1: " + substring1); // Output: "World!"

// Extracting a substring from index 0 to 5 (exclusive)
String substring2 = originalString.substring(0, 5);
System.out.println("Substring 2: " + substring2); // Output: "Hello"

// Extracting a substring from index 2 to 6 (exclusive)
String substring3 = originalString.substring(2, 6);
System.out.println("Substring 3: " + substring3); // Output: "llo,"
}
}

Uses of Substring:
Parsing and Processing Text: Substrings are useful when you need to parse and process parts of a larger string. For example, extracting the domain name from a URL.
Data Validation: You might use substrings to check or validate specific parts of user input or data fields, such as checking the prefix or area code in a phone number.
String Manipulation: Substring methods are essential for various string manipulation tasks, like removing prefixes or suffixes, or implementing algorithms that require inspection or modification of parts of a string.
Formatting: Substrings can help in formatting output, such as displaying only the first few characters of a long string to ensure a user interface remains uncluttered.
Issues with Substring:
Memory Leaks (Historical Issue in Java): In earlier versions of Java (before Java 7, update 6), the substring method could potentially lead to memory leaks. When a substring was created, it shared the same underlying character array as the original string, with only the start and end indexes being different. If the original string was large and the substring small, but the reference to the substring was kept, it prevented the large character array from being garbage collected, even if the original string reference was discarded. This behavior was changed in later versions of Java, where the substring method now copies the relevant part of the array, preventing this specific kind of memory leak.

Index Out of Bounds: If the starting or ending index is incorrect (e.g., negative, beyond the string’s length, or start index greater than the end index), it can throw an IndexOutOfBoundsException. Proper error handling and validation of indices are required to avoid this.

Performance Considerations: While generally efficient, substring operations can lead to performance issues in specific contexts, especially if used repeatedly in a loop on very large strings or in performance-critical applications. The creation of many substring objects can increase the workload on the garbage collector.

Immutability of Strings: In Java, strings are immutable. Every substring operation creates a new string object, which could lead to increased memory usage in extensive string processing applications. StringBuilder or StringBuffer might offer more efficient alternatives for frequent modifications.

What is OutOfMemoryError and How to solve it?

Symptoms

  • Sudden failure or major slowness of your application
  • Error found in log and console -java.lang.OutOfMemoryError: Java heap space
  • High usage of cpu due to increased java garbage collector activity

Possible root causes

  • Inadequate heap space capacity settings eg.xms,xmx
  • Java program memory leaks
  • Excessive application memory footprint and processing workload

Diagnostic tools

  • Jvm verbose:gc logs
  • Java visual vm, oracle mision control
  • Apm technologies

Solution

  • Increase the max heap capacity using xmx parameter
  • Monitor profile and resolve application memory leaks
  • Optimise and reduce your java application memory footprint
  • Split the workload of your java application to several process
  • We can ask hotspot jvm to generate heap dump
  • Gc log

How to create an immutable class, like String?

Don’t provide methods that modify the object’s state (known as mutators).
Ensure that the class can’t be extended. This prevents careless or malicious subclasses from compromising the immutable behavior of the class by behaving as if the object’s state has changed. Preventing subclassing is generally accomplished by making the class final or provide a private constructor
Make all fields final. This clearly expresses your intent in a manner that is enforced by the system. Also, it is necessary to ensure correct behavior if a reference to a newly created instance is passed from one thread to another without synchronization, as spelled out in the memory model
Make all fields private. This prevents clients from obtaining access to mutable objects referred to by fields and modifying these objects directly. While it is technically permissible for immutable classes to have public final fields containing primitive values or references to immutable objects, it is not recommended because it precludes changing the internal representation in a later release.
Ensure exclusive access to any mutable components. If your class has any fields that refer to mutable objects, ensure that clients of the class cannot obtain references to these objects. Never initialize such a field to a client-provided object reference or return the field from an accessor. Make defensive copies (Item 50) in constructors, accessors, and readObject methods.

Thanks for reading

  • 👏 Please clap for the story and follow me 👉
  • 📰 Read more content on my Medium (60 stories on Java Developer interview)

Find my books here:

--

--