J Intjava LTR
J Intjava LTR
J Intjava LTR
19 Nov 2004
The Java language, and the ever-growing Java platform, have revolutionized
programming. The goal of this tutorial is to introduce you to the Java syntax you're
most likely to encounter professionally, and to expose you to idioms that will help you
avoid trouble. Follow along with Java professional Roy Miller as he guides you through
the essentials of Java programming, including the OOP paradigm and how it applies to
Java programming; Java language syntax and use; creating objects and adding
behavior, working with collections, handling errors; and tips for writing better code.
Page 1 of 71
developerWorks
ibm.com/developerWorks
Software requirements
To run the examples or sample code in this tutorial, you'll need to have the Java 2
Platform, Standard Edition (J2SE) , version 1.4.2 or higher, and the Eclipse IDE
installed on your machine. Don't worry if you don't have these packages installed yet
-- we'll show you how to do that in Getting started . All code examples in this tutorial
have been tested with J2SE 1.4.2 running on Windows XP. One of the beautiful
things about the Eclipse platform, however, is that it runs on almost any OS you're
likely to use, including Windows 98/ME/2000/XP, Linux, Solaris, AIX, HP-UX, and
even Mac OS X.
ibm.com/developerWorks
developerWorks
have the Java SDK and Eclipse installed, feel free to skip to A brief Eclipse tour or to
the next section, OOP concepts , if you're comfortable jumping right in.
Open a browser and go to the Java Technology home page. In the top
middle of the page, you'll see links for various Java technology subject
areas. Select J2SE (Core/Desktop).
2.
3.
4.
There are several downloads on this page. Find and click the Download
J2SE SDK link.
5.
6.
7.
8.
When the download is complete, run the install program to install the SDK
on your hard drive, preferably in a well-named folder at the root of the
drive.
That's it! You now have a Java environment on your machine. The next step is to
install an integrated development environment (IDE).
Page 3 of 71
developerWorks
ibm.com/developerWorks
Install Eclipse
An integrated development environment (IDE) hides lots of the mundane technical
details of working with the Java language, so you can focus on writing and running
code. The JDK you just installed includes several command-line tools that would let
you compile and run Java programs without an IDE, but using those tools quickly
becomes painful for all but the simplest programs. Using an IDE hides the details,
gives you powerful tools to help you program faster and better, and is simply a more
pleasant way to program.
It is no longer necessary to pay for an excellent IDE. The Eclipse IDE is an open
source project and is yours to download for free. Eclipse stores and tracks your Java
code in readable files stored on your file system. (You can use Eclipse to work with
code in a CVS repository as well.) The good news is that Eclipse lets you deal with
files if you want to, but hides the file details if you'd rather deal only with various Java
constructs like classes (which we'll discuss in detail later).
Downloading and installing Eclipse is simple. Follow these steps:
1.
2.
3.
Click the Main Eclipse Download Site link to go to the Eclipse project
downloads page.
4.
You'll see a list of build types and names. Click the 3.0 link.
5.
In the middle of the page, you'll see a list of Eclipse SDKs by platform;
choose the one appropriate for your system.
6.
7.
When the download is complete, run the install program and install
Eclipse on your hard drive, preferably in a well-named folder at the root of
the drive.
Set up Eclipse
ibm.com/developerWorks
developerWorks
To use Eclipse to write Java code, you must tell Eclipse where Java is located on
your machine. Follow these steps:
1.
2.
3.
4.
Odds are good that Eclipse will find an installed Java Runtime
Environment (JRE), but you should explicitly point to the one you installed
in Install the Java SDK. You can do that in the Preferences dialog. If
Eclipse lists an existing JRE, click on it and press Edit; otherwise click
Page 5 of 71
developerWorks
ibm.com/developerWorks
Add.
5.
Specify the path to the JRE folder of the JDK you installed in Install the
Java SDK.
6.
Click OK.
Eclipse is now set up to compile and run Java code. In the next section, we'll take a
brief tour of the Eclipse environment to familiarize you with the tool.
Click File>New>Project to display the New Project wizard (see Figure 2).
This is actually a "wizard wizard" -- in other words, it's a wizard that lets
you choose which wizard to use (the New Project wizard, the New File
wizard, and so on).
Figure 2. New project wizard
ibm.com/developerWorks
developerWorks
2.
Make sure the Java Project wizard is selected and click Next.
3.
Enter whatever project name you want ("Intro" might work well), leave all
the defaults selected, and click Finish.
4.
At this point, Eclipse should ask you if it should switch to the Java
perspective. Click No.
You just created a Java project called Intro, which you should see in the Navigator
view in the upper-left corner of the screen. We didn't switch to the Java perspective
after creating the project because there's a better perspective to use for our current
purposes. Click the Open Perspective button on the tab in the upper-right corner of
the window, then select the Java Browsing perspective. This perspective shows you
what you need to see to create Java programs easily. As we create Java code, we'll
Page 7 of 71
developerWorks
ibm.com/developerWorks
walk through a few more Eclipse features so you can learn how to create, modify,
and manage your code. Before we do that, though, we have to cover some basic
object-oriented programming concepts, which we'll do in the next section. Right now,
we'll wrap up this section by taking a look at some online Java documentation.
ibm.com/developerWorks
developerWorks
What is an object?
Java is what's known as an object-oriented (OO) language, with which you can do
object-oriented programming (OOP). This is very different from procedural
programming, and can be a little strange for most non-OO programmers. The first
step is to understand what an object is, because that's what OOP is based on.
An object is a self-contained bunch of code that knows about itself and can tell other
objects about itself if they ask it questions that it understands. An object has data
members (variables) and methods, which are the questions it knows how to answer
(even though they may not be worded as questions). The set of methods that an
object knows how to respond to is its interface. Some methods are open to the
public, meaning that another object can call (or invoke) them. That set of methods is
known as the object's public interface.
When one object invokes a method on an another object, that's known as sending a
message, or a message send. That phrase is certainly OO terminology, but most
often in the Java world people tend to say, "Call this method" rather than, "Send this
message." In the next section, we'll look at a conceptual example that should make
this more clear.
Page 9 of 71
developerWorks
ibm.com/developerWorks
Procedural programmers might think this way of talking is strange, but it's perfectly
natural for OO programmers. In their programming world, everything's an object
(with some notable exceptions in the Java language), and programs are objects
interacting (or "talking") with each other.
Fundamental OO principles
The concept of an object is critical for OOP, of course, as is the idea of objects
communicating with messages. But there are three other foundational principles you
need to understand.
You can remember the three fundamental OO principles with the acronym PIE:
P olymorphism
I nheritance
E ncapsulation
Those are fancy names, but the concepts aren't really all that difficult to understand.
In the next few sections, we'll talk about each in more detail, in reverse order.
Encapsulation
Remember, an object is a self-contained thing that contains data elements and
actions it can perform on those data elements. This is an implementation of a
principle known as information hiding. The idea is that an object knows about itself. If
another object wants facts about the first object, it has to ask. In OOP terms, it has
to send the object a message to ask for its age. In Java terms, it has to call a
method on the object that will return the age.
Encapsulation ensures that each object is distinct, and that programs are
conversations among objects. The Java language lets a programmer violate this
principle, but it's almost always a bad idea to do so.
Inheritance
When you were born, biologically speaking, you were a combination of the DNA of
your parents. You aren't exactly like either one of them, but you're similar to both.
OO has the same principle for objects. Think about a Person object again. Recall
that each person has a race. Not all People are the same race, but are they similar
ibm.com/developerWorks
developerWorks
Polymorphism
Does a Baby "speak" like an Adult? Of course not. A Baby makes noise, but it's not
necessarily recognizable words like an Adult uses. So, if I instantiate a Baby object
(saying "instantiate a Baby" means the same thing -- the word "object" is assumed)
and tell it to speak, it might coo or gurgle. One would hope that an Adult would be
coherent.
In the humanity hierarchy, we have Person at the top, with Baby and Adult beneath
it, as subclasses. All People can speak, so Baby and Adult can, too, but they do it
differently. A Baby gurgles and makes simple sounds. An Adult says words. That's
what polymorphism is: Objects doing things their own way.
Page 11 of 71
developerWorks
ibm.com/developerWorks
ibm.com/developerWorks
developerWorks
(or not-so-subtle) platform differences that might make your code behave differently
on different platforms.
Garbage collection
When you create Java objects, the JRE automatically allocates memory space for
that object from the heap, which is the big pool of memory available on your
machine. The runtime then keeps track of that object for you. When your program
isn't using it anymore, the JRE gets rid of it. You don't have to worry about it.
If you've written any software in the C++ language, which is (arguably) OO as well,
you know that as a programmer you have to allocate and deallocate memory for
your objects explicitly by using functions called malloc() and free(). That is
onerous for programmers. It's also dangerous, because it allows memory leaks to
sneak into your programs. A memory leak is nothing more than your program
gobbling up memory at an alarming rate, which stresses the processor of the
machine it's running on. The Java platform keeps you from having to worry about
this at all, because it has what's known as garbage collection.
The Java garbage collector is a background process that gets rid of objects that
aren't being used anymore, rather than forcing you to do that explicitly. Computers
are good at keeping track of thousands of things, and at allocating resources. The
Java platform lets the computer do that. It keeps a running count of references to
every object in memory. When the count hits zero, the garbage collector reclaims
the memory used by that object. You can manually invoke the garbage collector, but
I've never had to do so in my career. It usually handles itself, and certainly will for
every coding example in this tutorial.
Page 13 of 71
developerWorks
ibm.com/developerWorks
code uses classes that I didn't write, all I have to do is tell Eclipse what libraries my
project references and where to find them. That is far simpler than using the
command line to type in heinously long statements that specify the classpath.
If you want or need to use the command-line tools, you can find out more about how
to use them at Sun's Java Technology Web site (see Resources ).
ibm.com/developerWorks
package
import
developerWorks
packageName;
packageNameToImport;
initialValue ];
There are a few new concepts here, which we'll discuss in the next few panels.
Packages
The package declaration comes first when you define a class:
package
packageName;
Every Java object exists in a package. If you don't explicitly say which one it
belongs to, the Java language puts it in the default package. A package is simply a
set of objects, all of which (typically) are related in some way. package s refer to a
file path on your file system. Package names use dot notation to translate that file
path into something the Java platform understands. Each piece of the package
name is called a node.
For example, in the package named java.util.ArrayList, java is a node,
util is a node, and ArrayList is a node. The last node refers the file
ArrayList.java.
Import statements
The import statements come next when you define a class:
import
packageNameToImport;
...
Page 15 of 71
developerWorks
ibm.com/developerWorks
When your object makes use of objects in other package s, the Java compiler
needs to know where to find them. An import statement tells the compiler where to
find the classes you use. For example, if I wanted to use the ArrayList class from
the java.util package, I would import it this way:
import java.util.ArrayList;
Each import ends with a semicolon, as do most statements in the Java language.
You can have as many import s as you need to tell Java where to find all the
classes you use. For example, if I wanted to use the ArrayList class from the
java.util package, and the BigInteger class from the java.math package, I
would import them like this:
import java.util.ArrayList;
import java.math.BigInteger;
If you import more than one class from the same package, you can use a shortcut to
say that you want to import all classes from that package. For example, if I wanted to
use ArrayList and HashMap, both from the java.util package, I would import
them like this:
import java.util.*;
You need an import statement for every unique package you import from.
Declaring a class
The class declaration comes next when you define a class:
accessSpecifier class ClassName {
accessSpecifier
dataType
variableName [= initialValue ];
...
accessSpecifier ClassName( arguments ) {
constructor statement(s)
}
accessSpecifier
returnValueDataType
methodName ( arguments ) {
statement(s)
}
}
You define a Java object as a class. Think of a class as a template for an object,
sort of like a cookie cutter. The class defines the type of object you can create with
it. You can stamp out as many objects of that type as you want. When you do that,
you've created an instance of the class -- or, to put it another way, you've
ibm.com/developerWorks
developerWorks
Variables
The values of the variables of a class are what distinguish each instance of the
class, which is why they're frequently called instance variables. A variable has an
access specifier, a data type, a name, and (optionally) an initial value. Here is a list
of the access specifiers and what they mean:
public: Any object in any package can see the variable.
protected: Any instance of the class, subclasses in the same package,
and any non-subclass in the same package can see the variable.
Subclasses in other packages can't see it.
private: No object other than a specific instance of this class can see
the variable, not even a subclass.
No specifier (or package protected ): Only classes within the same
package as the class containing the variable can see it.
If you try to access a variable that is inaccessible to you, the compiler will tell you
that the variable is not visible to you. The circumstances under which you should use
each specifier is a judgment call, and we'll revisit it later.
Methods
The methods of a class define what it can do. There are two flavors of method in the
Java language:
Constructors
Other methods
Introduction to Java programming
Copyright IBM Corporation 1994, 2006. All rights reserved.
Page 17 of 71
developerWorks
ibm.com/developerWorks
Both have access specifiers (which dictate which other objects can use them) and
bodies (between the curlies), and both contain one or more statements. Beyond that,
their form and function are quite different. We'll cover each in turn in the next two
panels.
Constructors
Constructors let you specify how to instantiate a class. You declare a constructor like
this:
accessSpecifier ClassName(
constructor statement(s)
arguments ) {
You get a default constructor (that takes no arguments) for free with every class you
create. You don't even have to define it. Constructors look different from other
methods in that they don't have a return value data type. That's because the return
value data type is the class itself. You invoke a constructor in your code like this:
ClassName variableHoldingAnInstanceOfClassName = new ClassName(
arguments );
When you call a constructor, you use the new keyword. Constructors can take
parameters or not (the default constructor doesn't). In the strict sense, constructors
aren't methods or members of a class. They're a special animal in the Java
language. In practice, though, they look and act like methods much of the time, and
many people lump the two together. Just remember that they're special.
Non-constructor methods
Non-constructor methods in the Java language are what you use most often. You
declare them like this:
accessSpecifier
returnValueDataType
methodName ( arguments ) {
statement(s)
}
Every method has a return type, but not every method returns something. If the
method returns nothing, you use the keyword void as the return type. You can
name a method anything you want, as long as it's a valid identifier (it can't start with
a period (. ), for example), but by convention, method names:
Are alphabetic
ibm.com/developerWorks
developerWorks
Page 19 of 71
developerWorks
ibm.com/developerWorks
Type intro.core as the package name and click Finish. You should see the
following package in the Packages view in the workspace:
intro.core
Notice that the icon to the left of the package is ghosted -- that is, it looks like a
grayed-out version of the package icon. That's a common Eclipse user interface
convention for empty items. Your package doesn't have any Java classes in it yet,
so its icon is ghosted.
ibm.com/developerWorks
developerWorks
You can create a Java class in Eclipse with File>New, but we'll use the toolbar
instead. Look above the Packages view to see the creation tools for projects,
packages, and classes. Click the New Java Class tool (the green "C") to display the
New Java Class wizard. Enter Adult as the class name, and accept all defaults by
clicking Finish. Now you should see a few changes:
The Adult class appears in the Classes view, to the right of the
Packages view (see Figure 4).
Figure 4. Workspace
Eclipse generated a shell or template for the class for you, and included the
Page 21 of 71
developerWorks
ibm.com/developerWorks
package statement at the top. The class body is empty for now. We simply have to
add things to flesh it out. You can configure templates for new classes, methods,
and so on in the Preferences wizard you used before ( Window>Preferences ). You
can configure code templates in the preferences path Java>Code Style>Code
Templates. In fact, to simplify things for displaying code from this point on, I'm going
to remove all comments from the templates -- meaning any lines preceded by //
comments , or wrapped in /* comments */, or wrapped in /** comments */.
From now on, you won't see any comments in code, unless we're specifically
discussing their use, which we'll do in the next panel.
Before we move on, however, let's demonstrate a way that the Eclipse IDE makes
your life easier. In the editor, change the word class to clas and wait a few seconds.
Notice that Eclipse underlines it with a wavy red line. If you mouse over the
underlined item, Eclipse pops up an information window to tell you that you've got a
syntax error. Eclipse helps you out by continuously compiling your code and alerting
you unobtrusively if there's a problem. If you were using the command line tool
javac, you'd have to compile the code and wait to see the errors. That can slow
your development to a crawl. Eclipse removes that pain.
Comments
Like most every language, the Java language supports comments, which are simply
statements that the compiler ignores when it checks syntax compliance. Java has
several varieties of comments:
// Single-line comment. The compiler ignores any text after the forward slashes.
/* Multi-line comment. The compiler ignores any text between the asterisks. */
/** javadoc comment. Compiler ignores text between asterisks, and the javadoc tool uses it. */
The last one is the most interesting. In a nutshell, the javadoc tool that comes with
the Java SDK distribution you installed and can help you generate HTML
documentation for your code. You can generate documentation for your own classes
that looks very similar to what you see in the Java API documentation that we looked
at in The Java API online. Once you've commented your code appropriately, you can
run javadoc from the command line. You can find instructions for doing that, and all
available information about javadoc, at the Java Technology Web site (see
Resources ).
Reserved words
There's one more item to cover before we start writing code that the compiler will
check. Java has some words that are off-limits for you when naming variables.
Here's the list:
ibm.com/developerWorks
developerWorks
abstract
boolean
break
byte
case
catch
char
class
const
continue
char
class
default
do
double
else
extend
false
final
finally
float
for
goto
if
implements
import
int
instanceof
interface
long
int
native
new
null
package
private
protected
public
package
private
static
strictfp
super
switch
synchronized
short
super
this
throw
throws
true
try
transient
return
void
volatile
while
assert
true
false
null
It's not a very long list, but Eclipse makes reserved words bold as you type, so you
don't have to remember them anyway. All but the last three in the list are Java
keywords. The last three are reserved words. The difference doesn't matter for our
purposes; you can't use either kind.
Now, on to some real code.
Adding variables
As I've said before, an Adult instance knows its name, age, race, and gender. We
can add those pieces of data to our Adult class by declaring them as variables.
Then every instance of the Adult class will have them. Most likely, each Adult will
have different values for those variables. That's why each object's variables are
often known as instance variables -- they're what distinguish each instance of a
class. Let's add them, using protected as the access specifier for each:
package intro.core;
public class Adult {
protected int age;
protected String name;
protected String race;
protected String gender;
Page 23 of 71
developerWorks
ibm.com/developerWorks
Now every Adult instance will contain those pieces of data. Notice here that each
line of code ends with a semicolon. The Java language requires it. Notice also that
each variable does indeed have a data type. We've got one integer and three string
variables. Data types for variables can be of two flavors:
Primitive data types
Objects (either user-defined or internal to the Java language), also known
as reference variables
Size
Default value
Example
boolean
N/A
false
true
byte
8 bits
char
16 bits
'u/0000'
'a'
short
16 bits
12
int
32 bits
123
long
64 bits
9999999
float
0.0
123.45
double
0.0
999999999.99999999
We used an int for age because we don't need any decimal values, and an integer
is big enough to hold any realistic human age. We used a String for the other
three variables, because they aren't numeric. String is a class in the java.lang
package, which you can access in your Java code automatically anytime you want
(we'll talk about this more in Strings ). You can also declare variables to be of
user-defined types, such as Adult.
We defined each variable on a separate line, but we didn't have to. When you have
two or more variables of the same type, you can define them on a single line,
separated by commas, like this:
accessSpecifier dataType variableName1,
variableName2,
variableName3,...
ibm.com/developerWorks
developerWorks
If we wanted to initialize these variables when we declared them, we would just add
initialization after each variable name:
accessSpecifier dataType variableName1 =
initialValue, ...
variableName2 =
initialValue,
Now our class knows about itself, and we can prove it, which we'll do next.
In the body of main(), we instantiate an Adult, then print out the values of its
instance variables. Look at that first line. This is a case where OO purists get upset
with the Java language. They say that new ought to be a method on Adult, and that
you should call it thusly: Adult.new(). I certainly see their point, but the Java
language doesn't work that way, and it's one reason purists can rightfully claim that it
isn't purely OO. Look at that first line again. Remember that every Java class has a
default constructor, which is what we're using here.
After we instantiate our Adult, we store it in a local variable called myAdult. Then
we print out the values of its instance variables. In almost any language, you can
print stuff out to the console. The Java language is no exception. The way you do it
in Java code is to call the println() method on the out stream of the System
object. Don't worry about understanding all of the details of the process for the
moment. Just know that we're using a helpful method call to print things out. For
Introduction to Java programming
Copyright IBM Corporation 1994, 2006. All rights reserved.
Page 25 of 71
developerWorks
ibm.com/developerWorks
each call, we pass a string literal and concatenate the value of an instance variable
on myAdult. We'll revisit those method details later.
Notice that the variables contained their default values. By default, any instance
variable of a user-defined or built-in type contains null. It's almost always a good
ibm.com/developerWorks
developerWorks
idea to initialize variables explicitly, though, especially objects, so you can be sure of
the values that they have in them. Go back and initialize those values to:
s
Variable
Value
name
"Bob"
age
25
race
"inuit"
gender
"male"
Rerun the code by clicking on the running man icon again. You should see the new
values on the console.
Now let's make our Adult capable of telling other objects about its data.
Page 27 of 71
developerWorks
ibm.com/developerWorks
Declaring accessors
We could add accessors for the age instance variable on Adult that look like this:
public int getAge() {
return age;
}
public void setAge(int anAge) {
age = anAge;
}
The getAge() method responds with the value of the age variable by using the
return keyword. Methods that don't return anything have an implicit return
void; statement at the end. In that getter, we referred to age by using its name.
We also could've said return this.age;. The this variable refers to the current
object. It's implicit when you refer to an instance variable directly. Some OO
programmers from the Smalltalk world prefer to use this whenever they refer to an
instance variable, just as they would always use the self keyword when coding in
Smalltalk. I rather like that myself, but the Java language doesn't require it, and it
does add extra stuff on the screen, so the examples in this tutorial won't use this
unless the code would be unclear without it.
Calling methods
Now that we have accessors, we should replace the direct age access in our
main() method with method calls. main() should now look like this:
ibm.com/developerWorks
developerWorks
If you run this again, the results should be the same. Notice that calling a method on
an object is easy. Use this form:
instanceName.methodName()
If the method takes no parameters (like our getter), you still have to include the
parentheses after the name of the method when you call it. If the method takes
parameters (like our setter), enclose them in the parentheses, separated by commas
if there are more than one.
A word about the setter before we move on: It takes an int parameter named
anAge. It then assigns that parameter's value to the instance variable age. We
could've named the parameter anything we wanted. The name is unimportant, but
when you refer to that parameter within the method, you have to use the name you
gave it.
Before we go, let's try using the setter. Add this line to main() immediately after we
instantiate an Adult:
myAdult.setAge(35);
Now rerun the code. Your results should show an age of 35. Here's what went on
behind the scenes:
We passed the value of an integer to the method as a parameter.
The JRE allocated memory for the parameter and named it anAge.
Non-accessor methods
Accessors are helpful, but we want our Adult objects to be able to do things other
than share their data, so we need to add other methods. We want our Adult s to
speak, so let's start there. The speak() method could look like this:
public String speak() {
return "hello";
}
By now, the syntax should be familiar. The method returns a string literal. Let's use
Page 29 of 71
developerWorks
ibm.com/developerWorks
it, and clean up main() while we're at it. Change the first call to println() to
read:
System.out.println(myAdult.speak());
Strings
We've used several variables of type String so far, but we haven't discussed them.
Handling strings in C is labor-intensive, because they're null-terminated arrays of
8-bit characters that you have to manipulate. In the Java language, strings are
first-class objects of type String, with methods that help you manipulate them. The
closest Java code gets to the C world with regard to strings is the char primitive
data type, which can hold a single Unicode character, such as 'a'.
You've already seen how to instantiate a String object and set its value, but there
are several other ways to do that. Here are a couple ways to create a String
instance with a value of "hello":
String greeting = "hello";
String greeting = new String("hello");
Because strings in the Java language are first-class objects, you can use new to
instantiate them. Setting a variable of type String has the same result, because
the Java language creates a String object to hold the literal, then assigns that
object to the instance variable.
You can do many things with String s, and the class has a large number of helpful
methods. Without even using a method, we've already done something interesting
with String s by concatenating a couple, which means combining them, one after
the other:
System.out.println("Name: " + myAdult.getName());
This code might look a little strange, so let's walk through it briefly, left to right:
System is a built-in object that lets you interact with various things in the
system environment (including some capabilities of the Java platform
itself).
ibm.com/developerWorks
developerWorks
Using strings
For now, let's make use of concatenation in our Adult class. To this point, we've
got a name instance variable. It would be nice to have a firstname and
lastname, then concatenate them when somebody asks an Adult for its name. No
problem! Add the following method:
public String getName() {
return firstname + " " + lastname;
}
Eclipse should be showing squiggly red lines in the method because those instance
variables don't exist yet, which means that the code won't compile. Now replace the
existing name instance variable with these two (with defaults that make more sense):
protected String firstname = "firstname";
protected String lastname = "lastname";
Page 31 of 71
developerWorks
ibm.com/developerWorks
Now we have a fancier getter for our name variables. It concatenates them nicely to
create a full name for our Adult. Alternatively, we could've made getName() look
like this:
public String getName() {
return firstname.concat(" ").concat(lastname);
}
This code does the same thing, but it illustrates the explicit use of a method on
String, and it also illustrates chaining method calls. When we call concat() on
firstname with a string literal (a space), it returns a new String that's a
combination of the two. We then immediately call concat() on that one to join the
first name and space with lastname. That gives us a nicely formatted full name.
Our method takes an integer parameter for the number of steps to take, updates
progress to reflect that number of steps, then reports some results. It also would
be wise to add a getter for progress, but not a setter. Why? Well, allowing another
object to teleport us forward to some total number of steps probably isn't smart. If
another object wants to tell us to walk, it can call walk(). That's a judgment call,
certainly, and this is a somewhat trivial example. On a real project, those sorts of
design decisions have to be made all the time, and frequently can't be made in
advance, no matter what serious OO design (OOD) gurus say.
In our method, we updated progress by adding steps to it. We stored the result in
progress again. We used the most basic assignment operator, =, to store the
result. We used the + arithmetic operator to add the two. There are other ways to
accomplish the same goal. This code would do the same thing:
public String walk(int steps) {
progress += steps;
ibm.com/developerWorks
developerWorks
Using the += assignment operator is a little less clumsy than the first approach we
used. It keeps us from having to reference the progress variable twice. But it does
the same thing: it adds steps to progress and stores the result in progress.
The table below is a list and brief description of the most commonly seen Java
arithmetic and assignment operators (note that some of the arithmetic operators are
binary, having two operands, and some are unary, having one operand).
Operator
Usage
Description
a + b
Adds a and b
+a
a - b
Subtracts b and a
-a
Arithmetically negates a
a * b
Multiples a and b
a / b
Divides a by b
a % b
++
a++
Increments a by 1; computes
the value of a before
incrementing
++
++a
Increments a by 1; computes
the value of a after incrementing
--
a--
Decrements a by 1; computes
the value of a before
incrementing
--
--a
Decrements a by 1; computes
the value of a after incrementing
+=
a += b
Same as a = a + b
-=
a -= b
Same as a = a - b
*=
a *= b
Same as a = a * b
%=
a %= b
Same as a = a % b
We've also already seen some other things that are called operators in the Java
language. For instance, there's . (period), which qualifies names of packages and
calls methods; ( params ), which delimits a comma-separated list of parameters
Page 33 of 71
developerWorks
ibm.com/developerWorks
Usage
>
a > b
a is greater than b
>=
a >= b
<
a < b
a is less than b
<=
a <= b
==
a == b
a is equal to b
! =
a != b
a is not equal to b
&&
a && b
||
a || b
a or b is true, conditionally
evaluates b
(if a is true, there's no need to
ibm.com/developerWorks
developerWorks
evaluate b )
!
! a
a is false
&
a & b
a | b
a ^ b
Now the logic in the method checks to see how big steps is. If it's too big, the
method returns immediately and says it's too far. Each method can return only once.
But aren't there two here? Yes, but only one will be executed. The Java if condition
uses the following form:
if (
boolean expression ) {
statements to execute if true...
} [else {
statements to execute if false...
}]
The curlies aren't required if there's only a single statement after the if and/or the
else, which is why our code didn't use them. You don't have to have an else
clause, and we don't. We could have put the remaining code in the method in an
else, but the effect would've been the same, and it would've added what's known
as unnecessary syntactical sugar, which decreases readability of the code.
Variable scope
Every variable in a Java applications has scope, or characteristics that define where
you can access that variable with just its name. If the variable is in scope, you can
Page 35 of 71
developerWorks
ibm.com/developerWorks
The scope of a variable extends to the end of the section (or block) of code in which
it was declared. For example, in our walk() method, we referred to the steps
parameter by its simple name, because it was in scope. Outside that method,
referring to steps would produce a compile error. Code can also refer to variables
declared in wider scope than that code. For example, we were allowed to refer to the
progress instance variable inside the walk() method.
Other forms of if
We could make the conditional test fancier by using another form of the if
statement:
if (
boolean expression ) {
statements to execute if true...
} else if ( boolean expression ) {
statements to execute if false...
} else if ( boolean expression ) {
statements to execute if false...
} else {
default statements to execute...
}
ibm.com/developerWorks
developerWorks
There's also a shorthand version of if that looks a little odd, but accomplishes the
same goal, although it doesn't allow multiple statements in either the if part or the
else part. It uses the ?: ternary operator. (A ternary operator is one that handles
three operands.) We could rewrite our simple if condition this way:
return (steps > 100) ? "I can't walk that far at once" : "Just took " + steps + " steps.";
That wouldn't accomplish our goal, however, because when steps is less than 100,
we want to return a message and we want to update progress. So in this case,
using the shortcut ?: operator wasn't an option because you couldn't execute
multiple statements with it.
The JRE evaluates the integer expression, picks the case that applies, then
executes the statements for that case. The final statement of each case except the
last one is break;. That "breaks out" of the switch statement, and control
continues to the next line of code after it. Technically, none of the break;
statements are required. The last one is particularly unnecessary, because control
would fall out of the statement anyway. But it's good practice to include them. If you
don't include break; in each case, program execution will fall through to the next
case, and so on, until it encounters a break; or reaches the end of the statement.
The default case is what executes if the integer value doesn't trigger any of the
cases. It's optional.
In essence, a switch statement really is an if-else if statement with an integer
condition; if your condition is based on a single integer value, you could use either
kind of statement. Could we rewrite our if condition in our walk() method as a
Page 37 of 71
developerWorks
ibm.com/developerWorks
switch? No, because we checked a boolean expression there ( steps > 100 ).
That's not allowed in a switch.
A switch example
Here's a trivial example of using switch (it's a rather classic example):
int month = 3;
switch (month) {
case 1: System.out.println("January"); break;
case 2: System.out.println("February"); break;
case 3: System.out.println("March"); break;
case 4: System.out.println("April"); break;
case 5: System.out.println("May"); break;
case 6: System.out.println("June"); break;
case 7: System.out.println("July"); break;
case 8: System.out.println("August"); break;
case 9: System.out.println("September"); break;
case 10: System.out.println("October"); break;
case 11: System.out.println("November"); break;
case 12: System.out.println("December"); break;
default: System.out.println("That's not a valid month number."); break;
}
Here we see that cases 2, 3, and 9 get the same treatment, and the remaining cases
get another one. Note that the cases don't have to be in order, and that having cases
fall through was what we needed in this situation.
Play it again
Introduction to Java programming
Page 38 of 71
ibm.com/developerWorks
developerWorks
for loops
The most basic looping construct in the Java language is the for statement, which
lets you iterate over a range of values to determine how many times to execute the
loop. Its most commonly used syntax looks like this:
for ( initialization; termination; increment ) {
statement(s)
}
The initialization expression establishes where the loop starts. The termination
expression establishes where it stops. The increment expression determines by how
much the initialization variable is incremented each time through the loop. Each time
through, the loop executes the statements in the block, which is the set of
statements between the curlies (though remember that any code block in Java code
is between curly braces, not just code in a for loop).
The syntax and capabilities of for are different in Java 5.0, so check out John
Zukowski's column on new and interesting features in the recently released version
of the language (see Resources for a link).
Page 39 of 71
developerWorks
ibm.com/developerWorks
While we're at it, we'll learn about a built-in Java class that makes assembling
strings a snap:
public String speak() {
StringBuffer speech = new StringBuffer();
for (int i = 0; i < 3; i++) {
speech.append("hello");
}
return speech.toString();
}
The StringBuffer class in the java.lang package lets you manipulate strings
easily, and is perfect for the job of appending strings together (which is the same
thing as concatenating them). We simply instantiate one, then call append() each
time we want to add something to the speech each Adult makes. The for loop is
where the real business goes on. In the parentheses of the loop, we declared an
integer variable i to server as our loop counter (the letters i, j, and k are very
common as loop counters, but you can name the variable whatever you want). The
next expression says we'll keep looping until that variable reaches a value less than
three. The expression after that says that we'll increment our counter by one each
time through the loop (remember the ++ operator?). Each time through the loop,
we'll call append() on speech and stick another "hello" on the end.
Now, replace our old speak() method with this one, remove all of the println
statements from main(), and then add one that calls speak() on Adult. When
you're done, the class ought to look like this:
package intro.core;
public class Adult {
protected int age = 25;
protected String firstname = "firstname";
protected String lastname = "lastname";
protected String race = "inuit";
protected String gender = "male";
protected int progress = 0;
public static void main(String[] args) {
Adult myAdult = new Adult();
System.out.println(myAdult.speak());
}
public int getAge() {
return age;
}
public void setAge(int anAge) {
age = anAge;
}
public String getName() {
return firstname.concat(" ").concat(lastname);
}
public String speak() {
StringBuffer speech = new StringBuffer();
for (int i = 0; i < 3; i++) {
speech.append("hello");
}
return speech.toString();
ibm.com/developerWorks
developerWorks
}
public String walk(int steps) {
if (steps > 100)
return "I can't walk that far at once";
else if (steps > 50)
return "That's almost too far";
else {
progress = progress + steps;
return "Just took " + steps + " steps.";
}
}
}
When you run it, you should get hellohellohello on the console. But using for
is only one way to get this job done. The Java language gives you two other
alternatives, which we'll look at next.
while loops
Let's try while first. The following version of speak() produces the same results
as the version we saw on the previous panel:
public String speak() {
StringBuffer speech = new StringBuffer();
int i = 0;
while (i < 3) {
speech.append("hello");
i++;
}
return speech.toString();
}
A while loop executes code in its block until its expression returns false. How do
you control the loop? You have to make sure the expression becomes false at some
point; otherwise, you have an infinite loop. In our case, we declared a local variable
called i outside the loop, initialized it to 0, then tested its value in our loop
expression. Each time through the loop, we increment i. When it's no longer less
than three, the loop will finish and we'll return the String stored in our buffer.
Here we see how for is rather convenient. In the for version, we declared and
initialized our control variable, tested its value, and incremented it in one line of
code. The while version required more housekeeping. If we forgot to increment our
counter, we'd have an infinite loop. If we didn't initialize it, the compiler would
complain. But the while version can be very useful if you have a complicated
boolean expression to test (enclosing that in the for loop one-liner can make it
Page 41 of 71
developerWorks
ibm.com/developerWorks
tough to read).
Now we've seen for and while loops, but the next section will illustrate that there's
a third way.
do loops
The following code will do exactly the same thing as the last two loops we've looked
at:
public String speak() {
StringBuffer speech = new StringBuffer();
int i = 0;
do {
speech.append("hello");
i++;
} while (i < 3) ;
return speech.toString();
}
A do loop is virtually the same as a while loop, except it tests its boolean
expression after each execution of the loop block. With a while loop, what happens
if the expression evaluates to false the first time it's tested? The loop won't
execute even once. With a do loop, you're guaranteed that the loop with execute at
least once. The distinction can come in handy at times.
Before we leave loops, let's cover two branching statements that can be helpful.
We've already seen break when we talked about switch statements. It has a
similar effect in loops: It stops the loop. The continue statement, on the other
hand, stops the current iteration of the loop and moves on to the next one. Here's a
trivial example:
for (int i = 0; i < 3; i++) {
if (i < 2) {
System.out.println("Haven't hit 2 yet...");
continue;
}
if (i == 2) {
System.out.println("Hit 2...");
break;
}
}
ibm.com/developerWorks
developerWorks
If you put that code in your main() method and run it, you'll get output like this:
Haven't hit 2 yet...
Haven't hit 2 yet...
Hit 2...
The first two times through the loop, i is less than 2, so we print "Haven't hit 2
yet...", then continue, which goes to the next iteration of the loop. When i is 2, the
block of code for the first if doesn't execute. We fall through to the second if, print
out "Hit 2...", then break out of the loop.
In the next section, we'll increase the richness of the behavior we can add by talking
about how to handle collections of things.
Section 9. Collections
Collections introduction
Most real-world software applications deal with collections of things (files, variables,
lines of files, etc.). Often, OO programs deal with collections of objects. The Java
language has a sophisticated Collections Framework that allows you to create and
manage collections of objects of various types. This framework could justify a tutorial
all its own, so we can't cover it all here. Instead, we'll cover one very commonly used
collection, and some techniques for using it. Those techniques apply to most
collections available in the Java language.
Arrays
Most programming languages include the concept of an array to hold a collection of
things, and the Java language is no exception. An array is nothing more than a
collection of elements of the same type.
There are two ways to declare an array:
Create it with a certain size, which is fixed forever.
Create it with a certain set of initial values. The size of this set determines
the size of the array -- it will be exactly large enough to hold all of those
Page 43 of 71
developerWorks
ibm.com/developerWorks
elementType [ arraySize ]
To create an integer array of five elements, you would do one of these two things:
int[] integers = new int[5];
int[] integers = new int[] { 1, 2, 3, 4, 5 };
The first statement creates an empty array of five elements. The second is a
shortcut for initializing an array. It lets you specify a comma-separated list of initial
values between curlies. Notice that we didn't include a size in the square brackets -the number of items in our initialization block dictated a size of five elements. That's
easier than creating the array and then coding a loop to put values in it, like so:
int[] integers = new int[5];
for (int i = 1; i <= integers.length; i++) {
integers[i] = i;
System.out.print(integers[i] + " ");
}
This code also declares the integer array of five elements. If you try to put more than
five elements in the array, you'll run into problem when you run the code. To load the
array, we loop through the integers from 1 to the length of the array, which we
discovered by accessing length() on our array. Each time through the loop, we
put an integer into the array. When we hit 5, we stop.
Once our array is loaded, we can access the elements in the array with a similar
loop:
for (int i = 0; i < integers.length; i++) {
System.out.print(integers[i] + " ");
}
Think of an array as a series of buckets. Each element in the array sits in one of the
buckets, which was assigned an index number when you created the array. You
access the element at a particular bucket by writing:
arrayName [ elementIndex ]
The indices of an array are zero-based, meaning that the first one is at 0. So now
our loop makes good sense. We start this loop with 0 because arrays are
zero-based, and we loop through each element of the array, printing out the value at
each index.
What is a collection?
Introduction to Java programming
Page 44 of 71
ibm.com/developerWorks
developerWorks
Arrays are nice, but working with them is a little awkward. Loading them takes effort,
and once you declare one, you can only load things of the array's type into it, and
only as many items as the array can accommodate. Arrays certainly don't feel very
OO. In fact, the primary reason the Java language has arrays at all is to serve as a
holdover from pre-OO programming days. They're ubiquitous in software, so not
having them in the language would make it tough to exist in the real world, especially
when you have to interact with other systems that use them. But the Java language
gives you many more tools for handling collections of things. Those tools are very
OO.
The concept of a collection isn't that difficult to understand. When you need a fixed
number of elements of the same type, you can use an array. When you need
elements of various types, or a dynamically changing number of them, you use a
Java Collection.
ArrayList
In this tutorial, we'll talk about only one type of collection, called an ArrayList. In
the process, you'll learn another reason many OO purists have a bone to pick with
the Java language.
In order to use an ArrayList in your code, you have to add an import statement for
it in your class:
import java.util.ArrayList;
Adding and removing things from lists is straightforward. There are multiple methods
to do it, but the two most common are as follows:
someArrayList.add(someObject);
Object removedObject = someArrayList.remove(someObject);
Page 45 of 71
developerWorks
ibm.com/developerWorks
That appends the object you add to the end of the list. So far, so good. But what
happens when you want to add a primitive to the list? You can't do that directly.
Instead, you have to wrap your primitives in objects. There's one wrapper class per
primitive type:
Boolean for boolean s
Byte for byte s
Character for char s
Integer for int s
Short for short s
Long for long s
Float for float s
Double for double s
For example, to put an int primitive into an ArrayList, we would have to use
code like the following:
Integer boxedInt = new Integer(1);
someArrayList.add(boxedInt);
Using collections
Most adults in the real world carry some money around with them. Let's assume
every Adult has a wallet that holds his or her money. For this tutorial, we'll assume
that:
Money is represented by bills only
The face value of the bill (as an integer) identifies each bill
All money in the wallet is in U.S. dollars
Each Adult starts its programmed "life" with no money
ibm.com/developerWorks
developerWorks
Remember our array of integers? Let's make an ArrayList instead. Add an import
for ArrayList, then add ArrayList to your Adult class at the end of the list of
other instance variables:
protected ArrayList wallet = new ArrayList();
We created the ArrayList and initialized it to an empty list, because our Adult
has to earn every dollar. We can add some wallet accessors also:
public ArrayList getWallet() {
return wallet;
}
public void setWallet(ArrayList aWallet) {
wallet = aWallet;
}
Which accessors to provide is a judgment call, but in this case we went with the
typical ones. There's no reason why we couldn't call setWallet() something like
resetWallet(), or even goBankrupt(), because we are resetting it to an empty
ArrayList. Should another object be able to reset our wallet with a new one?
Again, a judgment call. That's what OOD is all about!
Now we're all set up to add some methods that let us interact with our wallet:
public void addMoney(int bill) {
Integer boxedBill = new Integer(bill);
wallet.add(boxedBill);
}
public void spendMoney(int bill) {
Integer boxedBill = new Integer(bill);
boolean haveThatBill = wallet.contains(boxedBill);
if(haveThatBill) {
wallet.remove(boxedBill);
} else {
System.out.println("I don't have that bill.");
}
}
Page 47 of 71
developerWorks
ibm.com/developerWorks
Our main() method combines a lot of things that we know about at this point. First,
we call addMoney() a few times to put money in our wallet. Then we loop
through the contents of our wallet to print out what's in there. We use a while
loop to do that, but we have to do some extra work. We have to:
Get an Iterator for the list, which lets us access elements in the list
Call hasNext() on the Iterator as the boolean expression for our
loop to see if we still have elements to handle
Call next() on the Iterator to get the next element each time through
the loop
Typecast (or cast ) the returned object to the type we know is in the list
(an Integer, in this case)
That's the standard idiom for looping through a collection in the Java language.
Alternatively, we could call toArray() on it and get an array back, which we could
then loop through using for like we did a while back. The more OO way to do it is to
exploit the power of the Java Collections framework.
The only new concept here is the idea of casting. What's that? As we already know,
objects in the Java language have a type, or class. If you look at the signature of the
next() method, you'll see that it returns an Object, not a particular subclass of
Object. All objects in the world of Java programming are subclasses of Object,
but the Java language needs to know what specific type an object is in order for you
to be able to call methods specific to the type you want to deal with. If you don't cast,
you're limited to the methods available on Object, which is a pretty small list. In this
particular example, we didn't need to call any methods on the Integer s we got out
of our list, but if we did, we'd have to cast first.
ibm.com/developerWorks
developerWorks
Creating constructors
We've talked about constructors before. You might remember that every object in
your Java code gets a default, no-argument constructor for free. You don't have to
define it, and you won't see it in your code. In fact, we've been taking advantage of
that in our Adult class. You don't see a constructor there.
In practice, however, it's wise to define your own constructors. When you do that,
you can be absolutely sure that someone examining your class knows how to
construct it in the way that you intended it to be constructed. So let's define our own
no-argument constructor. Recall the basic structure of a constructor:
accessSpecifier ClassName(
constructor statement(s)
arguments ) {
We're done. Our no-argument constructor does nothing, really, except create an
Adult. Now when we call new to create an Adult, we'll be using our no-argument
Page 49 of 71
developerWorks
ibm.com/developerWorks
constructor instead of the default constructor. But what if we want the constructor to
do something? In the case of Adult, it would be very convenient to be able to pass
in a first name and a last name as String s, and have the constructor set our
instance variables to those initial values. That's equally simple to do:
public Adult(String aFirstname, String aLastname) {
firstname = aFirstname;
lastname = aLastname;
}
This constructor takes two arguments and sets our instance variables from them.
We now have two constructors. We really don't need the first constructor, but there's
nothing wrong with keeping it. It gives users of this class options. They can create an
Adult with a default name, or they can create one with a specific name that they
provide.
What we just did, even though you probably didn't know it, was overload a method.
We'll discuss this concept in more detail in the next panel.
Overloading methods
When you create two methods with the same name, but with a different number (or
different types) of parameters, you have overloaded that method. This is one of the
powerful things about objects. The Java language runtime will determine which
version of the method to call, based on what you pass in. In the case of our
constructors, if we don't pass in any arguments, the JRE will use the no-argument
constructor. If we pass in two String s, the runtime will use the version that takes
two String parameters. If we pass in arguments of a different type (or a single
String ), the runtime will complain that there's no constructor available that takes
those types.
You can overload any method, not just constructors, which makes it easy to create a
convenient interface for users of your classes. Let's try it by adding another version
of our addMoney() method. At the moment, that method takes a single int. That's
fine, but what if we want to add $100 to an Adult 's bankroll? We would have to call
the method as many times as necessary to add the particular set of bills that total
$100. That's very inconvenient. It would be much nicer to be able to pass in an array
of int s representing a set of bills. So let's overload the method to accept an array
parameter. Here's the method we have now:
public void addMoney(int bill) {
Integer boxedBill = new Integer(bill);
wallet.add(boxedBill);
}
ibm.com/developerWorks
developerWorks
This method looks very similar to our other addMoney() method, but this takes an
array parameter. Let's try using it by changing our main() method on Adult to look
like this:
public static void main(String[] args) {
Adult myAdult = new Adult();
myAdult.addMoney(new int[] { 1, 5, 10 });
System.out.println(myAdult);
}
When we run the code, we can see that our Adult has a wallet with $16 in it now.
That's a much nicer interface. But we're not done. Remember, we're professional
programmers, and we want to keep our code clean. Do you see any code
duplication in our two methods? The two lines in the first version show up verbatim
in the second version. If we wanted to change what we do when we add money,
we'd have to change code in two places, which is less than ideal. If we added
another version of the method to take an ArrayList as parameter instead of an
array, we'd have to change code in three places. That quickly becomes
unacceptable. Instead, we can refactor the code to remove the duplication. On the
next section, we'll do a refactoring called Extract Method to accomplish the job.
We made this method protected because it's really our own internal helper
Page 51 of 71
developerWorks
ibm.com/developerWorks
method, not part of the public interface of our class. Now let's replace those lines of
code in our methods with a call to this new method:
public void addMoney(int bill) {
addToWallet(bill);
}
If you rerun the code, you should see the same results. That kind of refactoring
should get to be a habit, and Eclipse makes it easier for you by including many
automated refactorings. Going into detail about them is beyond the scope of this
tutorial, but you can experiment with them. If we had selected those two lines of
duplicated code in, say, the first version of addMoney(), we could have right-clicked
on the selected code and selected Refactor>Extract Method. Then Eclipse would
have walked us through the refactoring. This is one of the most powerful features of
the IDE.
Class members
The variables and methods we have on Adult are instance variables and methods.
Recall that every instance will have those.
Classes themselves can also have variables and methods. Those are collectively
called class members, and you declare them with the static keyword. The
differences between class members and the instance varieties are:
Every instance of a class shares a single copy of a class variable
You can call class methods on the class itself, without having an instance
Instance methods can access class variables, but class methods cannot
access instance variables
Class methods can access only class variables
When does it make sense to add class variables or methods? The best rule of
thumb is to do so rarely, so that you don't overuse them. Some typical uses are:
To declare constants that any instance of the class can use
ibm.com/developerWorks
developerWorks
Class variables
To create a class variable, use the static keyword when you declare it:
accessSpecifier static
[=
variableName
initialValue ];
The JRE creates a copy of a class's instance variables for every instance of that
class. It creates only a single copy of each class variable, regardless of the number
of instances, the first time it encounters the class in a program. All instances will
share (and potentially modify) that single copy. That makes class variables a good
choice for constants that all instances should be able to use.
For example, we've been using integers to describe "bills" in the wallet of Adult.
That's perfectly acceptable, but it might be nice to name the integer values so that
we can easily see what the numbers represent when we read the code. Let's declare
a few constants to do that, in the same place we declare the instance variables for
our class:
protected
protected
protected
protected
protected
protected
static
static
static
static
static
static
final
final
final
final
final
final
int
int
int
int
int
int
ONE_DOLLAR_BILL = 1;
FIVE_DOLLAR_BILL = 5;
TEN_DOLLAR_BILL = 10;
TWENTY_DOLLAR_BILL = 20;
FIFTY_DOLLAR_BILL = 30;
ONE_HUNDRED_DOLLAR_BILL = 40;
By convention, class constants have names in all uppercase letters, with words
separated by underscores. We used static to declare them as class variables,
and we added the final keyword to make sure that they can't be changed by any
instance (that is, to make them constants). Now we can change main() to add
some money to our Adult using these new named constants:
public static void main(String[] args) {
Adult myAdult = new Adult();
myAdult.addMoney(new int[] { Adult.ONE_DOLLAR_BILL, Adult.FIVE_DOLLAR_BILL });
System.out.println(myAdult);
}
Reading this code makes it obvious what we're adding to our wallet.
Class methods
Page 53 of 71
developerWorks
ibm.com/developerWorks
We called the method on a named variable that holds an instance of a class. When
you call a class method, you call it like this:
ClassName.methodName();
We didn't need an instance to call this method. We called it on the class itself. The
main() method we've been using is a class method. Look at its signature. Notice
that it is declared as public static. We've seen that access specifier before. The
static keyword identifies this as a class method, which is the reason those
methods are sometimes called static methods. We didn't need to have an instance
of Adult to call main().
We can create class methods for Adult if we want to, although there's really no
reason to do so in this case. To illustrate how, though, let's add a trivial class
method:
public static void doSomething() {
System.out.println("Did something");
}
Comment out the current lines of code in main() and add this line:
Adult.doSomething();
Adult myAdult = new Adult();
myAdult.doSomething();
When you run that code, you should see the appropriate message in the console
twice. The first call to doSomething() is the typical way to call a class method. You
can also call them through an instance of a class, as in the third line of code. But
that's not really good form. Eclipse tells you that by underlining that line with a wavy
yellow line and suggesting that you should access that method in a "static way,"
meaning on the class, not an instance.
ibm.com/developerWorks
developerWorks
a == b
will return true if and only if a and b refer to exactly the same instance of a class
(that is, the same object). The exception is for primitives. When you compare two
primitives with ==, the Java language runtime compares their values (remember,
they aren't true objects anyway). Try this experiment in main(), and view the results
in the console:
int int1 = 1;
int int2 = 1;
Integer integer1 =
Integer integer2 =
Adult adult1 = new
Adult adult2 = new
new Integer(1);
new Integer(1);
Adult();
Adult();
System.out.println(int1 == int2);
System.out.println(integer1 == integer2);
integer2 = integer1;
System.out.println(integer1 == integer2);
System.out.println(adult1 == adult2);
The first comparison returns true, because we're comparing primitives with the
same value. The second returns false, because the two variables don't refer to the
same object instance. The third returns true, because the two variables now refer
to the same instance. Trying it with our class, we also get false because adult1
and adult2 don't refer to same instance.
The equals() method is on type Object, which is the parent of every class in the
Java language. That means that any class you create will inherit the basic
equals() behavior from Object. That basic behavior is no different from the ==
operator. In other words, by default, these two statements both use == and return
false:
a == b;
a.equals(b);
Look at the spendMoney() method on Adult again. What goes on behind the
scenes when we call contains() on our wallet? The Java language uses the ==
operator to compare the objects in the list to the one we asked for. If it finds a match,
the method returns true; otherwise it returns false. Because we're comparing
primitives, it can find a match based on the values of integers (remember that ==
compares primitives based on their value).
Page 55 of 71
developerWorks
ibm.com/developerWorks
That's great for primitives, but what if want to compare the contents of objects? The
== operator won't do it. To compare the contents of objects, we have to override the
equals() method on the class a is an instance of. That means that you create a
method with exactly the same signature as the method in one of your superclasses,
but you implement the method differently. If you do that, you can compare the
contents of two objects to see if they're equal, rather than just checking to see if two
variables refer to the same instance.
Try this experiment in main(), and view the results in the console:
Adult adult1 = new Adult();
Adult adult2 = new Adult();
System.out.println(adult1 == adult2);
System.out.println(adult1.equals(adult2));
Integer integer1 = new Integer(1);
Integer integer2 = new Integer(1);
System.out.println(integer1 == integer2);
System.out.println(integer1.equals(integer2));
The first comparison returns false, because adult1 and adult2 refer to different
instances of Adult. The second returns false as well, because the default
implementation of equals() simply compares the two variables to see if they refer
to the same instance. But that default behavior of equals() isn't usually what we
want. We'd like to compare the contents of two Adult s to see if they look the same.
We can override equals() to do that. As you see from the final two comparisons in
the example above, the Integer class overrides the method such that == returns
false, but equals() compares the wrapped int values for equality. We'll do
something similar for Adult in the next panel.
Overriding equals()
Overriding equals() to compare objects actually requires us to override two
methods:
public boolean equals(Object other) {
if (this == other)
return true;
if ( !(other instanceof Adult) )
return false;
Adult otherAdult = (Adult)other;
if (this.getAge() == otherAdult.getAge() &&
this.getName().equals(otherAdult.getName()) &&
this.getRace().equals(otherAdult.getRace()) &&
this.getGender().equals(otherAdult.getGender()) &&
this.getProgress() == otherAdult.getProgress() &&
this.getWallet().equals(otherAdult.getWallet()))
return true;
ibm.com/developerWorks
developerWorks
else
return false;
}
public int hashCode() {
return firstname.hashCode() + lastname.hashCode();
}
Page 57 of 71
developerWorks
ibm.com/developerWorks
Overriding toString()
The Object class has a toString() method, which every class you create will
inherit. It returns a String representation of your object, and is very helpful for
debugging. To see what the default implementation of toString() does, try this
experiment in main():
public static void main(String[] args) {
Adult myAdult = new Adult();
myAdult.addMoney(1);
myAdult.addmoney(5);
System.out.println(myAdult);
}
That's much more convenient and helpful than a cryptic object ID.
ibm.com/developerWorks
developerWorks
Exceptions
It would be nice if nothing ever went wrong in our code, but that's unlikely.
Sometimes things don't go as we'd like, and sometimes the problem is worse than
just producing unwanted results. When that happens, the JRE throws an exception.
The language includes some special statements that let you catch the exception and
handle it appropriately. Here is the general format for those statements:
try {
statement(s)
} catch ( exceptionType
name ) {
statement(s)
} finally {
statement(s)
}
The try statement wraps code that might throw an exception. If it does, execution
drops immediately to the catch block, also known as an exception handler. When
all the trying and catching is done, execution continues to the finally block,
regardless of whether or not there was an exception thrown. When you catch an
exception, you can attempt to recover from it, or you can exit the program (or
method) gracefully.
Handling exceptions
Try (no pun intended) this experiment in main():
public static void main(String[] args) {
Adult myAdult = new Adult();
myAdult.addMoney(1);
String wontWork = (String) myAdult.getWallet().get(0);
}
When we run this, we'll get an exception. The console will show something like this:
java.lang.ClassCastException
at intro.core.Adult.main(Adult.java:19)
Exception in thread "main"
This stack trace reports the type of exception and the line number where it occurred.
Remember that we have to cast when we remove an Object from a Collection. We
have a collection of Integer s, but we tried to get the first one with get(0) (where
0 refers to the index of the first element in the list, because the list is zero-based, just
like an array) and cast it to type String. The Java language runtime complains. At
the moment, the program just dies. Let's make that it shut down more gracefully by
handling the exception:
Page 59 of 71
developerWorks
ibm.com/developerWorks
try {
String wontWork = (String) myAdult.getWallet().get(0);
} catch (ClassCastException e) {
System.out.println("You can't cast that way.");
}
Here we catch the exception and print a nice message. Alternatively, we could have
done nothing in the catch block, and printed our nice message in the finally
block, but that wasn't necessary. In some cases, the exception object (commonly,
but not necessarily, named e or ex ) can give you more information about the error,
which can help you report better information or recover gracefully.
In your code, if you call a method that specifies that it throws one or types of
exceptions, you have to handle it somehow, or add a throws to your method
signature to pass it along up the call stack to methods that called your code. In the
event of an exception, the Java language runtime will search for an exception
handler somewhere up the stack, if there isn't one where the exception was thrown.
If it doesn't find one by the time it reaches the top of the stack, it halts the program
abruptly.
The good news is that most IDEs (and Eclipse is certainly among them) will tell you
if your code needs to catch an exception thrown by a method you call. Then you can
decide what to do with it.
There's much more to exception handling, of course, but it's too much for this
tutorial. Hopefully what we've covered here will help you know what to expect.
ibm.com/developerWorks
developerWorks
Follow these steps to create the class and really make it "drive" our program:
Create the class in Eclipse using the same New Java Class toolbar button
we used to create Adult in Declaring the class.
Name the class CommunityApplication, and make sure you've
selected the option to add a main() method to the class. Eclipse
generates the class for you, including main().
Delete the main() method from the Adult class.
All that's left to do is to put something in our new main():
package intro.core;
public class CommunityApplication {
public static void main(String[] args) {
Adult myAdult = new Adult();
System.out.println(myAdult.walk(10));
}
}
Page 61 of 71
developerWorks
ibm.com/developerWorks
Create a new launch configuration in Eclipse, just like we did for the Adult class in
Executing code in Eclipse, and run it. You should see that our object walked 10
steps.
What we have now is a simple application that starts in
CommunityApplication.main(), and uses our Adult domain object. Of course,
applications can be more complicated than this, but the basic idea remains the
same. It's not uncommon for Java applications to have hundreds of classes. Once a
primary driver class kicks things off, the program runs by having classes collaborate
with each other to get the job done. Following the execution of the program can be
quite disorienting if you're used to procedural programs that start at the beginning
and run to the end, but it gets easier to understand with practice.
JAR files
How do you package a Java application up so others can use it, or give them code
they can use in their own programs (such as a library of helpful objects, or a
framework)? You create a Java Archive (JAR) file that packages up your code so
other programmers can include it in their Java Build Path in Eclipse, or on their
classpath if they're using command-line tools. Once again, Eclipse makes life much
easier for you. Creating a JAR file in Eclipse (and many other IDEs) is a snap:
1.
2.
3.
Browse to the location you want to put your JAR file, and name the file
whatever you'd like and give it a .jar file extension
4.
Click Finish
You should see your JAR file in the location you specified. Once you have a JAR file
(yours or from another source), you can use the classes in it in your code if you put it
on your Java Build Path in Eclipse. Doing that is no sweat either. There's no code
we need to add to our path at the moment, but let's follow the steps you would take
to do so:
1.
2.
ibm.com/developerWorks
3.
developerWorks
There you see buttons for Add JARs... and Add External JARs..., which
you can use to put JARs on your Java Build Path
Once the code (that is, the class files) in a JAR fileis are on your Java Build Path,
you can use those classes in your Java code without getting a compile error. If the
JAR file includes source code, you can associate those source files with the class
files on your path. Then you can get pop-up code help and even open the code and
see it.
Page 63 of 71
developerWorks
ibm.com/developerWorks
Typically, a class with a very large number of methods has some that don't belong
there, because that gargantuan object is doing too much. In his book Refactoring
(see Resources for a link), Martin Fowler calls this the Foreign Method code smell. If
you have an object with 100 methods, you should think hard about whether or not
that single object should really be multiple objects. Large classes usually stink in
college. It's the same with Java code.
ibm.com/developerWorks
developerWorks
inconvenient, but a name long enough to be clear usually isn't ridiculously long. I
have no problem with a method name like
aReallyLongMethodNameThatIsAbsolutelyClear(). At 3:00 a.m. when I'm
trying to figure out why my program isn't working, if I come across a method named
a(), I want to beat somebody up.
Spend a few extra minutes coming up with a very descriptive method name; if
possible, consider naming methods in a such way that makes your code read more
like English, even if that means adding additional helper methods to do the job. For
example, consider adding a helper method to make this code more readable:
if (myAdult.getWallet().isEmpty()) {
do something
}
This technique is simple, and perhaps trivial in this case, but it's amazingly powerful
as code gets more complex.
Page 65 of 71
developerWorks
ibm.com/developerWorks
also constantly trying to reduce the number of classes without making our intent less
clear.
Why does that bother me? Because I'm personally biased against coding styles that
ibm.com/developerWorks
developerWorks
add lines of code that are, in my opinion, unnecessary. The Java compiler
recognizes the following code just the same, and I save several lines:
public void myMethod() {
if (this.a == this.b)
statements
}
Neither way is "right" or "wrong." One is simply shorter than the other. So what
happens when I code with somebody who likes the first form better? We talk about
it, pick a style we're going to use, then stick to it. The only hard and fast style rule is
to be consistent. If everybody working on a project uses a different style, reading
code will become difficult. Pick a style and don't vary it.
Avoid switch
Some Java programmers love switch statements. I used to think they were nice,
but then I realized that a switch is really just a bunch of if s, and that usually
means that conditional logic shows up in more than one place in my code. That's
code duplication, and it's a no-no. Why? Because having the same code in multiple
places makes code harder to change. If I have the same switch in three places,
and I want to change how I handle a particular case, I have to change three bits of
code.
Now, what if you can refactor the code such that you have a single switch
statement? Great! I don't believe there's anything evil about using it. In some cases,
it's more clear than nested if s. But if you see it cropping up in lots of places, you've
got a problem you should fix. An easy way to keep that from happening is to avoid
switch unless it's the best tool for the job. In my experience, it rarely is.
Be public
I saved the most controversial recommendation for last. Take a deep breath.
I believe you should err on the side of making all of your methods public. Instance
variables should be protected.
Of course, many professional programmers would shudder at the thought, because
if everything is public, anybody can change it, perhaps in unauthorized ways. In a
world where everything is publicly accessible, you have to depend on programmer
discipline to ensure that people won't access something they shouldn't, when they
shouldn't. But there's very little in programming life more frustrating than wanting to
access a variable or method that's not visible to you. If you restrict access to things
Introduction to Java programming
Copyright IBM Corporation 1994, 2006. All rights reserved.
Page 67 of 71
developerWorks
ibm.com/developerWorks
in your code that you assume others shouldn't access, you're assuming you're
omniscient. That's a dangerous assumption most of the time.
This frustration crops up frequently when you're using other people's code. You can
see a method that does exactly what you want to do, but it isn't publicly accessible.
Sometimes there's a good reason for that, and it makes sense to limit accessibility.
Sometimes, though, the only reason it's not public is that the folks who wrote the
code thought, "Nobody ever will need to access this." Or maybe they thought,
"Nobody should access this because..." and then they didn't have a solid reason.
Many times, people use private because its available. Don't do that.
Make methods public and variables protected until you have a good reason to
restrict access.
ibm.com/developerWorks
developerWorks
development environment. You can create objects that can do a good number of
things, although certainly not everything you can imagine. But you can extend your
learning in several ways, including perusing the Java API and exploring other
capabilities of the Java language through other developerWorks tutorials. See
Resources for links to all of those.
The Java language certainly isn't perfect; every language has its quirks, and every
programmer has a favorite. The Java platform is, however, a good tool that can help
you write very good professional programs that are in high demand.
Page 69 of 71
developerWorks
ibm.com/developerWorks
Resources
Learn
The official Java Technology home page has links to all things related to the Java
platform. You can find every "official" Java language resource you need right
here, including the language specification and API documentation.
Learn more about the command-line tools that come with Java.
Visit the Java documentation page for a link to API documentation for each of the
SDK versions.
John Zukowski's Taming Tiger column on developerWorks offers a look at that
latest version of the J2SE platform.
The javadoc home page covers all the ins and outs of javadoc, including how
to use the command-line tool and how to write your own Doclet s that let you
create custom formats for your documentation.
The Sun Java tutorial is an excellent resource. It's a gentle introduction to the
language, and contains much more material than this tutorial, including links to
other tutorials that go into more detail about various aspects of the language.
Refactoring, Martin Fowler et al (Addison-Wesley, 1999) is an excellent resource
for improving existing code. >
The New to Java technology page is a clearinghouse for developerWorks
resources for beginning Java developers, including links to tutorials and
certification resources.
You'll find articles about every aspect of Java programming in the
developerWorks Java technology zone.
Also see the Java technology zone tutorials page for a complete listing of free
Java-focused tutorials from developerWorks .
Get products and technologies
You can download Eclipse from the Eclipse Web site.
ibm.com/developerWorks
developerWorks
three years using the Java platform professionally at RoleModel Software, Inc., in Holly
Springs, NC. He has developed software, managed teams, and coached other
programmers at clients ranging from two-person start-ups to Fortune 50 companies.
Roy is also a frequent contributor to developerWorks. For technical questions or
comments about the content of this tutorial, contact Roy at [email protected].
Page 71 of 71