Java is a platform independent language in favor of Java Virtual Machine(JVM), which means that JVM is platform independent by the way. When a Java program(essentially a Java class) is written, the Java compiler compile the java file into class file(platform independent). The class file is then passed to JVM and been further translate to machine code. A Java project is nothing more than a bunch of Java programs(essentially Java classes) and may usually be packed into a jar file. A jar file should contain an entry point telling JVM where to start the program. So that’s it, basically how Java works.
Of course one would rely on other codes written before. Usually, we call them libraries in C, while they are called packages in Java. We can import other packages at the beginning of the program. Some packages are natively imported in JVM(java.io.*, etc.), while others should be specified in the classpath.
Android applications are written in Java and is basically a Java project. What’s different is that they involved much more delicate interactions with the virtual machine, since Android is quite a sophisticated customized system. The most noticeable part is the life-cycle model of Android applications. Meanwhile, Android system aims for mobile devices which may includes various architectures. So there comes a different virtual machine, Dalvik Virtual Machine(DVM). Unlike JVM, which runs stack-based byte code, DVM uses a register-based bytecode set. Android system spawns a new Dalvik VM instance for every application process for robust(one DVM fail only leads to one process crash). So the DVM is designed to be efficient in terms of memory usage and performance in multiple DVM instance scenario.
The image below makes it clear that how Java works with JVM and DVM.
Here’s another useful link to get familiar with Android Runtime.