Chapter 10: Development


You want to keep your classes organized.You need to have powerful ways for your classes to find each other. You want to make sure that when you're looking for a particular class you get the one you want, and not another class that happens to have the same name. In this chapter we'll explore some of the advanced capabilities of the java and javac commands. We'll revisit the use of packages in Java, and how to search for classes that live in packages.

Certification Objective —Using the javac and java Commands (Exam Objectives 7.1, 7.2, and 7.5)

7.1 Given a code example and a scenario, write code that uses the appropriate access modifiers, package declarations, and import statements to interact with (through access or inheritance) the code in the example.

7.2 Given an example of a class and a command-line, determine the expected runtime behavior.

7.5 Given the fully-qualified name of a class that is deployed inside and/or outside a JAR file, construct the appropriate directory structure for that class. Given a code example and a classpath, determine whether the classpath will allow the code to compile successfully.

So far in this book, we've probably talked about invoking the javac and java commands about 1000 times; now we're going to take a closer look.

Compiling with javac

The javac command is used to invoke Java's compiler. In Chapter 5 we talked about the assertion mechanism and when you might use the -source option when compiling a file. There are many other options you can specify when running javac, options to generate debugging information or compiler warnings for example. For the exam, you'll need to understand the -classpath and -d options, which we'll cover in the next few pages. Here's the structural overview for javac:

 javac [options] [source files] 

There are additional command-line options called @argfiles, but you won't need to study them for the exam. Both the [options] and the [source files] are optional parts of the command, and both allow multiple entries. The following are both legal javac commands:

 javac -help javac -classpath com:. -g Foo.java Bar.java 

The first invocation doesn't compile any files, but prints a summary of valid options. The second invocation passes the compiler two options (-classpath, which itself has an argument of com:. and -g), and passes the compiler two .java files to compile (Foo. java and Bar. java). Whenever you specify multiple options and/or files they should be separated by spaces.

Compiling with -d

By default, the compiler puts a .class file in the same directory as the .java source file. This is fine for very small projects, but once you're working on a project of any size at all, you'll want to keep your .java files separated from your .class files. (This helps with version control, testing, deployment) The -d option lets you tell the compiler in which directory to put the .class file(s) it generates (d is for destination). Let's say you have the following directory structure:

 myProject        |        |--source        |      |        |      |-- MyClass.java        |        |-- classes               |               |-- 

The following command, issued from the myProject directory, will compile MyClass.java and put the resulting MyClass.class file into the classes directory. (Note: This assumes that MyClass does not have a package statement; we'll talk about packages in a minute.)

 cd myProject javac -d classes source/MyClass.java 

This command also demonstrates selecting a .java file from a subdirectory of the directory from which the command was invoked. Now let's take a quick look at how packages work in relationship to the -d option.

Suppose we have the following .java file in the following directory structure:

 package com.wickedlysmart; public class MyClass { } myProject        |        |--source        |      |        |      |--com        |           |        |           |--wickedlysmart        |                       |        |                       |--MyClass.java        |--classes        |      |        |      |--com        |           |        |           |--wickedlysmart        |                       |        |                       |-- (MyClass.class goes here) 

If you were in the source directory, you would compile MyClass.java and put the resulting MyClass.class file into the classes/com/wickedlysmart directory by invoking the following command:

 javac -d ../classes com/wickedlysmart/MyClass.java 

This command could be read: "To set the destination directory, cd back to the myProject directory then cd into the classes directory, which will be your destination. Then compile the file named MyClass.java. Finally, put the resulting MyClass.class file into the directory structure that matches its package, in this case, classes/com/wickedlysmart." Because Myclass.java is in a package, the compiler knew to put the resulting .class file into the classes/com/wickedlysmart directory.

Somewhat amazingly, the javac command can sometimes help you out by building directories it needs! Suppose we have the following:

 package com.wickedlysmart; public class MyClass { } myProject        |        |--source        |      |        |      |--com        |           |        |           |--wickedlysmart        |                       |        |                       |--MyClass.java        |        |--classes        |      | 

And the following command (the same as last time):

 javac -d ../classes com/wickedlysmart/MyClass.java 

In this case, the compiler will build two directories called com and com/wickedlysmart in order to put the resulting MyClass.class file into the correct package directory (com/wickedlysmart/) which it builds within the existing .../classes directory.

The last thing about -d that you'll need to know for the exam is that if the destination directory you specify doesn't exist, you'll get a compiler error. If, in the previous example, the classes directory did NOT exist, the compiler would say something like:

 java:5: error while writing MyClass: classes/MyClass.class (No such file or directory) 

Launching Applications with java

The java command is used to invoke the Java virtual machine. In Chapter 5 we talked about the assertion mechanism and when you might use flags such as -ea or -da when launching an application. There are many other options you can specify when running the java command, but for the exam, you'll need to understand the -classpath (and its twin -cp) and -D options, which we'll cover in the next few pages. In addition, it's important to understand the structure of this command. Here's the overview:

 java [options] class [args] 

The [options] and [args] parts of the java command are optional, and they can both have multiple values. You must specify exactly one class file to execute, and the java command assumes you're talking about a .class file, so you don't specify the .class extension on the command line. Here's an example:

 java -DmyProp=myValue MyClass x 1 

Sparing the details for later, this command can be read as "Create a system property called myProp and set its value to myValue. Then launch the file named MyClass.class and send it two String arguments whose values are x and 1."

Let's look at system properties and command-line arguments more closely.

Using System Properties

Java 5 has a class called java.util.Properties that can be used to access a system's persistent information such as the current versions of the operating system, the Java compiler, and the Java virtual machine. In addition to providing such default information, you can also add and retrieve your own properties. Take a look at the following:

 import java.util.*; public class TestProps {   public static void main(String[] args) {     Properties p = System.getProperties();     p.setProperty("myProp", "myValue");     p.list(System.out);   } } 

If this file is compiled and invoked as follows:

 java -DcmdProp=cmdVal TestProps 

You'll get something like this:

 ... os.name=Mac OS X myProp=myValue ... java.specification.vendor=Sun Microsystems Inc. user.language=en java.version=1.5.0_02 ... cmdProp=cmdVal ... 

where the represent lots of other name=value pairs. (The name and value are sometimes called the key and the property.) Two name=value properties were added to the system's properties: myProp=myValue was added via the setProperty method, and cmdProp=cmdVal was added via the -D option at the command line. When using the -D option, if your value contains white space the entire value should be placed in quotes like this:

 java -DcmdProp="cmdVal take 2" TestProps 

Just in case you missed it, when you use -D, the name=value pair must follow immediately, no spaces allowed.

The getProperty() method is used to retrieve a single property. It can be invoked with a single argument (a String that represents the name (or key)), or it can be invoked with two arguments, (a String that represents the name (or key), and a default String value to be used as the property if the property does not already exist). In both cases, getProperty() returns the property as a String.

Handling Command-Line Arguments

Let's return to an example of launching an application and passing in arguments from the command line. If we have the following code:

 public class CmdArgs {   public static void main(String[] args) {     int x = 0;     for(String s : args)       System.out.println(x++ + " element = " + s);   } } 

compiled and then invoked as follows

 java CmdArgs x 1 

the output will be

 0 element = x 1 element = 1 

Like all arrays, args index is zero based. Arguments on the command line directly follow the class name. The first argument is assigned to args[0], the second argument is assigned to args[l], and so on.

Finally, there is some flexibility in the declaration of the main() method that is used to start a Java application. The order of main()'s modifiers can be altered a little, the String array doesn't have to be named args, and as of Java 5 it can be declared using var-args syntax. The following are all legal declarations for main():

 static public void main(String[] args) public static void main(String... x) static public void main(String bang_a_gong[]) 

Searching for Other Classes

In most cases, when we use the java and javac commands, we want these commands to search for other classes that will be necessary to complete the operation. The most obvious case is when classes we create use classes that Sun provides with J2SE (now sometimes called Java SE), for instance when we use classes in java.lang or java.util. The next common case is when we want to compile a file or run a class that uses other classes that have been created outside of what Sun provides, for instance our own previously created classes. Remember that for any given class, the java virtual machine will need to find exactly the same supporting classes that the javac compiler needed to find at compilation time. In other words, if javac needed access to java.uti1. HashMap then the java command will need to find java.util.HashMap as well.

Both java and javac use the same basic search algorithm:

  1. They both have the same list of places (directories) they search, to look for classes.

  2. They both search through this list of directories in the same order.

  3. As soon as they find the class they're looking for, they stop searching for that class. In the case that their search lists contain two or more files with the same name, the first file found will be the file that is used.

  4. The first place they look is in the directories that contain the classes that come standard with J2SE.

  5. The second place they look is in the directories defined by classpaths.

  6. Classpaths should be thought of as "class search paths." They are lists of directories in which classes might be found.

  7. There are two places where classpaths can be declared:

    • A classpath can be declared as an operating system environment variable. The classpath declared here is used by default, whenever java or javac are invoked.

    • A classpath can be declared as a command-line option for either java or javac. Classpaths declared as command-line options override the classpath declared as an environment variable, but they persist only for the length of the invocation.

Declaring and Using Classpaths

Classpaths consist of a variable number of directory locations, separated by delimiters. For Unix-based operating systems, forward slashes are used to construct directory locations, and the separator is the colon (:). For example:

 -classpath /com/foo/acct:/com/foo 

specifies two directories in which classes can be found: /com/foo/acct and /com/foo. In both cases, these directories are absolutely tied to the root of the file system, which is specified by the leading forward slash. It's important to remember that when you specify a subdirectory, you're NOT specifying the directories above it. For instance, in the preceding example the directory /com will NOT be searched.

image from book
Exam Watch

Most of the path-related questions on the exam will use Unix conventions. If you are a Windows user, your directories will be declared using backslashes (\) and the separator character you use will be a semicolon (;). But again, you will NOT need any shell-specific knowledge for the exam.

image from book

A very common situation occurs in which java or javac complains that it can't find a class file, and yet you can see that the file is IN the current directory! When searching for class files, the java and javac commands don't search the current directory by default. You must tell them to search there. The way to tell java or javac to search in the current directory is to add a dot (.) to the classpath:

 -classpath /com/foo/acct:/com/foo:. 

This classpath is identical to the previous one EXCEPT that the dot (.) at the end of the declaration instructs java or javac to also search for class files in the current directory. (Remember, we're talking about class files—when you're telling javac which .java file to compile, javac looks in the current directory by default.)

It's also important to remember that classpaths are searched from left to right. Therefore in a situation where classes with duplicate names are located in several different directories in the following classpaths, different results will occur:

 -classpath  /com:/foo:. 

is not the same as

 -classpath .:/foo:/com 

Finally, the java command allows you to abbreviate -classpath with -cp. The Java documentation is inconsistent about whether the javac command allows the -cp abbreviation. On most machines it does, but there are no guarantees.

Packages and Searching

When you start to put classes into packages, and then start to use classpaths to find these classes, things can get tricky. The exam creators knew this, and they tried to create an especially devilish set of package/classpath questions with which to confound you. Let's start off by reviewing packages. In the following code:

 package com.foo; public class MyClass { public void hi() { } } 

we're saying that MyClass is a member of the com.foo package. This means that the fully qualified name of the class is now com.foo.Myclass. Once a class is in a package, the package part of its fully qualified name is atomic—it can never be divided. You can't split it up on the command line, and you can't split it up in an import statement.

Now let's see how we can use com.foo.MyClass in another class:

 package com.foo; public class MyClass { public void hi () { } { 

And in another file:

 import com.foo.MyClass;   // either import will work import com.foo.*; public class Another {   void go() {     MyClass ml = new MyClass();                  // alias name     com.foo.MyClass m2 = new com.foo.MyClass();  // full name     m1.hi ();     m2.hi ();   } } 

It's easy to get confused when you use import statements. The preceding code is perfectly legal. The import statement is like an alias for the class's fully qualified name. You define the fully qualified name for the class with an import statement (or with a wildcard in an import statement of the package). Once you've defined the fully qualified name, you can use the "alias" in your code—but the alias is referring back to the fully qualified name.

Now that we've reviewed packages, let's take a look at how they work in conjunction with classpaths and command lines. First we'll start off with the idea that when you're searching for a class using its fully qualified name, that fully qualified name relates closely to a specific directory structure. For instance, relative to your current directory, the class whose source code is

 package com.foo; public class MyClass { public void hi() { } } 

would have to be located here:

 com/foo/MyClass.class 

In order to find a class in a package, you have to have a directory in your classpath that has the package's leftmost entry (the package's "root") as a subdirectory.

This is an important concept, so let's look at another example:

 import com.wickedlysmart.Utils; class TestClass {   void doStuff() {     Utils u = new Utils( );                     // simple name     u.doX("arg1", "arg2");     com.wickedlysmart.Date d =                 new com.wickedlysmart.Date();   // full name     d.getMonth("Oct");   } } 

In this case we're using two classes from the package com.wickedlysmart. For the sake of discussion we imported the fully qualified name for the Utils class, and we didn't for the Date class. The only difference is that because we listed Utils in an import statement, we didn't have to type its fully qualified name inside the class. In both cases the package is com.wickedlysmart. When it's time to compile or run TestClass, the classpath will have to include a directory with the following attributes:

  • A subdirectory named com (we'll call this the "package root" directory)

  • A subdirectory in com named wickedlysmart

  • Two files in wickedlysmart named Utils.class and Date.class

Finally, the directory that has all of these attributes has to be accessible (via a classpath) in one of two ways:

  1. The path to the directory must be absolute, in other words, from the root (the file system root, not the package root).

    or

  2. The path to the directory has to be correct relative to the current directory.

Relative and Absolute Paths

A classpath is a collection of one or more paths. Each path in a classpath is either an absolute path or a relative path. An absolute path in Unix begins with a forward slash (/) (on Windows it would be something like c: \). The leading slash indicates that this path is starting from the root directory of the system. Because it's starting from the root, it doesn't matter what the current directory is—a directory's absolute path is always the same. A relative path is one that does NOT start with a slash. Here's an example of a full directory structure, and a classpath:

 / (root)      |      |--dirA           |           |-- dirB                 |                 |--dirC -cp dirB:dirB/dirC 

In this example, dirB and dirB/dirC are relative paths (they don't start with a slash /). Both of these relative paths are meaningful only when the current directory is dirA. Pop Quiz! If the current directory is dirA, and you're searching for class files, and you use the classpath described above, which directories will be searched?

 dirA? dirB? dirC? 

Too easy? How about the same question if the current directory is the root (/)? When the current directory is dirA, then dirB and dirC will be searched, but not dirA (remember, we didn't specify the current directory by adding a dot (.) to the classpath). When the current directory is root, since dirB is not a direct subdirectory of root, no directories will be searched. Okay, how about if the current directory is dirB? Again, no directories will be searched! This is because dirB doesn't have a subdirectory named dirB. In other words, Java will look in dirB for a directory named dirB (which it won't find), without realizing that it's already in dirB.

Let's use the same directory structure and a different classpath:

 / (root)      |      |--dirA           |           |-- dirB                 |                 |--dirC -cp /dirB:/dirA/dirB/dirC 

In this case, what directories will be searched if the current directory is dirA? How about if the current directory is root? How about if the current directory is dirB? In this case, both paths in the classpath are absolute. It doesn't matter what the current directory is; since absolute paths are specified the search results will always be the same. Specifically, only dirC will be searched, regardless of the current directory. The first path (/dirB) is invalid since dirB is not a direct subdirectory of root, so dirB will never be searched. And, one more time, for emphasis, since dot (.) is not in the classpath, the current directory will only be searched if it happens to be described elsewhere in the classpath (in this case, dirC).




SCJP Sun Certified Programmer for Java 5 Study Guide Exam 310-055
SCJP Sun Certified Programmer for Java 5 Study Guide (Exam 310-055) (Certification Press)
ISBN: 0072253606
EAN: 2147483647
Year: 2006
Pages: 131

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net