Similar to Go's $GOPATH in functionality, the java $CLASSPATH is one of those things about the Java ecosystem that a lot of developers take for granted, are not aware exists, or that they feel they don't even need to know that it exists when they use an IDE as the main tool for their development efforts. But I am adamantly against educating developers to ignore the innards of the tools and languanges they use every day and I believe that everything in engineering is the way it is for a reason. So let's dive into the java $CLASSPATH and hopefully a lot of great learning should come out of this discussion.
If you've ever done development on Java without an IDE by pointing javac at a .java file, you have probably noticed that it produces one or many .class files with the same name depending on which classes you compiled for your program. There should be at least one class with a main() method here, which you can then execute by invoking it with the java command using the java NameOfClass command invocation. All is good and dandy.
The thing is though, what if you want to import classes from other sources, such as something shared online, from a third party? Do we have something similar to what other environments provide us, such as Rubygems or PyPi or CPAN or Packagist or NPM, but in the Java environment, and a convention as to where we should save those dependencies in the system and read them from?
Well, yes, we do, but let's not get ahead of ourselves: enter the $CLASSPATH
The java utility is pre-configured to, during compile-time, only look for classes included with the JDK itself or within the namespace of the class that you're trying to run. You can also make it look for more classes in whatever directories your $CLASSPATH environment variable points to.
So, if you request your program to import Car;,it will also look for a class called Car nested inside of the class you're compiling or under its namespace. If you import com.nullset2.Car;, it will look for a class named Car SPECIFICALLY under the {pwd}/com/nullset2 directory or under whatever directory is on $CLASSPATH.
So, again, whenever you compile something and it points to other classes, Java will try to find those other classes on whichever location $CLASSPATH is pointing to. This is something that you could even handle in a shell initialization script:
export CLASSPATH=.:~/my-java-libs
The classpath can also be set on a per-compilation basis using the -cp or -classpath parameter of the javac utility included with your jdk. So if your program uses classes from a test.jar, you could do javac -cp ./test/jar on compile time
What happens, though, if you want to import a class for your project that you didn't write but is widely available online as a binary, so you don't have the source code to it? What if you're trying to import com.google.common.collect.BiMap and you only have a jar for Guava, which is a simple compressed file aggregating a hierarchy of .class files? Well, you could try placing the jar on your repository, distribute it and ask people to have the same exact $CLASSPATH than you when they run their programs. But would that be a good idea? It would be a bit of a pain to ask them to do so much ceremony for no reason, not to mention there's probably going to be human error involved because it's easy to misconfigure an environment variable. Also, you're vendoring code from other people in your project, so you could be introducing a permanent bug into your application, and upgrading could be hard later down the line. As a rule of thumb, checking in .jar files or binaries into your system is kind of frowned upon in professional development settings (Go people vendor source code, though, and it seems to work for them, at least until semantic versioning dependency management catches on).
Is there something something similar to what other environments provide us, such as Rubygems or PyPi or CPAN or Packagist or NPM, but in the Java environment? We should be able to add a declarative source of truth to accompany our programs that state which other packages should be used in the project and then use a tool to pull those packages from a centralized source. I know that centralization is not for everyone and some are patently against it, but it's nothing short of impressive that there's so many generous people maintaining these amazing resources for the benefit of all, and the fact that it makes our jobs easier is also brilliant. We should leverage that same kind of pattern for us in the Java world, right?
Well, fortunately, more or less Java wrote the playbook on this to begin with, as a matter of fact :). There's many different public repositories maintained by amazing people that let you fetch dependencies from them for your project. Then it's just a matter of writing a model of which dependencies and which versions your application uses and going to town with a dependency manager. In the Java world, the two big dependency managers right now are gradle, which I personally prefer, and maven, which is kind of a little bit more oldschool and 90s-like, yet, it still packs quite the punch.
These tools are installed in your system or wrapped into your codebase and will automagically set the classpath to include a safe-default location where they will install all dependencies into. It works pretty great! But it's still not quite as intuitive as just invoking gem install rails for example. I think it's still fantastic though.