Strings and reference types
On This Lesson 11 topics
Lesson Overview
You have been using String since your first println. In this lesson we look at what a String really is, and why it is not one of the eight primitive types from Lesson 2. The difference is one of the most important ideas in Java, and it explains a long list of behaviors you will meet later.
Here is the picture. A primitive box holds its value inside the box: the box labeled age really contains 30. A reference box holds not the value itself but a pointer, like a slip of paper with an address on it. The text lives in a separate place in memory, and the box points to it. String works this way.
Why this matters: nearly all data a backend handles arrives as text, such as names, emails, JSON, and URLs. The moment you understand that a String variable holds a pointer, three things make sense: why a variable can hold null ("no object here"), why a program can crash when you use such a variable, and why text must be compared with .equals(...).
This lesson builds on Lessons 1 and 2. It deliberately keeps the memory picture simple. A deeper treatment (how objects are laid out, why == behaves as it does on text) comes in later modules.
Learning Objectives
By the end of this lesson you will be able to:
- Declare and initialize a
String, and join strings with+. - Explain the difference between primitive and reference types at a conceptual level.
- Say what
nullmeans, and what happens when you use aStringvariable that holdsnull. - Use
.length(),.toUpperCase(), and.equals(...)on aString. - Explain that a
Stringobject cannot be changed after it is created. - Compare text with
.equals(...)instead of==.
Written Explanation
The String type
A String holds text: zero or more characters. A string literal is written in double quotes.
String name = "Ana";
String empty = ""; // a String with no charactersIts name starts with a capital letter, which is a clue: the eight primitive types (Lesson 2) are all lowercase, while String is a class, a kind of reference type. Classes get their own module later. For now you need three facts about them.
Primitive versus reference
Java divides all types into two kinds: primitive types and reference types (the Java Language Specification, chapter 4). The eight types of Lesson 2 are the primitives. Everything else in the language you will use, starting with String, is a reference type.
Primitive box (int age = 30;) | Reference box (String name = "Ana";) | |
|---|---|---|
| What is inside the box | The value 30 itself | A pointer to an object |
| Where the real data lives | In the box | In a separate object elsewhere in memory |
| Can the box hold "nothing"? | No: it always has a value | Yes: it can hold null |
Calling .something() on it | Not possible | Possible, using the object it points to |
A diagram helps:
int age String name
+------+ +--------+ +---------+
| 30 | | --->-+-------->| "Ana" |
+------+ +--------+ +---------+
value is in the box box holds a pointer; text is in the object(This is a model for learning. How Java stores objects in detail is not needed here.)
Copying: values versus pointers
When you assign one variable to another, what is copied is what is in the box. For a primitive that is the value. For a reference it is the pointer, so both boxes then point at the same object. Because a String cannot be changed (see below), this sharing is harmless for text, but the rule matters for every other class you will meet.
String a = "Ana";
String b = a; // b points to the same text object
a = "Omar"; // a now points elsewhere; b is unaffectedAfter these lines a is Omar and b is still Ana. Reassigning a moved its pointer; it did not touch the text that b points to.
null: no object here
A reference box may hold the special value null, meaning "this box points to no object". It is the reference world's version of an empty box. The Language Specification describes null as the only value of a special null type that can be assigned to any reference type.
String middleName = null;null is not the same as "". The empty string "" is a real String object containing zero characters. null is the absence of any object. Printing a null reference shows the word null. Using it, for example asking for its length, fails at run time with a NullPointerException, because there is no object to ask. A primitive can never be null, so this failure cannot happen with an int.
Strings cannot be changed
A String object is immutable: once created, its characters never change. Methods such as toUpperCase() do not alter the original; they return a new string. If you want to keep the result, assign it to a variable.
String name = "Ana";
name.toUpperCase(); // makes "ANA" but the result is thrown away
name = name.toUpperCase(); // now the variable points to the new textA few things a String can do
Because a String is an object, you can call methods on it with a dot. A method is a named action the object knows how to do (methods get their own module later). Three to start with:
name.length()returns the number of characters, as anint.name.toUpperCase()returns an upper-case copy.name.equals(other)returns aboolean:trueif the two texts have exactly the same characters.
The + operator joins strings and, when one side is text, converts the other side to text as well, as you saw in BEG-008.
Comparing text: use .equals
To ask whether two strings contain the same text, write first.equals(second). Do not use first == second for this. With references, == asks whether the two boxes point at the same object, which is a different question from whether the texts are equal, and the answer can surprise you. The full explanation is in a later module (BEG-026); for now, remember the rule: text is compared with .equals. Note also that .equals is case sensitive: "Ana" and "ana" are different.
Examples
Example 1: working with a String (simple)
public class Greeting {
public static void main(String[] args) {
String name = "Ana";
String empty = "";
System.out.println(name.length());
System.out.println(empty.length());
System.out.println(name.toUpperCase());
System.out.println(name);
name = name + " Hassan";
System.out.println(name);
String other = "Ana Hassan";
System.out.println(name.equals(other));
System.out.println("Ana".equals("ana"));
}
}Output (JDK 21):
3
0
ANA
Ana
Ana Hassan
true
falseNotice line 4 of the output: calling name.toUpperCase() printed ANA but name itself was still Ana. Then name = name + " Hassan" built a new string and made the variable point to it. The last two lines show .equals and its case sensitivity.
Example 2: two pointers, and a null (practical)
public class Pointers {
public static void main(String[] args) {
String a = "Ana";
String b = a;
a = "Omar";
System.out.println(a + " " + b);
String middle = null;
System.out.println(middle);
System.out.println("Length: " + middle.length());
}
}Output:
Omar Ana
null
Exception in thread "main" java.lang.NullPointerException: Cannot invoke "String.length()" because "<local3>" is null
at Pointers.main(Pointers.java:10)The first line confirms that b kept its pointer. The second prints null without a problem, because printing only needs to show it. The third asks a method of a non-existent object and the program stops with NullPointerException. The text says <local3> instead of middle because this file was compiled with the default javac options, which do not keep local variable names in the class file; other tools or options may show the name instead. The line number tells you where to look: line 10.
Reading a stack trace like this is a skill you will build up. For now the key fact: a null reference is fine to hold and print, but using it as an object crashes.
Example 3: text is not a number (realistic)
Data from forms, files, and requests usually arrives as text, even when it looks like a number. A String containing 42 is not the number 42:
public class TextVsNumber {
public static void main(String[] args) {
String text = "42";
int number = 42;
System.out.println(text + 1);
System.out.println(number + 1);
}
}Output:
421
43With text, + joins, so "42" plus 1 becomes 421. With a number, + adds. A backend that reads an age or quantity from a request has to convert the text to a number before doing arithmetic (a later module shows how). The compiler also protects you from mixing them silently:
error: incompatible types: int cannot be converted to String (String s = 42;)
error: incompatible types: String cannot be converted to int (int n = "42";)
error: int cannot be dereferenced (int len = 5; len.length();)The last message says "dereferenced" because following a pointer is exactly what the dot does, and an int has no pointer to follow.
Backend Insight
Strings are the everyday material of backend work: user names, email addresses, URLs, SQL text, JSON bodies, log lines. Three consequences of this lesson recur constantly:
- Null handling. A value that is missing in a request or a database row often shows up as
null. Code that uses such a value without checking can throwNullPointerException, one of the most common production errors in Java. Later modules show safe ways to handle missing values. The first step is to know wherenullcan come from: only reference types. - Equality. Comparing a typed password, a status code like
"PAID", or a user name must use.equals.==can seem to work in a quick test and fail with real data. - Immutability. Because strings do not change, they can be shared safely, for example as keys and in many parts of a program at once. Building a very long text by joining thousands of pieces creates many temporary strings; later modules introduce better tools for that.
Also keep in mind that text has an encoding when it travels over a network or into a file; Module 1 introduced the idea.
Common Mistakes
Mistake 1: Comparing text with ==
Beginners write if (name == "Ana") because it reads naturally and it may even seem to work in tiny tests. It tests whether two boxes point at the same object, not whether the text is equal. Use name.equals("Ana"). (Why they differ is explained in BEG-026.)
Mistake 2: Forgetting that the result of toUpperCase() must be kept
name.toUpperCase(); on its own changes nothing, because strings are immutable. Write name = name.toUpperCase(); or String shout = name.toUpperCase();.
Mistake 3: Calling a method on a null String
String middle = null; middle.length(); compiles but crashes with NullPointerException. Make sure a reference points to an object before you use it, or keep the "no value" case in mind from the start.
Mistake 4: Confusing null and the empty string
"" is an object with zero characters and .length() is 0. null is no object, and .length() crashes. They are different states of the box, and they mean different things.
Mistake 5: Using single quotes for text
String s = 'Ana'; is rejected, because single quotes mean a one-character char, and Ana is three characters. Text needs double quotes.
Exercises
Try each exercise yourself before opening the solutions.
Level A: Foundation
A1. Which of these are primitive types and which are reference types: double, String, boolean?
Show solution A1
A1. double and boolean are primitive; String is a reference type.
A2. What does a String variable hold in its box: the text itself, or something else?
Show solution A2
A2. Conceptually it holds a pointer (a reference) to a String object that contains the text, not the text itself.
A3. What is the length of "", and what does String s = null; mean?
Show solution A3
A3. The length of "" is 0: it is a real string with no characters. String s = null; means the variable points to no object at all.
Level B: Application
B1. Predict the output:
String x = "go";
String y = x;
x = x + "!";
System.out.println(x + " " + y);Show solution B1
B1. x is first go, then go! (a new string). y still points at the original go. Output: go! go.
B2. Fix the bug: the programmer wants title to be in capitals, and prints it.
String title = "report";
title.toUpperCase();
System.out.println(title);Show solution B2
B2. toUpperCase() returns a new string and leaves title unchanged, so it prints report. Fix: title = title.toUpperCase();, which makes it print REPORT.
B3. Write a short program that declares String user = "admin"; and prints whether it equals "Admin" and whether it equals "admin".
Show solution B3
B3.
String user = "admin";
System.out.println(user.equals("Admin"));
System.out.println(user.equals("admin"));It prints false then true, because .equals is case sensitive.
Level C: Challenge
C1. Explain in four or five sentences how a String variable and an int variable differ in what they store, and give one thing that is possible with one but not the other.
Show solution C1
C1. An int variable stores the number itself in its box. A String variable stores a pointer to a text object that lives elsewhere. A String can be null and can have methods called on it (name.length()), but an int cannot be null and has no methods, so age.length() does not compile. The pointer also means that two String variables can refer to the same text.
C2. A method returns a String that might be null. Describe what goes wrong in result.length() if it is, and what question you should ask before writing that call.
Show solution C2
C2. If the value is null, there is no object to ask, so the call throws NullPointerException and the program stops. Before writing the call, ask "can this value be missing?" and decide what the program should do in that case.
Mini Challenge
Write a program Badge with three String variables: firstName, lastName, and role (for example "guest"). Build a fourth string fullName by joining the first two with a space. Print a line that says the full name in capitals, followed by the number of characters in fullName, followed by whether role equals "admin".
Check your work: you used + and .toUpperCase() correctly (and kept or printed the result), used .length() on the full name, compared with .equals and not ==, and the program compiles and prints a sensible line.
Summary & Next Lesson
Key takeaways
Stringholds text and is a reference type: its box holds a pointer to an object, not the text itself.- Primitives hold the value directly and can never be
null. A reference can benull, which means "no object". - Using a
nullreference as an object throwsNullPointerException. Stringobjects are immutable. Methods liketoUpperCase()return a new string.- Compare text with
.equals(...), not==.
Next lesson
You know what boxes there are. The next lesson, "Literals, constants, and naming", looks at how to write values in code (literals), how to make a box that cannot be changed with final, and the Java rules for naming things.
Self-check
- Is
Stringa primitive type? - What is the difference between
""andnull? - What does
name.toUpperCase();do tonameif nothing is assigned? - Which should you use to compare text:
==or.equals? - Why can an
intvariable never throw aNullPointerException?
Show answers
- No, it is a reference type (a class).
""is a real string with no characters;nullmeans no object at all.- Nothing: strings are immutable, and the result is discarded.
.equals.- An
intholds its value directly and can never benull, so there is no missing object to use.
References
- Oracle, The Java Language Specification, Java SE 21, section 4.1. It states that the language has primitive and reference types, and that the null type's only value is
null, which can be assigned to any reference type. - Oracle, The Java Tutorials: Primitive Data Types. It says
Stringis not a primitive but has special language support, and thatStringobjects are immutable. - The outputs and error messages above were produced by compiling and running the code with JDK 21. The
<local3>wording in theNullPointerExceptionmessage is observed behavior of the default compile options, not something documented here; other JDK versions or options may differ. The pointer picture is a teaching model. I did not verify the claim about string joining creating many temporary strings against a source; it is stated from general knowledge and a later module covers it properly.
Practice
Apply this lesson with the module's lab, exercises and quiz.
Marking a lesson complete is optional and you can undo it any time. Your progress stays in this browser.