Write once, run once
Slowly, yet surely, J2ME is driving me completely mad. One of the nicer features of Java once write-once, run-anywhere - sure, you often need to arse around with user-interface tweaks (Swing for GTK+ and Mac OS X being prime examples), or with application server differences, but there's a decent chance your business logic will run correctly. With J2ME though, you seem to be in deeper and more dangerous waters.
Embedded devices do differ dramatically - far more than a PC differs from a Mac. So a JVM sounds like a superb idea - abstract away your hardware and run in a nice virtual environment. Except it hasn't worked out like that. While you always need to cope with differing input methods and screen sizes the problems are far more fundamental - fragmentation and just plain differing implementations.
J2ME is not one standard. It's not even two or three. Most mobiles will implement the CLDC and MIDP profiles - but which version? And which combination? What about the CDC? Will they have support for the various JSRs like web services or Bluetooth? Even vendors like Intent, who produce a VM for Windows Mobile, offer the JSRs as add-on modules, leaving it up to the OEM to decide just what the VM they supply will offer.
Secondly, the differences in implementations come back to haunt you. For instance, I have been writing a tree component. Easy in principle. Hard in practice. What worked nicely on the Sun emulator didn't draw most of the time on the MPowerPlayer emulator. This proved to be my bug - a badly placed return in the paint method - but it still doesn't show a damn thing on the Intent VM on my TyTN. Why? Who knows. Frankly, trying to debug the WM VM offers more pain that I'm interested in for a learn-J2ME project. I imagine it does keep those with products to deliver on their toes though.