If you don't know who Josh Bloch is, then search on amazon for his books.
Personally, I have three favorite "features" on Java:
- All the rich data-structures and collections that just plug-and-play into your code with ease, particularly the ones on java.util.concurrent
- The tools available in Java, to code, debug, profile, analyze (Eclipse, Netbeans, etc)
- The Memory-Model. Java's memory-model is so good that C11 and C++11 are copying it. I just wish that we could specify the the memory model on a per-operation basis like the atomics in C11 and C++11, because binding the synchronization to a variable is too much of a constraint when writing performant concurrent algorithms, i.e. you don't always want to access a given variable with acquire/release barriers, sometimes we just want the "atomicity" without the synchronization guarantees, but oh well, Java atomics are easy to use, so that makes up for it.
If you think the bad parts of Java are bad, then "you ain't seen nothin' yet", just take a look at some the bad parts of C described in this paper:
The one on section 2.6 is really scary. After reading that, how can I ever trust the code generated by a C compiler ever again? Herb Sutter had already shown that compilers can re-order our code but that if you kept to single-threaded programming then you should be safe, but this is just insane. Just read it and judge for yourselves.