== is symmetric; that is to say, for any values x and y, (x == y) == (y == x).  This is a guarantee provided to us by the JLS §15.21.1 for numbers, and §15.21.3 for reference types (or everything that isn't a primitive value).
It could also be seen as transitive, in that if three values x, y, z exist, and x == y && y == z, then x == z.  This is again provided by the same JLS  specification - merely repeated to mitigate the issue of the common variable y.
The real problem here comes with regards to autoboxing; when you go to unbox null, then by the JLS, you're going to get a NullPointerException - independent of the comparison operation you're going to do next.
Effectively:
- You have a boxed primitive type on one side of the comparison, and a primitive on the other.  The value of either isn't yet considered. 
- Given that the value of the primitive will force numerical comparison due to it being a boxed primitive, Java will then try to unbox the boxed value. 
- You can't unbox - null, hence- NullPointerException.
 
This is (kind of) where equals() steps in - by its contract, two non-null instances must be equivalent to each other if they are indeed the same thing.  If either (but not both) of these values are null, then they're not the same instances.
I say "kind of" since there's really nothing to enforce the supposed contract on Object#equals; you could (with some effort) write an asymmetric equals() method, although one would wonder why you would want to.