It was a Java design decision, and one that some consider a mistake. Containers want Objects and primitives don’t derive from Object.
This is one place that .NET designers learned from the JVM and implemented value types and generics such that boxing is eliminated in many cases. In CLR, generic containers can store value types as part of the underlying container structure.
Java opted to add generic support 100% in the compiler without support from the JVM. The JVM being what it is, doesn’t support a “non-object” object. Java generics allow you to pretend there is no wrapper, but you still pay the performance price of boxing. This is IMPORTANT for certain classes of programs.
Boxing is a technical compromise, and I feel it is implementation detail leaking into the language. Autoboxing is nice syntactic sugar, but is still a performance penalty. If anything, I’d like the compiler to warn me when it autoboxes. (For all I know, it may now, I wrote this answer in 2010).
A good explanation on SO about boxing: Why do some languages need Boxing and Unboxing?
And criticism of Java generics: Why do some claim that Java’s implementation of generics is bad?
In Java’s defense, it is easy to look backwards and criticize. The JVM has withstood the test of time, and is a good design in many respects.