Burak Dede's Blog

How Classpath Works in Java?

I am writing this article for myself and not with the purpose of teaching others since I sometimes stumble some stupid problem on Java and solution generally involves knowing how classpath works and problems experienced with it. Yes, this is the result of using IDE and taking a grant of all those fundamentals. Interestingly not much book exist to talk about what is classpath, how it is really implemented and what are the pain points when creating a real application. I am not talking about books that mention java and javac commands and force you to compile stupid Hello World example from the command line. Yes learning how to use java and javac is important but it is just stupid exercise when you do not know how to apply same to compile 20 class and deeply package structured project (which you may need to do sometimes). Yes, IDEs are for this purpose but you can not take your fancy IDE to your deployment server or some unknown machine without UI.

Learn these and please do not forget

  • classpath is just searching mechanism specific to Java applications, allows you to find classes and other resources for running and compiling the application.
  • class names are not just what you give them, they are the combination of package name plus file name.
  • java or javac search files with the full package name, not the filename.

I am skipping simple java and javac usage you can find tons of tutorials on those.

I am going to create app structure at least resembles the real world one.

mkdir -p app/com/burakdede
cd app/com/burakdede
touch DBConnection.java User.java
package com.burakdede;

public class DBConnection {
    public static void main(String[] args) {
        System.out.println("This is the main method of DBConnection class");
    }
}
package com.burakdede;

public class User {
    public static void main(String[] args) {
        System.out.println("This is main method of User class");
        DBConnection db = new DBConnection();
    }
}

Two simple class, how do you compile it? (one referencing other and they are inside the package)

Shell Globbing

  • go to package subdirectory cd com/mycompany
  • javac *.java

Awesome it worked but why?

When you compile with shell glob *.java javac is smart enough to look at the arguments for reference since User class reference DBConnection class, Java could find it.

Explicit Naming Things

  • go to package subdirectory cd com/mycompany
  • javac DBConnection.java User.java

It worked again. I am good at this classpath thing.

When you compile with explicit names of the files, same story.

I Will Do One By One

  • go to package subdirectory cd com/mycompany
  • javac DBConnection.java

It worked.

When you only compile DBConnection it worked because it did not reference any other object (other than standard ones)

Fail Already

  • go to package subdirectory cd com/mycompany
  • javac User.java

It failed!!! but why?

When you only compile User it failed because it could not find the DBConnection. It is interesting since it is in the same folder. But that is not how classpath understand it will only see com.burakdede.DBConnection and com.burakdede.User so when it look for com.burakdede.DBConnection reference it will start searching from the current directory. The first thing it will look com.burakdede.com.burakdede.DBConnection and since it does not exist it will fail the compilation. Seems like javac is polite enough to refer class names with their full name.

Current directory is implicit in the classpath searching mechanism. Rough order is Java libraries directory, extensions directory, current directory and anything you add to the end to search for.

Now I am getting excited because it is not simple as I though it will be. It started looking for relative paths from current directory since I did not point it to look for anywhere (it goes default by looking current directory). So if I do this from the root app directory it seems like it will work.

cd ../../
pwd
javac com/burakdede/User.java

Yeah, it worked. So it goes to look for User class and had no problem finding it since we gave it explicitly. When it encounter DBConnection or in full name com.burakdede.DBConnection it will stop and consult to the classpath for searching. Look for Java library directory, nope it is not there, look for extension directory, nope not there either, how about current directory since I have no place to look other than that. Seems like there is DBConnection in com/burakdede/DBConnection and no complain and all success.

Compiling All

Yeah it is fun if you have 2 class but real world applications have more than that, there need to be a way to make this simple and I do not want all those .class files along with my source files.

cd [root-app-dir]
mkdir classes
javac -d classes com/burakdede/*.java

This will solve most of the problems. When I look at the help section javac -help for -d option it says something like below.

-d Specify where to place generated class files

All my class files will be stored in classes folder and if you will run a class with java and reference other classes that it use you can easily do that with the classes directory. But beware it will not directly store inside classes directory it will have their own relative folders like com/burakdede/DBConnection.class. So our app structure looks like below

/app /com /burakdede User.java DBConnection.java /classes /com /burakdede User.class DBConnection.class

java -classpath "classes" com.burakdede.User

Hey run the User class but it is using some other classes such as DBConnection and you can find them in classes directory.

Classpath Option

I did not use any classpath option until now but I think it has some use cases like if you have tens of classes or archive of classes you can include them with it for both running and compiling. I will run -help option this time on javac.

-classpath Specify where to find user class files and annotation processors -cp Specify where to find user class files and annotation processors

By the way, I can probably use -classpath option in above scenarios without going to root app folder but it will look ugly.

cd com/burakdede
javac -classpath "../../"  User.java

See I find a use case for it but seriously it looks ugly, I would prefer going to root directory and utilizing default current directory as a classpath.

Adding Jar & Compiling

So sometimes not all the classes will be on default directories like when you want to deal with something like MySQL and would include Java support library from Oracle. Let us make it a little bit harder and suppose I need logging support also with log4j. So I have two jars that are used by my application classes. I think I can use both -d and -cp options

mkdir classes
java -d classes/ -cp /lib/mysql-connector-java.jar:/lib/log4j.jar com/burakdede/*.java

This will take : separated path of jar files and will search them along with default paths. Oh since I used -d option all my class files will be stored in classes directory, nice!

Wait but I need to deploy this I do not want to deal with tons of Java class files, even though they are nicely organized in classes directory. I think I can turn them into jar file.

Jar is just a fancy version of zip actually, AFAIK it uses zip protocol.

jar cvf app.jar com/burakdede/*.class

Things That I Did Not Mention

  • I can set CLASSPATH env. variable and also put it to my bashrc but I can easily forget this so I would not do that.
  • Better use -cp on each application.
  • Jar is not a file it is a folder.
  • I think they could design this more simple way but…
  • I think jar separation is ; on WINDOWS but I am not sure better to ask Google.
  • There is a manifest option for classpath but I did not use it nor I would like to.
  • I read some people have problems with classpath names including space.

Awesome! now I know what a shitty job to do all of those by hand every time, back to my IDE.