
it feels old (C-like; primitive static typing that's tied too closely to what the hardware considers a "type") while trying in vain to be new (fancypants OO, "cleanly" designed, and recently adding on newer notions like generics). this, too, adds to the schizophrenia.
its class library (standard library) is badly designed. that's only a minor gripe, as i'm unaware of any really well designed libraries of the type and on that scale; slip-ups are inevitable in such a mass of code. but at very least its maintainers and designers could deign to prune out deprecated old junk, instead of just calling it deprecated in the docs and leaving it there to clutter up documentation and address space alike. i say call it deprecated for two, at most three, releases and then axe it already!
it tries, at times, to do the multi-paradigm thing. the standard library has things in it for "MVC" programming, and parts of it are written on that notion; it's got things in it for much less structured design, and bits of it are quite unstructured indeed. it does both threading and asynchronous I/O. there are i don't know how many different ways to split, assemble, join, and otherwise mess with, the common string datatype, and more keep getting added. what isn't there is a single, overarching vision to give the whole some semblance of focus; instead it rambles all over everywhere trying to do it all at once.
the only language that ever did multi-paradigm tolerably was Common Lisp, and look at what a mess that turned out to be. Java is worse. it tries to hold the users' hands and restrain them from doing "naughty" things in the true tradition of a bondage-and-discipline language (no operator overloading for you!) while failing to impose this on the language itself ("+" means "add primitive integers", except when it means "concatenate strings", and arrays are a kind of class-yet-not a class that i'm sure no mere luser could write the likes of however hard they tried). the hypocrisy becomes annoying quickly.
it sees the usefulness of fewer restrictions in theory -- there's the "reflection" API for metaclass hacking -- but it doesn't have the sense to take this seriously -- the reflection API is far too much a pain to actually use much. (plus, "security managers" can nullify much of the real usefulness of reflection. i'm sure there's some Turing rat-hole in that wall, too, if one only poked around enough, but the very notion of trying to make your language less useful disgusts me too much to put in the effort.)
it goes on like this throughout the whole story, trying to play both sides of every trade-off it encounters. the result is a thoroughly unsatisfying language, more verbose than COBOL while at the same time more confused and confusing. don't use unless forced.
Java has a split personality. it doesn't know whether it wants to be high-level enough to manage your memory and collect your garbage so you don't have to worry about it, or whether it wants to be low-level enough to force you to dick around with variable types and worry about the differences between a char and a Character. it should pick one or the other, and then throw out the cruftiness of trying to support the other half.
Java is verbose. verbose verbose verbose verbose. also it forces you to type too much. that might be somewhat acceptable in a low-level, close to the metal, systems language where you really do want to specify exactly what you want to happen to precisely which part of the machine...
Java suffers from an edit-compile-run cycle. well, that's not a huge complaint, because so does any language that has such a thing. it's just that, well, that's yet another indication that this doesn't want to be a truly high-level language where you can spend your time thinking about the problem and dinking around with possible solutions to it, testing them out as soon as you type them in. instead, languages like these are low-level ones where you write out a whole program, run it through an entire toolchain, then see if it'll make the iron glow red or not. which isn't necessarily a fatal flaw, if there's a real need to pander to the idiosyncracies of the iron and you really want to make it glow as bright red as you can, even at the expense of trainwrecking your thinking a lot...
it's a featureful enough language to drink the OO kool-aid and do the memory management mambo, yet it's not a featureful enough language to have any more complicated a datatype than an array built-in. want a list? go look in the standard library for a class that implements something like it. if you find something useful there, you have to use verbose verbose verbose function-call syntax to interact with it that makes your list feel like anything but a natural, first-class feature of the language.
...which is yet another worry which the language refuses to handle for you, forcing you to think about one more petty detail that's nothing to do with your problem at hand or the solution you're writing for it. i can do high-level, or i can do low-level; Java tries to make me do both, and it's making me feel schizo...
you can't pretty up the syntax, because the language doesn't let you overload operators - much less define your own. apparently, that'd be too much power for regular programmers to handle. unlike the programmers who wrote the java standard library; they overloaded operators when they damn well felt like it. what i feel is that there's some sort of deep, damning fundamental disconnect there - that there's something Just Plain Wrong(tm) about a language whose standard library cannot be readily written in the language itself. its power, such as it may be, is distributed somehow wrongly, leaving it lopsided in some curious but probably significant way which i do not like, sam i am.
a high level language will - for example - not force me to worry about whether a number is an integer, a rational, a float, or some truly ginormously-sized version of one of the above; it'll just do math on numbers, and insulate me as far as possible from implementation details of the bare-naked machine. that way, i can think about the math my program is doing, instead of worrying about how the math is carried out. a low level language forces me to think about how the machine is running, instead of merely what it's running, and will frequently force me to write my own implementation of any feature i want to use which the machine can't do more or less directly. Java hasn't picked sides yet. maybe we should leave it to brew another ten years, see if it's done by then.
The cost of living hasn't affected its popularity.