Composition over Inheritance
I have read the GoF Design Patterns book a few times now, it is one of those things where the more you read it, the more you get out of it. Most patterns in that book can be boiled down to a single principal "Delegate all related functionality as much as possible, and always code against the interface of that delegation, not the implementation." The first benefit of this is obvious, when you delegate out single responsibilities, that code becomes a lot more straight forward to read, and easier to reuse. The second part is a bit trickier, because it is something that can take awhile before you can be bitten by it.
Circles and Ellipses
There is a huge amount of emphasis on inheritance when there is any discussion on object orientation, even though it is probably the single worst thing about the paradigm.
The first comes from a pure modeling point of view, which is more widely known as the circle-ellipse problem. A circle is an ellipse with an additional constraint put on it, which makes it a classical "is-a" relationship. We are taught that as soon as we see that, we are talking about inheritance. So what happens when Ellipse has a stretchX method that mutates one axis but not another?
You have a few options. First, Circle could override stretchX, and raise an exception if it is called on Circle. This is a pretty ugly solution, and will be a huge gotcha in the API. Another possibility is that Circle could make stretchX mutate the Y axis at the same time. Our problem here is that it violates the Liskov Substitution Principal, which states that any subclass should be able to be swapped in for its base class without breaking the program. If we are working with an ellipse, and we call both the stretchX and stretchY methods, we will not end up with what we want.
When it comes to OO modeling, a circle is not an ellipse (just as a square is not a rectangle). You cannot use inheritance to effectively model a relationship where the subclass is a constrained version of the base class, even though the way we think of inheritance would lead us down that road.
Implicit Coupling
Let's say we are modeling some sort of car and truck registration system. We have a class called Vehicle, it has a method called start on it. start does things like flip an isStarted flag, does some logging, maybe stores data about fuel levels and whatnot. This works great while we continue with trucks and cars. But what happens when the system has to be expanded to include bicycles?
A bike doesn't have an engine, and starting a bike is a very different process then starting a car. Since we chose to leverage inheritance for code reuse, we are in a bit of a pickle, since we now have many subclasses that depend on specific implementation details of start. A refactoring of start means those changes will cascade down our inheritance hierarchy, and it will turn a minor job into a pain in the ass.
Using Composition
Lets say that when we were designing Vehicle, we saw there was a group of related functionality, and delegated out to an Engine interface. First of all, by the time we have a decent number of cars, the things that start touches will probably have to handle a few different cases. Having multiple Engine implementations will clean that up substantially. Secondly, when we have to implement our bike, all it takes is NoEngine implements Engine, and we move on to the next problem.
The more ways a class can be impacted by changes in related classes, the harder it is to maintain. By eliminating coupling, you end up reducing the amount of code you have to change when modifying the system, and also increase flexibility. Finally, compositional design is WAY easier to test then anything else.
When Inheritance is a Good Idea
Inheritance is a good idea when you are not talking about a constraint on a base class, when you are already behind a more complete level of abstraction, and when your subclass is basically the same as the super class with only a small behavioral change. So in our Vehicle case, maybe you have TruckEngine implements Engine, and the only difference between implementations are in terms of fuel consumption rate. Sure, there is a tight coupling, but in this case it will save us time down the road, since there is a very small chance that our changes will effect that one piece that is different.
Looking at inheritance in this fashion ends up dramatically reducing is usage in big systems, and becomes more used to get a job done it a quick, slightly hacky fashion, rather then the primary form of polymorphism and code reuse.