AOP is designed to cover what it refers to as cross-cutting concerns, that is functionality that is required by many objects within a system but which is not core to the concerns of those objects. If such requirements are met by coding the cross-cutting concern into objects throughout the system, we end up with a messy, hard-to-maintain implementation split across many objects.
The classic example of a cross-cutting concern is logging: logging is essential to a real, live system and needs to be implemented throughout that system, but is not really a concern of the objects within that system: the system may need a sales-tax calculation object to implement logging but logging is not really a concern of the sales-tax calculation object itself. AOP allows us to specify these system-wide (cross-cutting) requirements separately to our main business logic and then weave the two together, either at run-time or compile-time.
AOP works by intercepting calls to methods on objects. The interception points are known as pointcuts and the intercepted method is the advised method, with the code being advised on the intercepted method being known as the advice. I am only familiar with AOP via Spring.Net's AOP Framework which allows you to specify and apply pointcuts and advices both via configuration files and programmatically. Spring.Net AOP has four types of advice: before, after, around and throws which are invoked on interception of an advised method before the advised method is invoked, after it is invoked, both before and after its invocation and when an exception is thrown respectively. Whether applied via configuration or programmatically, the advised method has no knowledge of Spring.Net AOP or even that it has been advised.
Another example of where AOP is useful is transactions. If we try and implement this in code within objects we end up with objects that have to be aware that they are acting within a transaction which is, to my mind, not a desirable design feature. AOP allows us to create and co-ordinate transactions externally to the objects taking part in the transaction, which can give a much more stable, maintainance-friendly design.
The Spring.Net documentation is very good at explaining AOP in general and Spring.Net's implementation of AOP in particular and contains many examples. It is well worth a look even if you are not considering using the Spring.Net AOP framework.