With your first example:
String y = "Hey";
Java automatically interns string literals such as this (JLS section 3.10.5):
Moreover, a string literal always refers to the same instance of class
String. This is because string literals - or, more generally, strings
that are the values of constant expressions (§15.28) - are "interned"
so as to share unique instances, using the method String.intern.
So when you call x.intern(), you get the interned copy of "Hey", so they are the same object and == returns true.
But in the second example, b.intern() is a method call that is evaluated at runtime, and Java's concatenation operator will return a new String (not interned), which is a different object than the string literal "hey" (interned already), so == returns false (different objects).
EDIT
To clear up what happens with the string concatenation, turn to JLS Section 15.18.1:
The result of string concatenation is a reference to a String object
that is the concatenation of the two operand strings. The characters
of the left-hand operand precede the characters of the right-hand
operand in the newly created string.
The String object is newly created (§12.5) unless the expression is a
compile-time constant expression (§15.28).
But, b.intern() + "ey"; is not a compile-time constant expression, so the resultant String object has not been interned, and == will detect that it's a different object than the interned "hey".