True, but partially people would do stuff that actually created larger code.
F.i. (I> 2 ? 3 : 4) was actually a couple of bytes larger then an if/else structure. It looks smaller but it was not.
(dunno about >= jdk1.5 as we could only use 1.4)
There was also a problem when people would access fields from inside a thread, causing the compiler to create access methods.
(remembering all this out of memory, could be that the fields were private and that was what caused the added code)
Another thing you can do is when you have large data arrays, to load them from a file during runtime. You could pack the data better this way.
Also remove file extensions (at least it works on mobile) and subdirs.
If you are really going for gold, dump all data into a file and also load them during runtime. Also packs better.
Dumb question: Were you allowed to use an obfuscator?
Edit:
Have you tried removing/changing the field visibilities? IIRC that can help a little as well.
Edit2:
K looked into your code for the Abducer 4K and you had already stripped it down very well.
You can replace 2* and similar code segments with binary shifts, saves about 2 bytes per. Though I found it only effects segments where additional calculations are done.
example:
2kernelSize > kernelSize<<1 = no effect
2kernelSize-2 > kernelSize<<1-2 = 2 bytes
Not much but sometimes every little bit helps