Actually ... they
aren't.
A "platform independent programming language" will be any language for which there is a runtime-environment available for your particular operating system of choice. (Could be Windows, could be Macintosh, could be Linux, could be a mainframe computer, could be a microwave oven. Really.)
This "runtime environment," as I call it, actually is a native program (usually written in C/C++) that is designed for that particular OS, and which implements the functionality of the language ...
i.e. the ability to carry out programs written in that language. Java certainly is such a language ... but so is Perl, Python, PHP, Ruby, Haskell, Lisp, Prolog, JavaScript ... well, you get the idea.
The method that all of these systems employ to represent the program and to carry it out is called, variously,
p-code or
bytecode, which you may envision as being "the machine language of a hypothetical machine," said machine being implemented in software ... namely, in the runtime-environment executable.
Each of these languages implements
some capability of invoking executable code (in libraries ...) that is
not written in that language. The requirements that the runtime environments impose upon such libraries vary from implementation to implementation. (They will, for example,
insist that subroutine parameters must be passed in some certain way,
e.g. stdcall on Microsoft Windows.)
Fact is,
most language systems are implemented in just this way, because the "overhead" of running a p-code interpreter is truly negligible, while the advantages are many. The "80/20 rule" certainly applies here: 80% of the time is spent in 20% of the program. Considerable effort is devoted to designing an efficient p-machine and an efficient implementation of that machine. Execution speeds that are "inconsequentially less-than pure native speed" can be obtained,
and the result is platform-independent.