I don't see any problems with the inheritance and polymorphism relevant to your design. (Except I would say that squares, circles, rectangles are shapes so maybe use inheritance instead). There might be something about Java syntax you're missing though.
Consider any declaration:
A test = new B()
The first keyword, A, tells the computer what the type of variable is. In this case, you guarantee the computer that whatever your variable refers to is going to be an A. The second keyword, test, is just the name of your variable. The third part, = new B() tells the computer that you're assigning test to refer to a new object of type B. As long as B is an A, you're fine.
Now let's consider the first example, with Square and Shape. If you say
Shape s = new Square()
you are telling the computer that s is reference to a Shape - i.e. "Whatever s is referring to is always going to be a Shape. When you set it = new Square() you are saying that you want it to reference a Square. This is fine because a Square is a Shape. However, you cannot then say s.side = 3 because s you have only guaranteed the computer that s refers to a Shape and not all Shapes have just 1 side length.
Suppose for sake of contradiction that you should be able to access s.side. Then how would you reconcile these two code blocks?
Square s = new Square(); s.side = 3; System.out.println( s.side ); Shape s = new Square(); s.side = 3; s = new Circle(); System.out.println( s.side );
Clearly a Circle does not have a side which would "circumvent" our logic.
On the other hand, if you say
Square s = new Square();
You guarantee the computer that whatever s refers to will have a side property, and so you are allowed to access s.side.
Addition: Specific answer to your question "So, I am missing some design details?"
I don't think you are missing design details. I think you are just not quite getting what the code means. For example, there are cases where you want to say
Animal a = new Dog();
and there are cases where you want to say
Dog a = new Dog();
In determining what type identifier (Animal or Dog) you want to use, you ask yourself the question, "Am I operating on Animals or Dogs?" If you're operating on animals, you say
Animal a = new Animal();
Then, you could do more complex operations. For example, if you had a list of Animals and all Animals had a method live(), then you could go through the list and make all Animals live() without having to worry if its a Dog a Cat or a Camel. Code:
ArrayList< Animal > listOfAnimals = new ArrayList< Animal >(); //populate the list for ( Animal a : listOfAnimals ) { a.live(); }
If you're operating on only dogs (e.g. you need them all to bite), then you have to use the Dog identifier. Code:
ArrayList< Dog > listOfDogs = new ArrayList< Dog >(); //populate the list for ( Dog d : listOfDogs ) { d.bite(); }
What problems would arise if instead, we declared ArrayList< Animal > listOfDogs = new ArrayList< Animal >(); and made every element bite()?