The concept to keep in mind is that == is a simple comparison of values.
For primitives, values are straightforward (see note below regarding NaN).
For objects, the "values" are references to the instances. So in comparing objects using ==, the result will be true if and only if the instances are the same.
There are special cases in which the same instance is "re-used." For example, the String literal pool (see Strings Literally) and autoboxed wrappers of booleans or non-floating values within the range of -128 through 127 (see JLS - 5.1.7).
The Object class implements equals as a comparison of instances (like ==). Therefore, if equals is not overridden, then this is the default implementation. Otherwise, how equals works depends entirely on how it's implemented for a specific type.
Note: Equality testing of the primitive NaN (not a number) always results in false. The only exception is NaN != NaN, which returns true.
"We're kind of on the level of crossword puzzle writers... And no one ever goes to them and gives them an award." ~Joe Strummer sscce.org
For my next feat, I will require a volunteer from the audience! Perhaps this tiny ad?