Upcasting is storing a child object in a parent variable. Java does this automatically because it's always safe. Every Dog is an Animal.
Animal a = new Dog();
Downcasting is the reverse: taking a parent-type variable and casting it to a child type. This requires an explicit cast because it can fail.
Dog d = (Dog) a;
d.bark();
If a holds a Cat, the cast throws a ClassCastException at runtime. The compiler can't catch this because it only knows the declared type.
Always check with instanceof before downcasting:
if (a instanceof Dog) {
Dog d = (Dog) a;
d.bark();
}
Without the check, your program compiles fine but crashes when the cast is wrong.