• Check if a List Contains Any Prime Numbers Using Streams

    Given a list of integers, how can you check whether it contains at least one prime number using Java 8 Streams?

    Example :

    import java.util.Arrays;
    import java.util.List;
    
    public class PrimeCheck {
    
        public static void main(String[] args) {
            // Input parameter as "Java Knowledge Base"
            List<Integer> numbers = Arrays.asList(4, 54, 2, 6, 15, 16);
    
            boolean containsPrimeNumber = numbers.stream()
                    .anyMatch(PrimeCheck::isPrime);
    
            System.out.println("ArrayList contains prime numbers: " + containsPrimeNumber);
        }
    
        public static boolean isPrime(int num) {
            if (num <= 1) return false;
            for (int i = 2; i <= Math.sqrt(num); i++) {
                if (num % i == 0) return false;
            }
            return true;
        }
    }
    

    βœ… Explanation:

    • We define a List<Integer> numbers with values [4, 54, 2, 6, 15, 16].
    • We use Java 8 Stream API: numbers.stream().anyMatch(PrimeCheck::isPrime);
      • .stream() converts the list to a stream.
      • .anyMatch(PrimeCheck::isPrime) checks if any number in the list is prime by calling the isPrime() method.
    • The method isPrime(int num) efficiently checks for primality by iterating up to the square root of the number.

    βœ… Expected Output:

    ArrayList contains prime numbers: true
    

    βœ… Why Output is true:

    • The number 2 is prime, so .anyMatch() returns true.

  • Jackson Tree Model (JsonNode) – Complete JSON Guide

    When working with JSON, the most common approach is to bind JSON data directly to Java POJOs (Plain Old Java Objects) using Jackson’s data binding feature. However, sometimes the structure of the incoming JSON is dynamic, unknown, or too flexible to predefine Java classes.
    In these cases, Jackson’s Tree Model API comes in handy by allowing us to parse JSON into a tree structure that can be traversed dynamically.

    1.What Is the Tree Model (JsonNode)?

    • The Tree Model allows us to parse JSON into a tree of JsonNode objects.
    • Each JsonNode represents a part of the JSON data:
      • ObjectNode β†’ Represents a JSON object { ... }.
      • ArrayNode β†’ Represents a JSON array [ ... ].
      • ValueNode β†’ Represents JSON values (strings, numbers, booleans, null).

    This approach treats the JSON structure like a tree, allowing traversal, dynamic access, and even modification of individual nodes.

    2. Why Use the Tree Model?

    • πŸ‘‰ Useful when the JSON structure is unpredictable or changes frequently.
    • πŸ‘‰ Allows selective data extraction without having to map the entire JSON to a predefined class.
    • πŸ‘‰ Facilitates reading deeply nested properties or arrays in a flexible manner.
    • πŸ‘‰ Supports modifications before re-serializing the data back to JSON.

    3. How to Parse JSON into Tree Model using JsonNode

    βœ”οΈ Example JSON:
    {
        "name": "Ashish Kumar",
        "age": 30,
        "address": {
            "city": "Delhi",
            "zipcode": "110001"
        },
        "skills": ["Java", "Spring Boot", "Jackson"]
    }
    
    βœ”οΈExample Java Code:
    import com.fasterxml.jackson.databind.JsonNode;
    import com.fasterxml.jackson.databind.ObjectMapper;
    
    public class JacksonTreeModelExample {
      public static void main(String[] args) {
        try {
          String json = "{ \"name\": \"Ashish Kumar\", \"age\": 30, \"address\": { \"city\": \"Delhi\", \"zipcode\": \"110001\" }, \"skills\": [\"Java\", \"Spring Boot\", \"Jackson\"] }";
    
          ObjectMapper objectMapper = new ObjectMapper();
    
          // Parse JSON into a tree of JsonNode
          JsonNode rootNode = objectMapper.readTree(json);
    
          // Access simple properties
          String name = rootNode.get("name").asText();
          int age = rootNode.get("age").asInt();
    
          System.out.println("Name: " + name);
          System.out.println("Age: " + age);
    
          // Access nested object
          JsonNode addressNode = rootNode.get("address");
          String city = addressNode.get("city").asText();
          String zipcode = addressNode.get("zipcode").asText();
          System.out.println("City: " + city);
          System.out.println("Zipcode: " + zipcode);
    
          // Access array
          JsonNode skillsNode = rootNode.get("skills");
          System.out.print("Skills: ");
          for (JsonNode skill: skillsNode) {
            System.out.print(skill.asText() + ",");
          }
        } catch(Exception e) {
          e.printStackTrace();
        }
      }
    }
    βœ”οΈ Expected Output:
    Name: Ashish Kumar  
    Age: 30  
    City: Delhi  
    Zipcode: 110001  
    Skills: Java Spring Boot Jackson 
    
    βœ… Detailed Explanation of the Process
    1. βœ” Parsing JSON:
      • objectMapper.readTree(json) reads the entire JSON string and creates a tree of JsonNode objects.
      • rootNode represents the root of the tree.
    2. βœ” Accessing Simple Properties:
      • rootNode.get("name").asText() extracts the "name" property as a String.
      • rootNode.get("age").asInt() extracts the "age" property as an integer.
    3. βœ” Accessing a Nested Object:
      • rootNode.get("address") returns a JsonNode representing the "address" object.
      • Then we extract individual fields from addressNode:
        • addressNode.get("city").asText()
        • addressNode.get("zipcode").asText()
    4. βœ” Accessing Array Elements:
      • rootNode.get("skills") returns a JsonNode representing the JSON array.
      • Iterating through the array elements using a for-each loop and accessing each skill’s value with skill.asText().

    βœ… Advantages of Tree Model

    AdvantageExplanation
    Dynamic ParsingNo need to create Java classes in advance. Useful for unknown or changing JSON structures.
    Selective Data AccessAccess only the parts of the JSON you need, saving memory and effort.
    Nested and Array SupportEasily traverse deeply nested objects and arrays without mapping everything upfront.

    βœ… When to Use Tree Model vs Data Binding

    ScenarioRecommended Approach
    Static and well-defined structureUse POJOs + ObjectMapper (Serialization/Deserialization)
    Unknown, dynamic, or partial structureUse Tree Model (JsonNode)
    Need to modify part of the JSONUse ObjectNode (which extends JsonNode) to manipulate fields

    4. Creating JSON Objects Dynamically with ObjectNode

    In some scenarios, you don’t have a predefined Java object (POJO) to represent the JSON you want to generate.
    For example, when building dynamic JSON responses in REST APIs, or transforming data on the fly.
    Jackson’s ObjectNode class is ideal for such cases.

    βœ… What Is ObjectNode?

    • ObjectNode is a subclass of JsonNode that represents a JSON object { ... }.
    • Allows you to dynamically create, modify, and remove fields without needing a POJO.
    • Ideal when you want to construct a JSON object programmatically

    Example :

    import com.fasterxml.jackson.databind.ObjectMapper;
    import com.fasterxml.jackson.databind.node.ArrayNode;
    import com.fasterxml.jackson.databind.node.ObjectNode;
    
    public class DynamicJsonExample {
        public static void main(String[] args) throws Exception {
            ObjectMapper objectMapper = new ObjectMapper();
    
            // Create the root JSON object
            ObjectNode jsonObject = objectMapper.createObjectNode();
    
            // Add simple fields
            jsonObject.put("name", "Ashish Kumar");
            jsonObject.put("age", 30);
    
            // Add nested address object
            ObjectNode addressNode = objectMapper.createObjectNode();
            addressNode.put("city", "Delhi");
            addressNode.put("zipcode", "110001");
            jsonObject.set("address", addressNode);
    
            // Add skills array
            ArrayNode skillsArray = objectMapper.createArrayNode();
            skillsArray.add("Java");
            skillsArray.add("Spring Boot");
            skillsArray.add("Jackson");
            jsonObject.set("skills", skillsArray);
    
            // Output the pretty-printed JSON string
            System.out.println(jsonObject.toPrettyString());
        }
    }

    Explanation :

    Output :

    {
      "name" : "Ashish Kumar",
      "age" : 30,
      "address" : {
        "city" : "Delhi",
        "zipcode" : "110001"
      },
      "skills" : [ "Java", "Spring Boot", "Jackson" ]
    }

    πŸ“ŒWhy This Example Is Useful

    • βœ… Demonstrates how to create simple fields using put().
    • βœ… Shows how to add a nested object with ObjectNode.
    • βœ… Explains how to build an array field using ArrayNode.
    • βœ… Illustrates use cases like building dynamic JSON responses in REST APIs, building configuration files dynamically, or performing data transformations.

    5. Working with JSON Arrays in Java

    A JSON Array is an ordered collection of values, which may include objects, strings, numbers, booleans, or other primitives. In Java, using Jackson, you can easily parse and construct JSON arrays with the help of the ArrayNode class, which extends JsonNode.

    βœ”οΈ Example JSON Array
    [
        {"productId": 101, "productName": "Wireless Mouse", "price": 25.99},
        {"productId": 102, "productName": "Mechanical Keyboard", "price": 75.49}
    ]

    5.1 Parsing JSON Arrays with Jackson

    Imagine you receive a JSON array from an external API that lists products available in an online store. You can parse the JSON array string into an ArrayNode and iterate through its elements like this:

    String jsonArrayString = "[" +
        "{\"productId\": 101, \"productName\": \"Wireless Mouse\", \"price\": 1899.00}," +
        "{\"productId\": 102, \"productName\": \"Mechanical Keyboard\", \"price\": 5499.00}" +
        "]";
    
    ArrayNode jsonArray = (ArrayNode) objectMapper.readTree(jsonArrayString);
    
    for (JsonNode product : jsonArray) {
        System.out.println("Product ID: " + product.get("productId").asInt());
        System.out.println("Product Name: " + product.get("productName").asText());
        System.out.println("Price: β‚Ή" + product.get("price").asDouble());
        System.out.println("-----------------------------------");
    }
    
    βœ”οΈ Example output:
    Product ID: 101
    Product Name: Wireless Mouse
    Price: β‚Ή1899.0
    -----------------------------------
    Product ID: 102
    Product Name: Mechanical Keyboard
    Price: β‚Ή5499.0
    -----------------------------------

    5.2 Constructing JSON Arrays Dynamically

    Suppose your application needs to dynamically build a product catalog before returning it as a JSON response. You can use ArrayNode to construct the array programmatically:

    ArrayNode productsArray = objectMapper.createArrayNode();
    
    ObjectNode product1 = objectMapper.createObjectNode()
        .put("productId", 201)
        .put("productName", "USB-C Charger")
        .put("price", 999.00);
    
    ObjectNode product2 = objectMapper.createObjectNode()
        .put("productId", 202)
        .put("productName", "Noise Cancelling Headphones")
        .put("price", 7499.00);
    
    productsArray.add(product1);
    productsArray.add(product2);
    
    System.out.println(productsArray.toString());
    

    βœ” Example output:

    [
        {"productId":201,"productName":"USB-C Charger","price":999.0},
        {"productId":202,"productName":"Noise Cancelling Headphones","price":7499.0}
    ]
    

    πŸ‘‰ In a real-time e-commerce system, this dynamic creation of product lists allows you to build flexible APIs that respond with the most up-to-date product information in INR.

    6. Choosing Between ArrayNode and JsonNode

    When working with JSON data using Jackson, knowing when to use ArrayNode versus JsonNode helps you write cleaner, more efficient, and maintainable code.

    βœ… Use ArrayNode when:

    • You are working with a JSON structure where you know the data is always an array.
    • You need to perform specific operations on array elements, such as filtering, sorting, or mapping.
    • The structure is well-defined, and you want to work directly with array-specific methods (like .add(), .remove(), .size(), etc.).

    βœ… Use JsonNode when:

    • You are dealing with dynamic or nested JSON structures, where some fields could be arrays, objects, or primitive values.
    • You need a flexible and generic approach to process JSON without assuming a strict structure.
    • You want to parse arbitrary JSON data and explore it at runtime, especially when the schema is not predefined.

    πŸ‘‰ Choosing the right type between ArrayNode and JsonNode helps improve code clarity and performance in your application.

    7. Difference Between get()and path() in Jackson Tree Model

    When working with the Jackson Tree Model (JsonNode), you often need to access specific fields or nested data in a JSON structure. Two commonly used methods for this are .get() and .path(). Understanding their differences helps you write more robust and error-resistant code.

    7.1 get(String fieldName)

    • Behavior: Returns the value of the specified field as a JsonNode.
    • If the field does not exist: It returns null.
    • Use Case: When you are sure the field exists and want to get the exact node.

    Example:

    JsonNode nameNode = rootNode.get("name");  // returns null if "name" field is missing

    ⚠️ Risk: If you immediately call .asText() or .asInt() after .get(), and the field does not exist (i.e., null is returned), you will get a NullPointerException.

    7.2 path(String fieldName)

    • Behavior: Returns the value of the specified field as a JsonNode.
    • If the field does not exist: Returns a missing node (MissingNode), which is a special JsonNode that does not throw NullPointerException and safely returns default values.

    Example:

    JsonNode nameNode = rootNode.path("name");  // Returns MissingNode if "name" does not exist
    String name = nameNode.asText();            // Returns "" (empty string) if missing

    ⚑ Advantages of path()

    1. βœ” Safe Access Without NullPointerException
      Even if the field doesn’t exist, path() ensures that your code doesn’t throw a NullPointerException. Instead, it returns a MissingNode, and calling .asText(), .asInt(), etc., returns sensible defaults:
      • .asText() β†’ ""
      • .asInt() β†’ 0
    2. βœ” More Robust for Dynamic or Uncertain JSON Structures
      In real-world applications where the JSON structure might change or fields may be missing, using .path() prevents your code from breaking unexpectedly.

    βœ… Example Comparison

    // Using get() – Potential NullPointerException
    String name = rootNode.get("name").asText();        // Works if "name" exists
    String phone = rootNode.get("phone").asText();    // Throws NullPointerException if "phone" is missing
    
    // Using path() – Safe even if field missing
    String name = rootNode.path("name").asText();     // Returns "Ashish Kumar"
    String phone = rootNode.path("phone").asText();   // Returns "" (empty string)

    Note :

    πŸ“¦ When Was path() Introduced?

    The path() method has been available since early versions of Jackson (introduced around Jackson 2.x series) and has become a standard best practice for safely accessing JSON tree nodes when the presence of fields is not guaranteed.

    🎯 Conclusion

    Jackson’s Tree Model API is a powerful and flexible approach for handling JSON data in Java, especially when working with dynamic, unknown, or partially structured JSON. Unlike the traditional POJO-based data binding approach, the Tree Model provides the ability to parse JSON into a tree of JsonNode objects, enabling dynamic traversal, selective data access, and easy manipulation of JSON content.

    By using ObjectNode and ArrayNode, developers can dynamically construct JSON objects and arrays programmatically, making it ideal for use cases such as building REST API responses or handling configuration files. Furthermore, the choice between .get() and .path() methods ensures safer and more robust handling of potentially missing fields in JSON data.

    In summary, when dealing with unpredictable or frequently changing JSON structures, leveraging the Tree Model approach offers maximum flexibility, reduces boilerplate code, and improves the maintainability of your Java applications. Always prefer path() over get() in uncertain scenarios to avoid NullPointerException and ensure more stable code execution.

  • Handling Collections in Jackson – Detailed Explanation

    When working with JSON data in real-world applications, it is very common to deal with collections, especially lists of objects.
    Jackson provides easy and powerful mechanisms to handle JSON arrays and convert them into Java collections, such as List<User>.

    1. What Is Collection Handling?

    Collection handling refers to the process of deserializing a JSON array into a Java collection (usually a List, Set, or Map) of objects, and serializing a Java collection back into a JSON array.

    2. Why Is This Important?

    • API responses often return lists of items (e.g., list of users, orders, products).
    • Data persistence can involve storing or reading multiple records in a structured JSON array format.
    • Allows batch processing of JSON data.

    3. Example JSON Array

    [
        { "name": "Simone", "age": 28 },
        { "name": "Ashish", "age": 30 },
        { "name": "Ravi", "age": 25 }
    ]
    

    This JSON represents a list of User objects.

    4. How Jackson Maps JSON Array to List of Objects

    βœ… Step-by-Step Example:

    Define the User Class
    public class User {
        private String name;
        private int age;
    
        public User() { }
    
        public User(String name, int age) {
            this.name = name;
            this.age = age;
        }
    
        // Getters and Setters
    }
    

    4.1. Deserialize JSON Array into List<User>

    import com.fasterxml.jackson.core.type.TypeReference;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import java.io.File;
    import java.util.List;
    
    public class JacksonCollectionDeserializationExample {
      public static void main(String[] args) {
        try {
          ObjectMapper objectMapper = new ObjectMapper();
    
          // Read JSON array from file
          File jsonFile = new File("users.json");
    
          // Deserialize JSON array into List<User>
          List < User > users = objectMapper.readValue(jsonFile, new TypeReference < List < User >> () {});
    
          // Iterate over users
          for (User user: users) {
            System.out.println("Name: " + user.getName() + ", Age: " + user.getAge());
          }
        } catch(Exception e) {
          e.printStackTrace();
        }
      }
    }
    

    ➀ Explanation of Key Concepts:

    • new TypeReference<List<User>>() {} is necessary because of Java’s type erasure:
      It tells Jackson the full generic type to deserialize into, since List<User> cannot be determined at runtime without help.
    • The JSON file (users.json) contains an array of user objects.
    βœ”οΈ Example Output:
    Name: Simone, Age: 28  
    Name: Ashish, Age: 30  
    Name: Ravi, Age: 25  
    

    4.2. Serializing List<User> to JSON Array

    You can also serialize a List<User> into a JSON array like this:

    import com.fasterxml.jackson.databind.ObjectMapper;
    import java.io.File;
    import java.util.ArrayList;
    import java.util.List;
    
    public class JacksonCollectionSerializationExample {
        public static void main(String[] args) {
            try {
                ObjectMapper objectMapper = new ObjectMapper();
    
                List<User> users = new ArrayList<>();
                users.add(new User("Simone", 28));
                users.add(new User("Ashish", 30));
                users.add(new User("Ravi", 25));
    
                // Serialize list to JSON file
                objectMapper.writeValue(new File("users_output.json"), users);
    
                System.out.println("JSON array written to users_output.json successfully.");
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    

    The resulting users_output.json will look like:

    [
        {"name":"Simone","age":28},
        {"name":"Ashish","age":30},
        {"name":"Ravi","age":25}
    ]
    

    5. Why Is TypeReference Important?

    Without TypeReference, Jackson doesn’t know at runtime what generic type to use.
    For example:

    List<User> users = objectMapper.readValue(jsonArray, List.class);

    would return a List<Map<String, Object>> instead of List<User>.

    By using:

    new TypeReference<List<User>>() {}

    Jackson understands the correct mapping.

    6. When to Use Collection Handling

    ScenarioUse Case
    API returns a list of entitiesDeserialize JSON array into List<User>
    Bulk data persistenceSerialize List<User> into JSON array in a file
    Data transformationMap JSON array to Java collection, then manipulate data

    βœ… Conclusion

    Handling collections is a critical aspect of real-world JSON processing.
    Jackson’s combination of TypeReference, ObjectMapper.readValue(), and writeValue() makes it effortless to convert JSON arrays into Java collections and back.
    This ensures efficient, type-safe handling of structured batch data from APIs, configuration files, or persistent storage.

  • Common Jackson Annotations – A Complete Guide

    Jackson provides a set of powerful annotations that help customize how Java objects are serialized into JSON and deserialized back into Java objects. These annotations allow fine-grained control over the mapping behavior without modifying the core logic of your application.

    Common Jackson Annotations

    βœ… @JsonProperty

    • Purpose: Maps a Java field or getter/setter to a specific JSON property name during serialization and deserialization.
    • Useful when the JSON property name does not match the Java field name.
    Example:
    import com.fasterxml.jackson.annotation.JsonProperty;
    
    public class Employee {
        @JsonProperty("full_name")
        private String name;
        private int age;
    
        public Employee() { }
    
        public Employee(String name, int age) {
            this.name = name;
            this.age = age;
        }
    
        // Getters and Setters
    }
    

    πŸ‘‰ In this case, the JSON property will be "full_name" instead of "name".

    Serialization Example:
    ObjectMapper objectMapper = new ObjectMapper();
    Employee emp = new Employee("Ashish Kumar", 30);
    
    String jsonString = objectMapper.writeValueAsString(emp);
    System.out.println(jsonString);
    

    Output:

    {"full_name":"Ashish Kumar","age":30}

    βœ… @JsonIgnore

    • Purpose: Prevents a specific field from being serialized (Java to JSON) or deserialized (JSON to Java).
    • Useful for sensitive data (like passwords) or unnecessary properties.
    Example:
    import com.fasterxml.jackson.annotation.JsonIgnore;
    
    public class Employee {
        private String name;
        private int age;
    
        @JsonIgnore
        private String password;
    
        public Employee() { }
    
        public Employee(String name, int age, String password) {
            this.name = name;
            this.age = age;
            this.password = password;
        }
    
        // Getters and Setters
    }
    

    Serialization Example:

    ObjectMapper objectMapper = new ObjectMapper();
    Employee emp = new Employee("Ashish Kumar", 30, "secure123");
    
    String jsonString = objectMapper.writeValueAsString(emp);
    System.out.println(jsonString);
    

    πŸ‘‰ The password field will be excluded from the JSON output and will not be read during deserialization.

    Output:

    {"name":"Ashish Kumar","age":30}
    

    βœ… @JsonInclude

    • Purpose: Controls the inclusion of properties during serialization based on certain conditions (e.g., exclude null values).
    • Example usage: Exclude null fields to reduce output size.
    Example:
    import com.fasterxml.jackson.annotation.JsonInclude;
    
    @JsonInclude(JsonInclude.Include.NON_NULL)
    public class Employee {
        private String name;
        private Integer age;
        private String department;
    
        public Employee() { }
    
        public Employee(String name, Integer age, String department) {
            this.name = name;
            this.age = age;
            this.department = department;
        }
    
        // Getters and Setters
    }
    
    Serialization Example:
    ObjectMapper objectMapper = new ObjectMapper();
    Employee emp = new Employee("Ashish Kumar", 30, null);
    
    String jsonString = objectMapper.writeValueAsString(emp);
    System.out.println(jsonString);

    πŸ‘‰ Fields with null values (like department if not set) will be excluded from the serialized JSON.

    Output:

    {"name":"Ashish Kumar","age":30}

    βœ… Additional Useful Annotations (Worth Mentioning)

    βœ… @JsonIgnoreProperties

    • The @JsonIgnoreProperties annotation is used to ignore multiple unknown properties during deserialization.
    • By default, if the incoming JSON contains fields that do not match any properties in the target Java class, Jackson throws an exception (UnrecognizedPropertyException).
    • Adding @JsonIgnoreProperties(ignoreUnknown = true) prevents this exception by simply ignoring any unknown fields.

    ➀ Why Is It Useful?

    • Useful when the JSON comes from an external source (like a third-party API) that may include additional fields your Java class does not care about.
    • Avoids tightly coupling your Java model to every field in the JSON.

    Example:

    JSON Input:

    {
        "name": "Ashish Kumar",
        "age": 30,
        "extra": "this field is not mapped in Java class"
    }
    import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
    
    @JsonIgnoreProperties(ignoreUnknown = true)
    public class Employee {
        private String name;
        private int age;
    
        // Getters and Setters
    }
    

    Behavior:

    • The extra field will be ignored during deserialization.
    • No exception will be thrown, and only name and age will be mapped.

    βœ… @JsonCreator and @JsonProperty Combination

    • These annotations are used when deserializing into immutable objects or when the target class does not have a default (no-argument) constructor.
    • @JsonCreator: Marks the constructor to be used for deserialization.
    • @JsonProperty: Specifies how the constructor arguments map to JSON properties.

    ➀ Why Is It Useful?

    • Ensures proper deserialization when using final fields or classes designed to be immutable.
    • Promotes safe object construction without relying on setters.

    JSON Input:

    {
        "name": "Ashish Kumar",
        "age": 30
    }

    Java Class:

    import com.fasterxml.jackson.annotation.JsonCreator;
    import com.fasterxml.jackson.annotation.JsonProperty;
    
    public class Employee {
        private final String name;
        private final int age;
    
        @JsonCreator
        public Employee(@JsonProperty("name") String name, @JsonProperty("age") int age) {
            this.name = name;
            this.age = age;
        }
    
        // Getters
        public String getName() { return name; }
        public int getAge() { return age; }
    }
    Behavior:
    • Jackson uses the annotated constructor to instantiate the object.
    • No need for a default constructor or setters.
    • Allows fully immutable objects while still supporting deserialization.

      βœ… Conclusion

      • @JsonProperty β†’ Maps a Java field to a specific JSON property name.
      • @JsonIgnore β†’ Prevents a field from being serialized or deserialized.
      • @JsonInclude β†’ Controls inclusion of properties during serialization (e.g., omit nulls).

      These annotations remain stable and correct in the latest Jackson versions (e.g., 2.15.x) and Java versions (e.g., Java 17 or later).

      • @JsonIgnoreProperties(ignoreUnknown = true) helps prevent errors caused by unexpected fields in JSON.
      • @JsonCreator with @JsonProperty enables deserialization into immutable objects by mapping constructor arguments directly from JSON properties.
    • Jackson in Java – A Complete Guide with Examples

      Introduction

      Jackson is a widely used Java library for processing JSON data. It provides easy-to-use APIs for serializing Java objects to JSON and deserializing JSON into Java objects. Jackson is popular due to its high performance, flexible configuration, and powerful data-binding features.

      What is Jackson?

      Jackson is a high-performance JSON processor for Java. It helps convert Java objects to JSON and vice versa. The core class is ObjectMapper, and it also provides useful annotations for customization.

      Why Use Jackson?

      • Simple API
      • Fast and efficient
      • Handles complex data structures
      • Supports annotations for custom mapping
      • Flexible configuration

      1. Adding Jackson Dependency

      Maven

      <dependency>
          <groupId>com.fasterxml.jackson.core</groupId>
          <artifactId>jackson-databind</artifactId>
          <version>2.15.2</version>
      </dependency>

      Gradle

      implementation 'com.fasterxml.jackson.core:jackson-databind:2.15.2'

      After importing Jackson, we can use the ObjectMapper class to perform marshalling (serialization) and unmarshalling (deserialization) of JSON data.

      The ObjectMapper class is the core of Jackson’s functionality. It provides methods to:

      • writeValueAsString(Object obj) β†’ Serialize Java object to JSON string.
      • writeValue(File resultFile, Object obj) β†’ Serialize and write JSON directly to file.
      • readValue(String json, Class<T> valueType) β†’ Deserialize JSON string to Java object.
      • readTree(String json) β†’ Parse JSON into a tree of JsonNode.

      2. Serialization (Marshalling)

      What is Serialization?

      Serialization is the process of converting a Java object into a format (in this case, JSON) that can be easily stored or transmitted, and later reconstructed back into the original object. In the context of Jackson, serialization is also known as marshalling.

      When working with APIs, databases, or file storage, JSON is commonly used to represent structured data in a lightweight and human-readable format. Jackson’s ObjectMapper makes this conversion simple and efficient.

      2.1 Serialization Java Object to JSON

      βœ”οΈ Example User.java Class
      public class User {
        private String name;
        private int age;
      
        public User() {}
      
        public User(String name, int age) {
          this.name = name;
          this.age = age;
        }
      
        // Getters and Setters
        public String getName() {
          return name;
        }
        public void setName(String name) {
          this.name = name;
        }
        public int getAge() {
          return age;
        }
        public void setAge(int age) {
          this.age = age;
        }
      }
      βœ”οΈ Serialization Example
      import com.fasterxml.jackson.databind.ObjectMapper;
      
      public class JacksonSerializationExample {
          public static void main(String[] args) throws Exception {
              ObjectMapper objectMapper = new ObjectMapper();//Create an instance of ObjectMapper
              User user = new User("Ashish Kumar", 30);
      
              String jsonString = objectMapper.writeValueAsString(user);//Serialize the Object to JSON String
      
              System.out.println("Serialized JSON: " + jsonString);
          }
      }

      βž” Output:

      {"name":"Ashish Kumar","age":30}

      2.2 Writing JSON to a File

      ⚑ Instead of working only with strings, Jackson allows you to directly write JSON into files using:

      βœ”οΈ Example

      import com.fasterxml.jackson.databind.ObjectMapper;
      import java.io.File;
      
      public class JacksonWriteToFileExample {
          public static void main(String[] args) {
              try {
                  // Create ObjectMapper instance
                  ObjectMapper objectMapper = new ObjectMapper();
      
                  // Create a User object
                  User user = new User("Ashish Kumar", 30);
      
                  // Write the User object as JSON to the file "user.json"
                  objectMapper.writeValue(new File("user.json"), user);
      
                  System.out.println("JSON file has been written successfully.");
      
              } catch (Exception e) {
                  e.printStackTrace();
              }
          }
      }
      βž”Output : Resulting File Content (user.json):
      {"name":"Ashish Kumar","age":30}

      πŸ‘‰This approach is helpful when persisting data or creating configuration files.

      βœ… Why Is This Useful?

      • Data Transmission: Serialized JSON is easy to send over HTTP in REST APIs.
      • Data Storage: Persist objects in a JSON file for later retrieval.
      • Logging and Debugging: Easily log object states in a readable format.
      • Configuration Files: Store application settings in JSON.

      3. Deserialization (Unmarshalling)

      What is Deserialization?

      Deserialization is the process of converting JSON data into a Java object. In the context of Jackson, this process is often called unmarshalling. It allows us to take a structured JSON string (or file) and transform it into a corresponding Java object so that we can easily work with the data in a type-safe manner.

      βœ… Why Is Deserialization Important?

      • Reading API Responses: When calling REST APIs, the response is often in JSON format. To work with the data in Java, we deserialize it into objects.
      • Reading Configuration Files: JSON configuration files are commonly used for application settings.
      • Data Persistence: Deserialize stored JSON files back into Java objects for processing or display.

      βœ… How Does Jackson Perform Deserialization?

      Once Jackson is imported and the ObjectMapper is available, we use it to convert JSON strings or files into Java objects.

      3.1 Deserialization JSON String to Java Object

      βœ”οΈ Example JSON String
      {"name":"Simone","age":28}
      Deserialization Example
      import com.fasterxml.jackson.databind.ObjectMapper;
      
      public class JacksonDeserializationExample {
          public static void main(String[] args) {
              try {
                  ObjectMapper objectMapper = new ObjectMapper();
                  String jsonString = "{\"name\":\"Bhasker\",\"age\":28}";
      
                  User user = objectMapper.readValue(jsonString, User.class);
      
                  System.out.println("Name: " + user.getName());
                  System.out.println("Age: " + user.getAge());
              } catch (Exception e) {
                  e.printStackTrace();
              }
          }
      }

      Output:

      Name: Bhasker
      Age: 28

      πŸ‘‰This shows how the JSON string is parsed and converted into a User object.

      3.2 Deserializing from a JSON File to Java Object

      βœ”οΈ Example JSON File (user.json):
      {
          "name": "Bhasker",
          "age": 28
      }
      βœ”οΈ Example Java Code:
      import com.fasterxml.jackson.databind.ObjectMapper;
      import java.io.File;
      
      public class JacksonReadFileExample {
          public static void main(String[] args) {
              try {
                  ObjectMapper objectMapper = new ObjectMapper();
                  File jsonFile = new File("user.json");
      
                  User user = objectMapper.readValue(jsonFile, User.class);
      
                  System.out.println("Name: " + user.getName());
                  System.out.println("Age: " + user.getAge());
              } catch (Exception e) {
                  e.printStackTrace();
              }
          }
      }

      πŸ‘‰This approach is helpful when working with large or persistent JSON data stored in files.

      Output :

      Name: Bhasker
      Age: 28

      βœ… How Does Jackson Know How to Map JSON to Java Object?

      Jackson relies on:

      • Default Constructor: The target class must have a no-argument constructor (can be implicit).
      • Getters and Setters: Jackson uses public setters to set values during deserialization.
      • Field Matching: JSON property names must match Java field names, unless annotations (like @JsonProperty) are used to map them explicitly.

      4. Jackson Annotations

      Jackson provides useful annotations to control serialization and deserialization.

      Common Jackson Annotations

      • @JsonProperty β†’ Maps a Java field to a specific JSON property name
      • @JsonIgnore β†’ Prevents a specific field from being serialized or deserialized.
      • @JsonInclude β†’ Controls inclusion of properties during serialization (e.g., omit nulls).
      • @JsonIgnoreProperties β†’ Prevents multiple unknown fields from causing exceptions during deserialization.
      • @JsonCreator β†’ Useful when deserializing immutable objects with final fields and no default constructor.

      Learn more about Jackson Annotations here πŸ‘‰ Common Jackson Annotations – A Complete Guide

      βœ”οΈ Example : Employee.java

      import com.fasterxml.jackson.annotation.JsonIgnore;
      import com.fasterxml.jackson.annotation.JsonProperty;
      
      public class Employee {
          @JsonProperty("full_name")
          private String name;
          private int age;
      
          @JsonIgnore
          private String password;
      
          public Employee() { }
      
          public Employee(String name, int age, String password) {
              this.name = name;
              this.age = age;
              this.password = password;
          }
      
          // Getters and Setters
      }
      
      Example Serialization
      ObjectMapper objectMapper = new ObjectMapper();
      Employee emp = new Employee("Ashish", 30, "secure123");
      
      String jsonString = objectMapper.writeValueAsString(emp);
      
      System.out.println(jsonString);
      

      Output:

      {"full_name":"Ashish","age":30}
      

      5. Jackson Tree Model (JsonNode)

      The Tree Model in Jackson represents JSON data as a hierarchical tree of JsonNode objects.

      Learn more about Jackson Tree Model (JsonNode) πŸ‘‰ Jackson Tree Model (JsonNode) – Complete JSON Guide .

      Example : How to Parse JSON into Tree Model

      Json File

      {
          "name": "Ashish Kumar",
          "age": 30,  
      }
      import com.fasterxml.jackson.databind.JsonNode;
      import com.fasterxml.jackson.databind.ObjectMapper;
      
      public class JacksonTreeExample {
          public static void main(String[] args) throws Exception {
              String json = "{\"name\":\"Ashish\",\"age\":30}";
              ObjectMapper mapper = new ObjectMapper();
              JsonNode node = mapper.readTree(json);
      
              System.out.println("Name: " + node.get("name").asText());
              System.out.println("Age: " + node.get("age").asInt());
          }
      }

      Output :

      Name: Ashish   
      Age: 30 

      6. Handling Collections

      βœ… What Is Collection Handling?

      Collection handling refers to the process of deserializing a JSON array into a Java collection (usually a List, Set, or Map) of objects, and serializing a Java collection back into a JSON array.

      For detailed information, please refer to theπŸ‘‰ Handling Collections in Jackson – Detailed Explanation

      Example :

      Input JSON String:

      [
          {"name":"Bhasker","age":28},
          {"name":"Ashish","age":30}
      ]
      import com.fasterxml.jackson.core.type.TypeReference;
      import com.fasterxml.jackson.databind.ObjectMapper;
      
      import java.util.List;
      
      public class JacksonListExample {
        public static void main(String[] args) throws Exception {
          String json = "[{\"name\":\"Bhasker\",\"age\":28}, {\"name\":\"Ashish\",\"age\":30}]";
      
          ObjectMapper mapper = new ObjectMapper();
          List < User > users = mapper.readValue(json, new TypeReference < List < User >> () {});
      
          for (User u: users) {
            System.out.println(u.getName() + " - " + u.getAge());
          }
        }
      }

      Output :

      Bhasker - 28
      Ashish - 30

      7. Reading List of Objects from JSON File

      import com.fasterxml.jackson.core.type.TypeReference;
      import com.fasterxml.jackson.databind.ObjectMapper;
      
      import java.io.File;
      import java.util.List;
      
      public class JacksonReadListFromFileExample {
          public static void main(String[] args) throws Exception {
              ObjectMapper objectMapper = new ObjectMapper();
              File jsonFile = new File("users.json");
      
              List<User> users = objectMapper.readValue(jsonFile, new TypeReference<List<User>>() {});
      
              for (User user : users) {
                  System.out.println(user.getName() + " - " + user.getAge());
              }
          }
      }
      

      8. Pretty Printing JSON

      When working with JSON data, especially for debugging, configuration files, or manual inspection, a compact JSON string without any formatting can be hard to read.
      By default, Jackson produces a compact JSON output like this:

      {"name":"Ashish Kumar","age":30}

      This is fine for machine processing but not ideal for humans.
      To make the JSON output more readable, we use Pretty Printing to add line breaks, indentation, and proper spacing.

      βœ”οΈ Example

      import com.fasterxml.jackson.databind.ObjectMapper;
      import com.fasterxml.jackson.databind.SerializationFeature;
      
      public class JacksonPrettyPrintExample {
          public static void main(String[] args) throws Exception {
              ObjectMapper objectMapper = new ObjectMapper();
              objectMapper.enable(SerializationFeature.INDENT_OUTPUT);
      
              User user = new User("Ashish Kumar", 30);
      
              String prettyJson = objectMapper.writeValueAsString(user);
      
              System.out.println(prettyJson);
          }
      }
      

      Output:

      {
        "name" : "Ashish Kumar",
        "age" : 30
      }
      

      9. Common Exceptions to Handle

      • JsonProcessingException
      • UnrecognizedPropertyException
      • MismatchedInputException

      Summary of ObjectMapper Usage

      OperationMethod Example
      Serialize Object to StringwriteValueAsString(obj)
      Serialize Object to FilewriteValue(new File("output.json"), obj)
      Deserialize String to ObjectreadValue(jsonString, User.class)
      Deserialize File to ObjectreadValue(new File("user.json"), User.class)
      Read Tree ModelreadTree(jsonString)
      Deserialize Array from FilereadValue(new File("users.json"), new TypeReference<List<User>>(){})
      Pretty PrintEnable SerializationFeature.INDENT_OUTPUT

      🎯Conclusion

      Jackson is a powerful and flexible library that makes working with JSON in Java effortless. By mastering serialization, deserialization, annotations, tree model, file handling, collections, and pretty printing, you can efficiently handle JSON data in any Java project.

    • String Template in Java 21 – A Complete Guide

      βœ… What is a String Template in Java 21?

      A String Template is a structured way to define a string with embedded expressions, which are evaluated and inserted in a safe and readable way. It improves code clarity and avoids issues like manual escaping or mistakes in formatting.

      βœ”οΈ Syntax Example of String Template (Java 21)

      String name = "Ashish";
      int age = 30;
      
      String result = STR."My name is \{name} and I am \{age} years old.";
      System.out.println(result);
      
      • STR."..." is a string template literal.
      • Inside the template, expressions are written as \{expression}.
      • At compile-time, these expressions are evaluated and injected into the string.

      βœ… Advantages of String Templates

      FeatureBenefit
      Easier SyntaxCleaner and readable syntax compared to concatenation or String.format().
      Compile-Time SafetyErrors in embedded expressions are caught at compile time.
      Automatic EscapingNo need to manually handle escaping of quotes or special characters.
      Structured FormattingIdeal for complex multi-line templates.

      βœ”οΈ Example Compared to Earlier Approaches

      ➀ Pre-Java 21 (Traditional way):

      String name = "Ashish";
      int age = 30;
      
      // Using concatenation
      String result1 = "My name is " + name + " and I am " + age + " years old.";
      
      // Using String.format
      String result2 = String.format("My name is %s and I am %d years old.", name, age);
      
      System.out.println(result1);
      System.out.println(result2);
      

      ➀ Java 21 String Template Way:

      String name = "Ashish";
      int age = 30;
      
      String result = STR."My name is \{name} and I am \{age} years old.";
      System.out.println(result);
      

      βœ… Key Differences Between String Template and Earlier Approaches

      AspectPre-Java 21Java 21 String Template
      SyntaxVerbose (concatenation, String.format)Cleaner and easier to read
      SafetyRuntime errors if format string is wrongCompile-time checks
      EscapingManual, error-proneHandled automatically
      PerformanceModerate, because of repeated concatenationsEfficient at compile time
      Multi-line stringsComplicated, need workaroundsSupported naturally with templates
      ReusabilityHarderTemplates can be reusable components

      βœ… When to Prefer String Templates?

      • For dynamic string generation in a readable and safe way.
      • When working with multi-line strings (e.g., generating HTML or JSON templates).
      • When avoiding manual concatenation and improving code maintainability.

      ⚠️ Important Note

      • String Templates in Java 21 are still in Preview Mode.
      • You need to enable preview features to use them:
        javac --enable-preview and java --enable-preview.

      βœ… Conclusion

      String Templates in Java 21 represent a modern, safe, and clean way of working with dynamic strings compared to the older cumbersome ways. It simplifies code, reduces bugs, and improves readability.

      Let me know if you want me to provide a detailed blog-style explanation with examples and use-cases.

    • Class and Object in Java – A Complete Beginner’s Guide

      In Java, Object-Oriented Programming (OOP) plays a crucial role in structuring applications using real-world concepts. The two most fundamental concepts of OOP are Class and Object. Understanding these is essential for every Java developer.

      βœ… What is a Class in Java?

      A Class is a blueprint or template for creating objects. It defines the properties (attributes or fields) and behaviors (methods) that the objects created from the class will have.

      πŸ” Key Points About Class:

      • Acts like a template.
      • Defines fields (variables) and methods (functions).
      • Does not consume memory directly.

      βœ… Class Syntax Example:

      public class Car {
          // Fields (Properties)
          String color;
          String model;
          int year;
      
          // Method (Behavior)
          public void displayDetails() {
              System.out.println("Model: " + model);
              System.out.println("Color: " + color);
              System.out.println("Year: " + year);
          }
      }
      

      βœ… What is an Object in Java?

      An Object is an instance of a class. It occupies memory and holds actual values for the fields defined in the class. Through the object, you can access the methods and variables defined in the class.

      βœ… Object Creation Example:

      public class Main {
          public static void main(String[] args) {
              // Creating an object of the Car class
              Car myCar = new Car();
      
              // Assign values to the fields
              myCar.model = "Toyota Camry";
              myCar.color = "Red";
              myCar.year = 2022;
      
              // Call method using the object
              myCar.displayDetails();
          }
      }
      

      βœ… Output:

      Model: Toyota Camry
      Color: Red
      Year: 2022
      

      βœ… Class vs Object – Quick Comparison

      ClassObject
      Blueprint of real-world entityActual entity created from the class
      Contains fields and methodsHolds data and behavior of the instance
      No memory allocationMemory is allocated when object is created
      Syntax: class ClassName { ... }Syntax: ClassName obj = new ClassName();

      βœ… Why Use Class and Object?

      • Encapsulation: Organizes data and behavior in one unit.
      • Reusability: Once the class is created, multiple objects can be created and reused.
      • Abstraction: Internal implementation is hidden; you interact only via methods.
      • Real-World Representation: Classes represent real-world entities, making it easy to model complex problems.

      βœ… Example With Multiple Objects

      public class Main {
          public static void main(String[] args) {
              Car car1 = new Car();
              car1.model = "Honda Civic";
              car1.color = "Blue";
              car1.year = 2020;
      
              Car car2 = new Car();
              car2.model = "Ford Mustang";
              car2.color = "Black";
              car2.year = 2021;
      
              System.out.println("Car 1 Details:");
              car1.displayDetails();
      
              System.out.println("\nCar 2 Details:");
              car2.displayDetails();
          }
      }
      

      βœ… Key Concepts to Remember

      • A class defines what an object will look like and what it can do.
      • An object is a real instance of the class that holds actual values and can perform actions (methods).
      • You can create multiple objects from the same class, each with its own state.

      βœ… Conclusion

      Understanding Class and Object is the first step toward mastering Java’s object-oriented programming. It allows you to model real-world entities and perform structured programming with easy code reusability and better maintainability.

    • String vs StringBuffer vs StringBuilder in Java Guide

      When working with text data in Java, there are three commonly used classes to handle strings:

      1. String
      2. StringBuffer
      3. StringBuilder

      Each of these serves a different purpose and has its own advantages and limitations. Understanding when and how to use them is key for writing efficient Java programs.

      βœ… 1. String

      • Immutable Object: Once a String object is created, its value cannot be changed.
      • Every modification creates a new String object in memory.
      • Useful when the string content doesn’t change often.

      πŸ”§ Example:

      public class StringExample {
          public static void main(String[] args) {
              String text = "Java Knowledge Base";
              System.out.println("Original String: " + text);
      
              // Concatenation creates a new String object
              text = text + " - Learn Java Effectively";
              System.out.println("Modified String: " + text);
          }
      }
      

      βœ… Output:

      Original String: Java Knowledge Base
      Modified String: Java Knowledge Base - Learn Java Effectively
      

      ⚑ Key Point: Inefficient for many modifications due to creation of new objects and higher memory consumption.

      βœ… 2. StringBuffer

      • Mutable Class: Allows modification of the string content without creating new objects.
      • Thread-Safe: All methods are synchronized.
      • Suitable when thread safety is required.

      πŸ”§ Example:

      public class StringBufferExample {
          public static void main(String[] args) {
              StringBuffer textBuffer = new StringBuffer("Java Knowledge Base");
              System.out.println("Original StringBuffer: " + textBuffer);
      
              // Append text
              textBuffer.append(" - Learn Java Effectively");
              System.out.println("Modified StringBuffer: " + textBuffer);
      
              // Insert text
              textBuffer.insert(5, " Awesome");
              System.out.println("After Insert: " + textBuffer);
          }
      }
      

      βœ… Output:

      Original StringBuffer: Java Knowledge Base
      Modified StringBuffer: Java Knowledge Base - Learn Java Effectively
      After Insert: Java Awesome Knowledge Base - Learn Java Effectively
      

      ⚑ Key Point: Suitable for multi-threaded environments but slightly slower than StringBuilder.

      βœ… 3. StringBuilder

      • Mutable Class: Like StringBuffer, but not synchronized (not thread-safe).
      • Faster than StringBuffer due to the lack of synchronization.
      • Recommended when working in a single-threaded context.

      πŸ”§ Example:

      public class StringBuilderExample {
          public static void main(String[] args) {
              StringBuilder textBuilder = new StringBuilder("Java Knowledge Base");
              System.out.println("Original StringBuilder: " + textBuilder);
      
              // Append text
              textBuilder.append(" - Learn Java Effectively");
              System.out.println("Modified StringBuilder: " + textBuilder);
      
              // Insert text
              textBuilder.insert(5, " Awesome");
              System.out.println("After Insert: " + textBuilder);
          }
      }
      

      βœ… Output:

      Original StringBuilder: Java Knowledge Base
      Modified StringBuilder: Java Knowledge Base - Learn Java Effectively
      After Insert: Java Awesome Knowledge Base - Learn Java Effectively
      

      ⚑ Key Point: Best choice when working in a single-threaded environment and performance is critical.

      βš”οΈ Comparison Table

      FeatureStringStringBufferStringBuilder
      MutabilityImmutableMutableMutable
      Thread SafetyNot applicableThread-safe (synchronized)Not thread-safe
      PerformanceSlow for modificationsSlower than StringBuilderFast (better performance)
      Use CaseStatic text or rarely changedMulti-threaded contextSingle-threaded context
      Memory UsageHigh (creates new objects)ModerateModerate

      βœ… Conclusion

      • Use String when the string content does not change.
      • Use StringBuffer when you need thread safety.
      • Use StringBuilder for efficient single-threaded string manipulation
    • Understanding StringBuilder in Java – A Complete Tutorial with Examples

      Introduction

      In Java, handling strings efficiently is crucial, especially when frequent modifications are involved. The StringBuffer class is a powerful alternative to String for such scenarios. Unlike String, which is immutable, StringBuffer allows modification of string content without creating a new object every time, making it ideal for performance-critical applications.

      What is StringBuffer in Java?

      StringBuffer is a thread-safe, mutable sequence of characters provided by Java in the java.lang package. It allows modification of strings (append, insert, delete, reverse, etc.) without creating new objects, which improves memory efficiency and performance.

      Key Characteristics

      • Mutable: The content can be changed.
      • Thread-safe: Methods are synchronized, making it suitable for multi-threaded environments.
      • Efficient for frequent modifications.

      String vs StringBuffer vs StringBuilder

      FeatureStringStringBufferStringBuilder
      MutabilityImmutableMutableMutable
      Thread SafetyNot Thread-safeThread-safeNot Thread-safe
      PerformanceLess efficient in loopsSlower than StringBuilder but thread-safeFast and efficient for single-threaded use cases

      Creating a StringBuffer Object

      StringBuffer sb1 = new StringBuffer(); // Creates an empty buffer with default capacity (16)
      StringBuffer sb2 = new StringBuffer("Hello"); // Creates buffer initialized with "Hello"
      StringBuffer sb3 = new StringBuffer(50); // Creates empty buffer with specified capacity
      

      Common StringBuffer Methods and Examples

      1. append()

      Appends the specified string to this buffer.

      StringBuffer sb = new StringBuffer("Hello");
      sb.append(" World");
      System.out.println(sb); // Output: Hello World
      

      2. insert()

      Inserts the specified string at the specified index.

      StringBuffer sb = new StringBuffer("Hello World");
      sb.insert(5, ",");
      System.out.println(sb); // Output: Hello, World
      

      3. replace()

      Replaces characters from start index to end index with a new string.

      StringBuffer sb = new StringBuffer("Hello World");
      sb.replace(6, 11, "Java");
      System.out.println(sb); // Output: Hello Java
      

      4. delete()

      Deletes characters from start index to end index.

      StringBuffer sb = new StringBuffer("Hello Java");
      sb.delete(5, 10);
      System.out.println(sb); // Output: Hello
      

      5. reverse()

      Reverses the sequence of characters.

      StringBuffer sb = new StringBuffer("Hello");
      sb.reverse();
      System.out.println(sb); // Output: olleH
      

      6. capacity() and ensureCapacity()

      • capacity(): Returns current capacity.
      • ensureCapacity(int minCapacity): Increases capacity if needed.
      StringBuffer sb = new StringBuffer();
      System.out.println(sb.capacity()); // Default is 16
      sb.ensureCapacity(50);
      System.out.println(sb.capacity()); // At least 50
      

      Scenario Examples

      Scenario 1: Building a Large String Efficiently

      StringBuffer sb = new StringBuffer();
      for (int i = 0; i < 1000; i++) {
          sb.append(i).append(", ");
      }
      System.out.println(sb.substring(0, 50) + "...");
      

      Why StringBuffer?
      Using String would create a new object on each iteration, leading to performance overhead.

      Scenario 2: Multi-threaded Environment

      public class StringBufferExample {
          private static StringBuffer buffer = new StringBuffer();
      
          public static void main(String[] args) throws InterruptedException {
              Thread t1 = new Thread(() -> buffer.append("A"));
              Thread t2 = new Thread(() -> buffer.append("B"));
      
              t1.start();
              t2.start();
      
              t1.join();
              t2.join();
      
              System.out.println(buffer.toString()); // Output: AB or BA (depends on thread scheduling)
          }
      }
      

      When to Use StringBuffer vs StringBuilder

      • Use StringBuffer when working in multi-threaded environments where thread safety is needed.
      • Use StringBuilder when thread safety is not required for better performance.

      Important Points to Remember

      • StringBuffer is synchronized, making it thread-safe.
      • Always prefer StringBuilder for non-concurrent scenarios due to better performance.
      • StringBuffer’s capacity grows automatically but can be managed using ensureCapacity().

      Conclusion

      StringBuffer is a powerful tool in Java for mutable and thread-safe string manipulation. Understanding its methods and appropriate use cases is key to writing efficient Java applications.

    • Understanding StringBuffer in Java: A Complete Guide with Examples

      Introduction

      In Java, handling strings efficiently is crucial, especially when frequent modifications are involved. The StringBuffer class is a powerful alternative to String for such scenarios. Unlike String, which is immutable, StringBuffer allows modification of string content without creating a new object every time, making it ideal for performance-critical applications.

      What is StringBuffer in Java?

      StringBuffer is a thread-safe, mutable sequence of characters provided by Java in the java.lang package. It allows modification of strings (append, insert, delete, reverse, etc.) without creating new objects, which improves memory efficiency and performance.

      Key Characteristics

      • Mutable: The content can be changed.
      • Thread-safe: Methods are synchronized, making it suitable for multi-threaded environments.
      • Efficient for frequent modifications.

      String vs StringBuffer vs StringBuilder

      FeatureStringStringBufferStringBuilder
      MutabilityImmutableMutableMutable
      Thread SafetyNot Thread-safeThread-safeNot Thread-safe
      PerformanceLess efficient in loopsSlower than StringBuilder but thread-safeFast and efficient for single-threaded use cases

      Creating a StringBuffer Object

      StringBuffer sb1 = new StringBuffer(); // Creates an empty buffer with default capacity (16)
      StringBuffer sb2 = new StringBuffer("Hello"); // Creates buffer initialized with "Hello"
      StringBuffer sb3 = new StringBuffer(50); // Creates empty buffer with specified capacity
      

      Common StringBuffer Methods and Examples

      1. append()

      Appends the specified string to this buffer.

      StringBuffer sb = new StringBuffer("Hello");
      sb.append(" World");
      System.out.println(sb); // Output: Hello World
      

      2. insert()

      Inserts the specified string at the specified index.

      StringBuffer sb = new StringBuffer("Hello World");
      sb.insert(5, ",");
      System.out.println(sb); // Output: Hello, World
      

      3. replace()

      Replaces characters from start index to end index with a new string.

      StringBuffer sb = new StringBuffer("Hello World");
      sb.replace(6, 11, "Java");
      System.out.println(sb); // Output: Hello Java
      

      4. delete()

      Deletes characters from start index to end index.

      StringBuffer sb = new StringBuffer("Hello Java");
      sb.delete(5, 10);
      System.out.println(sb); // Output: Hello
      

      5. reverse()

      Reverses the sequence of characters.

      StringBuffer sb = new StringBuffer("Hello");
      sb.reverse();
      System.out.println(sb); // Output: olleH
      

      6. capacity() and ensureCapacity()

      • capacity(): Returns current capacity.
      • ensureCapacity(int minCapacity): Increases capacity if needed.
      StringBuffer sb = new StringBuffer();
      System.out.println(sb.capacity()); // Default is 16
      sb.ensureCapacity(50);
      System.out.println(sb.capacity()); // At least 50
      

      Scenario Examples

      Scenario 1: Building a Large String Efficiently

      StringBuffer sb = new StringBuffer();
      for (int i = 0; i < 1000; i++) {
          sb.append(i).append(", ");
      }
      System.out.println(sb.substring(0, 50) + "...");
      

      Why StringBuffer?
      Using String would create a new object on each iteration, leading to performance overhead.

      Scenario 2: Multi-threaded Environment

      public class StringBufferExample {
          private static StringBuffer buffer = new StringBuffer();
      
          public static void main(String[] args) throws InterruptedException {
              Thread t1 = new Thread(() -> buffer.append("A"));
              Thread t2 = new Thread(() -> buffer.append("B"));
      
              t1.start();
              t2.start();
      
              t1.join();
              t2.join();
      
              System.out.println(buffer.toString()); // Output: AB or BA (depends on thread scheduling)
          }
      }
      

      When to Use StringBuffer vs StringBuilder

      • Use StringBuffer when working in multi-threaded environments where thread safety is needed.
      • Use StringBuilder when thread safety is not required for better performance.

      Important Points to Remember

      • StringBuffer is synchronized, making it thread-safe.
      • Always prefer StringBuilder for non-concurrent scenarios due to better performance.
      • StringBuffer’s capacity grows automatically but can be managed using ensureCapacity().

      Conclusion

      StringBuffer is a powerful tool in Java for mutable and thread-safe string manipulation. Understanding its methods and appropriate use cases is key to writing efficient Java applications.