https://imshuai.com/using-javac
Tools such as IDE or maven have been responsible for compiling Java programs. However, the more advanced the tool is, the more details will be hidden. Once there is a problem, it will be confused. In the final analysis, the basic concept is not reliable. Returning to the original place, javac, will make the problem suddenly clear. Here is a step-by-step demonstration of running a conventional project with javac and Java freehand compilation.
Hello World practice
Let's make a simple start. Let's sacrifice the ancestral HelloWorld program. (if you are interested, you can try whether you can write it with your bare hands ~)
public class HelloWorld{ public static void main(String[] args){ System.out.println("Hello, World!"); } }
After writing, save as: HelloWorld Java, and then execute the javac compilation command in the current directory:
javac HelloWorld.java
Check the current directory (more accurately, the same level directory of java files), and it sure enough generates HelloWorld class:
maoshuai@ms:~/javaLinux/w1$ ls HelloWorld.class HelloWorld.java
Continue to run the java command in the current directory and print out Hello, World correctly!
maoshuai@ms:~/javaLinux/w1$ java HelloWorld Hello, World!
Old driver, steady! It looks simple: javac first, then java.
Simple as it is, novices often make the mistake of imagining to execute class files, such as written like this, will naturally report errors:
maoshuai@ms:~/javaLinux/w1$ java HelloWorld.class Error: Could not find or load main class HelloWorld.class
It should be understood that java parameters are passed in the name of the class where the main function is located, not the class file; java will automatically find the class file according to the class name.
With a package name
Everything is going well, but it's unprofessional to have no package name, so we add an awesome package. Com imshuai. javalinux:
package com.imshuai.javalinux; public class HelloWorld{ public static void main(String[] args){ System.out.println("Hello, World!"); } }
Still the same, compile with javac and check
HelloWorld. In the current directory Class generated, very smooth.
It's the same as the java command, and you get slapped in the face in an instant:
maoshuai@ms:~/javaLinux/w1$ java HelloWorld Error: Could not find or load main class HelloWorld
After thinking about it, HelloWorld already has its own package name, so its name is no longer HelloWorld without a last name. Its new name is com imshuai. javalinux. HelloWorld, it's natural to use a new name when passing it to java. Try again:
maoshuai@ms:~/javaLinux/w1$ java com.imshuai.javalinux.HelloWorld Error: Could not find or load main class com.imshuai.javalinux.HelloWorld
I was beaten in the face. At this time, the old driver told you to create a com/imshuai/javalinux directory, and then put HelloWorld Put class in and execute:
maoshuai@ms:~/javaLinux/w1$ mkdir -p com/imshuai/javalinux maoshuai@ms:~/javaLinux/w1$ mv HelloWorld.class com/imshuai/javalinux maoshuai@ms:~/javaLinux/w1$ java com.imshuai.javalinux.HelloWorld Hello, World!
Sure enough, Hello, World was printed normally!
The above steps illustrate two points:
- The package name has been added, so the class name has also changed. If you don't change your name, you should naturally take your last name (that is, the so-called fully qualified name).
- Java will map out the directory structure according to the package name, and search the directory from the class path to find the class file. Since the default class path is the current directory, com imshuai. javalinux. HelloWorld must be stored in/ com/imshuai/javalinux/HelloWorld.class
Of course, it's too troublesome to create the directory of package path every time- d parameter can do the above work:
maoshuai@ms:~/javaLinux/w1$ javac -d . HelloWorld.java maoshuai@ms:~/javaLinux/w1$ ls com HelloWorld.java maoshuai@ms:~/javaLinux/w1$ java com.imshuai.javalinux.HelloWorld Hello, World!
-d specifies the root directory of the generated class file (the current directory is used here), and the subdirectory will be created according to the package path of class.
Compile two dependent class es
The package name is solved. Let's make it more complicated and make a dependency call. First, we extract a HelloService:
package com.imshuai.javalinux; public class HelloService{ public void printHello(String name){ System.out.println("Hello, " + name + "!"); } }
Then modify HelloWorld Java, call HelloService to complete say hello:
package com.imshuai.javalinux; public class HelloWorld{ public static void main(String[] args){ HelloService service = new HelloService(); service.printHello("World"); } }
Then we compile in turn: helloservice Java and HelloWorld Java, finally run:
maoshuai@ms:~/javaLinux/w1$ javac -d . HelloService.java maoshuai@ms:~/javaLinux/w1$ javac -d . HelloWorld.java maoshuai@ms:~/javaLinux/w1$ ls com HelloService.java HelloWorld.java maoshuai@ms:~/javaLinux/w1$ java com.imshuai.javalinux.HelloWorld Hello, World!
Intuitively, you need to compile helloservice Java, that's right. If you compile HelloWorld first What about Java? Face slapping, of course:
maoshuai@ms:~/javaLinux/w1$ javac -d . HelloWorld.java HelloWorld.java:4: error: cannot find symbol HelloService service = new HelloService(); ^ symbol: class HelloService location: class HelloWorld HelloWorld.java:4: error: cannot find symbol HelloService service = new HelloService(); ^ symbol: class HelloService location: class HelloWorld 2 errors
If the order is determined according to the dependency during compilation, it's too low. I think the Java command should automatically solve it. Pass two java files to it at one time:
maoshuai@ms:~/javaLinux/w1$ javac -d . HelloWorld.java HelloService.java maoshuai@ms:~/javaLinux/w1$ ls com HelloService.java HelloWorld.java maoshuai@ms:~/javaLinux/w1$ java com.imshuai.javalinux.HelloWorld Hello, World!
Cow force, it automatically solves the order problem, like one (although I put HelloWorld.java in the front with bad intentions)!
Using src and target directories
As can be seen from the above example, although the class file must be placed in a directory with the same package name, the java source file does not have this requirement. However, for the convenience of management, we also put the java source files in the package structure directory:
maoshuai@ms:~/javaLinux/w1$ mkdir -p com/imshuai/javalinux maoshuai@ms:~/javaLinux/w1$ mv *.java com/imshuai/javalinux/ maoshuai@ms:~/javaLinux/w1$ ls com/imshuai/javalinux/ HelloService.java HelloWorld.java maoshuai@ms:~/javaLinux/w1$ javac -d . com/imshuai/javalinux/*.java maoshuai@ms:~/javaLinux/w1$ ls com/imshuai/javalinux/ HelloService.class HelloService.java HelloWorld.class HelloWorld.java maoshuai@ms:~/javaLinux/w1$ java com.imshuai.javalinux.HelloWorld Hello, World!
When compiling, javac needs to pass in a new Java file path (wildcard is used here), which is no different from others. You can see that the class file is generated in the same directory as the java file. It's not refreshing to put the class file and Java source file together. Can you put the java file in the src directory and the class file in the target directory as in the IDE? Let me try.
First create src and target directories and move the original java files to src Directory:
maoshuai@ms:~/javaLinux/w1$ mkdir src maoshuai@ms:~/javaLinux/w1$ mkdir target maoshuai@ms:~/javaLinux/w1$ mv com src maoshuai@ms:~/javaLinux/w1$ ls src target
Then compile and specify the - d parameter to the target directory:
maoshuai@ms:~/javaLinux/w1$ javac -d target src/com/imshuai/javalinux/*.java maoshuai@ms:~/javaLinux/w1$ ls target/com/imshuai/javalinux/ HelloService.class HelloWorld.class
How does it work? Running directly in the current directory is not enough. After all, there is an additional layer of target directory. Enter the target directory to run. It's appropriate:
maoshuai@ms:~/javaLinux/w1/target$ java com.imshuai.javalinux.HelloWorld Hello, World!
In addition to entering the target directory, the more common method is to set the classpath through the - classpath (or abbreviated as - cp) option:
maoshuai@ms:~/javaLinux/w1$ java -cp target com.imshuai.javalinux.HelloWorld Hello, World!
CLASSPATH classpath
The above demonstrates setting the classpath through - cp. Let's take a closer look at classpath.
Classpath is the path that JRE searches for user level class files or other resources. javac or java and other tools can specify the classpath. If not set, the default class path is the current directory. However, if the classpath is set, the default value will be overwritten, so if you want to keep the current directory as the classpath, you need to set the Join, a bit like the default constructor.
The CLASSPATH can be set through the environment variable CLASSPATH or the - cp parameter, which overrides the former. The - cp setting is recommended. It will only affect the current process.
Classpath is similar to the concept of path in the operating system, but it is the path for java tools to search class files. Similarly, Classpaths can be multiple and separated by semicolons:
export CLASSPATH=path1:path2:...
Or:
sdkTool -classpath path1:path2:... sdkTool Can be java, javac, javadoc Wait.
The classpath can be not only a directory, but also a jar package or zip package.
Class paths are set in order. java will give priority to searching in the first class path. This is similar to the path of the operating system.
The classpath can match jar or zip with wildcard * but
- The wildcard only matches jar or zip. For example, path / * only adds the following jar or zip to the classpath, but the path itself does not add to the classpath.
- The wildcard does not recursively search, that is, it refers to matching the jar or zip under the first layer directory.
- The order in which the wildcard matches the jar or zip and is added to the classpath is uncertain. Therefore, it is safer to enumerate all jars or zips displayed.
- Wildcards apply to the CLASSPATH variable or the - cp parameter, but not to the manifest file of the jar package.
More realistic scenes
The following Java projects have more package structures and jar package dependencies. How to compile them? (engineering code download: https://github.com/maoshuai/java-linux-weekly/tree/master/attachment/w1)
├── lib │ ├── logback-classic-1.2.3.jar │ ├── logback-core-1.2.3.jar │ └── slf4j-api-1.7.26.jar ├── resources │ └── logback.xml ├── src │ └── com │ └── imshuai │ └── javalinux │ ├── HelloWorld.java │ └── service │ ├── IGreetingService.java │ └── impl │ ├── AlienGreetingService.java │ ├── CatGreetingService.java │ ├── DogGreetingService.java │ └── HumanGreetingService.java └── target
The most direct way is the same as just now, but with more manual work. Enumerate all java files and use wildcards to add jar s under lib to the classpath:
javac \ -cp "lib/*" \ -d target \ src/com/imshuai/javalinux/HelloWorld.java \ src/com/imshuai/javalinux/service/IGreetingService.java \ src/com/imshuai/javalinux/service/impl/*.java
After the compilation is successful, run the java command (Note: jar s under target and lib need to be added to the class path):
maoshuai@ms:~/javaLinux/w1$ java -cp "target:lib/*" com.imshuai.javalinux.HelloWorld XiaoMing 22:16:15.887 [main] INFO HumanGreetingService - XiaoMing is saying hello: Ni Chou Sha!
If there are many files, it is unrealistic to list them manually. You can use the find command:
javac -cp "lib/*" -d target $(find src -name "*.java")
javac also provides a method to list files, that is, write the list of java files to be compiled into a text file, and we use the find command to complete it:
maoshuai@ms:~/javaLinux/w1$ find src -name "*.java" >javaFiles.txt
Generated javafiles Txt reads as follows:
src/com/imshuai/javalinux/service/IGreetingService.java src/com/imshuai/javalinux/service/impl/HumanGreetingService.java src/com/imshuai/javalinux/service/impl/CatGreetingService.java src/com/imshuai/javalinux/service/impl/DogGreetingService.java src/com/imshuai/javalinux/service/impl/AlienGreetingService.java src/com/imshuai/javalinux/HelloWorld.java
Start with @ javafiles @ Txt, which means that the list file name is passed to javac:
javac -cp "lib/*" -d target @javaFiles.txt
Not only that, parameters can also be put into files (Note: - cp cannot be put in). For example, javafiles Txt add - d target
-d target src/com/imshuai/javalinux/service/IGreetingService.java src/com/imshuai/javalinux/service/impl/HumanGreetingService.java src/com/imshuai/javalinux/service/impl/CatGreetingService.java src/com/imshuai/javalinux/service/impl/DogGreetingService.java src/com/imshuai/javalinux/service/impl/AlienGreetingService.java src/com/imshuai/javalinux/HelloWorld.java
In this way, only:
javac -cp "lib/*" @javaFiles.txt
However, for clarity, we can put the parameter - d target into a separate file javaoptions Txt, and then transfer two @ files:
javac -cp "lib/*" @javaOptions.txt @javaFiles.txt
The advantage of using list file is that it avoids the limit of command line parameter length and can run on any operating system.
With the above preparation, we can write a script for automatic compilation:
PROJECT_DIR=/home/maoshuai/javaLinux/w1 # clean target directory rm -rf $PROJECT_DIR/target/* # prepare arg files find $PROJECT_DIR/src -name "*.java">$PROJECT_DIR/target/javaFiles.txt echo "-d $PROJECT_DIR/target" >$PROJECT_DIR/target/javaOptions.txt # compile javac -cp "$PROJECT_DIR/lib/*" @$PROJECT_DIR/target/javaOptions.txt @$PROJECT_DIR/target/javaFiles.txt # copy resources to target cp -rf $PROJECT_DIR/resources/* $PROJECT_DIR/target # clean temp files rm -rf $PROJECT_DIR/target/javaOptions.txt $PROJECT_DIR/target/javaFiles.txt
It's time to take a closer look at javac
The official Oracle documentation describes javac as follows:
Reads Java class and interface definitions and compiles them into bytecode and class files.
The syntax of javac is as follows:
javac [ options ] [ sourcefiles ] [ classes] [ @argfiles ]
- options: some parameters, such as - cp, - d
- sourcefiles: compiled java files, such as HelloWorld java, which can be multiple and separated by spaces
- classes: used to process annotations. I don't know how to use it for the time being
- @argfiles is the file path containing the option or java file list, starting with the @ symbol, as shown above
@javaOptions.txt and @ javafiles txt
summary
The basic usage of javac is summarized as follows:
-The cp parameter sets the classpath. The basic usage is to add the jar package that depends on during compilation to the classpath. You can also use * to configure jar packages.
-The d parameter is used to set the class file to compile to a separate directory and create a subdirectory according to the package name.
Theoretically, you can pass all the paths of java files to javac, but operationally, you can output the file list to the file through the find command and pass it through the @ argfiles parameter.
reference resources
https://docs.oracle.com/javase/8/docs/technotes/tools/unix/javac.html
https://docs.oracle.com/javase/8/docs/technotes/tools/unix/java.html
https://docs.oracle.com/javase/8/docs/technotes/tools/unix/classpath.html
https://docs.oracle.com/javase/tutorial/getStarted/problems/index.html
http://docs.oracle.com/javase/8/docs/technotes/tools/findingclasses.html