23/02/2013

02. Getting dirty with the JDK tools

If everything is correct, you've been playing around with the Java language, trying to learn its basic operators, statements, language syntax and perhaps even using a class here or there. If you have been following the articles you will have been using Eclipse to assist you in this, with its syntax highlighting and error reporting. Quite an advanced tool that takes many chores away from you.

Nowadays you have all these fancy tools called Integrated Development Environments (IDE). Netbeans, Eclipse, JDeveloper, IntellJ; plenty of choice. Next to IDEs there are build management tools to help formulate your application as a project and automate compiling, packaging and deploying them with only single command invocations. ANT (optionally combined with Ivy), Maven, Gradle. IDEs tend to allow you to use such build management tools right from the IDE itself. I'm a big fan of Maven myself.

All these tools are for advanced and experienced usage however. You need to learn the basics first before you can properly use them without going nuts; we have a huge knowledge gap to fill. So lets go back over a decade in time where all these tools did not exist yet; Java was still around. How did people build their stuff? The answer: using the JDK itself. All the above tools do exactly that: they make it easier to build, package, deploy and run stuff using the JDK. This also means you don't actually need them, you can just do it yourself. And to be able to properly understand and use all the above tools you should know how - if at the very least to identify and fix problems. Hence we're going to make a trip back in time and start at the very basics - we're going to learn how to compile and run stuff using nothing but a command prompt / shell and the JDK.

In this article I want to explain to you the tools of the JDK and how to use them. This means that we are going to let go of Eclipse for now, until the latter part of this article where we are going to see how Eclipse helps us to use the JDK tools to automate repetitive and cumbersome tasks. But first, we are going to do it ourselves. This means even though this is a theory article, its still going to be quite practical. The theoretical side of this article is going to discuss the basics of classes, packages and the classpath - information which is vital to your understanding of how Java ticks and also vital to your initiation into properly using an IDE such as Eclipse without losing your head. So fire up a command prompt or shell, navigate to your playtime directory and lets get started!

Prerequisites:

  • You have successfully installed a JDK (running javac -version works)
  • You know how to work with a command prompt / shell
  • You know the language syntax (operators, statements, variables, methods)
  • You know about data types, including the void method return type

Before you get started, create a directory somewhere where you can save source files to play around with. I suggest c:\java\playtime, or a similar directory that matches your operating system's file system.

javac to compile stuff, java to run stuff

javac is the compiler; the tool that turns (compiles) our human-readable program code into a more optimized byte code that the Java virtual machine understands. This byte code comes in the form of .class files, as we've already seen before. You know how to use it to compile a single class file, you just invoke

javac Filename.java

and your Filename class stored in Filename.java is compiled into a Filename.class file. If Filename should have a main(), you can also run it using the java tool. Lets review that. Create a source file Playtime.java in your playtime directory with the following content:

public class Playtime {  
        
      public static void main(String[] args){  
        System.out.println("It's playtime!");  
      }  
    }  

Notice that the name of the class and the name of the source file (without extension) have to match exactly, including uppercase characters. This is one of the many rules of Java that allow it to work the way it does. Now in your command prompt/shell navigate to your playtime directory and type in the following:

javac Playtime.java

If everything is correct you get no response at all, which means there are no compile errors; the source code is correct according to the language rules (more commonly referred to as syntax) of the Java language. There is a result though: a new file has appeared: Playtime.class.

At this point you have to understand what it means for a language to be a compiled language. You don't execute the source files (like you would in an interpreted language such as Ruby or PHP); you first compile the source files into a far more compact and optimized binary format. Much like you would have an executable on Windows (a .exe file), you have .class files under Java. Not every class is executable; only the ones with a main() method. In the general case, only one class is actually executable in a Java application so most classes do not need to be executable, as we'll later see.

There is one problem: your operating system does not understand what a .class file is; only the Java Virtual Machine (JVM) does. So in order to run our Java application, we need to run "java"; which means we're going to use the Java Virtual Machine to run the program. This also means that in order to run a Java application, you need to have a Java runtime installed - this is also true for the people you give or sell your program to. More on that in a later article. The command to execute programs is actually called exactly that: java, so try typing it in on the command line.

java Playtime

Response on my computer:

E:\java\playtime>java Playtime
It's playtime!

Woohoo, it works! How it works we'll revisit later on, for now lets keep our focus on the compiler.

javac and multiple classes

Things get more interesting when you start to create real applications which will consist of many classes. Lets create a simple one now that consists of three classes. We'll create a class Book and a class Person. The person will be reading the book. The third class is going to be our application holding the main(), which we shall call Library. So the person will be reading a book in the library. Not too difficult to visualize, right?

public class Book {  
      private String title;  
      private String text;  
      
      public Book(String btitle, String btext){  
      
        title = btitle;  
        text = btext;  
      }  
      
      public String getTitle(){  
        return title;  
      }  
      
      public String getText(){  
        return text;  
      }  
    }  

This tiny snippet already contains a whole world of new information. What we define here is a class Book which has two properties - it has a title and a text, both a String which is a data type which can hold textual information. Normally a book is made up of several pages of course, but our book is just one large papyrus. We'll revisit that in the practical article to create a proper book using collection classes.

That public Book thing is called the constructor. The constructor is a special type of method; you can't ever call it yourself, only Java can do it. It is used only once - when we create a Book object. It allows us to set the basic properties of the book right away:

Book book = new Book("Harry Potter",   
                         "Once upon a time there was a small boy who lived in a cupboard. The end.");  

We also have two methods; getTitle() and getText(). These methods give us access to the properties of the book. Note that there are no methods to change the properties; once we create the book, the text and title are going to stay that way forever. This effectively makes the book read-only; the more proper and expensive term for that is that the book class is immutable. Impossible to change. Immutability is a quite important design decision to make and can help prevent all kinds of problems in larger applications. For now be aware that this term exists and what it means.

Our methods also have a special name: they are called getter methods; methods that exist only to return the value of a class property. Likewise if we would have had a method to change them, like this:

public void setTitle(String newTitle){  
        title = newTitle;  
      }  

This is a setter method. The two of them form the basis of a specific form of object oriented design called encapsulation. For now it is not important to know what that is exactly (but you can google it if you're curious); just remember that as a rule you should create your classes this way. The alternative would be to make the properties of the class public so you don't need methods to access them:

public class Book {  
      public String title;  
      public String text;  
    }  

But we've already created one problem this way: the book is no longer immutable, you can change the properties whenever you want. Encapsulation is important, for more reasons than this. But that's an advanced topic.

Rule: properties are private, you access them with getter and setter methods. Don't worry: you don't have to write those methods yourself when you use an IDE, the tool can generate them for you in one go.

Lets move on to the person, which is a step up.

public class Person {  
      private String name;  
      private Book book;  
      
      public Person(String pname, Book pbook){  
      
        name = pname;  
        book = pbook;  
      }  
      
      public String getName(){  
        return name;  
      }  
      
      public Book getBook(){  
        return book;  
      }  
      
      public void setBook(Book newbook){  
        book = newbook;  
      }  
      
      public void introduce(){  
        System.out.print("Hi, I'm " + name + ". ");  
      }  
      
      public void readBook(){  
      
        System.out.println("I am going to read the book " + book.getTitle() + " now.");  
        System.out.println(book.getText());  
        System.out.println();  
      }  
    }  

Our person is very similar to the book, with some huge differences: our person can have a Book and we can change the book he/she has and he/she has a method to read that book to us. Imagine having a piece of speech synthesizer software and plugging that into our readBook() method, then we'd have an audio book. But lets keep it simple and just output the book in plain text.

In the readBook() method you can see how the properties of the book are used; you should have learned about Strings and how to work with them by now, but the short of it is that Java allows you to do "additions" to pieces of text (strings) to concatenate them together. So lets say our book title is "Jurrassic Park", the first line of readBook() would print out "I am going to read the book Jurrassic Park now.". Also note the introduce() method in which the person introduces him/herself to us; note how in stead of using println the method actually uses print. The "ln" indicates that a newline character will be printed after the text - in essence that means that the next text printed will start on a new line. The introduce() method does not do that. We'll see later on what effect that will have.

Lets see how we can use the Book and Person class together with a little program. Create the source file Book.java and the source file Person.java, fill them with the above code. Also create a source file Library.java; make sure all source files are in the same directory.

public class Library {  
      
      public static void main(String[] args){  
          
        Book hpbook = new Book("Harry Potter", "Once upon a time there was a small boy who lived in a cupboard. The end.");  
        Book jpbook = new Book("Jurrassic Park", "Some bloke created an island full of dinosaurs, things went wrong, people died. The end.");  
      
        Person john = new Person("John", hpbook);  
      
        // introduce yourself  
        john.introduce();  
      
        // okay, we've created our objects, now lets do some reading.  
        john.readBook();  
          
        // the library isn't closed yet, john is going to read another book  
        john.setBook(jpbook);  
        john.readBook();  
      }  
    }  

In case you missed it while reading up on the Java language or you in fact did not do any studying at all (bad you): those lines with the two slashes in front of them are called comments; they allow you to add information to your source files to help you and others understand it. Proper commenting is important and the subject of a future article.

Now lets compile and run the stuff. We have three source files now; you could javac each one of them, but it is far easier in this case to just do this:

javac *.java

That will lazily recompile all java source files in the current working directory. Compilation is quite quick, so generally it won't be any slower than compiling everything separately. Remember: once we're through the basics we're going to automate the hell out of these cumbersome tasks and you will likely never do manual compilation again. Hang in there. In any case if everything is correct you don't get compilation errors and you have your Book.class, Person.class and Library.class files, so now we can run our Library class:

java Library

Output on my computer:

E:\java\playtime>java Library
Hi, I'm John. I am going to read the book Harry Potter now.
Once upon a time there was a small boy who lived in a cupboard. The end.

I am going to read the book Jurrassic Park now.
Some bloke created an island full of dinosaurs, things went wrong, 
people died. The end.

Now you see the introduce print() trick working: the introduction and the first readBook() seamlessly fall on the same line, creating a more fluent textual representation. This is what I like to call "polishing the code", going just one little step further to make it better. Its one of the things that makes programming more fun, I encourage you to push yourself to look for these kind of polish moments.

Packages and the classpath

Alright, now you've seen an example of a little program that consists of multiple classes. But this is only a tiny example that is not really realistic. What is more realistic is that a real application that actually does something productive will likely consist of hundreds of classes. Not only that, such applications tend to do far more exciting things, such as communicate with databases, work with different file formats and do all other types of complex chores. You really do not want to write all the code to do everything, and you don't have to because people have done lots of work for you which you can easily download and link to your application as a library. This library you can then use to do the more advanced things and you can focus on those hundreds of classes you will need to create for your own application, not the hundreds of classes needed to be able to work with a database.

Now do the math - hundreds of classes + numerous libraries composing likely thousands of classes more; those are a lot of classes that will make up your application. Now imagine that you cram your hundreds of classes in the same directory - madness. Also imagine that those libraries can contain classes that have the exact same name, more madness. When you have two classes called 'Date' (which is actually the case in standard Java already!), how would the compiler know which class you're talking about in your code? It can't, without a little extra information.

That's why Java applications and libraries are actually put in a specific hierarchy. On disc you put the classes in separate sub directories, in code these subdirectories become packages. Closely related to these packages is the classpath. The classpath is an incredibly important concept to know and understand well so I suggest you pay good attention to this bit of the article. Now, consider the realistic example I gave you where there are thousands of classes floating around, most of them inside different libraries. The classpath is there to tell Java:

  • where it can find your application classes
  • which libraries (and thus which classes inside the library) should be available to your application

This is done by creating one long line of text that holds all the directories and library filenames we want to add, and then we pass that to either the javac or java command. There are many ways to actually manage the classpath; most of that management goes away when you start to use an IDE and/or build management tools. But you won't understand how to properly make the IDE do the work for you until you know how to manage it manually, so lets see how that is done.

We're not dealing with libraries just yet, lets focus on putting our Library application classes in a package structure and putting it on the classpath. You've actually been using the classpath already without knowing it. When you don't put your classes in a package, Java puts them in the so called "default package". This package exists only for the purpose of this article; for people learning to use Java to not have to dive into the complexities of the classpath right away. In real life you will never ever use it.

When you don't provide a classpath to Java yourself, the classpath simply consists of the current working directory. That's why Java has been able to find your "default package" classes until now, it was adding the current working directory to the classpath and that just so happened to be the place where our .classs files could be found. But now we're going to take control. First of all, remove all .class files from the playtime directory you now have to prevent confusion. Create new directories and move the source files to them as follows:

c:\java\playtime\library\Library.java
c:\java\playtime\library\model\Person.java
c:\java\playtime\library\model\Book.java

So now we've created a new subdirectory library for our Library application. On top of that we've moved our Person and Book classes even deeper into a model subdirectory, separating our main application class from our model classes. Right now the classes won't compile because Java simply does not know where to find the Book and Person class when compiling the Library class; they are not in the same directory anymore. We need to provide some additional information to Java to help it and also prevent conflicts with class names: a package. Change as follows.

Library.java
package library;             // added
      
    import library.model.Person; // added
    import library.model.Book;   // added
      
    public class Library {  
      
      public static void main(String[] args){  
          
        Book hpbook = new Book("Harry Potter", "Once upon a time there was a small boy who lived in a cupboard. The end.");  
        Book jpbook = new Book("Jurrassic Park", "Some bloke created an island full of dinosaurs, things went wrong, people died. The end.");  
      
        Person john = new Person("John", hpbook);  
      
        // introduce yourself  
        john.introduce();  
          
        // okay, we've created our objects, now lets do some reading.  
        john.readBook();  
          
        // the library isn't closed yet, john is going to read another book  
        john.setBook(jpbook);  
        john.readBook();  
      }  
    }  

Book.java
package library.model;  // added
      
    public class Book {  
      private String title;  
      private String text;  
      
      public Book(String btitle, String btext){  
      
        title = btitle;  
        text = btext;  
      }  
      
      public String getTitle(){  
        return title;  
      }  
      
      public String getText(){  
        return text;  
      }  
    }  

Person.java
package library.model;  // added
      
    public class Person {  
      private String name;  
      private Book book;  
      
      public Person(String pname, Book pbook){  
      
        name = pname;  
        book = pbook;  
      }  
      
      public String getName(){  
        return name;  
      }  
      
      public Book getBook(){  
        return book;  
      }  
      
      public void setBook(Book newbook){  
        book = newbook;  
      }  
      
      public void introduce(){  
        System.out.print("Hi, I'm " + name + ". ");  
      }  
        
      public void readBook(){  
      
        System.out.println("I am going to read the book " + book.getTitle() + " now.");  
        System.out.println(book.getText());  
        System.out.println();  
      }  
    }  

Now its time to make some close observation and really understand what is going on. The key is the library class where we added two majorly important pieces of information:

  • A package statement right at the top of the source file (rule: the package statement is the first statement in the file, always)
  • Import statements to import the Book and Person classes


Now look at all the package statements together.

Library: package library;  
    Book: package library.model;  
    Person: package library.model;  

Notice how the package statements match exactly to the subdirectories that the class is in, starting from a certain base directory, in our case our playtime directory. This is the key: you mark a directory as the starting point (generally you can refer to it as the project directory) and in that directory you create your package structure, as subdirectories on disc and as package statements in your code.

Before we get to the kicker, lets try to compile our Library.java file now. I'll spoil the clue: it will fail. Navigate to the library subdirectory and invoke the well-known javac command:

javac Library.java

You'll get a large list of compilation errors. Don't be intimidated by that - more often than not compilation errors are actually the result of an earlier compilation error; fixing one will make many others go away. In this case the root of our misery is this:

E:\java\playtime\library>javac Library.java
Library.java:3: package library.model does not exist
import library.model.Person;
                    ^

This is because Java is adding the current working directory to the classpath: the library subdirectory. The joke is: that directory does not contain a library/model/Person.java class, it contains a model/Person.java class! What we want in fact is that Java starts to look for the package subdirectories in c:\java\playtime, regardless of where we invoke the compiler. This is the moment where we have to take control and tell Java what the classpath is, like this:

javac -cp c:\java\playtime Library.java

Using the -cp command line parameter (which is short for -classpath which also works) we tell Java "Hey java, put c:\java\playtime on the classpath and start looking for packages with classes there". If everything is correct the compilation now succeeds, because Java can now find the library.model package. You'll also be surprised to find that even though we are not compiling the Book and Person classes ourselves, you'll still find the .class files in the model subdirectory now. This is because we import and use those classes in our Library class; Java wouldn't be able to compile Library without those depending classes existing first. So it went ahead and compiled them for us, free of charge. Luckily you don't have to do everything yourself.

Now lets try to run our Library program. Again we will be met with catastrophic failure:

E:\java\playtime\library>java Library
Exception in thread "main" java.lang.NoClassDefFoundError: Library
(wrong name: library/Library)
        at java.lang.ClassLoader.defineClass1(Native Method)
        at java.lang.ClassLoader.defineClassCond(Unknown Source)
        at java.lang.ClassLoader.defineClass(Unknown Source)
        at java.security.SecureClassLoader.defineClass(Unknown Source)
        at java.net.URLClassLoader.defineClass(Unknown Source)
        at java.net.URLClassLoader.access$000(Unknown Source)
        at java.net.URLClassLoader$1.run(Unknown Source)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.net.URLClassLoader.findClass(Unknown Source)
        at java.lang.ClassLoader.loadClass(Unknown Source)
        at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
        at java.lang.ClassLoader.loadClass(Unknown Source)
Could not find the main class: Library.  Program will exit.

Ouch. That scary looking error is called an exception stacktrace; now it looks intimidating, but eventually it is going to become your best friend. This is the way that Java tells you that something is wrong, and where. In this case it is not very helpful, except for the message at the very top:

java.lang.NoClassDefFoundError: Library (wrong name: library/Library)

That is quite cryptic; this is something you'll have to learn through experience. Java likes to give out cryptic error messages and you have to read them through slitted eyes to see what it really means. Lets try to translate that from computer speak to human: "I couldn't find the class Library: actually I'm lying; I did find it but it has the wrong name 'library/Library' so I assume it is not the class you wanted to run".

Apparently, Java finds the package of our Library class (library) very important - it is part of the "name" of the class. This is in fact the truth and another rule: the name of a class includes the full package. So our Person class' fully qualified name is in fact library.model.Person. If you want to run a class you have to use its fully qualified name, so in stead we have to invoke this:

e:\java\playtime\library>java -cp e:\java\playtime library.Library
Hi, I'm John. I am going to read the book Harry Potter now.
...

Again we have to tell Java where to look for packages and their classes, and we use the fully qualified name of our class. Result: success. Now lets do a fun little exercise. Navigate all the way back to c:\ (or e:\ in my case) and invoke the exact same command. What I did on my machine:

e:\java\playtime\library>cd \

e:\>java -cp e:\java\playtime library.Library
Hi, I'm John. I am going to read the book Harry Potter now.
...

It still works! You can run the application from anywhere you want now. It is still cumbersome to have to type in that java command, but that is something Eclipse will automate for us soon enough. Even on the command line there are ways to make it easier as we'll soon see.

Packages continued

Got all that? If not be sure to read through it all a few more times, try to make it click. When you understand it all you in fact know most about what you need to know about packages! The classpath is another thing, there is more to explore there; namely adding multiple directories to the classpath and adding libraries to it. But that is for later articles.

Lets examine a few more interesting tidbits about packages and import statements. First of all: even if the packages form a hierarchy, there is no form of inheritance in there. It is not a parent-child relationship. There is no such thing as a "sub-package" (yet). So to Java, the 'library' package and the 'library.model' package are totally unrelated things, even if on disc one is a subdirectory of the other. That's why in the Library class you need to explictly import the model classes.

On the topic of importing: imagine that you need to use dozens of classes. Having to add import statements for each one individually would be quite taxing. The standard Java Development Kit also contains a big pile of classes that are in packages. Just check out the API documentation. This lists all the classes that are in the Java 7 JDK. The top-left fragment lists all the packages that are in there. Click on the java.util package, which is a package holding classes you'll use quite a lot and in almost every application you'll create. The bottom-left fragment now only shows the classes that are in the java.util package; still quite a lot. Now imagine that your application is so mega complex that you need ALL of them - what a bother!

Luckily you can make that a lot easier:

import java.util.*;

This will import all classes in the java.util package so you can use them. Don't worry - this does not generate any kind of overhead or slow down, Java is very clever to filter out what you don't use. Eventually when you start to use an IDE the import statements can be easily generated, as we'll see later in the practical article.

So translating that to our example, we could replace the two import statements in Library with a single one:

import library.model.*;

As you now know, the package name is part of the name of a class. As such, you can refer to classes by their fully qualified name without having to add any import statement at all.

library.model.Book book = new library.model.Book("Dune", "bla bla");

You will hardly ever need to do this; the exception is when you have to use two classes with exactly the same class name. If you look in the full class list of the API documentation I provided earlier, you'll see that the standard Java development kit has two Date classes: java.util.Date and java.sql.Date. Now say that we have to use both of them in exactly the same class.

import java.util.Date;
import java.sql.Date;

public static void main(String[] args){
  Date date = new Date(); // yeah, which one!?
}

See the problem? Which one of the two are you talking about here? When you try to compile this Java will in fact tell you that the reference to Date is "ambiguous", it has multiple choices. The only way to get around that, other than scratching one of the two classes from your code, is to use their fully qualified names everywhere. Like I said: that will hardly ever be necessary.


Separating source and class files

There is one thing that annoys me still: the .class files are created in the same place as the .java source files. That won't do, we want to separate the binaries from the source files so we can easily clean them up (and later package into a library) without risking throwing away our source files. Remove the individual .class files and create a directory c:\java\playtime\target. Now to compile, navigate back to the library subdirectory and invoke this:

c:\java\playtime\library>
javac -cp c:\java\playtime -d c:\java\playtime\target Library.java

The -d option is the key, it tells Java to put the generated classes in a different directory. The end result is that Java now creates the classes in the target directory, with the correct package structure in place. To run the application we now have to use the target directory as our home base of course:

java -cp c:\java\playtime\target library.Library

Conversationally: you don't have to navigate specifically to the library directory to compile stuff of course; you can also just compile it from the playtime directory like this:

javac -cp c:\java\playtime -d c:\java\playtime\target library\Library.java

But you knew that, its basic filesystem stuff.

Packaging the application

We now have a neatly separated directory with all our classes and we know how to run it. That's fine during development time, but what if we wanted to give this application to someone else so they can run it - what a bother it would be for them, and for you because you need to include a big pile of instructions or create a shell script to automate it.

Fortunately Java has many facilities to package and deploy the application in such a way that it is almost no bother (if you have a Java runtime properly installed). The first step that we're going to cover in this article is to turn your application into an executable jar. Remember that I told you about libraries - in Java terms a library is a jar. A jar is actually a simple zip file holding our package directories and class files, with a special control file in there called the manifest. The manifest provides specific information to Java, such as which class is the class to run (with the main()) and what libraries to put in the classpath. Jars don't have to be executable; when they are not they are simply class libraries, a way to use classes in several different projects easily.

What we want to do is turn the content of our target directory into a jar, which we can then give to someone else and they can run it without much hassle. To do this we need the... wait for it: jar command! Navigate to the playtime directory and invoke the following:

jar -cvf library.jar -C target/ .

This basic command tells Java to jar up the stuff into a file 'library.jar', to include the entire content of the target subdirectory and to create the jar in the current working directory ('.'). If everything is correct, you'll now end up with a new file library.jar and the jar tool told you exactly what is was adding to it. Open up this file in your favorite zip tool (I use 7zip which is free and powerful); you'll find that the jar contains:

  • the full library package with all the .class files
  • a META-INF/manifest.mf file

The manifest file now contains only minimal information. Now, to be able to run this jar you have three basic options, one more difficult than the other. We'll start with the middle option:

java -jar library.jar

Alas, this will fail. Output on my computer:

E:\java\playtime>java -jar library.jar
Failed to load Main-Class manifest attribute from library.jar

Translated: "you didn't tell me which class is the main class stupid!". Oops, we created a class library while we wanted an executable jar. The following command will fix the problem:

jar -cevf library.Library library.jar -C target/ .

Now retry to run the jar; this time it will succeed.

E:\java\playtime>java -jar library.jar
Hi, I'm John. I am going to read the book Harry Potter now.
...

That makes things easier. For the heck of it, open up the manifest.mf file in the jar and you will now find this line added to it:

Main-Class: library.Library

That's all that is needed to make a jar executable. So if you didn't want to use the jar tool, you could just zip up the stuff yourself and add a META-INF/MANIFEST.MF file with that line in it and be done with it.

It can be even easier to run the jar; if the runtime is properly installed, it is actually setup to be able to run executable jar files just by double clicking on them in Windows (or the shell of whatever other OS you're using), just like it were a regular executable. Unfortunately what we have here is a command line / shell application; what will happen is that a command prompt will open, it will quickly print the output and then close again, or nothing happens at all (depending on the OS and the version of the OS). We'll have to revisit that in a later article where we do something visual with JavaFX 2.

The last method is the hardest and intended to be a lesson: we can also treat our library.jar as a class library in stead of an executable jar. To run like that you invoke java like you were doing before you created the jar, only you change it to this:

java -cp library.jar library.Library

So in stead of putting the classes in the target directory on the classpath, we now put the library.jar on the classpath. All classes and packages in it will then be on the classpath so Java can find them, and so we can run the library.Library class that is inside it. Jars are a really powerful feature of the Java platform that allow you to create libraries that you share among different applications, without having to copy all the source files around.

Its not really on-topic for this article, but of course you can add multiple libraries and directories to the classpath. How exactly to do that unfortunately is operating system specific; each operating system has its own list separator character and for some odd reason this list separator is used to define the classpath. On windows you can do something like this:

javac -cp c:\java\project\library;c:\java\lib\databasedriver.jar;
c:\java\lib\pdfloader.jar *.java

(All on one line; the formatting of the blog requires me to put it on multiple lines). So separate the elements you put on the classpath using a semicolon (;). On Linux, and I suspect on Mac too (no experience with it) you use a colon in stead:

javac -cp /usr/xx/java/library:/usr/xx/java/lib/databasedriver.jar:
/usr/xx/java/lib/pdfloader.jar *.java

Quite cumbersome huh? Imagine if you have 20 or more libraries. But that's what build tools and IDEs are for. Since more recent versions of Java you can also include an entire directory of libraries too, much like you can import an entire package of classes to be lazy. Like this:

javac -cp "c:\java\project\mysuperproject;c:\java\lib\*" *.java

The quotes are mandatory, and you must use *, not *.jar. Be careful when using the wildcard, you may be including libraries on the classpath that should not be there. You won't get performance problems from adding unnecessary jars, but you may create conflicts between classes. Keep your libraries tidy and separate if necessary.


In closing

Time to take a breather. That was a really tough lesson; don't be ashamed if you need to read that a few times and you need to reference other resources to get the same thing explained in a different way. This really is a hard thing to wrap your head around and it is all the more difficult if you never really worked on the command line before; unfortunately it is paramount that you get this before you can really start to apply the Java platform and all tools that can make your life a lot easier. Lets review a little:

  • the classpath is a piece of configuration that tells java where to look for packages of classes; this can be in either directories or jar libraries
  • classes without a package are in the "default package"; you must not use the default package for serious projects
  • packages help to structure classes and makes it so multiple classes with the same name can exist on the classpath; the package name makes them unique
  • you can create jar libraries to easily share classes among different applications, or to package up an application so you can easily distribute and run it

So javac, java and jar - the three tools of the JDK that you would use around the clock of there weren't such things as Eclipse and ANT/Maven. In the two practical counterparts of this article we are going to apply Eclipse to do exactly the same things we did on the command line, seeing how the different concepts of Java (classpath, packages, source and class directories, invoking the tools, etc.) work into the IDE. On top of that the code is going to be spiced up a little bit and we're going to include some basic standard Java classes that are incredibly useful and you'll be using in almost every project.