Your chart contains many things that are incorrect.
The == operator
== really always means: "compare values", but you have to be carefully think about what this means for primitive types vs. reference types.
For primitive types, it's easy: it just compares the values.
For reference types, it also compares values, but it compares the values of the
references themselves, and not the values contained in the objects that the references point to. So, an == comparison between references is only true if the two references point to the exact same object. It's false if they point to two distinct objects, even if the member variables of those two objects have the same values.
Note that there is no special handling for strings or wrapper classes with regards to ==. These are handled just like any other reference type. So the columns in your chart for strings, wrappers and other types are incorrect.
The equals() method
equals() is just a method, like any other method. What it means depends on the implementation of the particular class of the object that you're calling it on.
The implementation in class Object does the same as ==. So, if the object you are calling it on is of a class that doesn't override the equals() method of class Object, then equals() behaves the same as == (see above).
Of course, many classes, including String and the wrapper classes, do implement equals() in such a way that they compare the content of the member variables of the objects. So, for example, if you have two separate String objects with the same content, then a.equals(b) will return true.
Things that make it confusing
The rules above are how it works, but there are some optimizations that make it seem like these rules are sometimes broken. In reality the rules above are not broken, it just seems that way.
String literal optimization
For example, the compiler optimizes how string literals are handled. If you use the same string literal more than once in one program, then the compiler is smart enough to detect that and create just a single String object for that literal, which is reused. So, if you create two variables with two times the same string literal, then == between them will be true - because there is, in fact, only one String object that both variables refer to.
Wrapper type optimization and autoboxing
This one is confusing for a lot of people, and you can only understand it if you know how
autoboxing works and about a specific optimization that's present in the wrapper classes.
First of all, autoboxing. If you assign a primitive literal value to a variable of a wrapper type, then that is automatically translated to a call to WrapperType.valueOf(...). For example:
Then, the second part. The valueOf(...) method of class Integer doesn't simply return a new Integer object every time you call it. If the value is between -128 and 127, it will return an object from an internal cache. This is an optimization to avoid creating lots of unnecessary objects, for values that are supposedly frequently used. Note that if the value is outside of the range of the cache, it will return a new object.
The == operator reveals this:
As you can see it seems like == on Integer objects sometimes does value comparison, and sometimes does reference comparison. But this is not really so. It always does reference comparison, but due to the optimization, you get the same Integer object if it's in the range of the cache.