IT234 Object Oriented Programming With Java

Object Oriented Programming With JavaUnit 812 min read

String Manipulation & Wrapper Classes: Methods, Buffer, Builder, Parsing, Autoboxing

Unit 8 of Object Oriented Programming With Java covers Java’s immutable String class, its mutable alternatives (StringBuffer/StringBuilder), core string manipulation methods, wrapper classes for primitives, autoboxing/unboxing, and parsing techniques—with real-world examples from eSewa, Daraz, and Ncell.

TAKEAWAYS:

  • Strings are immutable: Every modification creates a new object (e.g., concat() or replace()).
  • StringBuffer vs StringBuilder: Thread-safe Buffer (synchronized) vs faster Builder (unsynchronized).
  • Wrapper classes: Convert primitives (int, double) to objects (Integer, Double) for collections/generics.
  • Autoboxing/unboxing: Automatic conversion between primitives and wrappers (e.g., int x = 5; → Integer x = 5;).
  • Parsing: Convert strings to numbers/booleans using Integer.parseInt(), Double.valueOf(), etc.
  • Common pitfalls: == checks references (not content), equals() checks values, and substring() is zero-based.

1. The String Class: Immutable and Efficient

Java’s String is immutable (cannot be changed after creation). This ensures thread safety but requires careful handling for performance.

Key Methods

String str = "Hello";
Method Example Description
length() str.length() → 5 Returns string length.
charAt(index) str.charAt(1) → 'e' Gets character at index (0-based).
substring(start,end) str.substring(0,3) → "Hel" Extracts substring (end is exclusive).
concat() "Hi".concat("!") → "Hi!" Concatenates (creates new String).
toUpperCase() "abc".toUpperCase() → "ABC" Converts to uppercase.
equals() "abc".equals("ABC") → false Case-sensitive comparison.
equalsIgnoreCase() "abc".equalsIgnoreCase("ABC") → true Case-insensitive comparison.
startsWith() "file.txt".startsWith("file") → true Checks prefix.
split(regex) "a,b,c".split(",") → ["a","b","c"] Splits by delimiter.

Why Immutability?

  • Thread Safety: No risk of corruption in multi-threaded apps (e.g., Ncell’s SMS system).
  • Security: Passwords/tokens can’t be altered accidentally.
  • Caching: Java pools String objects (e.g., "Hello" is reused).

Performance Impact

String s = "";
for (int i = 0; i < 1000; i++) {
    s += i; // Creates 1000 new String objects!
}

Problem: Concatenation in loops is O(n²) due to repeated object creation. Solution: Use StringBuilder (see Section 3).



2. Real-World Example: eSewa’s Transaction IDs

eSewa generates unique transaction IDs like TXN1234567890 using:

String id = "TXN" + System.currentTimeMillis() + (int)(Math.random() * 1000);
  • Why String?
    • Immutable → safe for concurrent access (multiple users paying bills simultaneously).
    • Methods like substring() help extract prefixes (e.g., "TXN").
  • Why not StringBuilder?
    • IDs are created once and never modified → immutability is sufficient.

3. Mutable Strings: StringBuffer and StringBuilder

For frequent modifications, use:

  • StringBuffer: Thread-safe (synchronized), slower.
  • StringBuilder: Faster (no synchronization), preferred in single-threaded apps.

Key Methods

StringBuffer sb = new StringBuffer("Hello");
Method Example Description
append() sb.append("!") → "Hello!" Adds to the end.
insert(index, str) sb.insert(1, "i") → "Hillo!" Inserts at position.
delete(start,end) sb.delete(1,3) → "Hlo!" Deletes substring (end exclusive).
reverse() "abc".reverse() → "cba" Reverses the string.
replace(start,end,newStr) sb.replace(1,3,"XYZ") → "HXYZlo!" Replaces a substring.

Performance Comparison

Operation String (immutable) StringBuffer StringBuilder
Concatenation O(n²) O(n) O(n)
Thread Safety ✅ (safe) ✅ (synchronized) ❌ (unsafe)
Use Case Constants Multi-threaded Single-threaded

FIGURE: StringBuilder Operations

flowchart LR
    A["Start: \"Hello\""]
    B["Append '!' → \"Hello!\""]
    C["Insert 'i' at 1 → \"Hillo!\""]
    D["Delete 1-3 → \"Hlo!\""]
    A --> B --> C --> D

4. Real-World Example: Daraz’s Order Queue

Daraz uses StringBuilder to efficiently build order summaries for thousands of users:

StringBuilder orderSummary = new StringBuilder();
orderSummary.append("Order #").append(orderId)
            .append("\nItems: ").append(items)
            .append("\nTotal: Rs.").append(total);
  • Why StringBuilder?
    • Avoids the O(n²) cost of String concatenation in loops.
    • Faster than StringBuffer (no synchronization overhead).

5. Wrapper Classes: Primitives to Objects

Java primitives (int, double) cannot be used in collections (e.g., ArrayList). Wrapper classes convert them to objects:

Primitive Wrapper Class Example
int Integer Integer num = 10;
double Double Double pi = 3.14;
char Character Character c = 'A';
boolean Boolean Boolean flag = true;

Key Methods

Integer num = 10;
Method Example Description
parseInt() Integer.parseInt("123") → 123 Converts String to int.
valueOf() Double.valueOf("3.14") → 3.14 Converts String to wrapper.
toString() num.toString() → "10" Converts wrapper to String.
intValue() num.intValue() → 10 Converts wrapper to primitive.

FIGURE: Autoboxing/Unboxing

flowchart LR
    A["Primitive: int x = 5"] -->|"Autoboxing"| B["Wrapper: Integer x = 5"]
    B -->|"Unboxing"| C["Primitive: int y = x"]

6. Autoboxing and Unboxing

Java automatically converts between primitives and wrappers:

// Autoboxing (primitive → wrapper)
int a = 10;
Integer b = a; // int → Integer

// Unboxing (wrapper → primitive)
Integer c = 20;
int d = c; // Integer → int

Use Case: Simplifies code when working with collections:

List<Integer> numbers = new ArrayList<>();
numbers.add(5); // Autoboxing: int → Integer
int num = numbers.get(0); // Unboxing: Integer → int

Performance Note

  • Autoboxing adds minor overhead (e.g., ArrayList<Integer> is slower than int[]).
  • Avoid in performance-critical loops (e.g., game physics).

7. Real-World Example: Ncell’s Call Duration Calculation

Ncell uses Double wrappers to store call durations (in seconds) for billing:

Double duration = 120.5; // Wrapper for precision
double seconds = duration; // Unboxing for calculations
  • Why Double?
    • Call durations can be fractional (e.g., 120.5 seconds).
    • Double allows storage in ArrayList<Double> for easy sorting/filtering.

8. Parsing Strings to Primitives

Convert String to primitives using:

String numStr = "123";
int num = Integer.parseInt(numStr); // "123" → 123
Method Example Description
Integer.parseInt() Integer.parseInt("42") → 42 Parses String to int.
Double.parseDouble() Double.parseDouble("3.14") → 3.14 Parses String to double.
Boolean.parseBoolean() Boolean.parseBoolean("true") → true Parses String to boolean.

Error Handling

try {
    int num = Integer.parseInt("abc"); // Throws NumberFormatException
} catch (NumberFormatException e) {
    System.out.println("Invalid number!");
}

Use Case: Validating user input (e.g., age in a form).


9. Real-World Example: NEPSE Stock Price Validation

NEPSE’s API returns stock prices as String (e.g., "120.50"). To calculate changes:

String priceStr = "120.50";
double price = Double.parseDouble(priceStr); // "120.50" → 120.50
double change = (price - previousPrice) / previousPrice * 100;
  • Why parsing?
    • Ensures correct numeric operations (e.g., String + String = "120.50100.00").

10. Common Pitfalls and Best Practices

Pitfall Solution
Using == for String comparison Use equals() (e.g., "abc".equals("ABC") → false).
Concatenation in loops Use StringBuilder (e.g., log generation).
Forgetting try-catch for parsing Handle NumberFormatException (e.g., user input).
Unnecessary autoboxing Use primitives in performance-critical code.

FIGURE: String Comparison Pitfall

flowchart LR
    A["String s1 = \"abc\";"] --> B["String s2 = new String(\"abc\");"]
    B --> C["s1 == s2 → false (reference comparison)"]
    B --> D["s1.equals(s2) → true (content comparison)"]

11. Exam Tip

  1. String Immutability:

    • Always remember String is immutable. Modifications create new objects.
    • Example: "a" + "b" is optimized to "ab" (compiler uses String pool).
  2. StringBuffer vs StringBuilder:

    • StringBuffer is thread-safe (use in multi-threaded apps like bank transactions).
    • StringBuilder is faster (use in single-threaded apps like order processing).
  3. Wrapper Classes:

    • Know the 8 primitive wrappers (Integer, Double, etc.).
    • Autoboxing/unboxing is automatic but has overhead.
  4. Parsing:

    • parseInt() throws NumberFormatException for invalid inputs (e.g., "abc").
    • Always validate user input before parsing.
  5. Code Examples:

    • Be ready to write programs for:
      • Reversing a string using StringBuilder.
      • Checking if a string length is prime (using Integer wrapper).
      • Concatenating strings efficiently in a loop.

Past Exam Question Analysis:

  • Question: "Why do we need wrapper classes?" Answer:

    Wrapper classes (e.g., Integer) convert primitives to objects, enabling:

    1. Storage in collections (ArrayList<Integer>).
    2. Autoboxing/unboxing for concise code.
    3. Methods like Integer.parseInt() for string-to-number conversion.
  • Question: "Differentiate StringBuffer and String." Answer:

    Feature String StringBuffer
    Mutability Immutable Mutable
    Thread Safety ✅ (safe) ✅ (synchronized)
    Performance Slower for edits Faster for edits
    Use Case Constants, caching Dynamic string building

Final Worked Example: Prime Length Check

import java.io.*;

public class PrimeString {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        System.out.print("Enter a string: ");
        String input = br.readLine();

        int length = input.length();
        boolean isPrime = isPrime(length);

        if (isPrime) {
            FileWriter writer = new FileWriter("Prime.txt");
            writer.write(input);
            writer.close();
        } else {
            FileWriter writer = new FileWriter("Composite.txt");
            writer.write(input);
            writer.close();
        }
    }

    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;
    }
}

Trace:

Step Action Output File
Input: "abc" Length = 3 → isPrime(3) → true Prime.txt
Input: "abcd" Length = 4 → isPrime(4) → false Composite.txt

Key Points:

  • Uses String.length() to get length.
  • isPrime() uses int (primitive) for efficiency.
  • Files are written based on prime check.

Based on the TU BITM syllabus for Object Oriented Programming With Java (IT234), unit 8.

Discussion

Loading…