I was learning hashcode in more depth and figured that:
1. If you override equals(), you must override hashcode() too.
2. To find if
== (used on objects rather than on primitive values) tests whether 2 objects are actually the same object; it compares whether the pointers are actually pointing to the same memory location.
.equals() is defined by the object itself.
String s1 = new String("Hello");
String s2 = new String("Hello");
boolean b1 = ( s1 == s2 ) ; // false: s1 and s2 point to different objects
boolean b2 = ( s1.equals(s2) ) ; // true: s1 and s2 both represent the same
// piece of text - "Hello"
.hashCode() is an optimisation trick (in most of its uses, anyway). A lot of code in the standard libraries makes the assumption that if o1.equals(o2)==true then o1.hashCode()==o2.hashCode() and that if o1.hashCode()!=o2.hashCode() then o1.equals(o2)==false in order to work faster.
The most obvious example of such an optimisation is the HashMap class. This makes retrieving objects using a key really fast, but breaks badly if hashCode and equals don't work properly for the key elements. In fact, this is one of the reasons that the String class is immutable: if you were able to modify a String (and so change its hashCode) while that String was the key in a HashMap, then you would never be able to find it, since you would end up looking for it in the wrong place!
Other answers recommend Effective Java by Joshua Bloch. If you are asking such questions, then now is the perfect time in your career to buy the book and read it cover to cover. It'll also be worth re-reading it in a year or two's time, when you'll have forgotten some of it and more of it will make sense...