You do have the ability to control object destruction in Java. It simply uses a different idiom:
Connection conn = null;
try {
conn = ...
// do stuff
} finally {
try { conn.close(); } catch (Exception e) { }
}
You could point out at this point that this isn't object destruction and that you could, for example, pass that object to something else and still have a reference to it. You are right on both counts. It is simply as close as Java (and most managed platforms) get.
But no Java does not, as you say, have a destructor mechanism as in C++. Some people mistake finalizers for this. They are not destructors and it is dangerous to use them as such.
Memory management for a programmer is hard. You can easily leak memory, particularly when doing multithreaded programming (also hard). Experience has shown that the cost of GC, while real and sometimes substantial, is well and truly justified in productivity increases and bug incidences in the vast majority of cases, which is why the vast majority of platforms now are "managed" (meaning they use garbage collection).