Consider the following example:
// Let's say this method lives in a class MyClass
public void doSomething(Foo foo, Bar bar) {
foo.prepareProcessing();
Worker work = foo.createWorker();
work.dispatchTask(new Task(bar), 12);
work.start();
foo.waitForWorker(work);
foo.finishProcessing();
}
Contrast it to the following:
public void doSomething(Foo foo, Bar bar) {
foo.runTaskAtPriority(bar, 12);
}
In the first example, classes Foo and MyClass are tightly coupled: MyClass not only depends on Foo, but also on Foo's dependencies, like the Worker and Task classes. Furthermore, MyClass depends on very specific internals of Foo: It has to call several methods in a certain order. It's much more likely that changing something in Foo will also require changing MyClass.
The second example only depends on Foo itself. So if Foo stops using a Worker internally, MyClass doesn't have to care. MyClass and Foo are loosely coupled.
In the end, it's all about knowledge: The more you need to know about a certain class/method in order to use it, the tighter the coupling.
"Depending on abstractions instead of concretions" is a common way to achieve low coupling. But introducing abstract classes/interfaces doesn't automatically reduce coupling - you rather need to think about ways to reduce the possible interactions (knowledge) one class has with another one.