These terms refer to casting an object reference (i.e. a variable, return value, etc.) to a subclass or superclass of its declared type. If you view an inheritence hierarchy as a tree with Object at the top and all subclasses of Object as its children and so on, then upcasting is when you have a reference to a child and cast it to a parent class or interface type. Downcasting is the reverse: converting a parent reference to a child. Upcasting is perfectly safe and often can be done automatically. However, downcasting is not completely safe since the compiler cannot know what the actual type of the object is.
Perhaps I can illustrate with some examples from the Collections Framework:
You will probably see this quite often when using Collections. Notice that on the right hand side, we create an ArrayList object with the keyword new. The result is a reference to this ArrayList object. But we are assigning it to a reference to List instead. The compiler will perform an implicit downcast here since ArrayList implements the List interface.
Prior to Java 1.5, all Collections could only store objects as Object references. (Generics has changed this, but that's another issue and beyond the scope of this discussion.) In such a situation, you often have to explicitly downcast an object when you get it out of the Collection. As an example, we will use the List l declared above:
Since List.get() is declared to return an Object, we must cast it to a
String if there is actually a String at index 0 and we want to manipulate it as such. This is a classic exmaple of downcasting since the compiler cannot know what the actual type of the object that is returned by the get() method.
I hope this clarifies the differences between upcasting and downcasting. If you still have any questions, feel free to ask for clarifications.
Layne