The semantics of the String object are such that if two instances are ever observed to contain the same sequence of characters, they will always contain the same sequence of characters and one could--at least from the standpoint of the String object itself--replace all references to one of the strings with references to the other without changing program semantics. Such instances may be considered equivalent, because for all practical purpose, the only information encapsulated in an instance of String is the sequence of characters contained in the target instance.
By contrast, a variable of type StringBuffer encapsulates not just a sequence of characters, but also the identity of a particular instance. If two variables refer to the same instance, changing to the instance referred to by one variable will affect the instance referred to by the other (since it's the same instance). If they refer to different instances, changes to the instance referred to by one will not affect the instance referred to by the other. The decision not to have Java's StringBuffer override equals wasn't a result of laziness, but was rather based upon the fact that StringBuffer objects have a meaningful identity, and disjoint instances always have different identities.