I haven't used much Java either, but in C# for example string (lowercase) is an alias for the System.String class, so it's probably the same with your Java example. But as it's also a keyword, in this context it is treated like a primitive type. Conventionally, you use the uppercase String when using its methods and operations.
pretty much:
int -> System.Int32
string -> System.String
...
it also allows fairly easily converting between int and object.
int i;
object o;
o=(object)i;
i=(int)o;
it is also very unlikely that Oracle would make a language change which would break pretty much all existing code.
FWIW: my scripting language also does some similar type aliasing, but actually more so, as it is 3 way: variant <-> primitive <-> class.
for example, there is a primitive type (int), a class (Integer), and a variant (fixnum).
so, for example:
var v=3; //type is variant (fixnum)
int i=v; //type is primitive (int)
s=v.toString(); //may quietly send it off to the class (*1)
s=i.toString(); //likewise (*1)
*1: partly pretend here, as implemented, ".toString()" is actually an intrinsic operator in this case, but for some other types it may actually call the "toString()" method...
side note: a fixnum is basically a tagged reference (similar to a pointer), where a few tag bits may indicate whether the reference points to an object, or holds a value such as an integer or floating-point number. this is partly because creating object instances just to convert a primitive to a reference would be unreasonably expensive, and it is much cheaper simply to shove the integer into the reference (in the JIT, for example, converting an 'int' to a 'fixnum' is essentially 2 CPU instructions, namely CDQ+XOR, or would be MOVSX+XOR for x86-64).
however, I make no claim that the language "gets rid of" primitive types, rather than the line between primitive/variant/instance being sometimes slightly fuzzy...
actually, 'string' is another example: in my case it is actually an intrinsic type (as variant) as well, but converts to a 'String' class in the off-chance this is needed. this is partly because there are much cheaper ways to store strings than as object instances (IOW: M-UTF-8 globs of bytes off in a string table somewhere). synthesis-on-demand seemed to be a generally cheaper option than having every in-language string pay the overhead of both a heap-allocated object instance and also a heap-allocated character-array.
...
Java is essentially approaching things from the opposite direction, IIRC with the more recent JVM versions internally adding a fixnum type mostly to fake the presence of an Integer instance, ... (mostly in their effort to make JRuby and Jython perform better).