The three dots "..." in the argument of foo() indicate that this is a method with a variable number of arguments. When foo() is called, the argument oa will look like an array. You can pass this array to Arrays.toString() to neatly format it to a string, which looks like this:
[E1, E2, E3]
where E1, E2 and E3 are the elements of the array. So in this code example, foo() simply prints the arguments that are passed to it. Now let's look at the cases:
Here, oa is an array that contains one element: the array that you passed to it. When you convert that object to a string, you get [LC;@7f5580.
Here, because ca is an array itself (and you didn't cast it to an Object explicitly as in case 1) oa is made equal to ca. Because array ca doesn't contain any elements, you only see .
3. foo(new C());
Here, oa is an array that contains one C object. Calling toString on the C object results in C@1c6572b.
In this case, oa will become null. When you pass in null, Arrays.toString(...) returns the string "null".
Now, oa is an array that contains one element, which is null.