I am a big believer in reuse as a way to improve software engineering (and please ignore my penchant for rolling my own solution to problems because existing ones don't satisfy me). The reason that cars are relatively affordable and reliable is that the same robots that built your car also built millions of others. So once you have created the factory that builds the Ford Pinto, you can cheaply churn out millions of them and have some confidence that they will behave like the first.The power of inheritance is that it provides a well defined way to program against abstractions. When you program against abstractions, you decouple your code from its context. Code that can be decoupled from its context can be reused. Reuse is the key to multiplying productivity.
Software reuse has been around almost as long as software. A classic example is printf. Almost all of structured programming is about writing code that can be used and reused by at least other sections of the same program, if not by other programs. So reuse in and of itself is not novel. However, there are limitations to this type of reuse. In many cases you may want to do almost the same thing as the given library call but slightly differently (fprintf vs. printf). The challenge in writing reusable code is to write code that can support these differences without you having to know them up front, and without the user of the code having to actually edit it themselves.
Runtime Chosen Library
Sometimes when you are writing code you know what you want to do, but not how. When I took driver's ed they didn't know if I was going to be driving a Pinto or a Chevy Nova, but they were able to teach me to drive anyway. A standard example of this in Java is JDBC. You program against the common interfaces from JDBC and at runtime you load an implementation which inherits from this interface. Now you have the ability to replace the JDBC library you are using with little to no coding changes.
The most complicated, but potentially most powerful, type of library is the type that is configured by the calling program. Imagine you want to use your Pinto assembly line to make Mustangs. It would be efficient if you could just reuse the assembly line, but pass it the robots which are configured to make the Mustang instead. A common Java example of this is the Arrays.sort method. If you want to sort an array of Objects in a non-standard way you can just write a class which implements a Comparator and then take advantage of the built in sort. The Comparator is probably simpler to write and easier to test than your own sorting algorithm, especially if it has been a while since CS102. By using this inheritance the sort method gets to be reused in contexts that the original author knew nothing about.
How Not to Use Inheritance
A common thought among people first learning inheritance is that it is useful for reuse because it lets you get the functionality of the base class in the derived class. I know I thought this at first, and as a result wrote some horrendous code. In general when this is done, it is probably better to aggregate the base class rather than inherit.
Inheritance is Just a Tool
Inheritance is a tool in our software engineering arsenal. It is important to use it wisely, because like any powerful tool it has the ability to reek havoc. When misused inheritance hierarchies can obfuscate code and make it next to impossible to understand what is happening. However, when used well it can simplify code both now and, more importantly, in future maintenance.