InterviewLegend

OOP & Language Internals

Alex Chen30 min readUpdated 330 real interview reports
In this lesson
  • Answer the OOP and language-internals questions that decide SAP technical rounds, in Java and C++
  • Defend the distinctions interviewers push on: overriding vs overloading, interface vs abstract class, fail-fast vs fail-safe
  • Design a clean class under time: LRU Cache and a Premium question with O(1) operations
  • Coordinate two threads to produce an ordered sequence, the concurrency question SAP keeps asking

Most candidates prepare for SAP the way they prepare for everyone else: grind hard graphs and DP, memorize a few templates, hope a medium shows up. Then the interviewer opens with "what are the four pillars of OOP," asks you to explain a virtual function, and wants to know exactly what happens when two threads touch the same list. This is the round people fail. One candidate answered every coding question correctly at an onsite in Palo Alto and still got rejected; the gate was fundamentals and fit.

The coding on SAP loops is easy to medium and rarely the deciding factor. What separates offers from rejects is whether you can talk about the language you write in. Both Java and C++ come up, often in the same interview, and the questions are the textbook ones you stopped reviewing years ago. Overriding versus overloading, interface versus abstract class, throw versus throws, the final keyword, what the JVM and JRE are, how exceptions are implemented. Interviewers also lean hard on threads and concurrency once you have any experience. The fundamentals below come first, then the three design and concurrency problems that show up most.

SAP interviewers have said outright that they prefer clean, modular, readable code over a clever solution to a harder problem, and that they are not chasing the most optimized answer. Think out loud, write something you can explain, and be ready to defend every language choice you make. A plain solution you understand cold beats a dense one you cannot reason about under questions.

What this round drills

These are the questions that recur across SAP Labs and SAP America reports, grouped by theme. None of them is exotic. The failure mode is being fluent in algorithms and vague about the language. Treat each one as something you can explain in two clear sentences, ideally with a one-line code example ready.

OOP pillars and polymorphism

4 questions

The opener on nearly every technical round. Know the four pillars and be ready to demonstrate one in code, not just name it.

What is OOP and what are its four pillars?

Encapsulation, abstraction, inheritance, polymorphism. Expect a follow-up asking you to demonstrate one, "show me encapsulation in a language of your choice."

What is a virtual function in C++, and when do you need one?

A base-class function marked virtual so a call through a base pointer or reference dispatches to the derived override at runtime. Without it the call binds statically to the base version.

Explain polymorphism and its types.

Compile-time (overloading) versus run-time (overriding, dynamic dispatch). Interviewers ask you to separate the two cleanly.

How do you achieve multiple inheritance in Java?

Java has no multiple class inheritance; you compose behavior through multiple interfaces (and, since Java 8, default methods). C++ allows it directly, which invites the diamond-problem follow-up.

Language internals (Java and C++)

4 questions

The part candidates skip. SAP asks what the runtime is doing underneath the syntax.

What are the JRE and the JVM, and how do they differ?

The JVM executes bytecode; the JRE is the JVM plus the standard libraries needed to run a program. The JDK adds the compiler and tools on top.

What does the final keyword do in Java?

A final variable cannot be reassigned, a final method cannot be overridden, and a final class cannot be extended. Expect all three cases, not just the variable one.

What are classes and objects?

A basic screen for whether you can define terms precisely under pressure; a class is the blueprint, an object is an instance with its own state.

Dry-run this snippet and tell me the output.

Output-based questions on inheritance, static binding, and constructor order appear in both the live round and the OA MCQs. Trace state carefully rather than guessing.

Exceptions and error handling

2 questions

A recurring stumble. Candidates who freeze here often say later it was the question that sank them.

How are exceptions implemented in Java versus C++?

Both unwind the stack to a matching handler. Java splits checked from unchecked exceptions and enforces checked ones at compile time; C++ has no checked-exception concept.

What is the difference between throw and throws in Java?

throw raises an exception instance at a point in code; throws is a method-signature clause declaring which checked exceptions the method may propagate.

Collections and iterators

2 questions

Where the Java library meets concurrency. Common once you have a year or two of experience.

What are fail-fast and fail-safe iterators?

Fail-fast iterators throw ConcurrentModificationException if the collection is structurally modified during iteration; fail-safe iterators work over a copy or snapshot and never throw, at the cost of possibly stale reads.

What happens when more than one thread acts on a list, and how do you make it thread-safe?

Plain ArrayList corrupts under concurrent writes. Reach for synchronization, Collections.synchronizedList, or a CopyOnWriteArrayList, and be able to say when each fits.

Concurrency and threads

3 questions

Weighted toward experienced loops. The coordination question below shows up almost verbatim.

Two threads must print numbers 1 through 10 in order, one handling odds and one handling evens. How do you coordinate them?

A synchronization question, not an algorithm one. Solved with a shared lock and a turn variable, or wait/notify. Worked end to end later in this lesson.

What is the difference between a semaphore and a mutex?

A mutex is ownership-based mutual exclusion for one thread at a time; a semaphore is a counter permitting up to N concurrent holders and needs no single owner.

What is ExecutorService and why use it over raw threads?

It pools and reuses threads and decouples task submission from execution, so you are not spawning a thread per task by hand.

The distinctions they make you defend

SAP interviewers rarely accept a one-word answer to these. They ask for the difference, then a case where you would use each. The three pairs below come up the most, so rehearse them until you can give the distinction and an example without pausing.

Overriding

  • Run-time polymorphism: the method that runs is chosen by the object's actual type when the call is made.
  • Same name and same signature as the parent method; the subclass replaces the inherited behavior.
  • Across a class hierarchy: a subclass overriding a superclass method. In C++ the base method must be virtual for this to dispatch dynamically.
  • Use it to specialize behavior polymorphically, one base type, many concrete implementations behind it.

Overloading

  • Compile-time polymorphism: the method is chosen from the argument types the compiler sees.
  • Same name, different parameter list (count, types, or order). Return type alone does not overload.
  • Within a single class (or an inherited set), several methods sharing a name for related operations.
  • Use it for convenience variants of one operation, print(int), print(String), print(double).

Interface

  • A pure contract: method signatures a class promises to implement (plus default and static methods since Java 8).
  • A class can implement many interfaces, which is how Java gets multiple inheritance of type.
  • No instance state; fields are implicitly public static final constants.
  • Reach for it when unrelated classes must share a capability, Comparable, Runnable, without sharing a lineage.

Abstract class

  • A partial implementation: it can mix abstract methods with concrete ones and hold real state.
  • A class can extend one abstract class, the single-inheritance rule still applies.
  • Can have constructors, instance fields, and any access modifier on its members.
  • Reach for it when related classes share code and state, a common base with some behavior filled in and some left abstract.

Fail-fast iterator

  • Iterates over the live collection and tracks a modification count.
  • Throws ConcurrentModificationException the moment the collection is structurally changed mid-iteration.
  • The default for ArrayList, HashMap, and most java.util collections.
  • Best effort, not a guarantee, so never rely on it as a correctness check; treat it as a bug detector.

Fail-safe iterator

  • Iterates over a copy or snapshot of the collection's state.
  • Never throws on concurrent modification; changes made during iteration simply are not seen.
  • Used by concurrent collections like CopyOnWriteArrayList and ConcurrentHashMap.
  • The trade-off is a possibly stale view and the memory cost of the snapshot.
Expect a logic puzzle, and do not let it rattle you. Alongside the technical questions, SAP interviewers sometimes drop a brain-teaser. One is measuring 30 minutes with two candles that each burn for an hour. Others ask for the angle between the hour and minute hands at a given time, the speed of the minute hand, or an AND or OR gate built from a single boolean parameter. They aren't universal, but they recur often enough to prepare for. Talk through your reasoning out loud; the interviewer is watching how you think.
A note for full-stack applicants. Reports split both ways. Some candidates applying to full-stack roles were blindsided by JavaScript, TypeScript, and React questions that were never on their resume; others prepared frontend and got asked only backend Java, design, and SQL. The center of gravity is backend, but if the posting says full-stack, do not walk in with zero frontend fundamentals.

Asked at SAP

These three are the coding questions this round leans on, and each tests something beyond an algorithm. The two design problems reward a clean class with a tidy API, exactly the "readable over clever" bar SAP states. The concurrency problem is the two-thread coordination question in the battery above, worked end to end.

1

LRU Cache: O(1) Get and Put

MediumFrequently asked
Problem
Design a data structure for a Least Recently Used (LRU) cache. It is initialized with a positive capacity and supports two operations, both of which must run in O(1) average time:

- get(key) returns the value for key if present, otherwise -1. A successful get counts as a use.
- put(key, value) inserts or updates the value for key. If the cache is at capacity, evict the least recently used key first.

Any get or put makes that key the most recently used. When an insert overflows the capacity, the key that has gone longest without a get or put is the one that leaves.

Input: capacity = 2; put(1,1), put(2,2), get(1), put(3,3), get(2), put(4,4), get(1), get(3), get(4)

Output: 1, -1, -1, 3, 4

put(3,3) evicts key 2 (least recently used), so get(2) is -1. put(4,4) evicts key 1, so get(1) is -1. Keys 3 and 4 remain.

How this uses the pattern

Combine a hash map for O(1) lookup with a doubly linked list for O(1) recency reordering. The map points key to node; the list keeps nodes in use-order from most to least recent.

LRU Cache: O(1) Get and Put

Loading...
Click "Run" to execute your code

Test Cases

Input: [["LRUCache",[2]],["put",[1,1]],["put",[2,2]],["get",[1]],["put",[3,3]],["get",[2]],["put",[4,4]],["get",[1]],["get",[3]],["get",[4]]]Expected: [null,null,null,1,null,-1,null,-1,3,4]
Input: [["LRUCache",[1]],["put",[2,1]],["get",[2]],["put",[3,2]],["get",[2]],["get",[3]]]Expected: [null,null,1,null,-1,2]
Input: [["LRUCache",[2]],["put",[1,10]],["put",[1,20]],["get",[1]],["put",[2,2]],["put",[3,3]],["get",[1]],["get",[2]]]Expected: [null,null,null,20,null,null,-1,2]
The SAP angle
This is the most-cited coding question on SAP loops, and it is a design question more than an algorithm one. The interviewer is watching whether you pick the right two structures and keep the class readable, more than whether you can invent something clever. Say the plan out loud first, "hash map for lookup, doubly linked list for ordering," then build it. That framing alone signals you have seen the problem's shape.
Take it further
A common extension is to make it thread-safe for concurrent callers, which ties straight into the concurrency questions this round loves. Guard every mutation with a single lock, or reach for a concurrent map plus finer-grained locking on the list, and be ready to discuss the contention trade-off between the two.
2

A Premium question

Medium
Problem
Design a stack that supports push, pop, and top like a normal stack, plus a getMin operation that returns the smallest element currently in the stack. Every one of the four operations must run in O(1).

You can assume pop, top, and getMin are only called on a non-empty stack.

Input: push(-2), push(0), push(-3), getMin(), pop(), top(), getMin()

Output: -3, 0, -2

getMin returns -3 (the smallest). After pop removes -3, top is 0 and getMin returns -2.

How this uses the pattern

The catch is O(1) getMin, so you cannot scan on each call. Track the running minimum alongside the values, so the current minimum is always available at the top.

A Premium question

Loading...
Click "Run" to execute your code

Test Cases

Input: [["MinStack",[]],["push",[-2]],["push",[0]],["push",[-3]],["getMin",[]],["pop",[]],["top",[]],["getMin",[]]]Expected: [null,null,null,null,-3,null,0,-2]
Input: [["MinStack",[]],["push",[5]],["push",[5]],["push",[3]],["getMin",[]],["pop",[]],["getMin",[]],["pop",[]],["getMin",[]]]Expected: [null,null,null,null,3,null,5,null,5]
Input: [["MinStack",[]],["push",[2]],["push",[1]],["push",[4]],["top",[]],["getMin",[]],["pop",[]],["top",[]],["getMin",[]]]Expected: [null,null,null,null,4,1,null,1,1]
The SAP angle
This came up in a Palo Alto onsite as a straight design question. It's a favorite because the naive answer (scan for the minimum) is O(n), and the interviewer is checking whether you see that. Store the minimum-so-far next to each value. State that idea before you code and the problem is half solved.
Take it further
A tidy variation keeps one extra stack that stores a min entry only when a new minimum appears, with a count of repeats. That saves space when the data has long runs above the current minimum. Be ready to reason about when that optimization is and is not worth the extra bookkeeping.
4

Priority Job Queue: Heap Plus an Id Index for Delete

MediumFrequently asked
Problem
Design JobQueue, which holds jobs waiting to run. Each job carries an integer jobId, unique among the jobs currently waiting, and an integer priority, where a larger number means the job runs sooner. Five operations:

- insert(jobId, priority) queues a job at that priority.
- remove(jobId) drops whichever job carries that id and returns true. When no waiting job carries it, nothing changes and the answer is false.
- top() returns the id of the highest-priority waiting job, or -1 while the queue is empty.
- list() returns every waiting id ordered from top priority down. Jobs tied on priority come back in insertion order.
- size() returns how many jobs are waiting.

Aim for O(log n) on insert and remove, and O(1) on top and size.

Input encoding. A test case is a list of operations. The first entry, ["JobQueue", []], constructs the queue, and each later entry names a method with its argument list. The expected output lists the return value of every call, using null for the constructor and for each insert.

Input: insert(7,5), insert(3,9), insert(11,1), top(), size(), list(), remove(3), top()

Output: 3, 3, [3, 7, 11], true, 7

Job 3 holds priority 9, the highest of the three, so it leads both the top call and the ordered list. Dropping it by id promotes job 7 at priority 5.

Input: insert(1,5), insert(2,5), insert(3,5), top(), remove(1), top()

Output: 1, true, 2

All three tie at priority 5, so arrival order decides. Job 1 leads, and once it leaves the queue job 2 takes over.

How this uses the pattern

A binary heap answers "who runs next" in constant time and reorders in O(log n), yet it has no way to find an arbitrary job. Carry a hash map from job id to that job's current index in the heap array, refresh it on every swap, and deletion becomes swap-with-last plus one sift.

Priority Job Queue: Heap Plus an Id Index for Delete

Loading...
Click "Run" to execute your code

Test Cases

Input: [["JobQueue",[]],["insert",[7,5]],["insert",[3,9]],["insert",[11,1]],["top",[]],["size",[]],["list",[]],["remove",[3]],["top",[]],["list",[]]]Expected: [null,null,null,null,3,3,[3,7,11],true,7,[7,11]]
Input: [["JobQueue",[]],["top",[]],["size",[]],["list",[]],["insert",[42,2]],["remove",[99]],["remove",[42]],["size",[]],["top",[]],["list",[]]]Expected: [null,-1,0,[],null,false,true,0,-1,[]]
Input: [["JobQueue",[]],["insert",[1,5]],["insert",[2,5]],["insert",[3,5]],["top",[]],["remove",[1]],["top",[]],["remove",[3]],["top",[]],["list",[]]]Expected: [null,null,null,null,1,true,2,true,2,[2]]
Input: [["JobQueue",[]],["insert",[10,1]],["insert",[20,8]],["insert",[30,3]],["insert",[40,7]],["insert",[50,2]],["insert",[60,6]],["insert",[70,4]],["top",[]],["remove",[20]],["top",[]],["remove",[70]],["list",[]],["size",[]],["remove",[10]],["top",[]],["list",[]]]Expected: [null,null,null,null,null,null,null,null,20,true,40,true,[40,60,30,50,10],5,true,40,[40,60,30,50]]
Input: [["JobQueue",[]],["insert",[5,0]],["insert",[6,-3]],["top",[]],["remove",[5]],["top",[]],["insert",[5,10]],["top",[]],["list",[]],["size",[]]]Expected: [null,null,null,5,true,6,null,5,[5,6],2]
Input: [["JobQueue",[]],["insert",[1,1]],["insert",[2,2]],["remove",[2]],["remove",[1]],["size",[]],["top",[]],["insert",[3,3]],["top",[]],["remove",[3]],["list",[]],["size",[]]]Expected: [null,null,null,true,true,0,-1,null,3,true,[],0]
The SAP angle
The same question has come up twice in SAP's online assessment, once on a 2020 backend track and once at a 2021 SDE sitting, with near-identical wording both times. Each version frames it as a first-week work assignment on a distributed system that runs jobs in priority sequence, every job carrying a unique identifier, and each lists the same five operations. Four of those five are a plain priority queue. The fifth, deleting a job by its identifier, defeats a textbook heap, which can reach its root and its last slot and nothing in between. Both versions also carry a second half, asking for the RESTful endpoint definitions and for a data-consistency answer.
Take it further
The endpoint half goes quickly once the class exists, because each method maps to one route. A reported shape of the answer looks like POST /jobs with the id and priority in the body, DELETE /jobs/{jobId}, GET /jobs/top, GET /jobs?sort=priority for the ordered list, and GET /jobs/count. Be ready to defend two of those choices: keying the delete on an identifier in the path, and giving the highest-priority read its own route. An SAP grader reads REST instinct alongside the algorithm.

Part b asks how the data stays consistent, which is answered at the system level. Jobs belong in a store that survives a restart, and writes for one queue belong behind a single owner so two schedulers cannot both hand out the same top job. A worker that claims a job takes a lease with a visibility timeout, which puts the job back when that worker dies mid-run. Raise idempotency on insert as well, since a client retrying a timed-out POST should not end up with the job queued twice, and a caller-supplied identifier makes that retry safe to accept.
5

Blocking Queue With Two Producers and Two Consumers: Why synchronized Is Not Enough

HardSometimes asked
Problem
A bounded buffer holds at most capacity items at a time. Four threads share it.

- Producer TRUE walks flags left to right and pushes one item for every 1 it finds. The item carries that flag's position and the tag true.
- Producer FALSE does the same for every 0, tagging its items false.
- Consumer TRUE takes only true items, Consumer FALSE only false ones. A consumer may take the item at the head of the buffer and nothing else, and it appends that item's position to its own log.

A push blocks while the buffer already holds capacity items. A take blocks while the buffer is empty or its head carries the other tag. Each consumer finishes once it has handled every item of its own tag, so no sentinel value is needed to shut it down.

Write run_blocking_queue(flags, capacity). Start all four threads, wait for them, and return [true_log, false_log]. Under correct coordination true_log holds the positions of the 1s in order and false_log the positions of the 0s. Spinning on the state without releasing the lock does not count as a solution.

Every entry of flags is 0 or 1, and capacity is at least 1.

Input: flags = [1,0,1,1,0,0,1], capacity = 2

Output: [[0, 2, 3, 6], [1, 4, 5]]

Positions 0, 2, 3 and 6 hold a 1, so the true consumer logs those in order, and the false consumer logs 1, 4 and 5. Two slots means a producer that runs ahead of its consumer has to wait for a take before it can push again.

How this uses the pattern

One lock guards the buffer, and three condition variables hang off that same lock. Producers park on the first until a slot opens. Each consumer gets its own, signalled only when the item at the head carries that consumer's tag. Every wait belongs inside a while loop.

Blocking Queue With Two Producers and Two Consumers: Why synchronized Is Not Enough

Loading...
Click "Run" to execute your code

Test Cases

Input: [[1,0,1,1,0,0,1],2]Expected: [[0,2,3,6],[1,4,5]]
Input: [[1,0],1]Expected: [[0],[1]]
Input: [[0,0,1,0],3]Expected: [[2],[0,1,3]]
Input: [[1,1,1,1,0],1]Expected: [[0,1,2,3],[4]]
Input: [[1,0,0,1],10]Expected: [[0,3],[1,2]]
Input: [[0,1,1,0,1,0,0,1,1,0,1,0],3]Expected: [[1,2,4,7,8,10],[0,3,5,6,9,11]]
The SAP angle
SAP's concurrency question on experienced Java loops goes well past the alternating-print warm-up. A reported second onsite round asked for a blocking queue fed by two producer threads emitting true and false, drained by two consumers where one takes only true and the other only false. The candidate got it working with a synchronized block, the interviewer stayed unsatisfied, and the follow-up ask was the same thing on explicit Locks. SAP's platform code is full of worker pools feeding typed handlers, and the round turns on wait/notifyAll over one monitor waking every parked thread, including the three that cannot act. Two Condition objects on a single ReentrantLock answer that.
Take it further
Expect the production answer next. ArrayBlockingQueue hands you the bounded buffer and both waits already written, and an ExecutorService gives you the four workers without touching Thread directly. A hiring-manager round tends to push that way once the hand-rolled version is on the board. The generalization also shows up, five threads printing one item each in a fixed cycle, which is the same lock with one Condition per position in the cycle. Have an answer ready for shutdown too, since a poison-pill sentinel is how you stop consumers that cannot count their own quota in advance.

Key Takeaways

  • ✓This round decides SAP loops, and it turns on fundamentals. Candidates have aced every coding question and still been rejected on OOP and language internals.
  • ✓Explain the whole battery in two sentences with an example, in both Java and C++. That means OOP pillars, virtual functions, overriding vs overloading, interface vs abstract class, throw vs throws, final, JRE vs JVM, and fail-fast vs fail-safe iterators.
  • ✓Design questions (LRU Cache, a Premium question) reward a clean class with the right structures: a hash map plus doubly linked list for LRU, values paired with their running minimum for a Premium question. State the plan before you code.
  • ✓Concurrency is a real surface once you have experience. The two-thread ordered-print question is solved with a shared lock and a turn variable, waiting in a loop and notifying on completion, never with a busy-wait or a file hack.
Had your SAP interview?Tell us what they asked. Reviewed before it goes in, never with your name.
SAP interview guide

Know what SAP asks before you walk in.

16 coding · 2 system design · 35 behavioral questions

Get Premium$49 a year · Cancel anytime
Or start freeNo card.

Get their questions first.

InterviewLegend is independent and not affiliated with or endorsed by any company it covers.