Project Based Learning in Java

Strings and Text Processing

String operations, character analysis, comparison, and StringBuilder.

Foundation Topic CO1 aligned Unit 1

Learning outcome: Create and compare strings, process text character by character, and build results efficiently using StringBuilder.

Text processing is required in user interfaces, reports, validation, file handling, and web applications. Java represents text with the String class. A String is an object and is immutable: once created, its content cannot be changed. Internally it is indexed as UTF-16 code units, so one user-perceived Unicode character can sometimes occupy two char values.

Creating Strings

String course = "Project Based Learning in Java";
String department = new String("CSE");

String literals are normally preferred. Useful operations include:

Method Purpose Example result
length() number of UTF-16 code units "Java".length() is 4
charAt(index) UTF-16 code unit at an index "Java".charAt(1) is 'a'
substring(start, end) part of a string "Java".substring(1, 3) is "av"
toLowerCase() lowercase copy "JAVA".toLowerCase()
toUpperCase() uppercase copy "java".toUpperCase()
trim() removes leading and trailing characters up to U+0020 " Java ".trim()
strip() removes leading and trailing Unicode whitespace (Java 11+) " Java ".strip()
contains(text) checks for a sequence course.contains("Java")

The last valid character index is text.length() - 1.

Comparing Strings Correctly

Use equals to compare content. The == operator checks whether two references point to the same object.

In simple terms: == asks whether two people live at the same address; equals asks whether they have the same name. For text you almost always mean the name.

String first = new String("Java");
String second = new String("Java");

System.out.println(first.equals(second)); // true
System.out.println(first == second);      // false

Use equalsIgnoreCase when letter case should not affect equality.

if (answer.equalsIgnoreCase("yes")) {
    System.out.println("Confirmed");
}

compareTo supports case-sensitive lexicographic ordering by Unicode/UTF-16 values. It is not a locale-aware dictionary or human-language collation.

if (first.compareTo(second) < 0) {
    System.out.println(first + " appears first");
}

Traversing a String

A normal for loop provides each UTF-16 index and char value. This is sufficient for basic English text.

In simple terms: A string is a numbered row of characters, indexed from 0 just like an array. charAt(i) reads one position without disturbing the rest.

String text = "Java 21";

for (int index = 0; index < text.length(); index++) {
    char current = text.charAt(index);
    System.out.println(index + " -> " + current);
}

The Character class provides readable classification methods such as isLetter, isDigit, isWhitespace, toLowerCase, and toUpperCase. Advanced processing of all Unicode code points should use codePoints() rather than assuming every character fits in one char.

Analysing Characters in a String

The following program counts English vowels, other letters, digits, whitespace, and special characters. It supports uppercase English vowels by converting each letter to lowercase before testing a, e, i, o, and u. For scripts or languages with different vowel rules, the classification policy must be adapted.

import java.util.Scanner;

public class StringAnalysis {
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        System.out.print("Enter text: ");
        String text = input.nextLine();

        int vowels = 0;
        int otherLetters = 0;
        int digits = 0;
        int spaces = 0;
        int special = 0;

        for (int index = 0; index < text.length(); index++) {
            char current = text.charAt(index);

            if (Character.isLetter(current)) {
                char lower = Character.toLowerCase(current);
                if (lower == 'a' || lower == 'e' || lower == 'i'
                        || lower == 'o' || lower == 'u') {
                    vowels++;
                } else {
                    otherLetters++;
                }
            } else if (Character.isDigit(current)) {
                digits++;
            } else if (Character.isWhitespace(current)) {
                spaces++;
            } else {
                special++;
            }
        }

        System.out.println("Vowels: " + vowels);
        System.out.println("Other letters: " + otherLetters);
        System.out.println("Digits: " + digits);
        System.out.println("Spaces: " + spaces);
        System.out.println("Special characters: " + special);
    }
}

This is the prerequisite pattern for the Easy task in Laboratory Experiment 1.1.

Why String Is Immutable

Operations that appear to modify a string actually produce a new String object.

In simple terms: A String is printed, not written in pencil. Every operation that looks like an edit actually prints a fresh copy and leaves the original exactly as it was.

String name = "Asha";
name = name.toUpperCase();

Immutability makes strings safe to share and suitable for use as keys in collections. Repeated concatenation inside a loop, however, can create many temporary objects.

Building Text with StringBuilder

StringBuilder is mutable and is suitable when a program gradually constructs a result.

StringBuilder result = new StringBuilder();
result.append("Java");
result.append(' ');
result.append("Lab");

System.out.println(result); // Java Lab

Common methods include append, insert, delete, replace, reverse, and toString.

Repeating a Word with a Separator

When a separator is required only between values, append it before every value except the first.

In simple terms: Append the separator before every value except the first. Adding it after each value instead leaves one dangling at the end, which is the usual source of a trailing comma.

public class RepeatWithSeparator {
    static String repeat(String word, int count, String separator) {
        if (count <= 0) {
            return "";
        }

        StringBuilder result = new StringBuilder();
        for (int index = 0; index < count; index++) {
            if (index > 0) {
                result.append(separator);
            }
            result.append(word);
        }
        return result.toString();
    }

    public static void main(String[] args) {
        System.out.println(repeat("Java", 3, "-"));
    }
}

Output:

Java-Java-Java

This pattern avoids an unwanted separator at the beginning or end.

Useful Text-Processing Patterns

Requirement Suitable approach
Compare content equals or equalsIgnoreCase
Process each character loop with charAt
Classify a character methods of Character
Build a long result StringBuilder
Split structured input split with a suitable regular expression
Join several values String.join

Example of joining values:

String subjects = String.join(", ", "Java", "DBMS", "Cloud");

Common Mistakes

Mistake Correction
Comparing strings with == Use equals to compare content.
Calling charAt(text.length()) The last valid index is text.length() - 1.
Ignoring uppercase vowels Convert the character to one case before testing.
Concatenating repeatedly inside a large loop Use StringBuilder.
Adding a separator after every word Add it only between values.
Forgetting that a string method returns a new value Assign or use the returned string.
Assuming length() always counts visible characters It counts UTF-16 code units; supplementary Unicode characters use two.
Treating compareTo as locale-aware alphabetical order Use a Collator when human-language ordering is required.

Practice

  1. Count uppercase letters, lowercase letters, and digits in a line of text.
  2. Check whether a word is a palindrome without using StringBuilder.reverse().
  3. Replace repeated spaces in a sentence with a single space.
  4. Write a method that repeats a word with a user-supplied separator.
  5. Print two characters in alphabetical order without considering case.

Quick Check

1. Why should strings usually be compared with equals?

equals compares character content, while == compares object references.

2. Is a Java String mutable?

No. Every apparent modification produces another string value.

3. When should StringBuilder be preferred?

It should be preferred when text is assembled through several updates, particularly inside a loop.

4. What does Character.isDigit test?

It tests whether the supplied character is recognised as a digit.

Summary

String represents immutable text. Its methods support searching, comparison, extraction, and case conversion. Character-wise traversal is useful for analysis and validation, while StringBuilder provides an efficient and clear way to construct repeated or formatted output.