Java maintains a special memory region called the string pool. When you create a string with a literal like "hello", Java checks the pool first. If "hello" already exists there, Java reuses that same object instead of creating a new one.
String a = "hello";
String b = "hello";
System.out.println(a == b);
This prints true. Both variables point to the same pooled object, so even == returns true here. But if you use new String("hello"), Java creates a separate object outside the pool:
String c = new String("hello");
System.out.println(a == c);
This prints false. You can force a string into the pool with .intern(). After calling c.intern(), the returned reference matches the pooled version. The pool exists to save memory when your program uses the same text repeatedly. But you should still always use .equals() for comparisons, because relying on == breaks the moment a string is created outside the pool.