Java equals() vs == Operator
Java equals() vs == Operator
This is the most commonly asked Java interview question for freshers — and also the most commonly misunderstood concept in daily Java development. Dozens of production bugs trace back to someone writing == where they needed equals(), or calling equals() on a null reference where Objects.equals() was the safe choice.
The confusion is understandable. For int, double, and other primitives, == compares values and works exactly as expected. For objects — including String, Integer, and every custom class — == compares memory addresses, not content. Two objects that hold identical data can return false for == simply because they are stored at different locations in memory.
The Core Rule — One Sentence
== compares what is stored in the variable. For primitives, that is the value itself. For objects, that is the memory address of the object (the reference).
Primitive variable: Object variable:
int x = 5; String s = "hello";
┌───────┐ ┌───────────┐
│ 5 │ ← x stores value │ address │ ← s stores address
└───────┘ └─────┬─────┘
│
▼
┌─────────────┐
│ "hello" │ ← actual String object
└─────────────┘
x == 5 → compares 5 == 5 → true
s == "hello" → compares address == pool address
(may be true for literals, false for new String())
== With Primitives — Works as Expected
For all eight primitive types — int, long, double, float, boolean, char, byte, short — == compares the actual stored values directly. This works exactly as most beginners expect.
// File: PrimitiveEquality.java
public class PrimitiveEquality {
public static void main(String[] args) {
// int comparison
int a = 100;
int b = 100;
int c = 200;
System.out.println("=== int ===");
System.out.println("a == b : " + (a == b)); // true — both store 100
System.out.println("a == c : " + (a == c)); // false — 100 != 200
// double comparison
double price1 = 99.99;
double price2 = 99.99;
System.out.println("\n=== double ===");
System.out.println("price1 == price2 : " + (price1 == price2)); // true
// boolean comparison
boolean isActive = true;
boolean isPending = false;
System.out.println("\n=== boolean ===");
System.out.println("isActive == true : " + (isActive == true)); // true
System.out.println("isPending == isActive: " + (isPending == isActive)); // false
// char comparison — compares Unicode values
char grade1 = 'A';
char grade2 = 'A';
char grade3 = 'B';
System.out.println("\n=== char ===");
System.out.println("grade1 == grade2 : " + (grade1 == grade2)); // true — same Unicode
System.out.println("grade1 == grade3 : " + (grade1 == grade3)); // false
// Floating-point precision warning
double x = 0.1 + 0.2;
double y = 0.3;
System.out.println("\n=== Floating-point precision ===");
System.out.println("0.1 + 0.2 == 0.3 : " + (x == y)); // false — precision issue!
System.out.println("0.1 + 0.2 = " + x); // 0.30000000000000004
System.out.println("Use Math.abs for double comparison: "
+ (Math.abs(x - y) < 0.0001)); // true — safe comparison
}
}Output:
=== int ===
a == b : true
a == c : false
=== double ===
price1 == price2 : true
=== boolean ===
isActive == true : true
isPending == isActive: false
=== char ===
grade1 == grade2 : true
grade1 == grade3 : false
=== Floating-point precision ===
0.1 + 0.2 == 0.3 : false
0.1 + 0.2 = 0.30000000000000004
Use Math.abs for double comparison: true
One important caveat: never use == to compare double or float values that result from arithmetic. Floating-point arithmetic introduces tiny precision errors — 0.1 + 0.2 is not exactly 0.3 in binary floating-point. Use Math.abs(a - b) < epsilon for numeric tolerance comparison.
== With Objects — Compares References, Not Content
For any object type — String, Integer, ArrayList, or a custom class — == compares memory addresses. Two different objects with identical content return false.
// File: ObjectEquality.java
public class ObjectEquality {
static class Product {
String name;
double price;
Product(String name, double price) {
this.name = name;
this.price = price;
}
}
public static void main(String[] args) {
Product p1 = new Product("Laptop", 45000.0);
Product p2 = new Product("Laptop", 45000.0); // identical data
Product p3 = p1; // same reference
System.out.println("=== Object Reference Check ===");
System.out.println("p1 == p2 : " + (p1 == p2)); // false — two different objects
System.out.println("p1 == p3 : " + (p1 == p3)); // true — same object
System.out.println("p2 == p3 : " + (p2 == p3)); // false
System.out.println();
// Identity hash code confirms different objects
System.out.println("p1 address: " + System.identityHashCode(p1));
System.out.println("p2 address: " + System.identityHashCode(p2)); // different
System.out.println("p3 address: " + System.identityHashCode(p3)); // same as p1
System.out.println();
// What == actually means for objects
System.out.println("=== What == means for objects ===");
System.out.println("p1 == p2 means: do p1 and p2 point to the same object?");
System.out.println("Answer: " + (p1 == p2)); // false — they are different objects
System.out.println();
System.out.println("p1.name.equals(p2.name): " + p1.name.equals(p2.name)); // true
System.out.println("p1.price == p2.price : " + (p1.price == p2.price)); // true (primitive)
}
}Output:
=== Object Reference Check ===
p1 == p2 : false
p2 == p3 : false
p1 == p3 : true
p1 address: 1173230247
p2 address: 856419764
p3 address: 1173230247
=== What == means for objects ===
p1 == p2 means: do p1 and p2 point to the same object?
Answer: false
p1.name.equals(p2.name): true
p1.price == p2.price : true (primitive)
p3 = p1 does not copy the object — it copies the reference (memory address). Both p1 and p3 now point to the same Product object. p2 is a separate object with the same field values. The identity hash codes confirm this.
== With Strings — The Tricky Case
Strings behave in a way that confuses beginners most: == sometimes returns true and sometimes false for strings with identical content — depending on how the string was created.
// File: StringEquality.java
public class StringEquality {
public static void main(String[] args) {
// Case 1 — String literals — stored in String Pool — same object reused
String s1 = "hello";
String s2 = "hello";
System.out.println("=== String Literals (Pool) ===");
System.out.println("s1 == s2 : " + (s1 == s2)); // true — same pool object
System.out.println("s1.equals(s2) : " + s1.equals(s2)); // true
System.out.println();
// Case 2 — new String() — always creates a new heap object
String s3 = new String("hello");
String s4 = new String("hello");
System.out.println("=== new String() (Heap) ===");
System.out.println("s3 == s4 : " + (s3 == s4)); // false — different objects
System.out.println("s3.equals(s4) : " + s3.equals(s4)); // true — same content
System.out.println("s1 == s3 : " + (s1 == s3)); // false — pool vs heap
System.out.println();
// Case 3 — runtime-built strings — always new heap objects
String base = "hel";
String concat = base + "lo"; // built at runtime — new heap object
System.out.println("=== Runtime Concatenation (Heap) ===");
System.out.println("s1 == concat : " + (s1 == concat)); // false — concat is on heap
System.out.println("s1.equals(concat): " + s1.equals(concat)); // true — same content
System.out.println();
// The lesson — NEVER use == for string content comparison
// Strings from database, user input, API — always heap objects
System.out.println("=== The Real-World Problem ===");
String fromDB = fetchFromDatabase(); // always a new heap object
String expected = "active";
System.out.println("fromDB == expected : " + (fromDB == expected)); // false — bug!
System.out.println("expected.equals(fromDB): " + expected.equals(fromDB)); // true — correct
}
// Simulates reading a String from a database or API response
static String fetchFromDatabase() {
return new String("active"); // always a new heap object in reality
}
}Output:
=== String Literals (Pool) ===
s1 == s2 : true
s1.equals(s2) : true
=== new String() (Heap) ===
s3 == s4 : false
s3.equals(s4) : true
s1 == s3 : false
=== Runtime Concatenation (Heap) ===
s1 == concat : false
s1.equals(concat): true
=== The Real-World Problem ===
fromDB == expected : false
fromDB.equals(fromDB) : true
This is exactly why == for Strings fails in production: values from databases, files, APIs, and user input are always new heap objects — they are never in the String pool. equals() works correctly for all of them because it compares content, not address.
== With Wrapper Classes — The Integer Cache Trap
Wrapper classes (Integer, Long, Double) are objects. == compares their references. However, Java caches Integer values from -128 to 127 in a special pool — similar to the String pool — which makes == return true for small integers and false for larger ones. This is one of the most famous Java interview traps.
// File: WrapperEquality.java
public class WrapperEquality {
public static void main(String[] args) {
// Integer cache — -128 to 127 are cached
Integer a = 100;
Integer b = 100;
Integer c = 200;
Integer d = 200;
System.out.println("=== Integer Cache Trap ===");
System.out.println("a == b (value 100): " + (a == b)); // true — cached range!
System.out.println("c == d (value 200): " + (c == d)); // false — outside cache
System.out.println();
System.out.println("a.equals(b) : " + a.equals(b)); // true — always reliable
System.out.println("c.equals(d) : " + c.equals(d)); // true — always reliable
System.out.println();
// Why the cache exists — performance optimisation
// Autoboxing frequently used small integers reuses the same objects
System.out.println("=== Why This Happens ===");
System.out.println("Integer.valueOf(100) == Integer.valueOf(100): "
+ (Integer.valueOf(100) == Integer.valueOf(100))); // true — cached
System.out.println("Integer.valueOf(200) == Integer.valueOf(200): "
+ (Integer.valueOf(200) == Integer.valueOf(200))); // false — not cached
System.out.println();
// Same trap with Long
Long x = 50L;
Long y = 50L;
Long p = 200L;
Long q = 200L;
System.out.println("=== Long Cache ===");
System.out.println("50L == 50L : " + (x == y)); // true — in cache range
System.out.println("200L == 200L : " + (p == q)); // false — outside cache
System.out.println();
// Safe comparison — always use equals() for wrapper types
System.out.println("=== Always Use equals() for Wrappers ===");
Integer score1 = 500;
Integer score2 = 500;
System.out.println("score1 == score2 : " + (score1 == score2)); // false — unreliable
System.out.println("score1.equals(score2) : " + score1.equals(score2)); // true — reliable
// Unboxing — comparing wrapper to primitive uses == safely
int primitive = 500;
System.out.println("score1 == primitive : " + (score1 == primitive)); // true — auto-unboxed
}
}Output:
=== Integer Cache Trap ===
a == b (value 100): true
c == d (value 200): false
a.equals(b) : true
c.equals(d) : true
=== Why This Happens ===
Integer.valueOf(100) == Integer.valueOf(100): true
Integer.valueOf(200) == Integer.valueOf(200): false
=== Long Cache ===
50L == 50L : true
200L == 200L : false
=== Always Use equals() for Wrappers ===
score1 == score2 : false
score1.equals(score2) : true
score1 == primitive : true
The Integer cache is why interview code like Integer a = 127; Integer b = 127; System.out.println(a == b) prints true, but Integer a = 128; Integer b = 128; prints false. This is a guaranteed interview question — the only safe answer is: never use == for wrapper objects.
equals() — Object Content Comparison
equals() is a method inherited from java.lang.Object. The default implementation in Object behaves exactly like == — reference comparison. To make it compare content, you must override it in your class.
// File: EqualsOverrideDemo.java
import java.util.Objects;
public class EqualsOverrideDemo {
// Without override — uses Object.equals() which is the same as ==
static class ProductWithout {
String sku;
ProductWithout(String sku) { this.sku = sku; }
}
// With override — compares content correctly
static class Product {
private final String sku;
private final String name;
private final double price;
Product(String sku, String name, double price) {
this.sku = sku;
this.name = name;
this.price = price;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true; // Step 1: same reference
if (obj == null) return false; // Step 2: null check
if (!(obj instanceof Product other)) return false; // Step 3: type check
return Objects.equals(sku, other.sku); // Step 4: field comparison
// Two Products are "equal" if they have the same SKU
}
@Override
public int hashCode() {
return Objects.hash(sku); // must match equals — same field
}
@Override
public String toString() {
return "Product{" + sku + ", " + name + ", Rs." + price + "}";
}
}
public static void main(String[] args) {
// Without override — equals() behaves like ==
ProductWithout pw1 = new ProductWithout("SKU-001");
ProductWithout pw2 = new ProductWithout("SKU-001");
System.out.println("=== Without Override ===");
System.out.println("pw1 == pw2 : " + (pw1 == pw2)); // false
System.out.println("pw1.equals(pw2) : " + pw1.equals(pw2)); // false — same as ==
System.out.println();
// With override — equals() compares SKU content
Product p1 = new Product("SKU-001", "Laptop", 45000.0);
Product p2 = new Product("SKU-001", "Laptop", 45000.0); // same SKU
Product p3 = new Product("SKU-002", "Monitor", 18000.0); // different SKU
System.out.println("=== With Override ===");
System.out.println("p1 == p2 : " + (p1 == p2)); // false — different objects
System.out.println("p1.equals(p2) : " + p1.equals(p2)); // true — same SKU
System.out.println("p1.equals(p3) : " + p1.equals(p3)); // false — different SKU
System.out.println("p1.equals(null): " + p1.equals(null)); // false — null safe
System.out.println();
// HashSet deduplication depends on correct equals() + hashCode()
java.util.Set<Product> catalogue = new java.util.HashSet<>();
catalogue.add(p1);
catalogue.add(p2); // same SKU as p1 — should not be added
catalogue.add(p3);
System.out.println("Unique products in catalogue: " + catalogue.size()); // 2
}
}Output:
=== Without Override ===
pw1 == pw2 : false
pw1.equals(pw2) : false
=== With Override ===
p1 == p2 : false
p1.equals(p2) : true
p1.equals(p3) : false
p1.equals(null): false
Unique products in catalogue: 2
HashSet uses equals() and hashCode() together. Without overriding both, HashSet treats every object as unique regardless of content — which silently allows duplicates in a catalogue that should contain one entry per SKU.
== vs equals() — Complete Comparison Table
| Aspect | == | equals() |
|---|---|---|
| Type | Language operator | Method from java.lang.Object |
| For primitives | Compares values directly | Not applicable — primitives have no methods |
| For objects | Compares memory addresses (references) | Compares logical content — depends on override |
| Default object behaviour | Reference equality | Same as == if not overridden in the class |
| Can be overridden | No — operator is fixed | Yes — every class can define its own logic |
| Null safety | null == null is true | Throws NullPointerException if called on null |
| String literals | May return true (pool reuse) | Always returns true for equal content |
| Strings from DB / API | Returns false (heap objects) | Returns true for equal content |
| Integer values 127 or below | Returns true (cached) | Always returns true for equal values |
| Integer values above 127 | Returns false (heap) | Always returns true for equal values |
Used in HashSet / HashMap | Never — collections use equals() | Always — alongside hashCode() |
| When to use | Reference identity, null checks, primitives | Content equality for objects |
| Reliable for content | No — unpredictable for objects | Yes — always reliable when correctly overridden |
Null Handling — equals() vs == vs Objects.equals()
// File: NullHandlingDemo.java
import java.util.Objects;
public class NullHandlingDemo {
public static void main(String[] args) {
String a = "hello";
String b = null;
String c = null;
// == with null — always safe
System.out.println("=== == with null ===");
System.out.println("b == null : " + (b == null)); // true
System.out.println("a == null : " + (a == null)); // false
System.out.println("b == c : " + (b == c)); // true — both null
System.out.println();
// equals() with null — NPE if called on null reference
System.out.println("=== equals() with null ===");
try {
System.out.println(b.equals("hello")); // NPE — b is null
} catch (NullPointerException e) {
System.out.println("b.equals('hello') → NullPointerException!");
}
// Safe pattern — constant on left
System.out.println("'hello'.equals(b): " + "hello".equals(b)); // false — no NPE
System.out.println();
// Objects.equals() — null-safe on BOTH sides
System.out.println("=== Objects.equals() — null-safe both sides ===");
System.out.println("Objects.equals(a, 'hello'): " + Objects.equals(a, "hello")); // true
System.out.println("Objects.equals(b, 'hello'): " + Objects.equals(b, "hello")); // false
System.out.println("Objects.equals(b, c) : " + Objects.equals(b, c)); // true — both null
System.out.println("Objects.equals(a, null) : " + Objects.equals(a, null)); // false
System.out.println();
// When to use each
System.out.println("=== Choosing the right tool ===");
System.out.println("Use == null : to check if a reference is null");
System.out.println("Use equals() : when you KNOW left side is non-null");
System.out.println("Use Objects.equals(): when EITHER side could be null");
}
}Output:
=== == with null ===
b == null : true
a == null : false
b == c : true
=== equals() with null ===
b.equals('hello') → NullPointerException!
'hello'.equals(b): false
=== Objects.equals() — null-safe both sides ===
Objects.equals(a, 'hello'): true
Objects.equals(b, 'hello'): false
Objects.equals(b, c) : true
Objects.equals(a, null) : false
=== Choosing the right tool ===
Use == null : to check if a reference is null
Use equals() : when you KNOW left side is non-null
Use Objects.equals(): when EITHER side could be null
Basic Example
public class Main {
public static void main(String[] args) {
// Primitives — == compares values directly
int a = 5;
int b = 5;
System.out.println("a == b : " + (a == b)); // true
// Objects created with new — always different memory addresses
String s1 = new String("cat");
String s2 = new String("cat");
System.out.println("s1 == s2 : " + (s1 == s2)); // false
System.out.println("s1.equals(s2) : " + s1.equals(s2)); // true
// String literals — reused from the pool — same address
String s3 = "cat";
String s4 = "cat";
System.out.println("s3 == s4 : " + (s3 == s4)); // true
}
}Output
a == b : true s1 == s2 : false s1.equals(s2) : true s3 == s4 : true
How it works:
- ›
a == bcompares twointvalues directly, so it istruewhen both hold5. - ›
s1ands2are created withnew String(...), so each is a separate object at a different memory address —==returnsfalseeven though the text is identical. - ›
s1.equals(s2)compares the actual characters, not the address, so it correctly returnstrue. - ›
s3ands4are string literals, so both point to the same object in the String pool, making==returntruefor them.
Best Practices
Use equals() for all object content comparison — always. This applies to String, Integer, Long, Double, and every custom class. The only exception is primitives (int, double, boolean, char), which have no equals() method and where == is the correct tool.
Put the known non-null constant on the left side of equals(). "SUCCESS".equals(gatewayResponse) is safe when gatewayResponse might be null. gatewayResponse.equals("SUCCESS") throws NullPointerException. This one habit eliminates an entire category of null-related crashes.
Use Objects.equals(a, b) when either side might be null. This utility handles all null combinations without requiring explicit null checks. It is the cleanest option when comparing two values that might both legitimately be null — like optional fields in a database record.
Never use == to compare Integer, Long, or other wrapper objects. The cache range of -128 to 127 makes == unreliable — it returns true for small values and false for larger ones. Always use .equals() or unbox to a primitive first.
Common Mistakes
Mistake 1 — Using == for String Values From Outside the Literal Pool
// Common in login systems, API validation, configuration checks
String statusFromDB = getFromDatabase("order_status"); // new heap String
String expected = "PLACED";
if (statusFromDB == expected) { // false — heap vs literal
processOrder(); // never executes — silent bug
}
if (expected.equals(statusFromDB)) { // true — correct content comparison
processOrder(); // executes correctly
}Mistake 2 — Relying on Integer == for Values That Might Exceed 127
Integer attempts = getLoginAttempts(); // from session — might be Integer(200)
Integer threshold = 200;
if (attempts == threshold) { // false when value > 127 — intermittent bug
lockAccount(); // silently fails for high-value integers
}
if (attempts.equals(threshold)) { // true — reliable for all values
lockAccount();
}Mistake 3 — Calling equals() on a Possibly Null Variable
String role = getUserRole(); // might return null
if (role.equals("admin")) { // NullPointerException if role is null
grantAccess();
}
// Safe alternatives:
if ("admin".equals(role)) { // constant on left — null-safe
grantAccess();
}
// Or:
if (Objects.equals(role, "admin")) { // null-safe both sides
grantAccess();
}Mistake 4 — Thinking equals() Without Override Compares Content
class Ticket {
String id;
Ticket(String id) { this.id = id; }
// No equals() override
}
Ticket t1 = new Ticket("TKT-001");
Ticket t2 = new Ticket("TKT-001");
System.out.println(t1.equals(t2)); // false — Object.equals() is the same as ==
// Without override, equals() checks reference — not id field
// Fix: override equals() in Ticket class using the id fieldInterview Questions
Q1. What is the difference between == and equals() in Java?
== compares what is stored in the variable. For primitives, that is the value directly — so 5 == 5 is true. For objects, that is the memory address — so two different objects with the same content return false. equals() is a method that compares logical content. The default implementation in Object uses ==, so it also compares references. When a class overrides equals() — as String, Integer, and well-designed custom classes do — it compares meaningful fields instead. Use == for primitives and null checks. Use equals() for all object content comparison.
Q2. Why does Integer a = 127; Integer b = 127; a == b return true, but the same with 128 returns false?
Java caches Integer objects for values from -128 to 127 in a special pool (similar to the String pool). When you autobox a value in this range, Java reuses the same cached object rather than creating a new one. Both a and b get the same cached object, so == returns true. For values outside this range, Java creates a new Integer object on the heap each time, so a and b point to different objects and == returns false. This is why equals() must always be used for Integer comparison — its result is consistent regardless of value.
Q3. Why does equals() without an override behave the same as ==?
equals() is defined in java.lang.Object, the root of all Java classes. The default implementation simply returns this == obj — reference comparison. Every class inherits this default unless it explicitly overrides equals(). String, Integer, ArrayList, and other standard library classes override it to compare content. Custom classes must also override it if content equality is needed. Without an override, two different objects with identical fields return false from equals().
Q4. When should you use == instead of equals() for objects?
Use == for exactly two scenarios with objects: checking whether a reference is null — if (obj == null) — which is safe and idiomatic; and intentionally checking whether two variables point to the exact same object in memory — used in specific caching or singleton patterns. For all content comparison, use equals(). The rule: == checks identity, equals() checks equivalence.
Q5. What is Objects.equals() and why is it better than equals() in some cases?
Objects.equals(a, b) is a static utility method from java.util.Objects. It handles null on both sides without any explicit null check: if both are null, it returns true; if one is null, it returns false; if neither is null, it calls a.equals(b). This is safer than a.equals(b) when a might be null, and safer than "constant".equals(b) when both variables might be null. Use Objects.equals() when either argument could legitimately be null — like optional fields, database values that might not be set, or method return values that might return null.
Q6. Does equals() handle null arguments safely?
The Object.equals() contract requires that x.equals(null) must return false for any non-null x. Well-implemented classes — including String — satisfy this contract. However, calling equals() on a null reference — nullVariable.equals("something") — always throws NullPointerException. The method must be called on a non-null object. The constant-on-left pattern "something".equals(variable) handles this correctly regardless of whether variable is null.
FAQs
Can == ever be correct for String comparison?
Only in two specific situations: when you intentionally want to check whether two String variables are the same interned object — which is almost never a business requirement — or when comparing a String reference to null using str == null. For any check involving string content, equals() is the correct and only reliable choice.
Why does null == null return true with == but Objects.equals(null, null) also returns true?
== checks memory addresses. Both null references point to nothing — address 0 — so == returns true. Objects.equals(a, b) explicitly handles the both-null case: if both arguments are null, it returns true without calling any method. This means Objects.equals(null, null) is true and Objects.equals(null, "hello") is false — both safely.
Is it ever correct to compare double values with ==?
For exact equality of literal constants — price == 0.0 — == is technically safe. But comparing results of arithmetic — like 0.1 + 0.2 == 0.3 — is unreliable because floating-point arithmetic introduces tiny precision errors. The result of 0.1 + 0.2 is 0.30000000000000004, not exactly 0.3. Use Math.abs(a - b) < epsilon where epsilon is a small tolerance value like 0.0001.
Do records in Java automatically provide correct equals() behaviour?
Yes. Java 16+ records automatically generate equals(), hashCode(), and toString(). Two record instances are equal when all their component values are equal — the generated equals() compares all declared fields. You can override the generated equals() in the record body if you need different equality semantics.
What does the equals() contract require?
The Java specification requires equals() to be: reflexive — x.equals(x) is always true; symmetric — if x.equals(y) then y.equals(x); transitive — if x.equals(y) and y.equals(z) then x.equals(z); consistent — repeated calls return the same result when no fields change; and null-safe — x.equals(null) must return false, never throw. Violating any of these causes unpredictable behaviour in collections like HashSet and HashMap.
Summary
== stores what the variable holds. For primitives, that is a value — comparison is direct and correct. For objects, that is a memory address — comparison checks identity, not content. equals() is a method that compares logical content when properly overridden, which is what the vast majority of comparisons in application code actually need.
The two rules that prevent almost every comparison bug: use equals() for all object content comparison instead of ==; and put the known non-null value on the left side of equals(), or use Objects.equals() when either side might be null. The Integer cache and String pool are interview topics that become clear once you understand why == is unreliable for objects — the cache is an optimisation that occasionally makes == appear to work, which is more confusing than helpful.
What to Read Next
Learn where Java stores string values in memory.
Practice Programs
Put this topic into practice — each program has full code, output and a step-by-step explanation you can run online.
Compares two strings and reports their lexicographic order.
Check whether a string reads the same forwards and backwards.
Applies each relational operator to the same two numbers and prints the boolean result.