Polymorphism in Java allows a single action to be performed in different ways, categorized mainly into compile-time and runtime polymorphism. Compile-time polymorphism is demonstrated through method overloading, while runtime polymorphism is realized via method overriding, where the method called is determined at runtime based on the object type referenced. Additionally, it involves concepts like upcasting and the limitation that polymorphism applies to methods, not data members.