I had a suspicion that I kept to myself, but now you've provided evidence that it may be correct. It's similar to an issue that C++ has, but slightly different.
String s = "foo";
I think Java is first creating an anonymous constant (read: final) String for "foo", and then copy-constructs s from it. So, when you initialize two Strings this way, they are both referencing that anonymous constant.
String s = new String("foo");
Here, s is being constructed properly, so it has its own place in memory, rather than being a reference to "foo".
I'm not entirely sure how hashCode and idHash work, but I'd imagine hashCode creates a hash based on the contents of the String, while idHash creates a hash based on the location in memory. If that's true, then my interpretation seems to follow the results of your tests.
In C++, a similar problem occurs when you try to use string literals like "foo". To provide backwards compatibility with C, string literals are actually of type const char* (or const char[], but that's a different story), meaning that they're arrays of constant characters. That means you can't do stuff like use string::at on them, because they have no member methods. The reason I say this problem is similar is because, to construct a string in C++, you actually construct a character array first, and then construct a string object from that.
New rule for this thread: You can bash Java all you want, as long as you provide an original reason for bashing it. Including a positive quality is optional and not recommended.