Of all the barriers to entry for beginning Java developers, the classpath is probably the most baffling. The problem is compounded by the fact that the documentation isn't the greatest. Even after reading the official documentation, I've seen countless beginners still struggle with the concept. The biggest problem, I think, is that the documentation separates what should be one topic in to three. So here I'm going to tackle three subjects at once in hopes of making a coherent whole: the classpath, packages, and imports.
Just about any non "Hello, World" program is going to need to import something. Imports are handled, conveniently enough, using the import keyword. Items are imported using their fully qualified class names, or FQCN . For example, to import the IOException thrown by virtually any method involving anything remotely related to IO, you would say: import java.io.IOException. This makes the class IOException available without having to fully qualify it every time it appears in your source code. You could of course just use the full name all the time. That's completely valid and completely annoying.
Now, sometimes you'll have no choice but to fully qualify a class name. For example, (just to stick with classes in the JRE) there's a java.util.List and a java.awt.List . They do completely different things but are conveniently named the same. In the case where you find yourself needing to use both classes, you can import one but you'll have to fully qualify the other. My advice is to import the more frequently used class and fully qualify the other. The more libraries you use the more likely you are to run in to this problem.
Going back to that import for a moment, let's dissect that line:
Apart from the import keyword, there are two important components to note here. As I've hinted at earlier, IOException is the class name. java.io is the package name. This is the part of the FQCN that makes it "fully qualified." You will need at least this much to import anything. Like many other languages, Java supports wildcard, or star, imports. Personally, I consider wildcard imports bad practice because they can pull in too much and pollute the local namespace. They can also lead to confusing errors in the middle of your code because you didn't import what you thought you did. I find it best to explicitly import each class that you need so that you're always sure what you're getting. That's a stylistic preference and many will argue the opposite. Just know that those people are all wrong. Regardless, imports are a compile time construct and have no bearing on the structure or runtime performance of the compiled bytecode.
So what are packages? Simply put, packages are namespaces in which classes are declared. They serve as logical or functional grouping to help you organize your code. They can also serve to isolate your code from other sections based on the access control modifiers you put on your classes and class members. There's not a whole to say about them but there are a few things to note.
Packages are flat namespaces. Take these two packages: java.awt and java.awt.event . These two have almost nothing to do with each other. The names are "nested" simply to provide a logical grouping showing that the classes in java.awt.event are logically related to those in java.awt. However, importing "java.awt.*" will not import anything from java.awt.event nor any other subpackages of java.awt. This is one of many reasons I discourage the use of star imports as mentioned above. I've lost count of the beginners who make this mistake.
So how do you make a package? Two things: a directory and a keyword. You can declare that your class lives in a particular package by putting something like the following at the top of your source file:
The recommended package structure is the reverse order of your domain name. This works well for companies who actually have a domain name but if you're a student or a 14-year old in your bedroom, that likelihood diminishes rather quickly. Other options I've seen are to include whatever hosting service you're using for your source repository (provide that it's open source). For example, if you put your code up on github, I've seen people use com.github.projectname as a package structure. This works fine until you move your project somewhere else and then that package name gets slightly moldy. Barring that, you're free to use whatever package structure you want. You just want to make sure that it's globally unique so that you don't end up sharing a namespace with someone else.
Another thing to note before we move on from packages, is that the package structure reflects where on the disk your code lives. We'll see why in the next section, but for now just know this: if your source file is Foo.java with a package statement of org.something.somethingelse, it should live in path structure on disk that looks like this:
Those directories and the package statement need to match up. When you compile your source, the .class file will be created in a directory structure matching your package structures.
You've created your class with packages and everything. You've compiled it. And now you'd like to use that class. Before you can do that, though, you have to tell the JVM where to find it. How do you do that?
Finally. What you came here to learn about in the first place. There's nothing magical about the classpath. Think of it as the JVM analog of your operating system's PATH. It's a value that tells the JVM where to look for your classes. There are handful of ways to do this. The most basic version is to use the CLASSPATH environment variable. Using a global CLASSPATH (one visible to every user and application on your system) is generally a bad idea but if you set the CLASSPATH variable in a script, say, before launching your application it's not a big deal.
The "better" way to use the -cp option to both javac and java. Before continuing on, let me highlight something here. You need that classpath for both compiling and running your code. The values might differ between compiling and running but you'll still need to tell the JVM where your code is at runtime. I can't tell you how many times I've seen beginners compile with a classpath and try to run without it. Java classes aren't statically bound. They still need to be told where things are at runtime.
So now that you know how to use the classpath, how do we define one? Unlike the PATH environment variable, two things can go on the classpath: directories and files. The standard distribution format in java is the jar file. A jar file is "just" a zip file with some extra metadata. Using a plain zip file also works. The classpath works with either. A jar file just offers you the ability to do some more advanced things like declaring a bundled classpath and certain authentication/validation options of the classes inside. We're not going to cover them here as they'll only serve to cloud the discussion. The only requirement of the structure, whether in a jar or a zip file, is the structure and layout of the contents which we'll cover momentarily.
Directories can also be included on the classpath. The structure of the contents under a directory has the same requirements as a jar or zip. Each element on the classpath is called a root. Under each root, the class loader will append the fully qualified class name to try to find the class you've asked to use. For this reason, you don't add the directory where Foo.class lives to your classpath. You add the directory above where your package name starts. I know that sounds mildly confusing so let's look at an example.