Boy, this one has been hashed over again and again. I don't claim to be presenting anything original here, but these are important things to understand in any technical discussion of Java.
Java as a whole consists of three distinct, but interlocking, parts.
Very little in Java is completely new, but part of its power is the way that it brings together all these ideas into a single development platform.
Java is an excellent object-oriented language that has borrowed many of the good bits from previous languages without carrying too much baggage along with them.
It is a general-purpose language; you could write pretty much anything in it. Sun's javac compiler is written only in Java, for eample. It's better for some things than others, of course. I'll address that later.
Syntactically, Java resembles C for the most part, and has pretty standard C looping constructs and operators. Architecturally, it resembles SmallTalk more than C++, since it includes garbage collection, and (except for primitives) everything is a first-class object.
Java is stricter than C in some respects; for example you must use a boolean expression in conditionals, not integers or pointers or what-have-you. On the whole, however, it manages to be both cleaner and more forgiving than C without being as annoying as, say, Pascal to do real work in. It seems to already be replacing Pascal and C as a first-language in universities; the language is set up so that a good compiler can catch most of the stupid mistakes that trip up youngsters, and its robust design prevents the more bizarre forms of memory allocation and access bugs.
Java was designed from the beginning to prevent the sort of access violations, memory leaks and array indexing errors that comprimise the security and stability of C and C++ applications. Most introductions to Java would say that they did that by doing away with pointers; I would disagree - everything (again, except for primitives) is a pointer in Java, in the sense that it is a reference rather than a pure value. More complex references are planned for Java 1.2 and later, including weak references that don't prevent garbage collection.
But most really severe bugs in C apps come from bad array references, memory management problems, dangling or mangling pointers, etc. None of this is possible in Java without a lot of pretty pathological work. The addition of garbage collection eliminates whole rafts of problems, and bounds checking prevents most of the rest. Wimpy? Perhaps - but then again, having the compiler decide which registers to put stuff in was considered wimpy at one point. As the (cost of programmer time/cost of computer time) ratio gets bigger, doing your own memory management looks less and less attractive.
You can certainly compile java language apps to native binaries, but part of the original idea was binary portability, a much more stringent requirement. This is obviously required when you are sending code over a hetorogenous network and expecting it to work on all manner of hardware and operating systems.
To get binary portability, you have to have an intermediate form, usually referred to as pseudocode or p-code. This is a machine language for a machine that is built in software, that then translates the p-code to real machine code on the fly in some fashion. P-code is nothing new; some SmallTalk implementations use it, and some microkernel-based operating systems use it. And a software emulator is simply a virtual machine whose p-code is the code for an actual CPU.
A Java VM has to conform to the Java VM specification, which specifies the number and type of registers, endianness, and the size of integers and longs and such. This last prevents Java applications from breaking on assumptions made about the size of things. In addition, Java p-code can be verified at run-time to see if it came from a valid compiler; this prevents weird virii and such from being dumped on an unsuspecting VM to exploit faults in the implementation.
That being said, there's no reason that you have to write in Java for a Java VM; there are a couple of projects that compile other things into Java p-code, even one in its infancy for Perl.
This is the part Microsoft is scared about. There have been cross-platform toolkits. There have been virtual machine environments. But put them together, and you have binary-portable, cross-platform, fully GUI, system and network-capable applications. Instead of locally-installed, client-server, platform-specific applications, you have applets, or Marimba channels, or applications servers that don't care what sort of machine it's serving up apps to, as long as the machine speaks Java. And as more corporate information systems are written in Java, the need to standardize on single business-wide platform diminishes, giving everyone more of a choice for their desktops.