Varargs (variable arguments) do indeed get packaged into an array when your program is run. There is no functional difference between making a call like this
public int add(int... addends);
and this
public int add(int[] addends);
Java will create an int[] for you and store it in the variable addends in the former. In the latter, you make it so the caller of the add function has to create the array themselves. This can be a bit ugly, so Java made it easy. You call the varargs method like so:
int sum = add(1,2,3,4,5,6);
And you call the array method like so:
int sum = add( new int[] {1,2,3,4,5,6} );
Varargs can be used for more than just primitives, as you suspected; you can use them for Objects (of any flavor) as well:
public Roster addStudents(Student... students) {}
A quote from the article linked:
So when should you use varargs? As a client, you should take advantage of them whenever the API offers them. Important uses in core APIs include reflection, message formatting, and the new printf facility. As an API designer, you should use them sparingly, only when the benefit is truly compelling. Generally speaking, you should not overload a varargs method, or it will be difficult for programmers to figure out which overloading gets called.
Edit: Added example for using Objects