I had to do this once and ended up using the UUID inside SMBios.
In my case, I used C in unix, not Java, but you might be interested in reading the SMBios specification and take a picture while reading the UUID, which should be slightly better than reading the MAC serial numbers, hard drives, etc. , because the user can replace them more often than bios.
EDIT : Updated to add what we discussed in the comments (just to have everything together in one place).
On virtual machines (VMWare, that is), the UUID is generated from the UUID of the physical machine and the path to where vm "lives" (see http://www.vmware.com/support/ws55/doc/ws_move_uuid_format.html )
For Java, I know there javax.realtime.RawMemoryAccess , which allows you to read and write memory. I have never tried this, but it seems like the right way to do this with Java (if anyone has experience with this, please comment!)
Otherwise, there is a non-portable solution: JNI , which means C ++. As I said, my only experience was on unix systems, and in my particular case, I found the dmidecode source quite useful. For windows, you can try this SO question , which may be helpful.
Yes, I know that at first it might seem like a lot of research is needed :) But I assume that by reading the specification and (if necessary) looking at the source of dmidecode, you can do it and will (in my experience) give better results " in the field "than hash serial numbers from disks or network cards.