Everything is an object, Part 2

Build your first Java program

There are several issues you must understand before seeing your first Java program.

TEXTBOX: TEXTBOX_HEAD: Everything is an object: Read the whole series!

Name visibility

A problem in any programming language is the control of names. If you use a name in one module of the program, and another programmer uses the same name in another module, how do you distinguish one name from another and prevent the two names from "clashing?" In C, this is a particular problem because a program is often an unmanageable sea of names. C++ classes (on which Java classes are based) nest functions within classes so they cannot clash with function names nested within other classes. However, C++ still allowed global data and global functions, so clashing was still possible. To solve this problem, C++ introduced namespaces using additional keywords.

Java was able to avoid all of this by taking a fresh approach. To produce an unambiguous name for a library, the specifier used is not unlike an Internet domain name. In fact, the Java creators want you to use your Internet domain name in reverse since those are guaranteed to be unique. Since my domain name is BruceEckel.com, my utility library of foibles would be named com.bruceeckel.utility.foibles. After your reversed domain name, the dots are intended to represent subdirectories.

In Java 1.0 and Java 1.1, the domain extensions com, edu, org, net, and so forth were capitalized by convention, so the library would appear as COM.bruceeckel.utility.foibles. Partway through the development of Java 2, however, it was discovered that this caused problems, and so now the entire package name is lowercase.

This mechanism means that all of your files automatically live in their own namespaces, and each class within a file must have a unique identifier. So you do not need to learn special language features to solve this problem -- the language takes care of it for you.

Using other components

Whenever you want to use a predefined class in your program, the compiler must know how to locate it. Of course, the class might already exist in the same source code file that it's being called from. In that case, you simply use the class -- even if the class doesn't get defined until later in the file. Java eliminates this forward referencing problem so you don't need to think about it.

What about a class that exists in some other file? You might think that the compiler should be smart enough to simply go and find it, but there is a problem. Imagine that you want to use a class of a particular name, but more than one definition for that class exists (presumably these are different definitions). Or worse, imagine that you're writing a program, and as you're building it you add a new class to your library that conflicts with the name of an existing class.

To solve this problem, you must eliminate all potential ambiguities. This is accomplished by telling the Java compiler exactly what classes you want using the import keyword. import tells the compiler to bring in a package, which is a library of classes. (In other languages, a library could consist of functions and data as well as classes, but remember that all code in Java must be written inside a class.)

Most of the time you'll be using components from the standard Java libraries that come with your compiler. With these, you don't need to worry about long, reversed domain names; you just say, for example:

import java.util.ArrayList;

to tell the compiler that you want to use Java's ArrayList class. However, util contains a number of classes and you might want to use several of them without declaring them all explicitly. This is easily accomplished by using * to indicate a wild card:

import java.util.*;

It is more common to import a collection of classes in this manner than to import classes individually.

The static keyword

Ordinarily, when you create a class you are describing how objects of that class look and how they will behave. You don't actually get anything until you create an object of that class with new, and at that point data storage is created and methods become available.

But there are two situations in which this approach is not sufficient. One is if you want to have only one piece of storage for a particular piece of data, regardless of how many objects are created, or even if no objects are created.

The other is if you need a method that isn't associated with any particular object of this class. That is, you need a method that you can call even if no objects are created. You can achieve both of these effects with the static keyword. When you say something is static, it means that data or method is not tied to any particular object instance of that class. So even if you've never created an object of that class you can call a static method or access a piece of static data. With ordinary, non-static data and methods you must create an object and use that object to access the data or method, since non-static data and methods must know the particular object they are working with. Of course, since static methods don't need any objects to be created before they are used, they cannot directly access non-static members or methods by simply calling those other members without referring to a named object (since non-static members and methods must be tied to a particular object).

Some object-oriented languages use the terms class data and class methods, meaning that the data and methods exist only for the class as a whole, and not for any particular objects of the class. Sometimes the Java literature uses these terms too.

To make a data member or method static, you simply place the keyword before the definition. For example, the following produces a static data member and initializes it:

class StaticTest {
    static int i = 47;
}

Now even if you make two StaticTest objects, there will still be only one piece of storage for StaticTest.i. Both objects will share the same i. Consider:

StaticTest st1 = new StaticTest();
StaticTest st2 = new StaticTest();

At this point, both st1.i and st2.i have the same value (47) since they refer to the same piece of memory.

There are two ways to refer to a static variable. As indicated above, you can name it via an object, by saying, for example, st2.i. You can also refer to it directly through its class name, something you cannot do with a non-static member. (This is the preferred way to refer to a static variable since it emphasizes that variable's static nature.)

StaticTest.i++;

The ++ operator increments the variable. At this point, both st1.i and st2.i will have the value 48.

Similar logic applies to static methods. You can refer to a static method either through an object as you can with any method, or with the special additional syntax ClassName.method(). You define a static method in a similar way:

class StaticFun {
  static void incr() { StaticTest.i++; }
}

You can see that the StaticFun method incr() increments the static data i. You can call incr() in the typical way, through an object:

StaticFun sf = new StaticFun();
sf.incr();

Or, because incr() is a static method, you can call it directly through its class:

StaticFun.incr();

While static, when applied to a data member, definitely changes the way the data is created (one for each class versus the non-static one for each object), when applied to a method it's not so dramatic. An important use of static for methods is to allow you to call that method without creating an object. This is essential, as we will see, in defining the main() method that is the entry point for running an application.

Like any method, a static method can create or use named objects of its type, so a static method is often used as a "shepherd" for a flock of instances of its own type.

Your first Java program

Finally, here's the program. It starts by printing a string, and then the date, using the

Date

class from the Java standard library. Note that an additional style of comment is introduced here: the

//

, which is a comment until the end of the line:

// HelloDate.java
import java.util.*;
public class HelloDate {
  public static void main(String[] args) {
    System.out.println("Hello, it's: ");
    System.out.println(new Date());
  }
}

At the beginning of each program file, you must place the import statement to bring in any extra classes you'll need for the code in that file. Note that I say "extra"; that's because there's a certain library of classes that are automatically brought into every Java file: java.lang.

Start up your Web browser and look at the documentation from Sun. (If you haven't downloaded it from Java.sun.com or otherwise installed the Java documentation, do so now). If you look at the list of the packages, you'll see all the different class libraries that come with Java.

Select java.lang. This will bring up a list of all the classes that are part of that library. Since java.lang is implicitly included in every Java code file, these classes are automatically available. There's no Date class listed in java.lang, which means you must import another library to use that. If you don't know the library where a particular class is, or if you want to see all of the classes, you can select "Tree" in the Java documentation. Now you can find every single class that comes with Java. Then you can use the browser's "find" function to find Date. When you do, you'll see it listed as java.util.Date, which lets you know that it's in the util library and that you must import java.util.* in order to use Date.

If you go back to the beginning, select java.lang and then System; you'll see that the System class has several fields, and if you select out you'll discover that it's a static PrintStream object. Since it's static you don't need to create anything. The out object is always there and you can just use it. What you can do with this out object is determined by the type it is: a PrintStream.

Conveniently, PrintStream is shown in the description as a hyperlink, so if you click on that you'll see a list of all the methods you can call for PrintStream. There are quite a few. For now all we're interested in is println(), which in effect means "print what I'm giving you out to the console and end with a new line." Thus, in any Java program you write you can say System.out.println("things") whenever you want to print something to the console.

The name of the class is the same as the name of the file. When you're creating a stand-alone program such as this one, one of the classes in the file must have the same name as the file. (The compiler complains if you don't do this.) That class must contain a method called main() with the signature shown:

public static void main(String[] args) {

The public keyword means that the method is available to the outside world (described in detail in Chapter 5 [of Thinking in Java]). The argument to main() is an array of String objects. The args won't be used in this program, but the Java compiler insists that they be there because they hold the arguments invoked on the command line.

The line that prints the date is quite interesting:

System.out.println(new Date());

Consider the argument: a Date object is being created just to send its value to println(). As soon as this statement is finished, that Date is unnecessary, and the garbage collector can come along and get it anytime. We don't need to worry about cleaning it up.

Compiling and running

To compile and run this program, you must first have a Java programming environment. There are a number of third-party development environments, but in this [article] we will assume that you are using the JDK from Sun, which is free. If you are using another development system, you will need to look in the documentation for that system to determine how to compile and run programs.

Get on the Internet and go to http://java.sun.com. There you will find information and links that will lead you through the process of downloading and installing the JDK for your particular platform.

Once the JDK is installed, and you've set up your computer's path information so that it will find javac and java, download and unpack the source code for [Chapter 2 of Thinking in Java] (at http://www.BruceEckel.com). This will create a subdirectory for each chapter in Thinking in Java. Move to subdirectory c02 and type:

javac HelloDate.java

This command should produce no response. If you get any kind of an error message it means you haven't installed the JDK properly and you need to investigate those problems.

On the other hand, if you just get your command prompt back, you can type:

java HelloDate

and you'll get the message and the date as output.

This is the process you can use to compile and run each of the programs in Thinking in Java. However, you will see that the source code also has a file called makefile in each chapter, and this contains "make" commands for automatically building the files for that chapter. See http://www.BruceEckel.com for details on how to use the makefiles.

Comments and embedded documentation

There are two types of comments in Java. The first is the traditional C-style comment that was inherited by C++. These comments begin with a /* and continue, possibly across many lines, until a */. Note that many programmers will begin each line of a continued comment with a *, so you'll often see:

/* This is a comment
*  that continues
*  across lines
*/
Related:
1 2 Page 1
Page 1 of 2