This week's book giveaway is in the OO, Patterns, UML and Refactoring forum. We're giving away four copies of Refactoring for Software Design Smells: Managing Technical Debt and have Girish Suryanarayana, Ganesh Samarthyam & Tushar Sharma on-line! See this thread for details.
The short answer is "because this is Java, not C++". The long answer is that you have to keep track of what type of reference you are using at any given time. Consider the line "c1.m1(c4)". c4 is a C reference. c1 is an A reference. A objs have method m1 that takes an A argument c4 gets converted into an A reference the c1 method gets invoked This isn't like C++ where you invoke according to the most resolved type, one of those subtle language differences. If class A had an overloaded method that also took a C reference as an arg, then you'd get the behaviour you expected. Resolution is done in terms of information available at compile-time, and so the definition of A (the reference type) determines what gets invoked. Much the same argument for the other two statements.
Here is my take on it... In the C class m1 is an overloaded method not an overridden method. Overrides must have the exact same signature as the original. Thus the call from the A object reference call can only invoke the method in class A. If the m1 method in class C was called p1 it wouldn't be confusing...but with a different signature it might as well have a different name. Any method call from the A object reference not defined within that class = you can't get there from here. Alternate version of the class with comments...took out some of the stuff that didn't seem? to matter.
"...the reference type (not the object type) determines which overloaded method is invoked" - Sun Certified Programmer & Developer for Java 2 Study Guide (Exam 310-035 & 310-027)