Does the 'java' command compile Java programs?

Most websites on the internet say:

"use the javac command to compile a .java file. Then run it using the java command"

But today I tried to run a java program without javac and I got a strange result.

Here are the contents of a file called hello.java:

public class Myclass {
 public static void main(String[] args){
    System.out.println("hello world");
  }
}

Then I ran:

$ javac hello.java

Which gives me this error:

hello.java:1: error: class Myclass is public, should be declared in a file named Myclass.java
public class Myclass {
       ^
1 error

But when I run it without the javac command, it executed without any errors.

$ java hello.java
hello world

Does the java command also compile the program? If yes, why do we need the javac command?

The version of my java is:

openjdk version "12.0.2" 2019-07-16
OpenJDK Runtime Environment (build 12.0.2+10)
OpenJDK 64-Bit Server VM (build 12.0.2+10, mixed mode)

Solution 1:

Prior to Java 11, to run your code you have to first compile it, then you can run it. Here's an example:

javac test.java
java test

Since Java 11, you can still do javac + java, or you can run java by itself to compile and auto-run your code. Note that no .class file will be generated. Here's an example:

java test.java

If you run java -help, you'll see the various allowed usages. Here's what it looks like on my machine. The last one is what you ran into: java [options] <sourcefile> [args] which will "execute a single source-file program".

$ java -help
Usage: java [options] <mainclass> [args...]
           (to execute a class)
   or  java [options] -jar <jarfile> [args...]
           (to execute a jar file)
   or  java [options] -m <module>[/<mainclass>] [args...]
       java [options] --module <module>[/<mainclass>] [args...]
           (to execute the main class in a module)
   or  java [options] <sourcefile> [args]
           (to execute a single source-file program)

UPDATE:

As pointed out by @BillK, OP also asked:

why do we need the javac command?

The reason we need javac is to create .class files so that code can be created, tested, distributed, run, shared, etc. like it is today. The motivation for JEP 330 was to make it easier for "early stages of learning Java, and when writing small utility programs" without changing any other existing uses.

Solution 2:

If you are running Java 11, there is a new feature that allows single source file execution. The single source compiler is more promiscuous in terms of class name versus file name, so that is how you are able to run but not successfully compile.

If you are on a previous version of Java, then your current hello.java does not compile, because of compile errors, specifically around the class name. So there's absolutely no way that calling java hello.java compiled your code, because it does not compile.

It seems most entirely likely that you were running some previously compiled code when executing the java command.

Solution 3:

To answer why this error is given, the class name for the file must match the file's basename.

You have two options to have this code work for the traditional javac; java sequence:

  1. Rename the class to public class Hello or

  2. Rename hello.java to myclass.java.

The java interpreter for Java 11 does not impose this requirement. The class that contains main can have any name, as long as it is the first class in the file. This was mainly intended to ease the learning process for beginners, and to allow "java scripting" with the shebang (ref.).

Solution 4:

Yes, but not in the way you probably mean.

When you use the javac command to compile a .java file to a .class file the output is something called bytecode. Bytecode is a the machine code (native instructions) for a theoretical CPU based on the Java Virtual Machine specification.

This virtual CPU specification is sort of an average of types of CPUs that were common at the time the specification was written. Because of this it is close to lots of different types of CPU making it easier to run the same Java .class files on multiple CPU types.

When Java was first launched the java command would read the .class file and interpret the bytecode instructions one at a time and then map them to the equivalent native instruction for what ever CPU it was actually running on. This worked but wasn't particularly fast. To improve this Just in Time (JIT) compilation was added to the Java Runtime.

With JIT the java command takes the bytecode and compiles it again to the native instructions for the CPU it is running on. Modern Java runtimes tend to start out interpreting the bytecode while JIT compiling in the background and switch to the compiled native instructions when it's ready and will also profile the running application and then recompile the bytecode again with different optimisation to get the best possible performance.

EDIT (to appease the down voters):

So in your specific case (as you are running a JRE newer than v11) the code is compiled (at least) twice

  1. As a single .java file to bytecode
  2. Via the JIT compiler as it interprets the bytecode (though for helloWorld it might not actually get time to run any of the compiled native code)