From this page to the end of the book, there is one goal in mind: to show you how to consume Java code as well as anyone else. By understanding how Java programs can be composed, what language tools there are to assist you, and how written code can be read in a methodical, straightforward fashion, you’ll be ready to manage existing code and even write some small programs yourself.
The shorter-term goal is to help prepare you for the Java SE 7 OCA exam. topics are organized with the long-term goal in mind, however, and the discussion is not limited to the exam objectives. If you’re a literal, need-to-know-only type, a map of the exam objectives to each chapter appears in the back of the book. You can decide what to ignore.
To start, some basic questions will be answered: How is Java code organized? How do you write a Java program? How do you use existing Java code in a program you write? And how do you manage the state of a program as it runs?
In this chapter, we’ll cover the following topics:
Java has a few top-level structures for containing code. There are structures called interfaces and abstract classes, for example, which we’ll discuss in Chapter 10, “Understanding Java Interfaces and Abstract Classes.” The one to understand right away is a simple Java class. The one thing you’ll do most often with Java classes is make objects. An object is a runtime instance of a class somewhere in the memory space of a Java program. All the different objects of all the different classes you use to create one program, taken as a whole, represent the state of that program.
Java classes have two primary elements: methods, often called functions or procedures in other languages; and fields, more generally known as variables. Together these are called the members of the class. Methods operate on the state of objects to which they belong. If the change is important to remember, a variable stores that change. That’s all classes really do. It is the programmer who creates and arranges these elements in such a way that the resulting code is useful and, ideally, easy for other programmers to understand.
A Java class can be quite simple. It can include variables to represent something you’re calling data, such as personal contact information, defined in a class you name to express that concept:
public class ContactInfo {
String firstName;
String lastName;
String address;
String city;
String cell;
}
The ContactInfo class is about as simple as a Java class can get. It contains data, expressed as a number of fields, you could use in a program to track people who matter to you. You can represent each person’s information as one ContactInfo object, which you can then use to add, access, or modify this information over time.
Each field is declared as a String type, which is another class. In Java, you always create new classes from existing types. A few of these types, called primitives, don’t have their own fields and methods, but you’ll tend to focus on classes and objects because you can define them to do exactly what you think they ought to do. In the case of a String, you get to use a type that lets you put text in an object, manipulate it, and retrieve it.
ContactInfo methods, once you add them, would perform these same general actions. What they provide that is new is a context: a named type with operations that are specific to contact information, something that isn’t expressed well or fully by a bunch of named strings alone.
Of course a Java class gets a little busier once you devise methods for it—again, a subject we’ll explore throughout this book. For the sake of illustration, let’s say you wanted the ContactInfo class to provide methods that read or write the value of its firstName variable. The methods to support that might look like this:
public class ContactInfo {
...
public void setFirst(String newName) {
firstName = newName;
}
public String getFirst() {
return firstName;
}
}
The ellipsis in this example is a placeholder, not legal code. Pretend your variables were listed here instead. You have now added two methods in the ContactInfo class, called setFirst() and getFirst(). One lets a caller retrieve the value of the firstName field; the other lets the caller provide a new value to replace the current one.
Following this same syntax, you could write methods to represent each of the remaining fields. You might even end up with code that will compile. You’d need just a little luck because there are several requirements I haven’t yet spelled out. For now, you’re just getting an idea what Java code looks like.
A method is an operation that can be called by other methods. The called method may require information from the calling method in the form of parameters. A method also declares what kind of value it returns in the form of a type. Parameters and return values are stored and represented in code as variables. The full declaration of a method, then, is said to have a signature, like this one:
int getJellyBeans(int howMany)
The parameter howMany has to be an integer. More specifically, it must correspond to Java’s int type. The parameter name should at least hint at its intent. The method getJellyBeans() returns an integer too. Maybe it’s the number of jelly beans the caller receives; maybe it’s the number remaining. You won’t know unless the method’s author tells you; you just know it will be an int type.
Some languages infer a variable’s type by the assigned value. If you declare x = 5, for example, it’s assumed you mean the integer 5, not the character 5.
One way to reinforce simplicity is by visualizing classes with simple diagrams, like the one shown in Figure 1-1.
This diagram loosely applies visual conventions described by the Unified Modeling Language (UML), a widely used design language. The class name Item appears in the top section. Immediately below we list the class fields stem, options, and answer. We also indicate each field’s type, which are String, an array of String (signified by the brackets), and an integer (int), respectively.
At the bottom, we list three methods: addOption(), addStem(), and setAnswer(). Each method name, along with the parameter types specified, implies a direct relationship with one of the members, but we’d probably like more information to be sure.
As with variables, a good method communicates purpose through its name. In our diagram, we could further clarify things by including the type required for each parameter and the return to the caller, but we don’t aim to be thorough. Instead we try to strike a balance between useful information and an easy-to-digest depiction. If we’re really good, the details we leave out are something a savvy reader can infer. If the reader is really good, they can fill in the blanks in a matter that best suits the overall project. Naturally those skills take time and experience to develop.
So what’s the purpose of a class again? Most of the time, we use it to make objects. An object is a runtime entity whose type is defined by its class. All Java objects have a type. All types have methods. All types have fields that, taken as a whole, reflect the object’s state. Every time a field in an object changes value, the overall state of that object also changes.
As an example of what is meant by object state, let’s imagine a class called Point3D. We’ll give it three integer fields: x, y, and z. It will include methods setX(), setY(), and setZ(), which allow a caller to modify any one element of a Point3D object at a time. A diagram of this class might look like Figure 1-2.
We can assume, while designing, that these methods will do the work suggested by their names. Whether they operate correctly and predictably is, of course, a code concern. But the objects made from this class will have the Point3D type. Each object will expose the methods setX(), setY(), and setZ(). The state of each object is based (mostly) on the three integer values listed. In that sense, a Point3D object whose x, y, and z values are, say, 1, 2, and 3, has a different state than one whose values are 3, 5, and 9.
One way to think of overall Java program state is to list all the different objects that make it up. Most of the time, the sheer number of objects in play makes this approach impractical. There are thousands of objects, even in a modestly sized Java program, so we usually take this approach only when there’s big trouble. If a program runs too slowly, for example, or consumes more resources than expected, and there is no quicker way to isolate the cause, then it’s time to sift through the objects individually.
There are other elements we can use to complement a Java class. I’m keeping things as simple as I can for now, but I should mention those other elements and where in the book we’ll discuss them fully.
Constructors are a key element in many Java classes. A constructor is method-like in its form, but its only role is to define the way objects of the class are created. Every constructor has the same name as its class, has no return type, and may accept parameters. When the programmer doesn’t need to alter default object construction, the Java compiler will include one in the class file it generates. Constructors are discussed fully in Chapter 8, “Using Java Constructors.” Until then, if we need a specialized constructor to complete an example, we’ll show it but keep commentary on it to a minimum.
Java classes may also contain other Java classes. These are called inner classes, and they are handy when you want to include a new type that depends completely on the class that contains it. Inner classes fall outside the scope of this book, but you should know about them. Many Java programmers use them and, in some circles, how they use them raises a bit of controversy.
You should also know what are called class members in a class. We discuss them at length in Chapter 7, “Using Java Methods to Communicate.” For now, I mention them to qualify a statement I made earlier: Classes are primarily used to make objects. That statement still holds, but there are times when you’ll want to name methods or variables that actually belong to the class itself.
Static members exploit a difference in the way classes are loaded into memory. A class loads into memory as part of the class itself, that is, without the possibility of changing over time the way objects do. That means you can call a static method without making an object from its class first, as you’ll see in the next section. You can access a static variable by its class name or by an object of that class’s type. Its value will be visible to all objects of the class and will be the same for all objects: more on that in Chapter 7. For now, bear in mind there’s something more to classes than just making objects.
Earlier I described a method as an operation that can be called by another method. You might have wondered where the calling starts? Which method is the first to get called, and who calls it?
The answer for a Java program is its main() method. A main() method is the gateway between the startup of a Java process, which is managed by the Java Virtual Machine (JVM), and the beginning of the programmer’s code. The JVM calls on the underlying system to allocate memory and CPU time, access files, and so on. This happens behind the scenes normally, so if you run java -version on a JVM-configured system, you don’t see anything happening beyond the intended output. Like any other executable program, however, the JVM gets permission from the operating system to run and asks for the resources it needs to set itself up. When that is done, the JVM processes the -version flag, prints the result, releases the resources it allocated, and tells the operating system it’s ready to terminate.
The main() method lets us hook our code into this process, keeping it alive long enough to do the work we’ve coded. The simplest possible class looks like this:
1. public class EntryPoint {
2. public static void main(String[] args) {
3. // this line is a comment only
4. }
5. }
This code doesn’t do anything useful (or harmful). It has no instructions other than to declare the entry point. It does illustrate, in a sense, that what you can put in a main() method is arbitrary. Any legal Java code will do. In fact, the only reason we even need a class structure to start a Java program is because the language requires it. To compile and execute this code, type it into a file called EntryPoint.java and execute the following:
$ javac EntryPoint.java
$ ls
EntryPoint.class EntryPoint.java
$ java EntryPoint
$
To compile Java code, the file must have the extension .java. The name of the file must match the name of the class. The result is a file of bytecode by the same name, but with a .class filename extension. Notice that we must omit the .class extension to run the program because a period has a reserved meaning for the JVM.
The rules for what a Java code file contains, and in what order, are more detailed than what has been explained so far. You can in fact store several Java classes in one file under certain conditions. To keep things simple for now, we’ll follow a subset of the rules: one, that each file can contain only one class, and two, the file name must match the class name, including case, and have a .java extension.
If we replace line 3 in EntryPoint.java with System.out.println("Hello world!"); then compile and run the code again, we’ll get the line of output that matches what’s between the quotes. It is not required that a class with the same name as the file be declared public. Please try it.
Let’s first review the words in the main() method’s signature, one at a time. The keyword public is what’s called an access modifier. It declares this method’s level of exposure to potential callers in the program. Naturally, public means anyone in the program. The least-open access modifier is private, which restricts visibility to methods in the same class.
The keyword static binds a method to its class so it can be called by just the class name, as in, for example, EntryPoint.main(). In fact, the JVM does this, more or less, when loading the class name given to it. If a main() method isn’t present in the class we name with the java executable, the process will throw an error and terminate.
The keyword void represents the return type. A method that returns no data returns control to the caller silently. In general, it’s good practice to use void for methods that change an object’s state. In that sense, the main() method changes overall program state from started to finished.
Finally we arrive at the main() method’s parameter list, represented as an array of java.lang.String objects. In practice, you can write either String[] args or String args[]; the compiler accepts both. The variable name args hints that this list contains values that were read in (arguments) when the JVM started. We will discuss the String type in Chapter 2, “Applying Data Types in Java Programming” and the array type in Chapter 4, “Using Java Arrays.”
Let’s say we wanted to remake our Point3D class so the values for x, y, and z could be read in at start time with a command like this:
$ java Point3D 15 28 7
These values are each interpreted by the JVM as a series of character sequences separated by white space. Each sequence is used to create a String object, which is then assigned to an array index in the order received. Using the example, we’d expect to find a 15 in args[0], a 28 in args[1], and a 7 in arg[2]. To convert each one to a numeric type, we need to know a little bit more.
To see how this works, add a main() method to the Point3D class and put the following statements in it:
System.out.println(args[0]);
System.out.println(args[1]);
System.out.println(args[2]);
Compile and run the program. You’ll see that the values added to the program output in the order they were input. And if you don’t use these specific numbers, or numbers at all, it still works! That’s because these values aren’t being processed by the class; they’re just passing through. Values introduced at program start usually influence program operation in some way. When you see a main() method’s args array used just to initialize one object, it’s usually toy code: That kind of work can also be handled by a constructor.
Executing code written in any language relies on some amount of routine setup work. Toolmakers for a language will bake some of that routine into their works to save us all a bit of tedium. You can see some that work, in Java, with the following command. Use a window with an output buffer and scrolling capability:
java -verbose -version
This incantation produces 275 lines of extra output on my system, using an early release of Java SE 7. Each line reports a class that was loaded into the JVM because some already-loaded class asked for it. The name and order of classes can vary, depending on the Java version you’re using (and possibly your operating system). In all cases, however, the first classes you should see loaded are java.lang.Object and java.io.Serializable.
The JVM imports these classes to help build the runtime process itself. The java.lang.Object class comes first because it is the parent class to all other Java classes. It provides the fundamental methods and state every object needs to operate in the JVM ecosystem. You should therefore commit the names of the java.lang.Object methods to memory, even if they don’t mean a lot to you right now.
Figure 1-3 diagrams the key methods we’ll discuss in this book.
From the diagram, it’s apparent that any Java object should be able to make a copy of itself (clone()), determine if another object is equal to it, return its class/type, and convert itself into a String form. The finalize() method prepares an object for removal from memory by releasing the system resources it was using. I’ll describe these methods in more detail as they become relevant to discussion.
There are thousands of different classes in the Java runtime alone, all of which inherit from java.lang.Object. With so many classes, there has to be some way to ensure that any name you give to a class won’t conflict with someone else’s. Java supports this by using packages. java.lang is a package; so is java.io (short for input/output).
The first part of a package name indicates who produced the code. Subsequent package name elements, separated by dots, often name a category or subdomain of the classes. Third-party code packages usually start with the company’s Internet domain name in reverse, such as, for example, org.apache.
The java.lang package includes types that are used by the JVM and are essential to the foundation of every Java program. Almost every package included in the Java Development Kit (JDK) starts with java or javax (extended).
But again, there are thousands of Java classes, and certainly many, many more that have been written the world over. The -verbose flag shows us a mere 270+. How does the JVM find just the ones it needs?
Each class has a fully qualified package name, or FQPN for short. To use it, your system environment must be configured to find these classes in the filesystem. The environment variable the JVM uses is called the classpath. The classpath lists one or more directories (folders) that contain Java packages.
In Java code, we use import statements to leverage the classpath environment. An import statement lets you declare the full package names for the classes you want to use. You can refer to any imported class by its class name alone (that is, Object instead of java.lang.Object) and save yourself some typing.
There’s no practical limit to listing imported classes, other than your limit for reading through them and staying awake. Be aware that classes you import but don’t use cause no harm. The compiler won’t complain. Some code-development tools will warn you about unused import statements. It’s not illegal to leave them in, just misleading.
Import statements also serve as an inventory of the classes used in a code file. You can import each class, one per statement, to create a cast of characters for the program, like this:
import java.lang.Object;
import java.io.Serializable;
Looking again at the output from java -verbose, I noticed that 99 of the classes my system loaded came from the java.lang package. Who wants to read through that many import declarations for one package? Instead, you can import all the classes from one package with a wildcard:
import java.lang.*;
import java.io.*;
However, because you need java.lang classes in all cases, the toolmakers just bake that one in. That’s why you don’t see import statements in simple test programs.
Another reason you don’t see import statements in example code is that classes you commonly use are responsible for their own imports. Take the java.lang.System class as an example. Its member out (short for output stream) is in fact a variable of type java.io.PrintStream. To access it, the System class imports the java.io package. Even classes belonging to the same group from the same author need import statements to get to the other packages in that group.
Classes from other packages you try to use without a supporting import statement will make the compiler complain. To fix that problem, you might end up using a mix of FQPN and wildcard imports. There’s no right or wrong to that, but mixing those conventions can be distracting to people who need to review or debug your code. Sticking to one approach is, at the least, a common courtesy and a sign of a consistent coder.
The package wildcard does not apply to packages organized under the imported package. If you want to use both java.awt.Dialog and java.awt.applet.Applet in a file, for example, you must provide an import statement for each.
Small code examples aside, all Java classes should declare their own package too. This practice helps the reader understand where the current code is organized relative to a larger body of code. The import statements also help a code reviewer understand which classes support the current class. Classes in the same package don’t need to do this; they have a built-in scope that precludes the need for importing.
The package declaration is always the first statement in a Java file, followed by import statements, followed by class definitions. The internal arrangement of classes and their members, however, is a matter of preference. Most Java programmers will list fields first, constructors second (if any), and then methods, but the compiler will accept any arrangement.
A package declaration is a simple statement:
package com.ernestco;
Configuring your system so the compiler and JVM can find them shouldn’t be hard, but there are rules you must follow. Remember the full path to the rt.jar file in the verbose example output? The JVM seems to find that critical file on its own (but it’s another baked-in convenience). The classes we create have to be locatable by the classpath. If the class files reside in an archived format, known as a JAR file (like rt.jar), the classpath must include the filename as part of the path.
So far we’ve covered simple class structure, executing code in the JVM, and importing classes from other packages. You can now write a HelloWorld example program and appreciate how much Java you need just to run more Java. But we haven’t yet written a non-static method or dug further into using variables. Let’s do that now with the Point3D class outlined earlier.
The code we need to store and modify one coordinate in a Point3D object looks like this:
public class Point3D
{
int x;
public void setX(int xcoord) {
x = xcoord;
}
}
The method setX() will receive an int named xcoord and assign its value to the field x. This is how methods communicate in Java, by passing values from the calling method to the caller. The called method declares the type of information it accepts for a message. The name of the method should, as always, imply what the message is for; the parameter name should clarify that intent.
This message is also known as a temporary variable. It has a name and value that expire as soon as the called method returns control to the caller. Any variable that is declared in the parameter list, or inside a method body, lasts in memory only for the duration of the method call.
We consider the term variable less precise than the term field because it refers to program data storage for any duration. A field is available as long as the object containing it remains in memory (and is therefore called a member of that object). How long other variables remain in memory depends on the context, or scope, in which we declared them.
We refer to this aspect of variables as their lifetime. Every time you use a French curly brace in Java (the { symbol ), whether it’s to open a class, method, or other construct you haven’t yet learned, you create a scope for the variables it contains.
Each scope not only has a lifetime, it also has a namespace. Let’s say you have a variable i you’d like to use for a variety of temporary purposes. With one global namespace only, you could use it only once. Because each method has its own scope, you can use i without getting any baggage related to a previous use. But there is also a drawback, which the following code fragment illustrates:
int x = 1
public void setX(int x) {
x = x;
}
How do you know which x is which? You don’t. And, as it turns out, neither does the compiler. In this case it cannot figure out that member x is on the left and parameter x is on the right, as you might expect.
Fortunately, Java provides means to distinguish the two. The keyword this refers to the current object and can be used to access any member of the object. Using it, the meaning becomes unambiguous:
public void setX(int x) {
this.x = x;
}
In doing so, you realize one more benefit. Now you don’t have to conjure two names for the same datum in straightforward situations like this one. And there are other ways the this keyword helps resolve sticky situations, which we’ll cover in Chapter 9, “Inheriting Code and Data in Java.”
You will use lots of variables for lots of purposes in a busy Java program. Some of them will act as counters or placeholders or serve another role useful for temporary storage. The ones that store values you want to track over the life of an object are its fields, and contribute to what I’ve been calling the object’s state. As fields change, so does the state of the object; these fields are the object’s state. In fact, if a field changes often in a class but it never seems to matter to the program, ask yourself why you’re using it.
The more fields a class contains, the more complex its state changes become. It’s never too early to think about limiting a class to just the fields it needs to reflect state changes. You’ll get way ahead in becoming an effective and economical Java programmer by keeping this goal in mind.
3.138.204.186