Overloading is having multiple methods with the same name, but with different signatures (the argument types). This is resolved at compile time. The compiler works out which one to call based on the reference type.
Overriding is when you inherit a method, but redefine it (with exactly the same name and signature) in the subclass. Overridden methods are resolved at run-time (which is where
polymorphism comes in), and is determined by the actual type.
So in your case 1, the reference type is
Animal.
Animal has an
eat() method, so the compiler is happy. Then, at run-time, the JVM realises it's actually a
Horse and calls the
Horse version of
eat(). The output should be "Horse eating hay".
In case 2, the reference type is
Animal.
Animal does not have an
eat(String) method, so the compiler is unhappy. The compiler doesn't try to work out what the actual object is, all it knows is that it's an
Animal. So it can't allow that call.
Does that help?