Back to the basics of Java — Part 1: Classpath
--
My favourite programming language has always been Java, coincidentally it was also my first language I ever used. If you’re like me, or just wants to learn more about the awesome language & technology that is Java, then this two part series is for you.
In these short series, you will learn about the following.
- Part 1: Classpath
- Part 2: The JAR
So, why did I decide to write these articles? Well, there was some aspects of Java I did not fully comprehend (such as the classpath) and I realized the reason for this is because I have been using an IDE and build tools.
The issue with an IDE is that it hides a lot of stuff which hinders a fully comprehensive understanding. In other words, I thought it was time to get a little bit more comfortable with the java cli tools and going back to basics.
Now let’s get down to it. In this part I will explain what the classpath is and how to use it correctly (and incorrectly).
Classpath
The classpath is simply a list of directories, JAR files, and ZIP archives to search for class files [1]. The runtime needs to know where to find your compiled classes so that’s what you’re providing here.
There are some things that you should be aware of though regarding how you specify the paths. To explain this I will create a dummy project, see below for the structure.
ClasspathProject/src/java/myprogram/
|
---> Main.java
|
---> utils/
|
---> Util.java
And here are the source files just so you know what we are dealing with.
Main.java
Util.java
I will be using this structure for the following parts as well so If you want to follow along I suggest you create it as well before proceeding.
A short note on packages.
If you are new to java it can be helpful to know that packages are generally suppose to be mirrored by the file structure. Take the Utils.java file for instance, it has the package myprogram.utils because it is in that folder.
The structure as provided is not really required by the compiler. However, it is required by the java command to run.
Let’s compile this little project.
First we create a bin folder to hold the compiled stuff in the root project folder then compile the two files [2].
> mkdir bin
> javac src/java/myprogram/Main.java src/java/myprogram/utils/Util.java -d bin/
You should now have the following file structure.
ClasspathProject/
|
---> src/java/myprogram/
|
---> Main.java
|
---> utils/
|
---> Util.java
|
---> bin/myprogram/
|
---> Main.class
|
---> utils/
|
---> Util.class
If we now move to the bin folder we can run the main class.
> java myprogram.Main
Here is 1337
Great, so why does this work? Because the default value for the classpath is the current directory. It needs to find two class files; Main.class and Utils.class, how and where does it look? Given the current class path of bin and our main class myprogram.Main it will look in this directory to try and find the following:
- myprogram/Main.class
- myprogram/utils/Util.class
Which we know exists, so the program runs successfully.
Now let’s change it up a little bit. Let’s add a new folder lib on the same level as bin and move the Utils.class so that it now looks like this.
ClasspathProject/
|
---> src/java/myprogram/
|
---> Main.java
|
---> utils/
|
---> Util.java
|
---> bin/myprogram/
|
---> Main.class
|
---> lib/myprogram/
|
---> utils/
|
---> Util.class
Error 1— No classpath specified
If we now move to the bin folder and try and run the same command we will get an error because it cant find the Util.class file.
> java myprogram.Main
Exception in thread "main" java.lang.NoClassDefFoundError: myprogram/utils/Util
at myprogram.Main.main(Main.java:8)
Caused by: java.lang.ClassNotFoundException: myprogram.utils.Util
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:602)
at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:521)
... 1 more
Error 2 — Adding a class file to the classpath
We will try and solve this by adding the class file to the classpath (we are running all these commands from the bin folder).
> java -classpath ../lib/myprogram/utils/Util.class myprogram.Main
Error: Could not find or load main class myprogram.Main
Caused by: java.lang.ClassNotFoundException: myprogram.Main
Ouch, now it cannot find the Main.class file? Because as previously explained, it will try and find the files in the specified directories provided in the classpath. And we did not provide a directory. So let’s do that instead.
Error 3 — Adding one directory to the classpath
> java -classpath ../lib/myprogram/utils/ myprogram.Main
Error: Could not find or load main class myprogram.Main
Caused by: java.lang.ClassNotFoundException: myprogram.Main
Okay, same error. What happened here? Since we provided a classpath argument that overrides the default one (which is the current directory) it cannot find our Main.class file anywhere. Let’s add that. On Unix systems you separate with a colon (:) and on Windows, you use a semi-colon (;).
Error 4 — Adding more paths to the classpath
> java -classpath ../lib/myprogram/utils/:. myprogram.Main
Exception in thread "main" java.lang.NoClassDefFoundError: myprogram/utils/Util
at myprogram.Main.main(Main.java:8)
Caused by: java.lang.ClassNotFoundException: myprogram.utils.Util
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:602)
at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:521)
... 1 more
Okey, now it seems that it can find the Main.class file. But it still cannot find the Util.class file. As previously explained it will try and find the following files using any of the classpaths.
- myprogram/Main.class
- myprogram/utils/Util.class
In this case we have two classpaths.
- ../lib/myprogram/utils/
- .
So simply put, it will try and append the classpaths to the required files to see if they exist, I image the process would look something like this.
Cp1 = ../lib/myprogram/utils/
Cp2 = ./
File1 = myprogram/Main.class
File2 = myprogram/utils/Util.classTrying to find File1Cp1 + File1 = ../lib/myprogram/utils/myprogram/Main.class
NOT FOUND
Cp2 + File1 = ./myprogram/Main.class
FOUNDTrying to find File2Cp1 + File2 = ../lib/myprogram/utils/myprogram/utils/Util.class
NOT FOUND
Cp2 + File2 = ./myprogram/utils/Util.class
NOT FOUND---> ERROR
Final solution
Can we solve this by adding those subfolders under lib? Sure we can, which will work. However, instead of adding those folders let’s modify the classpath argument instead so that it looks like this.
> java -classpath ../lib/:. myprogram.Main
Here is 1337
This works because it will now be able to find Utils.class file using the first path.
../lib/ + myprogram/utils/Util.class
FOUND
I hope that some of the errors here have helped your understanding of the class path. It seems that a lot of tutorials only present the correct way of doing things but I find that seeing the incorrect way of doing things helps my learning a lot more.
References
[1] java options
https://docs.oracle.com/javase/7/docs/technotes/tools/windows/java.html
[2] javac — Java programming language compiler
https://docs.oracle.com/javase/7/docs/technotes/tools/windows/javac.html