Java is my most recently acquired programming language. I cannot yet claim fluency, but that fact alone is not enough to prevent me from having some strong opinions. For the most part, I enjoy programming in Java. It strikes me as a language which is a reasonable compromise between (say) C++ on one side and Smalltalk on the other. By that I mean that it retains the strong-type checking (mostly) of C++, and the dynamic behaviour (mostly) of Smalltalk.
I can't resist but to moan about some features that I wish were in Java. Actually, I could go on at length, but my most-wished for feature is the ability to return multiple values from functions, and my least desired non-feature (bug?) is the unfortunate rules for resolving (that is, prohibiting) member name collisions when implementing two or more interfaces.
Virtually every language I have used support some way to return multiple values from functions -- at least those languages that have the 'function' concept. Lisp supports the concept directly. In C, you can fake it by passing pointers to variables that receive the values. In Java, there is no simple way to achieve this -- but there are work-arounds. I see quite a few people have a function which returns an array of values (the old pre-Common Lisp trick). Simulate the C trick by passing in single elements arrays to receive the values. Still others declare a class whose sole purpose is to hold the return values. All of these gimmicks require dynamic allocation of storage to perform the value return. With language support, value return could be performed on the stack. Of course, compilers are getting smarter all the time -- maybe a good Java compiler could detect the case and optimize. Now, if we could only get rid of the large per-class overhead in Java...
I won't get in to the debate about whether Java should have allowed proper multiple-inheritence or not (oops, just did), but there is an annoying restriction in Java when it comes to implementing multiple interfaces. If two interfaces declare a member with the same name, it is flagged as an error! I would rather that in such an event the programmer would be forced to explicitly disambiguate the conflicting members by prefixing them with their class names. This exact strategy is actually used for similar purposes in the Java 1.1 inner class features. Of course, the implications now are significant. Not only would the compilers have to handle this case, but (I believe), the VMs as well.
Of course, I can pile on many, many, more requests, like multi-methods, templates, better namespace management, and on and on, but you can't have everything. Or you can, but then it gets stuck in the ANSI and ISO committees for years and years and years.
For all my moaning, Java is definitely a 'fun' language to use. It feels very comfortable to me, reminiscent of the old MacPascal days...