This is easier to understand if you approach it gradually. Consider this code:
In your imagination, the array might be a box with three compartments (like a bookcase lying down), with one int in each compartment. Now consider:
It's tempting to picture this array as a box with three compartments, with a
String in each compartment. But that's not exactly right. A String is an object, not a primitive, and in Java we always relate to objects via references. So the array's compartments actually contain references to Strings, rather than strings.
Ok, now for the "2D" case:
String[][] argCopy = new String[2][2];
[CODE]
This declares that argCopy is an array that contains two elements. Each is a reference to an array of strings. So argCopy[0] is a reference to an array of strings. (And that's just a convenient way of talking about it. We understand that really there's no such thing as an array of strings ... there's really an array containing references to strings.)
When the "argCopy[0] = args" statement executes, the reference in argCopy[0] is replaced by another reference. That new reference has to be of type String[], otherwise the code won't compile. But the new String array can be of any size. In the code you sited, the new size was 3.
Hope this helps.
-- Phil