JP Morgan Real Time Interview
JP Morgan Real Time Interview
### 1. **`LinkedHashSet`**
This makes `LinkedHashSet` the ideal choice when you want to maintain the
uniqueness of the elements while also keeping track of the order in which they were
added.
```java
import java.util.LinkedHashSet;
// Adding elements
set.add("Apple");
set.add("Banana");
set.add("Orange");
set.add("Apple"); // Duplicate, won't be added
// Displaying elements
System.out.println("Elements in insertion order (unique): " + set);
}
}
```
#### Output:
```
Elements in insertion order (unique): [Apple, Banana, Orange]
```
### Explanation:
- The `LinkedHashSet` ensures that only **unique elements** are stored.
- It also preserves the **insertion order** — the order in which elements are added
is maintained.
A `HashSet` also ensures uniqueness, but it does **not guarantee** the order of
elements. The elements in a `HashSet` can be iterated in any order, which is not
what you want if you need to preserve insertion order.
### 4. **Alternatives:**
- If you don't need the order of insertion but only require uniqueness, you could
use `HashSet`.
- If you need both **insertion order** and **sorting** (not just uniqueness), you
could use `LinkedHashSet` combined with a custom sorting mechanism.
### Summary:
In this case, you override the `equals()` method but **do not override the
`hashCode()` method**. The default `hashCode()` method comes from the `Object`
class, which returns distinct hash codes for different object instances unless
explicitly overridden.
In a `HashMap`, the hash code is used to determine the "bucket" in which an object
is placed. If two objects have different hash codes, they will be placed in
different buckets, even if they are considered equal by the `equals()` method. This
can result in unexpected behavior where the `HashMap` might treat two objects as
**different keys** even though they are logically equal.
#### Example:
```java
import java.util.HashMap;
class Person {
private String name;
private int age;
#### Output:
```
Map size: 2
```
#### Explanation:
- Since the `hashCode()` method was not overridden, `person1` and `person2` end up
in **different buckets** because the default `hashCode()` from `Object` is used,
which typically returns distinct hash codes for different object instances (even if
`equals()` says the objects are equal).
- Therefore, even though `equals()` considers `person1` and `person2` to be equal,
the map stores them as **separate entries**, which is **not the expected
behavior**.
In this case, you override the `hashCode()` method but **do not override the
`equals()` method**.
#### Example:
```java
import java.util.HashMap;
class Person {
private String name;
private int age;
#### Output:
```
Map size: 2
```
#### Explanation:
- The `hashCode()` method is overridden, so both `person1` and `person2` end up in
the **same bucket** because their hash codes are the same (both have the same name
and age).
- However, the default `equals()` method compares object references, not the
content. Since `person1` and `person2` are **different objects** (even though they
have the same content), the `HashMap` treats them as **different keys** and stores
both of them as separate entries.
- The map will have two entries even though logically, they should be considered
the same.
This is the correct and recommended scenario when using objects as keys in a
`HashMap`. You override both `hashCode()` and `equals()` to ensure that logically
equal objects have the same hash code and are considered equal when compared.
#### Example:
```java
import java.util.HashMap;
class Person {
private String name;
private int age;
@Override
public int hashCode() {
return name.hashCode() + age;
}
}
#### Output:
```
Map size: 1
Value for person1: Person 2
```
#### Explanation:
- Both `hashCode()` and `equals()` are overridden properly, so both `person1` and
`person2` have the **same hash code** and are considered **equal** by the
`equals()` method.
- The `HashMap` will treat them as the **same key**, and since `person1` was
inserted first, `person2` will overwrite the value associated with `person1`.
- The map will have **only one entry** for the `Person` object with the value
`"Person 2"`.
#### Example:
```java
@Component
public class UserService {
@Autowired
private UserRepository userRepository; // Injected automatically
#### Explanation:
- `@Autowired` is applied to the `userRepository` field in the `UserService` class.
- Spring will look for a bean of type `UserRepository` in the application context
and inject it into this field when the `UserService` bean is created.
- The field can be either `private` or `public`, but it’s common to keep it
`private` and let Spring use reflection to set the value.
#### Example:
```java
@Component
public class UserService {
#### Explanation:
- The `UserService` class has a constructor that takes `UserRepository` as a
parameter.
- Spring will automatically inject an instance of `UserRepository` into the
constructor when creating the `UserService` bean.
- This method is recommended because it makes the dependencies explicit and makes
the class easier to test.
#### Example:
```java
@Component
public class UserService {
#### Explanation:
- The `@Autowired` annotation is applied to the setter method
`setUserRepository()`.
- When Spring creates the `UserService` bean, it will call this setter method to
inject the `UserRepository` bean.
When Spring sees the `@Autowired` annotation, it performs the following steps to
resolve and inject the appropriate dependency:
1. **Type Matching**: Spring will first try to match the type of the dependency
(e.g., `UserRepository`) with the available beans in the application context.
- If there is exactly one bean of the matching type, it will be injected.
- If there are multiple beans of the same type, Spring will throw an exception
unless you specify which one to inject using the `@Qualifier` annotation (we’ll
talk about this below).
2. **Qualifier Matching**: If there are multiple beans of the same type, you can
use `@Qualifier` to specify which bean to inject.
```java
@Autowired
@Qualifier("mysqlUserRepository") // Specify which bean to inject
private UserRepository userRepository;
```
```java
@Autowired(required = false)
private SomeService someService; // If no bean exists for SomeService, Spring
will not throw an error
```
Spring also supports injecting collections of beans. If there are multiple beans of
the same type, Spring can inject them into a `List`, `Set`, or other collection
type.
#### Example:
```java
@Component
public class NotificationService {
@Autowired
public NotificationService(List<NotificationSender> senders) {
this.senders = senders;
}
#### Explanation:
- If you have multiple `NotificationSender` beans (e.g., `EmailSender`,
`SmsSender`), Spring will inject all of them into the `senders` list, and you can
iterate over them to send notifications.
You can inject dependencies based on interface types, and Spring will automatically
inject the appropriate implementation based on the context. If there are multiple
implementations, you will need to use `@Qualifier` to specify which implementation
should be injected.
#### Example:
```java
@Component
public class UserService {
@Autowired
public UserService(@Qualifier("creditCardPaymentService") PaymentService
paymentService) {
this.paymentService = paymentService;
}
}
```
If you are using Java-based configuration (using `@Configuration`), you can also
use `@Autowired` to inject beans into methods.
#### Example:
```java
@Configuration
public class AppConfig {
@Bean
public UserRepository userRepository() {
return new UserRepositoryImpl();
}
@Bean
public UserService userService(UserRepository userRepository) {
return new UserService(userRepository);
}
}
```
If you have multiple beans of the same type and want to designate one as the
default (primary) bean to be injected, you can use the `@Primary` annotation.
#### Example:
```java
@Component
@Primary
public class DefaultPaymentService implements PaymentService {
// Implementation
}
@Component
public class CreditCardPaymentService implements PaymentService {
// Implementation
}
@Component
public class OrderService {
@Autowired
public OrderService(PaymentService paymentService) {
this.paymentService = paymentService; // The DefaultPaymentService will be
injected automatically
}
}
```
Without `@Primary`, if you have multiple beans of the same type, Spring would not
know which one to inject, and it would throw an exception. The `@Primary`
annotation provides a way to explicitly mark one of the beans as the **preferred
candidate** for injection when there is ambiguity. This allows you to resolve
conflicts without having to use `@Qualifier` in every injection point.
```java
public interface PaymentService {
void processPayment();
}
@Component
public class CreditCardPaymentService implements PaymentService {
@Override
public void processPayment() {
System.out.println("Processing payment with Credit Card");
}
}
@Component
public class PaypalPaymentService implements PaymentService {
@Override
public void processPayment() {
System.out.println("Processing payment with PayPal");
}
}
```
```java
@Component
public class OrderService {
@Autowired
public OrderService(PaymentService paymentService) {
this.paymentService = paymentService;
}
```java
@Component
@Primary
public class CreditCardPaymentService implements PaymentService {
@Override
public void processPayment() {
System.out.println("Processing payment with Credit Card");
}
}
```
```
org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying
bean of type 'com.example.PaymentService' available: expected single matching bean
but found 2: creditCardPaymentService,paypalPaymentService
```
```java
@Component
public class OrderService {
@Autowired
public OrderService(PaymentService paymentService) {
this.paymentService = paymentService;
}
#### Output:
```
Processing payment with Credit Card
```
While `@Primary` marks a **default** bean to be injected when there are multiple
candidates, `@Qualifier` is used to **explicitly specify** which bean to inject
when there is ambiguity.
- **`@Primary`** is used to define the "default" or "preferred" bean when there are
multiple candidates of the same type.
- **`@Qualifier`** is used to **explicitly name** the bean you want to inject when
multiple beans of the same type exist, regardless of whether they are marked as
`@Primary`.
If you have multiple beans of the same type and you want to inject one
specifically, you can use `@Qualifier`:
```java
@Component
public class OrderService {
@Autowired
public OrderService(@Qualifier("paypalPaymentService") PaymentService
paymentService) {
this.paymentService = paymentService;
}
### Summary:
- **`@Primary`**: Marks a bean as the default choice for injection when there are
multiple candidates of the same type in the Spring context.
- It helps **resolve ambiguity** without having to use `@Qualifier` at every
injection point.
- If you have multiple beans of the same type and want one to be the default for
injection, use `@Primary`.
- You can still use `@Qualifier` if you need to specify a particular bean to be
injected, regardless of `@Primary`.
========================================
Java manages memory through a combination of several key components, including the
**heap**, **stack**, **method area**, and **garbage collection**. These areas work
together to ensure efficient memory allocation, object management, and resource
cleanup. Here's a breakdown of how Java handles memory internally:
- The **Garbage Collector** (GC) focuses on reclaiming memory from the Young
Generation first and then cleans up the Old Generation periodically.
```java
public class MemoryExample {
private static String classVar = "I am static"; // Stored in method area
- When `obj` is created, a reference is placed in the stack, but the actual object
(`MemoryExample`) is created in the heap.
- The method `someMethod()` will have its own stack frame with the local variable
`localVar` and a reference to the `str` string.
- The string `"Hello, World!"` might be stored in the **String Pool** (a part of
the heap or metaspace).
- Once the method finishes executing, its stack frame is removed, and the object
reference is cleaned up when no longer in use, allowing garbage collection to
reclaim memory.
### Conclusion:
Java’s memory management is designed to efficiently handle the dynamic allocation
and cleanup of objects and variables, using the stack for method calls and local
variables, the heap for object storage, and the method area for class metadata. The
automatic garbage collection process in the heap ensures that memory is reclaimed
when objects are no longer in use, reducing the risk of memory leaks.
===========================
A **Singleton class** in Java ensures that only one instance of the class is
created during the lifetime of the application. The **Double-Checked Locking**
pattern is used to implement Singleton in a thread-safe and efficient manner. It
aims to reduce the overhead of synchronization after the Singleton instance has
been created, making it more efficient.
### Implementation:
```java
public class Singleton {
### Explanation:
1. **Volatile keyword**:
- The `volatile` keyword ensures that the value of `instance` is always read
from the main memory and not from the thread's local cache. This is crucial because
without `volatile`, one thread may see an incomplete object (due to optimizations
like instruction reordering).
3. **Synchronization**:
- The `synchronized (Singleton.class)` block ensures that only one thread can
enter this block at a time, thus preventing the creation of multiple instances in a
multi-threaded environment.
5. **Lazy Initialization**:
- The Singleton instance is lazily initialized, meaning the instance is created
only when it is needed (i.e., when `getInstance()` is called for the first time).
- **Step 1**: The first `if (instance == null)` check ensures that only one thread
creates the instance. If the instance is already created (i.e., not `null`), it
directly returns the instance without entering the synchronized block.
- **Step 2**: If the instance is `null`, the thread enters the synchronized block,
ensuring only one thread can create the Singleton instance.
- **Step 3**: Inside the synchronized block, the second `if (instance == null)`
check ensures that only one thread will initialize the instance even if multiple
threads were waiting to enter the synchronized block.
### Considerations:
```java
public class SingletonTest {
public static void main(String[] args) {
// Multiple threads trying to get the Singleton instance
Thread thread1 = new Thread(() -> {
Singleton s1 = Singleton.getInstance();
System.out.println(s1);
});
thread1.start();
thread2.start();
}
}
```
### Output:
```
Singleton@15db9742
Singleton@15db9742
```
In the output, both threads print the same instance of the `Singleton` class, which
shows that only one instance was created, even though multiple threads attempted to
access it simultaneously.
### Summary:
The **Double-Checked Locking** pattern ensures that a **Singleton** is created
lazily and is thread-safe without the performance overhead of synchronization on
every access. By using `volatile` and performing two checks (one outside the
synchronized block and one inside), this approach minimizes synchronization
overhead after the instance is created.
===========================
**Eager Loading** and **Lazy Loading** are two different strategies used to load
data or objects in an application, particularly in the context of databases,
object-relational mapping (ORM) frameworks like **Hibernate**, and **Java**
programming. They determine **when** related data or objects should be fetched —
either immediately (eager) or only when needed (lazy).
**Eager loading** refers to the strategy where related data or objects are fetched
**immediately** when the main object is loaded. In other words, all associated data
is retrieved upfront, even if it is not necessarily needed in the current context.
```java
@Entity
public class Author {
@Id
private Long id;
@OneToMany(fetch = FetchType.EAGER)
private Set<Book> books;
In the above example, the `Author` entity is loaded with all associated `Book`
objects immediately when it is retrieved from the database. The `fetch =
FetchType.EAGER` ensures that the related `books` are loaded eagerly.
#### Pros:
- **Simplicity**: You don't need to worry about lazy initialization or additional
database queries for related data later.
- **Good for data that will always be needed**: If you know that you need all
related data when you fetch the primary object, eager loading may be more
efficient.
#### Cons:
- **Performance Overhead**: Fetching related data upfront can be costly in terms of
performance, especially if the data set is large.
- **Memory Consumption**: It may unnecessarily load a lot of data into memory when
not all of it is required.
---
**Lazy loading** refers to a strategy where related data or objects are **not
fetched immediately**, but are loaded only when they are actually needed. With lazy
loading, the related data is fetched **on demand** or **lazily** when accessed for
the first time.
@OneToMany(fetch = FetchType.LAZY)
private Set<Book> books;
In this example, the `Book` entities will not be loaded when the `Author` is
loaded. The books will only be loaded when you explicitly access
`author.getBooks()` for the first time.
#### Pros:
- **Improved Performance**: Only the primary object is loaded initially, which can
be faster and use less memory.
- **Reduced Database Load**: Only the data that is actually needed is fetched,
which can reduce the number of queries and the amount of data transferred.
#### Cons:
- **Potential N+1 Query Problem**: If you access many related entities in a loop,
it may result in multiple queries to the database (one for the parent and one for
each child), leading to performance degradation.
- **LazyInitializationException**: If you try to access a lazily-loaded collection
outside of the session or transaction scope (e.g., after the session has been
closed), a `LazyInitializationException` can occur.
---
---
---
You can control the fetching strategy using annotations like `@OneToMany`,
`@ManyToOne`, `@ManyToMany`, etc., with the `fetch` attribute.
---
### Conclusion:
- **Eager Loading** is useful when you know you'll need the related data upfront,
but it can lead to performance and memory issues if overused.
- **Lazy Loading** is more efficient initially because it loads data only when
needed, but it can lead to excessive database queries or
`LazyInitializationException` if not managed properly.
Let's break this down and understand how to properly handle serialization and
deserialization for Singleton classes.
### Serialization and Deserialization of Singleton Class
#### Example:
Consider a simple `Singleton` class:
```java
import java.io.Serializable;
private Singleton() {
// private constructor to prevent instantiation
}
To maintain the Singleton property after deserialization, you need to override the
`readResolve` method. This method is called **immediately after** the
deserialization process, and it allows you to return the **existing instance** of
the Singleton rather than creating a new one.
#### Explanation:
- When an object is deserialized, Java invokes the `readObject` method to recreate
the object.
- By overriding `readResolve`, you can intercept the deserialization process and
return the **already existing instance** instead of creating a new instance.
- The `readResolve` method ensures that after deserialization, only one instance of
the `Singleton` class will exist, even if the object is deserialized multiple
times.
```java
import java.io.*;
private Singleton() {
// private constructor to prevent instantiation
}
### Output:
```java
Hello from Singleton!
Hello from Singleton!
Are both instances the same? true
```
### Conclusion:
- **Serialization and Deserialization** can break the Singleton pattern because
they create a new instance of the class.
- To maintain the **Singleton property** during **deserialization**, you need to
implement the **`readResolve`** method.
- By overriding `readResolve`, you ensure that after deserialization, the class
returns the existing instance, thus preserving the Singleton pattern.
This approach ensures that even after the object is serialized and deserialized,
only one instance of the Singleton class exists in the JVM.
===============================
When **`serialVersionUID`** does not match during **deserialization** in Java, it
leads to an **`InvalidClassException`**. This exception occurs because the
**`serialVersionUID`** is a version control mechanism for **Serializable** classes,
ensuring that the sender and receiver of a serialized object are compatible.
Let's break down what happens and why this issue occurs, and how to handle it.
Consider the following example where we serialize an object, modify the class, and
try to deserialize it.
```java
import java.io.*;
@Override
public String toString() {
return "Person{name='" + name + "', age=" + age + '}';
}
}
```
#### Serialization:
```java
import java.io.*;
Now, suppose we modify the `Person` class and change the `serialVersionUID` (or
even change the class in any way):
```java
import java.io.*;
@Override
public String toString() {
return "Person{name='" + name + "', age=" + age + ", address='" + address +
"'}";
}
}
```
#### Deserialization:
Now, let's try to deserialize the object we serialized earlier with the previous
`serialVersionUID`.
```java
import java.io.*;
2. **Backward Compatibility**:
- To maintain backward compatibility with older versions of a class after
changes, you can implement custom deserialization logic using **`readObject`** and
**`writeObject`** methods.
- For example, if you've added a new field, you can use custom deserialization
to ignore the field during deserialization and provide a default value.
If you want to handle changes gracefully, you can define custom deserialization
behavior.
```java
import java.io.*;
@Override
public String toString() {
return "Person{name='" + name + "', age=" + age + ", address='" + address +
"'}";
}
}
```
Now, even if the class has been modified, deserialization will handle missing or
incompatible fields gracefully without throwing an exception.
### Conclusion:
- **Mismatch in `serialVersionUID`** between serialized and deserialized classes
results in an **`InvalidClassException`**.
- **To avoid this**, ensure you **maintain compatibility** across class versions,
either by **changing the `serialVersionUID`** correctly when you break
compatibility, or by **customizing deserialization** if you need backward
compatibility.
- Always specify a `serialVersionUID` manually to have full control over versioning
and avoid potential serialization issues.
==============================
Yes, in Java, if you don't want a field to be serialized, you can use the
**`transient`** keyword. However, the **`static`** keyword does not achieve the
same effect, and it's important to understand the distinction between the two.
Let's dive into both of these keywords and how they affect serialization.
The **`transient`** keyword is used to mark fields of a class that should **not be
serialized** when the object is serialized. When you mark a field as `transient`,
the Java serialization mechanism will **ignore** this field during the
serialization process.
```java
import java.io.*;
@Override
public String toString() {
return "Employee{name='" + name + "', age=" + age + ", password='" +
password + "'}";
}
#### Output:
```
Deserialized Employee: Employee{name='John Doe', age=30, password='null'}
```
In this example:
- The `password` field is marked as `transient`, so **it will not be serialized**.
- During deserialization, the `password` field will have a value of `null` because
it wasn't serialized.
The **`static`** keyword, on the other hand, is used to define class-level fields
(i.e., fields that are shared among all instances of the class). **Static fields
are never serialized**, regardless of whether they are marked as `transient` or
not.
```java
import java.io.*;
public class Employee implements Serializable {
private String name;
private int age;
private static int employeeCount = 0; // Static field, will not be serialized
@Override
public String toString() {
return "Employee{name='" + name + "', age=" + age + ", employeeCount=" +
employeeCount + "}";
}
#### Output:
```
Deserialized Employee1: Employee{name='John Doe', age=30, employeeCount=0}
Deserialized Employee2: Employee{name='Jane Smith', age=25, employeeCount=0}
```
In this example:
- The `employeeCount` field is static and is therefore **not serialized**.
- Even though `employeeCount` was incremented before serialization, when the
objects are deserialized, the static field's value is reset to `0` because static
fields are **not part of the serialized object**.
1. **`transient` Fields**:
- Use `transient` to mark individual instance fields that should **not be
serialized**.
- Example: Sensitive data like passwords or session tokens that should not be
saved.
2. **`static` Fields**:
- Static fields are **not serialized** because they belong to the class, not an
instance of the class.
- Example: A field counting the number of instances (`employeeCount`) would not
be serialized.
3. **Best Practice**:
- If you need to prevent a field from being serialized (but it's not static),
mark it as `transient`.
- If a field is static, it will automatically be ignored during serialization,
so you don't need to mark it as `transient`.
4. **Default Serialization**:
- Fields that are **neither `static` nor `transient`** are included in the
serialization by default.
### Conclusion:
- Use **`transient`** for **instance** fields you don't want to serialize.
- **Static** fields are **never serialized**, so there's no need to use `transient`
with them. They are ignored by the serialization mechanism because they belong to
the class itself and are shared across all instances.
===================================
In the scenario you describe, where all the `Employee` objects (e.g., `e1`, `e2`,
`e3`, etc.) have the same `hashCode()` value (which in your case is always `1`),
the size of the `HashMap` will depend on how the `HashMap` handles collisions.
Let's walk through the details of how `HashMap` works and how it handles your case.
1. **Hashing**:
- When you add an entry to a `HashMap`, it calculates the hash of the key using
the `hashCode()` method.
- The `hashCode()` value is used to determine which "bucket" (internal storage
location) the key-value pair will be placed in.
- If multiple keys have the same `hashCode()` (like in your case where
`hashCode()` always returns `1`), these keys will be placed in the same bucket.
This is called a **collision**.
2. **Handling Collisions**:
- In case of a collision (i.e., multiple keys having the same hash code), the
`HashMap` typically uses a **linked list** or **tree** (from Java 8 onward) to
store the entries in the same bucket.
- If the number of entries in a bucket exceeds a threshold, the bucket might
switch from a linked list to a **balanced tree** to optimize lookups (this is known
as **treeification**).
- **HashCode**: Since you said that `hashCode()` always returns `1`, all 5
`Employee` objects will be placed in the same bucket.
- **The size of the `HashMap` will be 5**, as long as each `Employee` object is
distinct and their `equals()` method returns `false` for each comparison (i.e.,
they are not considered equal to each other).
### Example:
```java
import java.util.HashMap;
class Employee {
String name;
Employee(String name) {
this.name = name;
}
@Override
public int hashCode() {
return 1; // Always returns the same hash code
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
Employee employee = (Employee) obj;
return name.equals(employee.name); // Employees with different names are
considered different
}
}
### Output:
```
Size of HashMap: 5
```
### Explanation:
### Conclusion:
```java
import java.util.Arrays;
### Explanation:
### Output:
```text
[5, 10, 15, 20, 25, 30, 35, 40, 45, 50]
```
This code uses Java 8's **Stream API** to process the array elements and transform
them in a functional style.
===========================================
In Java, if a method in the **child class** overrides a method in the **parent
class**, and the overridden method in the child class throws a
`NullPointerException`, the code will still **compile**. However, the behavior of
the program will depend on the method's signature in both the parent and child
classes.
1. **Method Overriding**:
- When a method in a child class overrides a method in the parent class, the
method signature (name, return type, and parameters) must remain the same.
```java
class Parent {
public void someMethod() {
System.out.println("Parent method");
}
}
### Explanation:
- In this example, the `someMethod()` in the `Child` class overrides the method in
the `Parent` class.
- The `Child`'s version of `someMethod()` explicitly throws a
`NullPointerException`, which is an unchecked exception (subclass of
`RuntimeException`).
- Since `NullPointerException` is unchecked, the compiler does not require it to be
declared in the method signature.
- The program will compile successfully, but at runtime, a `NullPointerException`
will be thrown when `someMethod()` is called.
### Conclusion:
- **Yes**, the code will compile if the child class method throws a
`NullPointerException` or any other unchecked exception, even though the parent
class method doesn't declare any exceptions.
- **No** exception handling or declaration is required for unchecked exceptions
like `NullPointerException`.
========================================
In the given code, you have two `show` methods: one that takes an `Integer` as a
parameter and another that takes a `String`. You're calling `p.show(null);` with
`null` as the argument.
2. **Ambiguity**:
- Since `null` can be passed to both `Integer` and `String`, there is an
**ambiguity** in choosing which overloaded method to invoke. The compiler cannot
decide between the two options, and this results in a **compilation error**.
```java
package com.nareshtechhub15;
- When you invoke `p.show(null);`, the compiler needs to decide whether to call
`show(Integer i)` or `show(String s)`.
- Since `null` can be assigned to both an `Integer` and a `String`, **the compiler
faces ambiguity**.
- This ambiguity will lead to a **compilation error**.
```
reference to show is ambiguous
both method show(Integer) in PassingNullCheck and method show(String) in
PassingNullCheck match
```
### Conclusion:
When you call `p.show(null);` with overloaded methods where both `Integer` and
`String` could accept `null`, it causes a **compilation error due to ambiguity**.
Java cannot determine which method to call.
```java
p.show((Integer) null); // Calls show(Integer i)
p.show((String) null); // Calls show(String s)
```
2. Change the method signatures to avoid the ambiguity, e.g., using different types
of parameters or logic to handle `null` in one method.
====================================
In Java, **wrapper objects** are used to represent primitive data types as objects.
They are part of the **java.lang** package and provide methods to manipulate the
values of primitive types.
- `Byte`
- `Short`
- `Integer`
- `Long`
- `Float`
- `Double`
- `Character`
- `Boolean`
When you declare a wrapper class object but do not explicitly initialize it, it
will have a default value of `null`. This is because wrapper classes are reference
types, and like any other reference type, they are initialized to `null` by
default.
#### Example:
```java
public class WrapperDefaultValues {
public static void main(String[] args) {
// Declaring wrapper objects without initialization
Integer intObj;
Double doubleObj;
Boolean boolObj;
Character charObj;
So, primitive types are initialized to default values, but **wrapper objects**
(like `Integer`, `Boolean`, etc.) are initialized to `null`.
3. **Autoboxing**:
- When you assign a primitive value to a wrapper object, Java automatically
converts the primitive to its corresponding wrapper class (this is known as
**autoboxing**). For example, assigning `5` to an `Integer` object would be done
automatically like this:
```java
Integer integerValue = 5; // Autoboxing: converts 5 (int) to Integer
```
- This conversion only occurs when you explicitly assign a value to a wrapper
object.
4. **Unboxing**:
- If you use a wrapper object and try to assign it to a primitive, the wrapper
object will automatically be converted back to the primitive type (this is called
**unboxing**). For example:
```java
Integer integerValue = 10;
int primitiveValue = integerValue; // Unboxing: converts Integer to int
```
```java
public class WrapperExample {
public static void main(String[] args) {
Integer intValue = null; // wrapper object initialized to null
int primitiveInt = 10; // primitive type initialized to 10
if (intValue == null) {
System.out.println("intValue is null");
}
### Conclusion:
- **Wrapper objects** (e.g., `Integer`, `Double`, `Boolean`) are initialized to
`null` by default when declared as instance variables.
- **Primitive types**, on the other hand, have default values (e.g., `0` for `int`,
`false` for `boolean`).
==========================================
Yes, **aggregation** and **composition** are related in terms of both being types
of **association** in object-oriented programming. Both represent **"has-a"**
relationships between classes, where one class contains or is associated with
another. However, they differ in terms of the **strength of the relationship** and
the **lifetime dependency** between the objects involved.
class University {
private List<Department> departments; // Aggregation: University has
Departments
class House {
private List<Room> rooms; // Composition: House has Rooms
Thus, the choice between aggregation and composition depends on the **nature of the
relationship** and how tightly coupled the objects should be. If you want a loose
relationship (where the contained objects can exist on their own), aggregation is
more appropriate. If you want a strong, tightly coupled relationship (where the
contained objects cannot exist without the container), composition is the better
choice.
=================================
To make the given code more concise and compact while maintaining readability, we
can leverage **Java 8 features** such as **Streams** and **Method References**.
Additionally, we can simplify the logic, remove unnecessary checks, and use lambda
expressions to streamline the process.
```java
import java.util.Arrays;
### Explanation:
- **`Arrays.stream(input.split(","))`**: Converts the comma-separated string into a
stream.
- **`map(String::trim)`**: Trims any spaces around the numbers.
- **`mapToInt(Integer::parseInt)`**: Converts the string elements into integers.
- **`sorted()`**: Sorts the integers in ascending order.
- **`noneMatch(i -> i!=expected)`**: Ensures that no element breaks the
consecutiveness condition.
================================
**Spring Boot Profiling** is a way to manage different configurations and behaviors
in your Spring Boot application based on specific environments. It allows you to
define **profiles** that can be activated for different deployment environments
(e.g., development, testing, production) to customize the application’s behavior or
configuration for each environment.
Spring Boot provides support for multiple profiles, allowing you to customize and
switch between configurations without changing your main application code.
- **Command-line arguments** or
- **Properties files** (such as `application.properties` or `application.yml`)
1. **Activating Profiles**
- **In `application.properties`:**
```properties
spring.profiles.active=dev
```
- **In `application.yml`:**
```yaml
spring:
profiles:
active: dev
```
You can pass a profile as a parameter when starting your Spring Boot
application:
```bash
java -jar your-application.jar --spring.profiles.active=dev
```
```java
@Configuration
@Profile("dev")
public class DevConfig {
@Bean
public DataSource dataSource() {
// Dev database configuration
return new H2DataSource();
}
}
```
```java
@Configuration
public class AppConfig {
@Bean
@Profile("prod")
public DataSource prodDataSource() {
return new MySQLDataSource();
}
@Bean
@Profile("dev")
public DataSource devDataSource() {
return new H2DataSource();
}
}
```
In the example above, the `prodDataSource` bean will be used when the `prod`
profile is active, and the `devDataSource` will be used when the `dev` profile is
active.
- **In `application.properties`:**
```properties
# Default properties
server.port=8080
# Properties specific to 'dev' profile
spring.profiles.active=dev
- **In `application.yml`:**
```yaml
spring:
profiles:
active: dev
---
spring:
profiles: dev
datasource:
url: jdbc:h2:mem:devdb
username: devuser
password: devpassword
---
spring:
profiles: prod
datasource:
url: jdbc:mysql://prod-db-server:3306/proddb
username: produser
password: prodpassword
```
This allows you to define multiple configurations in a single file, but only the
configuration for the active profile will be used.
4. **Default Profile**
If no active profile is set, Spring Boot will use the **default profile**. You
can set default configurations in your `application.properties` or
`application.yml` files without any profile-specific annotations.
```properties
spring.datasource.url=jdbc:h2:mem:defaultdb
```
Let’s say you want to configure different **data sources** for `dev` and `prod`
environments.
#### Step 1: Define Profile-Specific Configurations
```java
@Configuration
public class DataSourceConfig {
@Bean
@Profile("dev")
public DataSource devDataSource() {
// Dev DataSource configuration (e.g., H2)
return new H2DataSource();
}
@Bean
@Profile("prod")
public DataSource prodDataSource() {
// Prod DataSource configuration (e.g., MySQL)
return new MySQLDataSource();
}
}
```
- **For `application-dev.properties`:**
```properties
spring.datasource.url=jdbc:h2:mem:devdb
spring.datasource.username=devuser
spring.datasource.password=devpassword
```
- **For `application-prod.properties`:**
```properties
spring.datasource.url=jdbc:mysql://prod-db-server:3306/proddb
spring.datasource.username=produser
spring.datasource.password=prodpassword
```
- **In `application.properties`:**
```properties
spring.profiles.active=dev
```
```bash
java -jar myapp.jar --spring.profiles.active=prod
```
### Conclusion
```java
int compareTo(T o);
```
Let’s say you have a `Person` class, and you want to be able to compare persons by
their age in ascending order.
```java
public class Person implements Comparable<Person> {
private String name;
private int age;
// Constructor
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return name + " (" + age + ")";
}
}
```
- In this example, the `compareTo` method compares two `Person` objects based on
their `age`.
- **`Integer.compare(this.age, other.age)`** returns:
- A negative integer if `this.age` is less than `other.age`.
- Zero if they are equal.
- A positive integer if `this.age` is greater than `other.age`.
```java
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
```
Before sorting:
Alice (30)
Bob (25)
Charlie (35)
### Explanation:
- The `compareTo` method in the `Person` class defines the natural ordering (by
`age` in this case).
- The `Collections.sort(people)` method uses `compareTo` to sort the list of
`Person` objects.
- The output shows the list sorted in ascending order of age, as expected.
If you want to sort by multiple fields (e.g., first by `age`, then by `name` if
ages are equal), you can extend the `compareTo` method to handle the secondary
comparison.
```java
@Override
public int compareTo(Person other) {
// First compare by age
int ageComparison = Integer.compare(this.age, other.age);
if (ageComparison != 0) {
return ageComparison;
}
// If ages are the same, compare by name
return this.name.compareTo(other.name);
}
```
If you want to sort in **descending** order, you can reverse the comparison:
```java
@Override
public int compareTo(Person other) {
// Reverse order for descending
return Integer.compare(other.age, this.age); // Compare in reverse
}
```
The `Comparable` interface can be used with other types of objects too, not just
custom objects.
```java
String str1 = "apple";
String str2 = "banana";
### Conclusion
- **`Comparable`** allows you to define a natural order for your objects, enabling
sorting of collections like `List`, `Set`, etc.
- Implement the `compareTo` method to specify how two objects should be compared.
- **`Collections.sort()`** uses `compareTo` to sort the collection.
@Override
public int compareTo(MyClass other) {
// Compare the current object (this) with the specified object (other)
if (this.value < other.value) {
return -1;
} else if (this.value > other.value) {
return 1;
} else {
return 0;
}
}
System.out.println(obj1.compareTo(obj2));
System.out.println(obj2.compareTo(obj1));
System.out.println(obj1.compareTo(obj1));
}
}
===========================
***************VMWare RealTimeInterview***************
==========================================================