Java - realestateexplorer/relatedToCh GitHub Wiki

create Java interview questions and answers


Of course! Here is a comprehensive list of Java interview questions and answers, categorized by experience level and topic.

How to Use This Guide

  • Junior (0-2 years): Focus on Core Java Fundamentals, OOP Concepts, and basic Collections.
  • Mid-Level (2-5 years): Master everything a Junior knows, plus Java 8+, Concurrency, basic Spring/JPA, and Design Patterns.
  • Senior (5+ years): Know all of the above in depth, plus JVM internals, advanced Spring, system design, and performance tuning.

1. Core Java Fundamentals (Junior to Mid)

Q1: What is the difference between final, finally, and finalize()?

Answer: This is a classic question to check fundamental knowledge.

  • final: A keyword used to apply restrictions.

    • final variable: Its value cannot be changed (it's a constant).
    • final method: It cannot be overridden by a subclass.
    • final class: It cannot be extended (inherited from).
  • finally: A block used in exception handling.

    • The finally block is always executed, whether an exception is thrown or not. It's used for cleanup code, like closing database connections or file streams.
  • finalize(): A method from the Object class.

    • It is called by the Garbage Collector just before an object is garbage collected. Its use is highly discouraged as it's not guaranteed to be called. Modern alternatives like try-with-resources or AutoCloseable are far better for resource management.

Q2: Explain the difference between == and .equals().

Answer:

  • == operator: It's a reference comparison operator. It checks if two object references point to the exact same memory location. For primitive types (like int, char), it compares their values.

  • .equals() method: It's a method for content comparison. It evaluates whether two objects are "meaningfully" equal. The default implementation in the Object class behaves exactly like ==. However, classes like String, Integer, etc., override it to compare the actual content of the objects.

Key Follow-up: If you override .equals(), why must you also override hashCode()?

  • Because of the contract between them. If two objects are equal according to .equals(), they must have the same hash code. If you don't override hashCode(), collections like HashMap or HashSet will not work correctly, as they rely on hash codes to store and retrieve objects efficiently.

Q3: Why is the String class immutable in Java?

Answer: Immutability means that once a String object is created, its value cannot be changed. This provides several key benefits:

  1. Thread Safety: Since strings are immutable, they can be safely shared between multiple threads without the need for synchronization.
  2. Security: System parameters, like database connection URLs or file paths, are often passed as Strings. Immutability prevents malicious code from changing their values.
  3. Caching (String Pool): Java maintains a "String Pool" in the heap. When you create a String literal (e.g., String s1 = "Java"), the JVM checks the pool. If the string already exists, it returns a reference to the existing object instead of creating a new one. This saves memory.
  4. Performance: Because the hash code of a String is cached at creation time, it doesn't need to be re-calculated every time it's used as a key in a HashMap.

Follow-up: What if you need a mutable string?

  • Use StringBuilder (not thread-safe, but faster) or StringBuffer (thread-safe, slower).

2. Object-Oriented Programming (OOP) (Junior to Mid)

Q4: What are the four main principles of OOP? Explain them briefly.

Answer:

  1. Encapsulation: The practice of bundling data (attributes) and the methods that operate on that data into a single unit (a class). It hides the internal state of an object from the outside. This is achieved using private access modifiers and providing public getter and setter methods.
  2. Abstraction: Hiding complex implementation details and showing only the essential features of the object. This is achieved using abstract classes and interfaces. For example, you know how to use a remote control without knowing how its internal circuits work.
  3. Inheritance: A mechanism where a new class (subclass/child) inherits attributes and methods from an existing class (superclass/parent). This promotes code reuse. It is represented by the extends keyword.
  4. Polymorphism: The ability of an object to take on many forms. The most common use is when a parent class reference is used to refer to a child class object. It allows a single action to be performed in different ways.
    • Method Overriding (Runtime Polymorphism): A subclass provides a specific implementation for a method that is already defined in its parent class.
    • Method Overloading (Compile-time Polymorphism): A class has multiple methods with the same name but different parameters (number, type, or order).

Q5: What is the difference between an abstract class and an interface?

Answer:

Feature Abstract Class Interface
Multiple Inheritance No, a class can only extend one abstract class. Yes, a class can implement multiple interfaces.
Constructors Can have constructors (called during subclass instantiation). Cannot have constructors.
Member Variables Can have instance variables (final, non-final, static, non-static). Can only have public static final variables (constants).
Methods Can have both abstract and non-abstract (concrete) methods. Before Java 8: Only abstract methods. Java 8+: Can have default and static methods with implementations. Java 9+: Can have private methods.
Purpose To provide a common base with some shared implementation for a group of related classes. (is-a relationship). To define a contract that a class must adhere to (can-do relationship).
Keyword abstract, extends interface, implements

When to use which?

  • Use an abstract class when you want to share code among several closely related classes.
  • Use an interface when you want to define a role that other, possibly unrelated, classes can play.

3. Java Collections Framework (Junior to Mid)

Q6: Explain the difference between ArrayList and LinkedList.

Answer:

  • ArrayList:

    • Underlying Structure: It's backed by a dynamic array.
    • Performance:
      • Fast for random access (get(index) operation is O(1)).
      • Slow for adding or removing elements from the middle of the list, as it requires shifting all subsequent elements (O(n)).
  • LinkedList:

    • Underlying Structure: It's backed by a doubly-linked list (each element holds a reference to the previous and next element).
    • Performance:
      • Slow for random access, as it needs to traverse the list from the beginning or end (O(n)).
      • Fast for adding or removing elements from the beginning, end, or middle, as it only involves updating a few references (O(1) if you have an iterator at the position).

When to use which?

  • Use ArrayList when you have a read-heavy workload (frequent random access).
  • Use LinkedList when you have a write-heavy workload (frequent insertions and deletions in the middle).

Q7: How does a HashMap work internally?

Answer: A HashMap stores key-value pairs. Internally, it uses an array of "buckets" or "bins."

  1. put(key, value):

    • The hashCode() of the key is calculated.
    • This hash code is used to determine the index of the bucket in the underlying array (usually index = hashCode() & (n-1), where n is the array size).
    • If the bucket is empty: A new Node object (containing the key, value, and hash) is placed in the bucket.
    • If the bucket is not empty (a "hash collision"): The new entry is added to the existing structure in that bucket.
      • Before Java 8: The bucket was a LinkedList. New entries were appended to the list. This could lead to O(n) performance in the worst case.
      • In Java 8+: If the number of entries in a bucket exceeds a certain threshold (TREEIFY_THRESHOLD), the LinkedList is converted into a balanced red-black tree. This improves the worst-case performance from O(n) to O(log n).
  2. get(key):

    • The hashCode() of the key is calculated to find the bucket index.
    • The HashMap then iterates through the entries in that bucket (either a list or a tree) and uses the equals() method to find the exact key.

4. Java 8+ Features (Mid to Senior)

Q8: What are Lambda Expressions and Functional Interfaces?

Answer:

  • Functional Interface: An interface that contains exactly one abstract method. The @FunctionalInterface annotation is used to enforce this rule at compile time. Examples include Runnable, Callable, and Predicate.

  • Lambda Expression: An anonymous function (a function without a name) that can be used to provide an implementation for the abstract method of a functional interface. It allows for writing more concise and readable code.

Syntax: (parameters) -> { body }

Example: Instead of writing this:

Runnable r = new Runnable() {
    @Override
    public void run() {
        System.out.println("My thread is running");
    }
};

You can write this with a lambda:

Runnable r = () -> System.out.println("My thread is running");

Q9: Explain the Java Stream API. What are intermediate and terminal operations?

Answer: The Stream API, introduced in Java 8, is used to process sequences of elements (like collections) in a declarative way. A stream is not a data structure; it takes input from a collection, array, or I/O channel.

  • Intermediate Operations: These operations transform a stream into another stream. They are always lazy, meaning they don't get executed until a terminal operation is invoked.

    • Examples: map (transforms each element), filter (selects elements based on a condition), sorted, distinct.
  • Terminal Operations: These operations produce a result or a side-effect. After a terminal operation is performed, the stream pipeline is considered consumed and cannot be used again.

    • Examples: forEach (performs an action for each element), collect (returns a result in a collection), reduce (combines elements into a single result), count.

Example:

List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "Anna");

List<String> filteredAndUppercased = names.stream() // 1. Create stream
    .filter(name -> name.startsWith("A"))        // 2. Intermediate op (lazy)
    .map(String::toUpperCase)                    // 3. Intermediate op (lazy)
    .collect(Collectors.toList());               // 4. Terminal op (executes 1,2,3)

System.out.println(filteredAndUppercased); // Output: [ALICE, ANNA]

5. Concurrency & JVM (Mid to Senior)

Q10: What is the difference between volatile and synchronized?

Answer:

Feature volatile synchronized
Purpose Guarantees visibility of changes to a variable across threads. Guarantees both visibility and atomicity (mutual exclusion).
Locking Does not use locks. It is non-blocking. Acquires and releases a lock (monitor). It is blocking.
Scope Can only be applied to variables. Can be applied to methods or blocks of code.
Performance Generally faster as it doesn't involve locking overhead. Slower due to the cost of acquiring and releasing locks.
Use Case Used for simple flags or status variables where atomicity is not required. Used to protect compound actions (e.g., read-modify-write operations) that must be executed as a single, atomic unit.

In short, use volatile when you need to ensure one thread sees the most up-to-date value of a variable written by another thread. Use synchronized when you need to protect a block of code that must not be executed by multiple threads at the same time.


Q11: Explain the main memory areas of the JVM.

Answer: The JVM runtime data area is divided into several logical parts:

  1. Heap:

    • This is where all class instances (objects) and arrays are allocated at runtime.
    • The heap is shared among all threads.
    • It's managed by the Garbage Collector (GC). It is often divided into "Young Generation" and "Old Generation".
  2. Stack:

    • Each thread has its own private JVM stack, created at the same time as the thread.
    • It stores "stack frames." A new frame is created for each method invocation.
    • A frame contains local variables, operand stacks, and a reference to the runtime constant pool of its class.
    • Memory in the stack is automatically managed and deallocated when a method completes.
  3. Method Area (Metaspace since Java 8):

    • This area is shared among all threads.
    • It stores per-class structures, such as the runtime constant pool, field and method data, and the code for methods and constructors.
    • Prior to Java 8, this was a fixed-size area called the "PermGen," which often caused OutOfMemoryError: PermGen space. Now, Metaspace can grow dynamically.
  4. PC (Program Counter) Registers:

    • Each thread has its own PC register.
    • It contains the address of the JVM instruction currently being executed.

6. Spring / Spring Boot (Mid to Senior)

Q12: What is Dependency Injection (DI) and what are its benefits?

Answer: Dependency Injection (DI) is a design pattern where an object's dependencies (i.e., the other objects it needs to work with) are "injected" into it by an external entity (the DI container or framework) rather than being created by the object itself. It's a form of Inversion of Control (IoC).

Types of DI:

  1. Constructor Injection: Dependencies are passed as arguments to the constructor. This is the recommended approach as it ensures an object is created in a valid state.
  2. Setter Injection: The container calls setter methods on the object after it has been instantiated.
  3. Field Injection (e.g., using @Autowired): Dependencies are injected directly into the fields. It's convenient but makes testing harder and is generally discouraged.

Benefits:

  • Loose Coupling: Components are not tightly bound. You can easily swap out an implementation (e.g., change from a MySqlRepository to a PostgresRepository) without changing the business logic class.
  • Easier Testing: You can inject "mock" or "fake" dependencies during unit testing, isolating the class you want to test.
  • Centralized Configuration: Dependencies are configured externally (e.g., in XML or Java config), making the system easier to manage.

Q13: What does the @SpringBootApplication annotation do?

Answer: @SpringBootApplication is a convenience annotation that combines three other powerful annotations:

  1. @Configuration (or @SpringBootConfiguration): Tags the class as a source of bean definitions for the application context. It allows for Java-based configuration.
  2. @EnableAutoConfiguration: Tells Spring Boot to start adding beans based on classpath settings, other beans, and various property settings. For example, if spring-webmvc is on the classpath, this annotation flags the application as a web application and activates key behaviors like setting up a DispatcherServlet.
  3. @ComponentScan: Tells Spring to scan the current package and its sub-packages for other components, configurations, and services, allowing it to find and register beans annotated with @Component, @Service, @Repository, @Controller, etc.

In essence, @SpringBootApplication provides all the configuration and component scanning needed to get a typical Spring Boot application up and running with a single line.


create Java interview questions and answers for Memory Streams


Of course. Here are Java interview questions and answers specifically focused on Memory Streams, a topic that tests a candidate's understanding of the java.io package and its practical applications beyond simple file handling.

These questions are suitable for developers from Junior to Senior levels, as they range from basic definitions to more nuanced discussions about performance and best practices.


Foundational Concepts

Q1: What are memory streams in Java and which classes represent them?

Answer: Memory streams (or in-memory streams) are I/O streams that use a memory buffer (typically a byte or character array) as their source or destination, instead of a physical resource like a file or a network socket. They allow you to perform I/O operations directly in memory.

The primary classes are:

  • Byte-based:
    • ByteArrayInputStream: Reads bytes from an in-memory byte array.
    • ByteArrayOutputStream: Writes bytes to an in-memory byte array. The buffer grows automatically as data is written to it.
  • Character-based:
    • StringReader: Reads characters from an in-memory String.
    • StringWriter: Writes characters to an in-memory StringBuffer.
    • CharArrayReader / CharArrayWriter: Read from or write to a char array.

Q2: What are the main advantages of using memory streams over file-based streams?

Answer: The main advantages stem from avoiding interaction with the underlying file system:

  1. Performance: They are significantly faster because they operate entirely in RAM, avoiding slow disk I/O operations (like seeking, reading, or writing to a physical disk).
  2. Simplicity & Isolation: There is no need to manage file paths, handle file system permissions, or worry about file locking. This makes code cleaner and more self-contained.
  3. Testing: They are excellent for unit testing. You can mock the input for a method that expects an InputStream by feeding it a ByteArrayInputStream with predefined test data, without creating temporary files.
  4. Transience: Perfect for handling temporary data that doesn't need to be persisted. Once the stream object goes out of scope, the in-memory buffer is eligible for garbage collection.

Practical Usage & Code

Q3: Describe a common, real-world scenario where ByteArrayOutputStream would be the ideal choice.

Answer: A very common scenario is generating a file-like resource on the fly to be served over a network, for example, in a web application.

Scenario: A user clicks a "Download PDF Report" button.

  1. The server-side code uses a library (like iText or Apache PDFBox) to generate a PDF document.
  2. Instead of writing this PDF to a temporary file on the server's disk, the library is configured to write its output directly to a ByteArrayOutputStream.
  3. Once the PDF is fully generated in memory, you call bos.toByteArray() to get the complete byte[] array of the PDF file.
  4. This byte array is then written directly to the OutputStream of the HttpServletResponse, sending the file to the user's browser for download.

This approach is efficient because it avoids the overhead and potential cleanup issues of creating temporary files on the server.

// Conceptual Code
public void downloadPdfReport(HttpServletResponse response) throws IOException {
    // 1. Create an in-memory stream
    ByteArrayOutputStream baos = new ByteArrayOutputStream();

    // 2. Use a library to generate a PDF and write it to the memory stream
    PdfGenerator.generate(baos); // This method writes PDF bytes to 'baos'

    // 3. Prepare the HTTP response
    response.setContentType("application/pdf");
    response.setHeader("Content-Disposition", "attachment; filename=\"report.pdf\"");
    response.setContentLength(baos.size());

    // 4. Write the in-memory data to the response
    baos.writeTo(response.getOutputStream());
}

Q4: You have written data to a ByteArrayOutputStream. How do you read that data back? Provide a code snippet.

Answer: You cannot read directly from an OutputStream. The flow is a two-step process:

  1. First, you "materialize" the data written to the ByteArrayOutputStream into a byte array by calling its toByteArray() method.
  2. Second, you use that byte array to construct a ByteArrayInputStream, which you can then read from.

Code Snippet:

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;

public class MemoryStreamDemo {
    public static void main(String[] args) throws IOException {
        // Step 1: Write data to a ByteArrayOutputStream
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        String originalData = "Hello, Memory Streams!";
        outputStream.write(originalData.getBytes(StandardCharsets.UTF_8));

        System.out.println("Data has been written to the output stream.");

        // Step 2: Create a byte array from the output stream
        byte[] dataBytes = outputStream.toByteArray();

        // Step 3: Create a ByteArrayInputStream from the byte array
        ByteArrayInputStream inputStream = new ByteArrayInputStream(dataBytes);

        // Step 4: Read the data back
        // We can read it byte by byte or into a buffer
        byte[] buffer = new byte[1024];
        int bytesRead = inputStream.read(buffer);

        String readData = new String(buffer, 0, bytesRead, StandardCharsets.UTF_8);

        System.out.println("Data read from input stream: " + readData);
        System.out.println("Is original data equal to read data? " + originalData.equals(readData));

        // Always good practice to close streams, though for memory streams it's a no-op
        outputStream.close();
        inputStream.close();
    }
}

Nuances and Advanced Topics

Q5: What happens when you call the close() method on a memory stream like ByteArrayInputStream or StringWriter? Does it throw an IOException?

Answer: Calling close() on a memory stream has no effect.

The Javadoc for these classes explicitly states this. For example, for ByteArrayInputStream.close(): "Closing a ByteArrayInputStream has no effect. The methods in this class can be called after the stream has been closed without generating an IOException."

This is because the purpose of close() in the java.io framework is to release underlying system resources (like file handles or network sockets). Since memory streams are not tied to any such resources, there is nothing to release. Therefore, the close() method is implemented as an empty method (a "no-op") and does not throw an IOException.


Q6: Given the answer above, is it necessary to use a try-with-resources block for memory streams?

Answer: This is a great question that tests understanding of both technical necessity and software engineering best practices.

  • Technically, no. It is not strictly necessary because failing to close a memory stream will not cause a resource leak. The in-memory buffer will be garbage collected normally when the stream object is no longer referenced.

  • As a best practice, yes. It is highly recommended to use try-with-resources for several reasons:

    1. Code Consistency: It makes the code uniform. Developers are trained to see I/O streams being managed by try-with-resources. Deviating from this pattern can be confusing.
    2. Maintainability: The code might be refactored later to use a file-based or network-based stream. If try-with-resources is already in place, the change is safer and requires less modification.
    3. Clarity of Intent: It clearly signals to other developers that the stream's lifecycle is confined to the try block, which improves readability.

So, while you won't get a resource leak if you forget, using try-with-resources is the professional and correct way to write the code.


Q7: Are memory stream classes like ByteArrayOutputStream thread-safe?

Answer: No, the primary memory stream classes (ByteArrayOutputStream, ByteArrayInputStream, StringWriter, etc.) are not thread-safe.

Their methods are not synchronized. If multiple threads write to the same ByteArrayOutputStream instance concurrently, you will likely encounter race conditions. This can lead to a corrupted internal byte array, incorrect size, and unpredictable behavior.

If you need a thread-safe in-memory stream, you must implement external synchronization yourself.

Example of protecting a ByteArrayOutputStream:

// This is NOT thread-safe
ByteArrayOutputStream unsafeBaos = new ByteArrayOutputStream();

// This is thread-safe
ByteArrayOutputStream safeBaos = new ByteArrayOutputStream();
Object lock = new Object();

// In Thread 1:
synchronized (lock) {
    safeBaos.write(data1);
}

// In Thread 2:
synchronized (lock) {
    safeBaos.write(data2);
}

what is Spring Boot and Java Beens in Spring Boot


Excellent question. This is a fundamental concept for any Java developer working with the modern enterprise ecosystem.

Here is a clear and structured explanation of Spring Boot and Java Beans within its context.


Part 1: What is Spring Boot?

At its core, Spring Boot is an extension of the Spring Framework that makes it incredibly fast and easy to create production-ready, stand-alone, Spring-based applications.

Think of it this way:

  • The Spring Framework is like a huge box of powerful, individual Lego pieces. You can build anything with it, but you have to know which pieces to pick and how to connect them all yourself. This involves a lot of manual configuration (e.g., XML files or Java configuration classes).
  • Spring Boot is like a pre-packaged Lego kit for building a specific model (e.g., a web application). It comes with a smart instruction manual and all the necessary pieces already selected and known to work together. It makes intelligent decisions for you, but still allows you to swap out pieces if you need to.

Key Features that Define Spring Boot:

  1. Auto-Configuration: This is the "magic" of Spring Boot. It automatically configures your application based on the dependencies (JARs) you have on your classpath.

    • Example: If Spring Boot sees that you have included spring-boot-starter-web, it automatically assumes you want to build a web application. So, it will configure a DispatcherServlet, a Jackson message converter (for JSON), and an embedded web server like Tomcat. You don't have to configure any of these manually.
  2. Opinionated Starter Dependencies: Spring Boot provides a set of "starter" dependencies that simplify your build configuration. Instead of picking and choosing compatible versions of various libraries, you just include one starter.

    • Example: If you want to build a web application that talks to a JPA database, you would simply include spring-boot-starter-web and spring-boot-starter-data-jpa in your pom.xml or build.gradle. Spring Boot guarantees that all the transitive dependencies (like Hibernate, Spring MVC, Tomcat, etc.) have compatible versions.
  3. Standalone Applications: Spring Boot allows you to create applications that can be run with a simple java -jar command. It does this by embedding a web server (like Tomcat, Jetty, or Undertow) directly inside your application's JAR file. You no longer need to deploy a WAR file to an external application server.

The Main Goal: Spring Boot follows the principle of "Convention over Configuration." Its goal is to let developers focus on writing business logic instead of spending time on boilerplate configuration, allowing for extremely fast application development.


Part 2: What are "Beans" in Spring Boot?

The concept of a "Bean" is not specific to Spring Boot; it is the most fundamental concept of the core Spring Framework.

In simple terms, a Spring Bean is an object that is instantiated, assembled, and managed by the Spring Inversion of Control (IoC) container.

Let's break that down.

The Inversion of Control (IoC) Container

Normally, in a Java application, you control the lifecycle of your objects:

// You are in control of creating this object
MyService service = new MyService();
service.doSomething();

With Spring, this control is inverted. You don't create the objects; the Spring framework does. You simply declare that an object should be created and managed, and the Spring IoC container (also known as the ApplicationContext) takes care of the rest.

Think of the IoC container as a sophisticated factory that builds and manages all your application components (the "beans").

How do you tell Spring to manage an object as a Bean?

You use special annotations to mark your classes. When Spring Boot starts, it scans your application's classpath for these annotations and automatically registers them as beans in its container.

The most common annotations are:

  • @Component: A generic, stereotype annotation for any Spring-managed component.
  • @Service: A specialization of @Component. It's used to indicate that a class holds business logic.
  • @Repository: A specialization of @Component used for classes that access the database (Data Access Objects).
  • @Controller / @RestController: A specialization of @Component used in the presentation layer (e.g., for handling web requests).
  • @Configuration: Used for classes that contain bean definitions using the @Bean annotation.

Example of a Bean:

package com.example.myservice;

import org.springframework.stereotype.Service;

@Service // This tells Spring: "Please create an instance of this class and manage it."
public class ProductService {

    public String getProductDetails(String productId) {
        // Business logic to fetch product details...
        return "Details for product " + productId;
    }
}

Now, ProductService is a Spring Bean.

How Do You Use a Bean? (Dependency Injection)

Once an object is a bean in the Spring container, you never need to create it with new. Instead, you ask Spring to inject it as a dependency wherever you need it. This is done using the @Autowired annotation.

Example of Injecting a Bean:

package com.example.mycontroller;

import com.example.myservice.ProductService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

@RestController // This is also a bean
public class ProductController {

    private final ProductService productService;

    // Spring sees @Autowired and "injects" the ProductService bean from its container
    // This is called "Constructor Injection" and is the recommended approach.
    @Autowired
    public ProductController(ProductService productService) {
        this.productService = productService;
    }

    @GetMapping("/products/{id}")
    public String getProduct(@PathVariable String id) {
        // We can now use the service without ever creating it ourselves!
        return productService.getProductDetails(id);
    }
}

Summary

Topic In a Nutshell
Spring Boot A framework that makes it easy to create stand-alone, production-ready Spring applications by providing auto-configuration, starter dependencies, and an embedded server. It lets you focus on code, not configuration.
Spring Bean An object whose lifecycle (creation, wiring, destruction) is managed by the Spring IoC Container. You declare a bean using annotations like @Component, @Service, etc., and use it via Dependency Injection (@Autowired).

⚠️ **GitHub.com Fallback** ⚠️