We group our abstractions (classes) into a hierarchy to keep them organized (inheritance structure).
The "is a" hierarchy defines classes like this: "a dog is a mammal", "an apple growing plan is a fruit growing plan which is a growing plan". This is the test to apply if you are not sure whether there is a classical inheritance relationship between classes.
As we form the class hierarchy, we push the common state and behavior between lower level classes into the upper level classes. This allows for the lower level classes (which are usually "discovered" first in an OOA) to be built on top of the higher level classes, thus making them smaller and more readily understandable.
This activity (pushing commonality up the inheritance hierarchy) makes for a generalization/specialization hierarchy. The classes at the top are more general (or abstract) and the classes at the bottom are more specific (or concrete). It is usually the concrete classes that we instantiate objects from.
Abstract classes are simply that; abstract means of organizing shared information.
Forming the hierarchy eliminates redundant code and organizes our abstractions into something easier to understand.
The "isa" hierarchy is a compile-time hierarchy, in that it involves classes extending other classes.
The other type of hierarchy is the "part of" hierarchy we find in our designs. This is hierarchy through aggregation.
Objects would be overly large if it was not possible to aggregate them. For example, a Graph object quite naturally contains a List of nodes in the graph. Without this ability, the List class would have to be duplicated in Graph.
The "hasa" hierarchy is another means of code organization.
The "hasa" hierarchy is a run-time hierarchy, in that it involves objects containing other objects.