← Back to quizzesFree quiz

Java Memory, Concurrency, and Design Patterns

When you write Student s1 = new Student("Ali"); the Java Virtual Machine (JVM) performs two distinct actions:

10 questions~5 min
Java Memory, Concurrency, and Design Patterns — Qwi
0 / 10
Score: 0%
1

When a Java program creates a new object with the expression `Student s1 = new Student("Ali");`, which memory area is primarily affected?

2

In a heap stress test that continuously allocates `int[10000]` arrays until an OutOfMemoryError occurs, what primary JVM setting determines the point at which the test stops?

3

Which of the following statements correctly describes the difference between a shallow copy and a deep copy of an object in Java?

4

Consider the following DAO method signature: `List getCarsByBrand(String brand);`. Which design principle does this method exemplify by returning a collection of domain objects rather than raw database rows?

5

In a multithreaded Java program, two threads concurrently execute `deposit(50)` on the same account object without synchronization. Which problem is most likely to occur?

6

When using the Builder pattern to create a `Car` object, which of the following is a key advantage over telescoping constructors?

7

Which of the following correctly explains why the `transient` modifier is used on a field in a serializable class?

8

In Java, what is the effect of overloading a method with a parameter type of `int` and another with `double` when calling the method with a `short` argument?

9

During a stack stress test that uses deep recursion, which JVM option directly controls the maximum stack depth before a StackOverflowError occurs?

10

Given the classes `Vehicle` and `BMW extends Vehicle` with an overridden `getTopSpeed()` method, what will be printed by `service.drive(v2);` where `Vehicle v2 = new BMW();`?

Understanding Java Memory: Stack vs Heap

When you write Student s1 = new Student("Ali"); the Java Virtual Machine (JVM) performs two distinct actions:

  • Stack allocation: A reference variable s1 is placed on the current thread’s stack. The stack holds primitive values and references to objects.
  • Heap allocation: The new Student("Ali") expression creates an actual Student instance on the heap, where all objects live for the duration of the program (or until they become unreachable).

The heap is the primary memory area affected because the object’s fields, including the String "Ali", are stored there. The stack only grows by the size of the reference, which is typically a few bytes.

Key takeaway: In Java, object data lives on the heap, while references live on the stack.

JVM Heap Size and OutOfMemoryError

During a heap stress test that repeatedly allocates int[10000] arrays, the test will stop when the JVM can no longer allocate memory for a new object. The setting that controls this limit is the -Xmx flag, which defines the maximum heap size.

Why -Xmx matters:

  • It caps the amount of memory the heap can grow to.
  • When the allocation request exceeds this cap, the JVM throws java.lang.OutOfMemoryError: Java heap space.
  • Adjusting -Xmx directly changes when the stress test will terminate.

How to remember: Think of the heap as a water tank; -Xmx is the tank’s top edge. When the water (objects) reaches that edge, it overflows (OutOfMemoryError).

Shallow Copy vs. Deep Copy

Copying objects in Java can be confusing. The distinction lies in how referenced objects are handled:

  • Shallow copy: Creates a new object instance, but copies the field values as references. If the original object contains references to other objects, both the original and the copy point to the same nested objects.
  • Deep copy: Recursively copies every reachable object, producing a completely independent graph of objects.

For example, consider a Student object that holds a reference to an Address object. A shallow copy of Student will share the same Address instance, while a deep copy will create a new Address with the same field values.

Practical tip: Use deep copy when you need full isolation between the original and the copy, such as in multithreaded environments or when caching objects.

DAO Pattern and Abstraction of Persistence

The method signature List<Car> getCarsByBrand(String brand) exemplifies a core principle of the Data Access Object (DAO) pattern: abstraction of persistence details. By returning a collection of domain objects (Car) instead of raw database rows, the DAO hides SQL, connection handling, and result‑set mapping from the rest of the application.

This abstraction provides several benefits:

  • Separation of concerns: Business logic works with plain Java objects, not with JDBC code.
  • Testability: DAOs can be mocked or stubbed, allowing unit tests to focus on business rules.
  • Maintainability: Changes to the database schema affect only the DAO implementation, not the callers.

Remember the DAO acronym: Data Access Object – it isolates data access.

Concurrency Pitfalls: Race Conditions

When two threads invoke deposit(50) on the same account object without any synchronization, the most likely problem is a race condition. Both threads read the current balance, add 50, and write the result back. If the reads and writes interleave, the final balance may reflect only one of the deposits.

Typical symptoms of a race condition include:

  • Incorrect totals that vary between runs.
  • Hard‑to‑reproduce bugs that appear only under high contention.

To prevent race conditions, you can:

  • Synchronize the method or critical section using the synchronized keyword.
  • Use explicit locks from java.util.concurrent.locks.
  • Employ atomic classes such as AtomicInteger for simple numeric updates.

Builder Pattern vs. Telescoping Constructors

Creating a Car object with many optional parameters can lead to a “telescoping constructor” problem—multiple constructors with increasing numbers of arguments, which become unreadable and error‑prone.

The Builder pattern solves this by providing a fluent API that:

  • Allows you to set only the desired properties.
  • Produces an immutable object once build() is called, eliminating the need for setters.
  • Improves code readability: Car car = new Car.Builder().make("Toyota").model("Corolla").year(2023).build();

Thus, the key advantage is the ability to create immutable objects without the explosion of constructor overloads.

Transient Fields in Serialization

The transient keyword tells the Java serialization mechanism to skip a field when converting an object to a byte stream. This is useful for:

  • Sensitive data such as passwords.
  • Derived fields that can be recomputed after deserialization.
  • Resources that are not serializable (e.g., FileInputStream).

When a class implements java.io.Serializable, any field marked transient will be restored to its default value (null, 0, false) during deserialization.

Method Overloading and Primitive Widening

Consider two overloaded methods:

void process(int value) { ... }
void process(double value) { ... }

If you call process(shortVar), the compiler chooses the int version. This is because short can be widened to int directly, while converting to double would require an additional conversion step. Java prefers the most specific applicable method, and int is more specific than double for a short argument.

Remember: Primitive widening follows the order byte → short → int → long → float → double. The first method that matches this chain is selected.

Summary of Key Concepts

  • Memory Management: Objects live on the heap; references live on the stack.
  • Heap Limits: -Xmx defines the maximum heap size; exceeding it throws OutOfMemoryError.
  • Copying Objects: Shallow copy copies references; deep copy clones the entire object graph.
  • DAO Abstraction: Returns domain objects, hiding persistence details.
  • Concurrency: Unsynchronized access leads to race conditions; use synchronization or atomic classes.
  • Builder Pattern: Creates readable, immutable objects without telescoping constructors.
  • Transient Modifier: Excludes fields from default serialization.
  • Method Overloading: Java selects the most specific method based on primitive widening rules.

Mastering these topics will strengthen your Java programming foundation, improve code quality, and help you design robust, maintainable applications.