In your specific case here, nothing is hidden, because you are using Outer as the type of the variable here:
Outer o = new Outer();
To hide the implementation of Outer behind abs, you can write a method that returns abc, that actually returns an Outer:
public static getAbc() { return new Outer(); }
Now in your main method:
// you should probably put your main method in another class, together with getAbc
// this way the code can better demonstrate how to hide implementation.
public static void main(String[] args)
{
    abc o = getAbc();
    o.dance();
}
Now your main method does not know anything about Outer It only knows about abc.
You can study examples in the JDK as well. For example, the Stream interface has a method called of that returns a Stream:
Stream<String> stream = Stream.of("Hello", "foo", "bar");
What it actually returns is a ReferencePipeline.Head after calling a bunch of methods in Arrays class and StreamSupport class. See the hiding here? You, as the user of the interface, don't need to know what of actually returns. You just need to know that it returns something that can perform all the operations specified by the Stream interface.
One benefit of this is that if one day StreamSupport's code are changed to return something else, your code will still work and compile because it is guaranteed that whatever it returns, will implement the Stream interface so has all its capabilities.