التعامل مع Strings
في هذا الدرس 11 موضوعات
هذا الدرس متاح حاليًا باللغة الإنجليزية فقط، ولم تُنشر نسخته العربية بعد.
Lesson Overview
In BEG-009 you met String as a reference type: a variable holding a pointer to text that cannot be changed. In BEG-011 you read text from the keyboard and parsed numbers out of it. Now we collect the everyday tools for text: measure it, pick out a character, cut out a piece, find a word, clean the edges, change the case, and chop a line into parts.
The picture for this module is a printed sheet that cannot be erased. You can read it, point at a spot, copy a piece of it, or print a new, changed sheet. You never scribble on the original. Keep that in mind: every method in this lesson that "changes" text really returns a new String.
Why this matters: almost every input a backend receives is text: names, emails, URLs, JSON, CSV lines. Cleaning and cutting that text correctly is daily work.
This lesson builds on BEG-009-L03 (strings and equals), BEG-011 (input and parsing), and arrays (Lessons 1-2).
Learning Objectives
By the end of this lesson you will be able to:
- Get the length of a String and a single character by index.
- Cut out part of a String with
substring, knowing which end is excluded. - Find text with
indexOfand handle the-1result. - Remove surrounding spaces and change case.
- Split a line into a
String[]and process the pieces. - Compare text with
equalsandequalsIgnoreCase, and say why not==. - Predict which calls throw
StringIndexOutOfBoundsException.
Written Explanation
Positions work like array indices
A String is a sequence of characters numbered from 0. The character at position i is s.charAt(i), and the size is s.length(). This time length has parentheses: for a String it is a method; for an array it is a field.
s = "Java" J a v a
index 0 1 2 3 length() is 4Valid positions for charAt are 0 to length() - 1. Any other position throws a StringIndexOutOfBoundsException, the String version of the array error from Lesson 1.
The everyday methods
| Method | What it returns | Example on "Hello, World" |
|---|---|---|
length() | Number of characters | 12 |
charAt(i) | The character at position i | charAt(0) is 'H' |
substring(a, b) | Characters from a up to but not including b | substring(0, 5) is "Hello" |
substring(a) | From a to the end | substring(7) is "World" |
indexOf(text) | Position of the first match, or -1 | indexOf("World") is 7 |
trim() | The text without leading and trailing spaces | " hi " becomes "hi" |
toUpperCase(), toLowerCase() | A copy in that case | "HELLO, WORLD" |
equals(other) | true if the characters are identical | "a".equals("a") is true |
equalsIgnoreCase(other) | Same, ignoring upper/lower case | "A".equalsIgnoreCase("a") is true |
isEmpty() | true when length() is 0 | "".isEmpty() is true |
split(sep) | A String[] of the pieces | "a,b".split(",") gives {"a", "b"} |
substring: start included, end excluded
substring(a, b) includes position a and stops before b, so the result has b - a characters. The call fails with a StringIndexOutOfBoundsException when a is negative, b is greater than length(), or a is greater than b. Two useful consequences: substring(0, s.length()) is the whole String, and s.substring(s.length()) is the empty String "", with no error.
indexOf returns -1 when the text is missing
indexOf never throws for a missing text; it returns -1. If you pass that -1 straight to substring you get an exception. Always test it first:
int at = email.indexOf("@");
if (at == -1) { /* not an email */ }trim: clean the edges
trim() removes characters up to the space character from both ends, which covers ordinary spaces, tabs, and newlines. Text typed by users and read from files often has stray spaces, so trim before you compare or parse. (Newer Java versions also have strip(), which follows the Unicode definition of white space; trim() is enough for this course.)
split: from a line to an array
split(sep) cuts the String around every match of sep and returns the pieces as a String[], so the loops of Lesson 2 apply. Three details:
- If the separator does not occur, the result is an array with one element: the original text.
- Empty strings at the end of the result are dropped, so
"a,b,,".split(",")has 2 elements, not 4. The empty String""split by","gives an array with one element. - The argument is a regular expression, not plain text. A comma is fine, but
"a.b".split(".")returns an empty array because.matches any character. To split on a dot, writesplit("[.]").
The pieces keep their spaces: "Ana, 17".split(",")[1] is " 17". Trim before parsing a number.
Comparing text: equals, not ==
== on two String variables asks whether they point at the very same object. That can be true or false for identical text depending on how the Strings were created, so it is not a text comparison. Use a.equals(b), which compares the characters, or a.equalsIgnoreCase(b) when case does not matter. The demo lesson at the end of this module shows the == surprise live. Put the known value first when the other might be null, as in "yes".equals(answer), which returns false for a null answer instead of crashing.
Methods return new Strings
Each method above that produces text (trim, toUpperCase, substring) returns a new String and leaves the original untouched. Lesson 5 explains why; for now remember: s.trim(); on a line of its own does nothing useful, and you must write s = s.trim();.
Examples
Example 1: a tour of the methods (simple)
public class Ex1 {
public static void main(String[] args) {
String s = " Hello, World ";
String t = s.trim();
System.out.println("[" + t + "]");
System.out.println(t.length());
System.out.println(t.charAt(0));
System.out.println(t.indexOf("World"));
System.out.println(t.indexOf("Java"));
System.out.println(t.substring(0, 5));
System.out.println(t.substring(7));
System.out.println(t.toUpperCase());
System.out.println(t.equals("hello, world"));
System.out.println(t.equalsIgnoreCase("hello, world"));
System.out.println(s.length());
}
}Output (compiled and run with JDK 21):
[Hello, World]
12
H
7
-1
Hello
World
HELLO, WORLD
false
true
16The square brackets show that the edges are clean. s still has length 16, so trim() made a new String and s was untouched.
Example 2: splitting a line of data (practical)
public class Ex2 {
public static void main(String[] args) {
String line = "ana,bob,carl";
String[] parts = line.split(",");
System.out.println(parts.length);
for (String n : parts) {
System.out.println(n.toUpperCase());
}
String csv = "Ana, 17, Cairo";
String[] f = csv.split(",");
System.out.println("[" + f[1] + "]");
System.out.println("[" + f[1].trim() + "]");
int age = Integer.parseInt(f[1].trim());
System.out.println(age + 1);
System.out.println("none".split(",").length);
}
}Output:
3
ANA
BOB
CARL
[ 17]
[17]
18
1Splitting gave three pieces and the enhanced for upper-cased each. The second field kept its leading space, which is why it was trimmed before Integer.parseInt (BEG-011). A text without the separator gives one element.
Example 3: validating and extracting (realistic)
public class Ex3 {
static boolean looksLikeEmail(String s) {
String t = s.trim();
int at = t.indexOf("@");
return at > 0 && t.indexOf(".", at) > at + 1 && t.indexOf(".", at) < t.length() - 1;
}
static String initials(String fullName) {
String[] words = fullName.trim().split(" ");
String result = "";
for (String w : words) {
result += w.charAt(0);
}
return result.toUpperCase();
}
public static void main(String[] args) {
System.out.println(looksLikeEmail(" ana@example.com "));
System.out.println(looksLikeEmail("ana.example.com"));
System.out.println(looksLikeEmail("@x.com"));
System.out.println(initials(" ada king lovelace "));
String name = "Ana";
System.out.println(name.substring(1, 3));
System.out.println(name.charAt(3));
}
}Output (lines inside the JDK's own classes are left out of the trace):
true
false
false
AKL
na
Exception in thread "main" java.lang.StringIndexOutOfBoundsException: Index 3 out of bounds for length 3
at Ex3.main(Ex3.java:24)looksLikeEmail is a deliberately rough check: an @ that is not first, followed by a dot that has something before and after it. It rejects the second and third inputs, and real validation is stricter. initials trims, splits on a space, takes the first character of each word, and upper-cases the result. The last line asks for position 3 of a three-character String, the same off-by-one as with arrays.
Backend Insight
Text from the outside world is messy: extra spaces, wrong case, missing parts. A reliable service normalizes before it compares: trim the edges, then compare with equalsIgnoreCase (for example, for a username lookup), then check the format before taking the text apart with indexOf and substring. The two-line habit "trim, then compare with equals" prevents a long list of "it works on my machine" bugs, such as a user who typed a trailing space in a login form.
Splitting is also how simple data formats are read. A CSV line is split(",") on the surface, but real CSV files can contain commas inside quotes, which this simple call does not understand, so production code uses a library for that. Know the limit of the tool.
Common Mistakes
Mistake 1: Comparing text with ==
- Incorrect:
if (answer == "yes") - Correct:
if ("yes".equals(answer))
Mistake 2: Forgetting that substring excludes the end
"Hello".substring(0, 4) is "Hell", not "Hello". The result has end - start characters.
Mistake 3: Using indexOf without checking for -1
s.substring(0, s.indexOf("@")) throws when there is no @, because the end index is -1. Test first.
Mistake 4: Throwing away the result
- Incorrect:
name.trim();(the cleaned String is discarded;nameis unchanged) - Correct:
name = name.trim();
Mistake 5: Splitting on a special character
"1.5".split(".") does not split on a dot, because the argument is a regular expression. Use split("[.]").
Mistake 6: Using the wrong length
s.length (a field) does not exist on a String, and a.length() does not exist on an array. String: length(). Array: length.
Exercises
Try each exercise yourself before opening the solutions.
Level A: Foundation
A1. For String s = "Java Backend"; give s.length(), s.charAt(5), s.indexOf("Back"), and s.substring(0, 4).
أظهر الحل A1
A1. 12; charAt(5) is 'B' (positions: J0 a1 v2 a3 space4 B5); indexOf("Back") is 5; substring(0, 4) is "Java".
A2. True or false: (a) "Ana".equals("ana"); (b) "Ana".equalsIgnoreCase("ana"); (c) "x".indexOf("y") == -1.
أظهر الحل A2
A2. (a) false, case differs. (b) true. (c) true.
A3. What does " hi ".trim().length() return?
أظهر الحل A3
A3. 2: trimming leaves "hi".
Level B: Application
B1. Given String email = "sara@mail.com"; write code that prints the part before the @ and the part after it.
أظهر الحل B1
B1.
int at = email.indexOf("@");
System.out.println(email.substring(0, at));
System.out.println(email.substring(at + 1));This prints sara and mail.com. In real code, check at != -1 first.
B2. Given String time = "10:45:30"; split it on the colon and print the total number of seconds since midnight (use Integer.parseInt).
أظهر الحل B2
B2.
String[] p = time.split(":");
int seconds = Integer.parseInt(p[0]) * 3600
+ Integer.parseInt(p[1]) * 60
+ Integer.parseInt(p[2]);
System.out.println(seconds);The result is 10 * 3600 + 45 * 60 + 30 = 38730.
B3. A user types a command with stray spaces and any case, such as " QUIT ". Write one condition that is true for every spelling of quit.
أظهر الحل B3
B3. if (input.trim().equalsIgnoreCase("quit")). Trim removes the stray spaces and equalsIgnoreCase ignores the case.
Level C: Challenge
C1. Predict the results, or the exception, for: (a) "abcdef".substring(2, 2); (b) "abcdef".substring(4, 2); (c) "abcdef".substring(6); (d) "abcdef".charAt(6).
أظهر الحل C1
C1. (a) "", an empty String, with no error. (b) StringIndexOutOfBoundsException, since the start is greater than the end. (c) "", because the start equals the length, which is allowed. (d) StringIndexOutOfBoundsException, since the last valid position is 5.
C2. Write static String capitalize(String raw) that returns the text trimmed, with the first letter upper case and the rest lower case (" aNA " becomes "Ana"), and returns "" for a blank input.
أظهر الحل C2
C2.
static String capitalize(String raw) {
String t = raw.trim();
if (t.isEmpty()) {
return "";
}
return t.substring(0, 1).toUpperCase() + t.substring(1).toLowerCase();
}The empty check is needed because substring(0, 1) on "" would throw. For " aNA " the trimmed text is "aNA", giving "A" + "na".
Mini Challenge
Write a program that takes one line such as " Omar Hassan, 42 ,omar@mail.com " (put it in a variable) and prints three labelled lines: the name, the age as a number plus 1, and the part of the email before the @. Use trim, split, indexOf, and substring. Then test with a line that has no email part and decide what your program should say.
Check your work: you trimmed each piece before using it, you checked indexOf for -1 before cutting, and no == is used on text.
Summary & Next Lesson
Key takeaways
- Positions start at 0:
charAt(i)andlength(); a bad position throwsStringIndexOutOfBoundsException. substring(a, b)includesaand excludesb;substring(s.length())is"".indexOfreturns the position or-1; check for-1before you use the result.trim,toUpperCase,toLowerCase, andsubstringreturn new Strings; assign them if you want to keep them.splitreturns aString[]and treats its argument as a regular expression.- Compare text with
equalsorequalsIgnoreCase, never==.
Next lesson
The next lesson, "Immutability and StringBuilder", explains why a String can never change and what to use when you need to build text piece by piece.
Self-check
- What does
"Hello".substring(1, 3)return? - What does
indexOfreturn when the text is missing? - What is
"a,b,c".split(",").length? - Why is
name.trim();on its own line useless? - Which comparison do you use for the text of two Strings?
أظهر الإجابات
"el"(positions 1 and 2).-1.3.trim()returns a new String and the original is unchanged, so the result must be assigned.equals(orequalsIgnoreCase), not==.
References
- Oracle,
Stringclass, Java SE 21 API. It supports: Strings cannot be changed and methods liketrimreturn new Strings;substring(begin, end)includesbeginand excludesend, and throws for an invalid range;indexOfreturns-1when there is no match;splitworks on a regular expression and returns the original text in an array if nothing matches;trimremoves characters up to the space character at both ends;equalsIgnoreCasecompares ignoring case;isEmptyis true when the length is 0;charAtaccepts0tolength() - 1. - The dropped trailing empty strings in
split, the"a.b".split(".")result, and every output above were checked by compiling and running the code with JDK 21. I did not verify thesplitrules on the page itself beyond the points listed above.
التطبيق
طبّق هذا الدرس بمختبر الوحدة وتمارينها واختبارها.
تعليم الدرس كمكتمل اختياري ويمكنك التراجع عنه في أي وقت. يبقى تقدمك محفوظًا في هذا المتصفح.