Java Tutorial
Java Tutorial
This two-part tutorial introduces the structure, syntax, and programming paradigm of the Java language and platform. You'll learn the Java syntax you are most likely to encounter professionally and Java programming idioms you can use to build robust, maintainable Java applications. In Part 1, J. Steven Perry guides you through the essentials of object-oriented programming on the Java platform, including fundamental Java syntax and its use. You'll get started with creating Java objects and adding behavior to them, and conclude with an introduction to the Java Collections Framework, with considerable ground covered in between. View more content in this series
developerWorks
ibm.com/developerWorks/
Development Kit (JDK) and the Eclipse IDE. Once you have been introduced to your development environment's components, you will begin learning basic Java syntax hands-on. Part 2 covers more-advanced language features, including regular expressions, generics, I/O, and serialization. Programming examples in Part 2 build on the Person object that you begin developing in Part 1.
Objectives
When you've finished Part 1, you will be familiar with basic Java language syntax and able to write simple Java programs. You should follow up with "Introduction to Java programming, Part 2: Constructs for real-world applications" to build on this foundation.
Prerequisites
This tutorial is for software developers who are not yet experienced with Java code or the Java platform. The tutorial includes an overview of OOP concepts.
System requirements
To complete the exercises in this tutorial, install and set up a development environment consisting of: JDK 6 from Sun/Oracle. Eclipse IDE for Java Developers. Download and installation instructions for both are included in the tutorial. The recommended system configuration is: A system supporting Java SE 6 with at least 1GB of main memory. Java 6 is supported on Linux, Windows, and Solaris. At least 20MB of disk space to install the software components and examples.
ibm.com/developerWorks/
developerWorks
you see three frames in the Javadoc. The top-left frame shows all of the packages in the API, and beneath that are the classes in each package. The main frame (to the right) shows details for the currently selected package or class. For example, if you select the java.util package in the top-left frame and then select the ArrayList class listed below it, in the right-hand frame you will see details about ArrayList, including a description of what it does, how to use it, and its methods.
Like any programming language, the Java language has its own structure, syntax rules, and programming paradigm. The Java language's programming paradigm is based on the concept of object-oriented programming (OOP), which the language's features support. The Java language is a C-language derivative, so its syntax rules look much like C's: for example, code blocks are modularized into methods and delimited by braces ({ and }), and variables are declared before they are used. Structurally, the Java language starts with packages. A package is the Java language's namespace mechanism. Within packages are classes, and within classes are methods, variables, constants, and so on. You'll learn about the parts of the Java language in this tutorial.
The JVM
At run time, the JVM reads and interprets .class files and executes the program's instructions on the native hardware platform for which the JVM was written. The JVM interprets the bytecodes just as a CPU would interpret assembly-language instructions. The difference is that the JVM is a piece of software written specifically for a particular platform. The JVM is the heart of the Java language's "write-once, run-anywhere" principle. Your code can run on any chipset for which a suitable JVM implementation is available. JVMs are available for major platforms like Linux and Windows, and subsets of the Java language have been implemented in JVMs for mobile phones and hobbyist chips.
developerWorks
ibm.com/developerWorks/
allocates memory space for that object from the heap, which is a pool of memory set aside for your program to use. The Java garbage collector runs in the background, keeping track of which objects the application no longer needs and reclaiming memory from them. This approach to memory handling is called implicit memory management because it doesn't require you to write any memory-handling code. Garbage collection is one of the essential features of Java platform performance.
ibm.com/developerWorks/
developerWorks
focus on writing and testing code. In addition, you can use Eclipse to organize source code files into projects, compile and test those projects, and store project files in any number of source repositories. You need an installed JDK in order to use Eclipse for Java development.
Install JDK 6
Follow these steps to download and install JDK 6: 1. Browse to Java SE Downloads and click the Java Platform (JDK) box to display the download page for the latest version of the JDK (JDK 6, update 21 at the time of this writing). 2. Click the Download button. 3. Select the operating system platform you need. 4. You will be asked for your account username and password. Enter them if you have an account, sign up if you don't, or you can click Continue to skip this step and proceed to the download. 5. Save the file to your hard drive when prompted. 6. When the download is complete, run the install program. (The file you've just downloaded is a self-extracting ZIP file that is also the install program.) Install the JDK to your hard drive in an easy-to-remember location (such as C:\home \jdk1.6.0_20 on Windows or ~/jdk1.6.0_20 on Linux). It's a good idea to encode the update number in the name of the install directory you choose. You now have a Java environment on your machine. Next, you will install the Eclipse IDE.
Install Eclipse
To download and install Eclipse, follow these steps: 1. 2. 3. 4. Browse to Eclipse Galileo Sr2 Packages. Click on Eclipse IDE for Java Developers. Under Download Links on the right-hand side, select your platform. Select the mirror you want to download from, then save the file to your hard drive. 5. Extract the contents of the .zip file to a location on your hard drive that you'll be able to remember easily (such as C:\home\eclipse on Windows or ~/eclipse on Linux).
Set up Eclipse
The Eclipse IDE sits atop the JDK as a useful abstraction, but it still needs to access the JDK and its various tools. Before you can use Eclipse to write Java code, you have to tell it where the JDK is located. To set up your Eclipse development environment:
Introduction to Java programming, Part 1: Java language basics Page 5 of 57
developerWorks
ibm.com/developerWorks/
1. Launch Eclipse by double-clicking on eclipse.exe (or the equivalent executable for your platform). 2. The Workspace Launcher will appear, allowing you to select a root folder for your Eclipse projects. Choose a folder you will easily remember, such as C: \home\workspace on Windows or ~/workspace on Linux. 3. Dismiss the Welcome to Eclipse screen. 4. Click Window > Preferences > Java > Installed JREs. Figure 1 shows the setup screen for the JRE: Figure 1. Configuring the JDK used by Eclipse
5. Eclipse will point to an installed JRE. You need to make sure you use the one you downloaded with JDK 6. If Eclipse does not automatically detect the JDK you installed, click Add... and in the next dialog Standard VM, then click Next. 6. Specify the JDK's home directory (such as C:\home\jdk1.6.0_20 on Windows), then click Finish. 7. Confirm that the JDK you want to use is selected and click OK. Eclipse is now set up and ready for you to create projects and compile and run Java code. The next section will familiarize you with Eclipse.
ibm.com/developerWorks/
developerWorks
Eclipse is not just an IDE, it is an entire development ecosystem. This section is a brief hands-on introduction to using Eclipse for Java development. See Resources if you want to learn more about Eclipse.
The primary unit of organization in Eclipse is the workspace. A workspace contains all of your projects. A perspective is a way of looking at each project (hence the name), and within a perspective are one or more views.
The Java perspective contains the tools you need to begin writing Java applications. Each tab shown in Figure 2 is a view for the Java perspective. Package Explorer and Outline are two particularly useful views.
Introduction to Java programming, Part 1: Java language basics Page 7 of 57
developerWorks
ibm.com/developerWorks/
The Eclipse environment is highly configurable. Each view is dockable, so you can move it around in the Java perspective and place it where you want it. For now, though, stick with the default perspective and view setup.
Create a project
Follow these steps to create a new Java project: 1. Click on File > New > Java Project ... and you will see a dialog box open like the one shown in Figure 3: Figure 3. New Java Project wizard
2. Enter Intro as the project name and click Finish. 3. If you want to modify the default project settings, click Next. (This is recommended only if you have experience with the Eclipse IDE.) 4. Click Finish to accept the project setup and create the project. You have now created a new Eclipse Java project and source folder. Your development environment is ready for action. However, an understanding of the OOP
Introduction to Java programming, Part 1: Java language basics Page 8 of 57
ibm.com/developerWorks/
developerWorks
paradigm covered in this tutorial's next two sections is essential. If you are familiar with OOP concepts and principles, you might want to skip to Getting started with the Java language.
What is an object?
Structured programming languages like C and COBOL follow a very different programming paradigm from object-oriented ones. The structured-programming paradigm is highly data-oriented, which means that you have data structures on one hand, and then program instructions that act on that data. Object-oriented languages like the Java language combine data and program instructions into objects. An object is a self-contained entity that contains attributes and behavior, and nothing more. Rather than having a data structure with fields (attributes) and passing that structure around to all of the program logic that acts on it (behavior), in an objectoriented language, data and program logic are combined. This combination can occur at vastly different levels of granularity, from fine-grained objects like a Number, to coarse-grained objects such as a FundsTransfer service in a large banking application.
developerWorks
ibm.com/developerWorks/
Object summary
A well-written object: Has crisp boundaries Does a finite set of activities Knows only about its data and any other objects that it needs to accomplish its activities In essence, an object is a discrete entity that has only the necessary dependencies on other objects to perform its tasks. Now you'll see what an object looks like.
You can probably think of more (and you can always add more attributes later), but this list is a good start. Behavior An actual person can do all sorts of things, but object behaviors usually relate to some kind of application context. In a business-application context, for instance, you might want to ask your Person object, "What is your age?" In response, Person would tell you the value of its Age attribute. More-complex logic could be hidden inside of the Person object, but for now suppose that Person has the behavior of answering these questions: What is your name? What is your age? What is your height?
Introduction to Java programming, Part 1: Java language basics Page 10 of 57
ibm.com/developerWorks/
developerWorks
What is your weight? What is your eye color? What is your gender?
Encapsulation
Recall that an object is above all discrete, or self-contained. This is the principle of encapsulation at work. Hiding is another term that is sometimes used to express the self-contained, protected nature of objects. Regardless of terminology, what's important is that the object maintains a boundary between its state and behavior, and the outside world. Like objects in the real world, objects used in computer programming have various types of relationships with different categories of objects in the applications that use them. On the Java platform, you can use access specifiers (which I'll introduce later in the tutorial) to vary the nature of object relationships from public to private. Public access is wide open, whereas private access means the object's attributes are accessible only within the object itself. The public/private boundary enforces the object-oriented principle of encapsulation. On the Java platform, you can vary the strength of that boundary on an object-byobject basis, depending on a system of trust. Encapsulation is a powerful feature of the Java language.
Introduction to Java programming, Part 1: Java language basics Page 11 of 57
developerWorks
ibm.com/developerWorks/
Inheritance
In structured programming, it is common to copy a structure, give it a new name, and add or modify the attributes that make the new entity (such as an Account record) different from its original source. Over time, this approach generates a great deal of duplicated code, which can create maintenance issues. OOP introduces the concept of inheritance, whereby specialized objects without additional code can "copy" the attributes and behavior of the source objects they specialize. If some of those attributes or behaviors need to change, then you simply override them. You only change what you need to change in order to create specialized objects. As you know from the Object-oriented programming concepts section, the source object is called the parent, and the new specialization is called the child. Inheritance at work Suppose you are writing a human-resources application and want to use the Person object as the basis for a new object called Employee. Being the child of Person, Employee would have all of the attributes of a Person object, along with additional ones, such as: Taxpayer identification number Hire date Salary Inheritance makes it easy to create the new Employee class of the object without needing to copy all of the Person code manually or maintain it. You'll see plenty of examples of inheritance in Java programming later in the tutorial, especially in Part 2.
Polymorphism
Polymorphism is a harder concept to grasp than encapsulation and inheritance. In essence, it means that objects that belong to the same branch of a hierarchy, when sent the same message (that is, when told to do the same thing), can manifest that behavior differently. To understand how polymorphism applies to a business-application context, return to the Person example. Remember telling Person to format its attributes into a String? Polymorphism makes it possible for Person to represent its attributes in a variety of ways depending on the type of Person it is. Polymorphism is one of the more complex concepts you'll encounter in OOP on the Java platform and not within the scope of an introductory tutorial. See Resources if you want to learn more about polymorphism.
Introduction to Java programming, Part 1: Java language basics Page 12 of 57
ibm.com/developerWorks/
developerWorks
It would be impossible to introduce the entire Java language syntax in a single tutorial. The remainder of Part 1 focuses on the basics of the language, leaving you with enough knowledge and practice to write simple programs. OOP is all about objects, so this section starts with two topics specifically related to how the Java language handles them: reserved words and the structure of a Java object.
Reserved words
Like any programming language, the Java language designates certain words that the compiler recognizes as special, and as such you are not allowed to use them for naming your Java constructs. The list of reserved words is surprisingly short:
abstract assert boolean break byte case catch char class const continue default do double else enum extends
Page 13 of 57
developerWorks
ibm.com/developerWorks/
final finally float for goto if implements import instanceof int interface long native new package private protected public return short static strictfp super switch synchronized this throw throws transient try void volatile while
Note that true, false, and null are technically not reserved words. Although they are literals, I included them in this list because you can't use them to name Java constructs. One advantage of programming with an IDE is that it can use syntax coloring for reserved words, as you'll see later in this tutorial.
ibm.com/developerWorks/
developerWorks
In the Java language, objects are defined as demonstrated in Listing 1: Listing 1. Object definition
package packageName; import ClassNameToImport; accessSpecifier class ClassName { accessSpecifier dataType variableName [= initialValue]; accessSpecifier ClassName([argumentList]) { constructorStatement(s) } accessSpecifier returnType methodName([argumentList]) { methodStatement(s) } // This is a comment /* This is a comment too */ /* This is a multiline comment */ }
Listing 1 contains various types of constructs, which I've differentiated with font formatting. The constructs shown in bold (which you'll find in the list of reserved words) are literals; in any object definition, they must be exactly what they are here. The names that I've given the other constructs describe the concepts they represent. I'll explain all of the constructs in detail in the rest of this section. Note: In Listing 1 and some other code examples in this section, square brackets indicate that the constructs within them are not required. The brackets themselves (unlike { and }) are not part of the Java syntax. Comments in code Notice that Listing 1 also includes some comment lines:
// This is a comment /* This is a comment too */ /* This is a multiline comment */
Just about every programming language allows the programmer to add comments to help document the code. Java syntax allows for both single-line and multiline comments. A single-line comment must be contained on one line, although you can use adjacent single-line comments to form a block. A multiline comment begins with / *, must be terminated with */, and can span any number of lines. You'll learn more about comments when you get to this tutorial's Writing good Java code section.
Packaging objects
The Java language lets you choose the names for your objects, such as Account, Person, or LizardMan. At times, you may end up using the same name to express two
Introduction to Java programming, Part 1: Java language basics Page 15 of 57
developerWorks
ibm.com/developerWorks/
slightly different concepts. This is called a name collision, and it happens frequently. The Java language uses packages to resolve these conflicts. A Java package is a mechanism for providing a namespace: an encapsulated area in which names are unique, but outside of which they might not be. To identify a construct uniquely, you must fully qualify it by including its namespace. Packages also give you a nice way to build more complex applications into discrete units of functionality. Package definition To define a package, you use the package keyword followed by a legal package name, terminated with a semicolon. Often package names are separated by dots, and follow this de facto scheme:
package orgType.orgName.appName.compName;
This package definition breaks down like so: orgType is the organization type such as com, org, or net. orgName is the name of the organization's domain, such as makotogroup, sun, or ibm. appName is the name of the application, abbreviated. compName is the name of the component. The Java language doesn't force you to follow this package convention. In fact, you don't need to specify a package at all, in which case all of your objects must have unique class names and will reside in the default package. As a best practice, I recommend that you define all of your Java classes in packages. You'll follow that convention throughout this tutorial.
Import statements
Up next in the object definition (referring back to Listing 1) is the import statement. An import statement tells the Java compiler where to find classes you reference inside of your code. Any nontrivial object uses other objects for some functionality, and the import statement is how you tell the Java compiler about them. An import statement usually looks like this:
import ClassNameToImport;
You specify the import keyword followed by the class that you want to import followed by a semicolon. The class name should be fully qualified, meaning it should include its package. To import all classes within a package, you can put .* after the package name. For example, this statement imports every class in the com.makotogroup package:
Introduction to Java programming, Part 1: Java language basics Page 16 of 57
ibm.com/developerWorks/
developerWorks
import com.makotogroup.*;
Importing an entire package can make your code less readable, however, so I recommend that you import just the classes you need. Eclipse simplifies imports When writing code in the Eclipse editor, you can type the name of a class you want to use, followed by Ctrl+Shift+O. Eclipse figures out which imports you need and adds them automatically. If Eclipse finds two classes with the same name, it displays a dialog box asking you which class you want to add imports for.
Class declaration
In order to define an object in the Java language, you must declare a class. Think of a class as a template for an object, like a cookie cutter. The class defines the object's basic structure, and at run time your application creates an instance of the object. The word object is often used synonymously with the word class. Strictly speaking, a class defines the structure of a thing of which the object is an instance. Listing 1 includes this class declaration:
accessSpecifier class ClassName { accessSpecifier dataType variableName [= initialValue]; accessSpecifier ClassName([argumentList]) { constructorStatement(s) } accessSpecifier returnType methodName([argumentList]) { methodStatement(s) } }
A class's accessSpecifier could have several values, but most of the time it is public. You'll look at other values of accessSpecifier soon. Class-naming conventions You can name classes pretty much however you want, but the convention is to use CamelCase: start with a capital letter, capitalize the first letter of each concatenated word, and make all the other letters lowercase. Class names should contain only letters and numbers. Sticking to these guidelines will ensure that your code is more accessible to other developers following the same conventions. Classes can have two types of members: variables and methods.
Variables
The values of a given class's variables distinguish each instance of that class and define its state. These values are often referred to as instance variables. A variable has:
Introduction to Java programming, Part 1: Java language basics Page 17 of 57
ibm.com/developerWorks/
public: Any object in any package can see the variable. (Don't ever use this value.) protected: Any object defined in the same package, or a subclass (defined in any package), can see the variable. No specifier (also called friendly or package private access): Only objects whose classes are defined in the same package can see the variable. private: Only the class containing the variable can see it. A variable's dataType depends on what the variable is it could be a primitive type or another class type (again, more about this later). The variableName is up to you, but by convention variable names use the CamelCase convention I described earlier, except that they begin with a lowercase letter. (This style is sometimes called lowerCamelCase.) Don't worry about the initialValue for now; just know that you can initialize an instance variable when you declare it. (Otherwise, the compiler generates a default for you that will be set when the class is instantiated.)
Page 18 of 57
ibm.com/developerWorks/
developerWorks
The basic class definition for Person isn't very useful at this point because it defines only its attributes (and private ones at that). To be more interesting, the Person class needs behavior and that means methods.
Methods
A class's methods define its behavior. Sometimes this behavior is nothing more than to return the current value of an attribute. Other times, the behavior can be quite complex. There are essentially two categories of methods: constructors and all other methods of which there are many types. A constructor method is used only to create an instance of a class. Other types of methods can be used for virtually any application behavior. Looking back at Listing 1, it shows the way to define the structure of a method, which includes things like:
accessSpecifier returnType methodName argumentList
The combination of these structural elements in a method's definition is called its signature. Next you'll look in more detail at the two types of methods, starting with constructors. Constructor methods Constructors let you specify how to instantiate a class. Listing 1 shows the constructor declaration syntax in abstract form; here it is again in Listing 3: Listing 3. Constructor declaration syntax
accessSpecifier ClassName([argumentList]) { constructorStatement(s) }
A constructor's accessSpecifier is the same as for variables. The name of the constructor must match the name of the class. So if you call your class Person, then the name of the constructor must also be Person.
Introduction to Java programming, Part 1: Java language basics Page 19 of 57
developerWorks
ibm.com/developerWorks/
For any constructor other than the default constructor, you pass an argumentList, which is one or more of:
argumentType argumentName
Arguments in an argumentList are separated by commas, and no two arguments can have the same name. argumentType is either a primitive type or another class type (the same as with variable types). Class definition with a constructor Now you'll see what happens when you add the capability to create a Person object in two ways: by using a no-arg constructor and by initializing a partial list of attributes. Listing 4 shows how to create constructors and also how to use argumentList: Listing 4. Person class definition with a constructor
package com.makotogroup.intro; public class Person { private String name; private int age; private int height; private int weight; private String eyeColor; private String gender; public Person() { // Nothing to do... } public Person(String name, int age, int height, String eyeColor, String gender) { this.name = name; this.age = age; this.height = height; this.weight = weight; this.eyeColor = eyeColor; this.gender = gender; } }
Note the use of the this keyword in making the variable assignments in Listing 4. This is Java shorthand for "this object" and must be used when referencing two variables with the same name (as in this case where age, for example, is both a constructor parameter and a class variable), and helps the compiler disambiguate the reference. The Person object is getting more interesting, but it needs more behavior. And for that, you need more methods. Other methods A constructor is a particular kind of method with a particular function. Similarly, many other types of methods perform particular functions in Java programs. Exploration of other methods begins in this section and continues throughout the tutorial.
Introduction to Java programming, Part 1: Java language basics Page 20 of 57
ibm.com/developerWorks/
developerWorks
Other methods look much like constructors, with a couple of exceptions. First, you can name other methods whatever you like (though, of course, there are rules). I recommend the following conventions: Start with a lowercase letter. Avoid numbers unless absolutely necessary. Use only alphabetic characters. Second, unlike constructors, other methods have an optional return type.
Person's
other methods
Armed with this basic information, you can see in Listing 5 see what happens when you add a few more methods to the Person object. (I've omitted constructors for brevity.) Listing 5. Person with a few new methods
package com.makotogroup.intro; public class Person { private String name; private int age; private int height; private int weight; private String eyeColor; private String gender; public String getName() { return name; } public void setName(String value) { name = value; } // Other getter/setter combinations... }
Notice the comment in Listing 5 about "getter/setter combinations." You''ll work more with getters and setters later in the tutorial. For now, all you need to know is that a getter is a method for retrieving the value of an attribute, and a setter is a method for modifying that value. I've shown only one getter/setter combination (for the Name attribute), but you could define more in a similar fashion. Note in Listing 5 that if a method doesn't return a value, you must tell the compiler by specifying the void return type in its signature. Static and instance methods There are generally two types of (nonconstructor) methods: instance methods and static methods. Instance methods are dependent on the state of a specific
Introduction to Java programming, Part 1: Java language basics Page 21 of 57
developerWorks
ibm.com/developerWorks/
object instance for their behavior. Static methods are also sometimes called class methods, because their behavior is not dependent on any single object's state. A static method's behavior happens at the class level. Static methods are used largely for utility; you can think of them as a way of having global methods ( la C) while keeping the code itself grouped with the class that needs it. For example, throughout this tutorial you'll use the JDK Logger class to output information to the console. To create a Logger class instance, you don't instantiate a Logger class; instead, you invoke a static method called getLogger(). The syntax for invoking a static method is different from the syntax used to invoke a method on an object instance. You also use the name of the class that contains the static method, as shown in this invocation:
Logger l = Logger.getLogger("NewLogger");
So to invoke a static method, you don't need an object instance, just the name of the class.
Creating a package
If you're not already there, get to the Package Explorer perspective in Eclipse. You're going to get set up to create your first Java class. The first step is to create a place for the class to live. Packages are namespace constructs, but they conveniently map directly to the file system's directory structure as well. Rather than use the default package (almost always a bad idea), you'll create one specifically for the code you'll be writing. Click File > New > Package to bring up the Java Package wizard, shown in Figure 4:
Page 22 of 57
ibm.com/developerWorks/
developerWorks
Type com.makotogroup.intro into the Name text box and click Finish. You will see the new package created in the Package Explorer.
Page 23 of 57
ibm.com/developerWorks/
Eclipse generates a shell class for you and includes the package statement at the top, along with the main() method you asked for and the comments you see. You just need to flesh out the class now. You can configure how Eclipse generates new classes via Window > Preferences > Java > Code Style > Code Templates. For simplicity, you'll go with Eclipse's out-of-the-box code generation. In Figure 5, notice the asterisk (*) next to the new source-code file, indicating that I've made a modification. And notice that the code is unsaved. Next, notice that I made a mistake when declaring the Name attribute: I declared Name's type to be Strin. The compiler could not find a reference to such a class and flagged it as a compile error (that's the wavy red line underneath Strin). Of course, I can fix my mistake by adding a g to the end of Strin. This is just a small demonstration of the power of an IDE over using command-line tools for software development.
ibm.com/developerWorks/
developerWorks
A dataType can be either a primitive type or a reference to another object. For example, notice that Age is an int (a primitive type), and Name is a String (an object). The JDK comes packed full of useful classes like java.lang.String, and those in the java.lang package do not need to be imported (a shorthand courtesy of the Java compiler). But whether the dataType is a JDK class such as String or a user-defined class, the syntax is essentially the same. Table 1 shows the eight primitive data types you're likely to see on a regular basis, including the default values that primitives take on if you do not explicitly initialize a member variable's value: Table 1. Primitive data types
Type boolean byte char short int long float double Size n/a 8 bits 16 bits 16 bits 32 bits 64 bits 32 bits 64 bits Default value false 0 (unsigned) 0 0 0 0.0 0.0 Range of values true or false -128 to 127 \u0000' \u0000' to \uffff' or 0 to 65535 -32768 to 32767 -2147483648 to 2147483647 -9223372036854775808 to 9223372036854775807 1.17549435e-38 to 3.4028235e+38 4.9e-324 to 1.7976931348623157e+308
Built-in logging
Before going further into coding, you need to know how your programs tell you what they are doing. The Java platform includes the java.util.logging package, a built-in logging mechanism for gathering program information in a readable form. Loggers are named entities that you create through a static method call to the Logger class, like so:
import java.util.logging.Logger; //. . . Logger l = Logger.getLogger(getClass().getName());
When calling the getLogger() method, you pass it a String. For now, just get in the habit of passing the name of the class the code you're writing is located in. From any regular (that is, nonstatic) method, the code above will always reference the name of the class and pass that to the Logger. If you are making a Logger call inside of a static method, just reference the name of the class you're inside of:
Logger l = Logger.getLogger(Person.class.getName());
Page 25 of 57
developerWorks
ibm.com/developerWorks/
In this example, the code you're inside of is the Person class, so you reference a special literal called class that retrieves the Class object (more on this later) and gets its Name attribute. This tutorial's Writing good Java code section includes a tip on how not to do logging.
is a handy method to have because it gives you a quick test harness for the class. In enterprise development, you would use test libraries, but for the purpose of this tutorial, you will use main() as your test harness.
main()
Go into the Eclipse source code editor for Person and add code to make it look like Listing 4. Eclipse has a handy code generator to generate getters and setters (among other things). To try it out, put your mouse caret on the Person class definition (that is, on the word Person in the class definition), and go to Source > Generate Getters and Setters.... When the dialog box opens, click Select All, as shown in Figure 6:
Page 26 of 57
ibm.com/developerWorks/
developerWorks
For the insertion point, choose Last member and click OK. Notice that the getters and setters appear after the main() method. There's more to main() Now you'll add some code to main() to allow you to instantiate a Person, set a few attributes, and then print those to the console. Start by adding a constructor to Person. Type the code in Listing 6 into your source window just below the top part of the class definition (the line immediately beneath public class Person ()): Listing 6. Person constructor
public Person(String name, int age, int height, int weight, String eyeColor, String gender) { this.name = name; this.age = age; this.height = height; this.weight = weight; this.eyeColor = eyeColor; this.gender = gender; }
developerWorks
ibm.com/developerWorks/
Next, go into the main() method and make it look like Listing 7: Listing 7. The main() method
public static void main(String[] args) { Person p = new Person("Joe Q Author", 42, 173, 82, "Brown", "MALE"); Logger l = Logger.getLogger(Person.class.getName()); l.info("Name: " + p.getName()); l.info("Age:" + p.getAge()); l.info("Height (cm):" + p.getHeight()); l.info("Weight (kg):" + p.getWeight()); l.info("Eye Color:" + p.getEyeColor()); l.info("Gender:" + p.getGender()); }
Don't worry about the Logger class for now. Just enter the code as you see it in Listing 7. You're now ready to run your first Java program.
Notice that the Console view opens automatically and shows Logger output. I have also selected the Outline view in the left-hand pane, which reveals the basic structure of the Person class at a glance.
Introduction to Java programming, Part 1: Java language basics Page 28 of 57
ibm.com/developerWorks/
developerWorks
Accessor methods
In order to encapsulate a class's data from other objects, you declare its variables to be private and then provide accessor methods. As you know, a getter is an accessor method for retrieving the value of an attribute; a setter is an accessor method for modifying that value. The naming of accessors follows a strict convention known as the JavaBeans pattern, whereby any attribute Foo has a getter called getFoo() and a setter called setFoo(). The JavaBeans pattern is so common that support for it is built right into the Eclipse IDE. You've even already seen it in action when you generated getters and setters for Person in the preceding section. Accessors follow these guidelines: The attribute itself is always declared with private access. The access specifier for getters and setters is public. Getters don't take any parameters and return a value whose type is the same as the attribute it accesses. Settings only take one parameter, of the type of the attribute, and do not return a value. Declaring accessors By far the easiest way to declare accessors is to let Eclipse do it for you, as shown back in Figure 6. But you should also know how to hand-code a getter-and-setter pair. Suppose you have an attribute, Foo, whose type is java.lang.String. A complete declaration for it (following the accessor guidelines) would be:
private String foo; public String getFoo() { return foo; } public void setFoo(String value) { foo = value; }
You may notice right away that the parameter value passed to the setter is named differently than if it had been generated by Eclipse. This is my own convention, and
Introduction to Java programming, Part 1: Java language basics Page 29 of 57
developerWorks
ibm.com/developerWorks/
one I recommend to other developers. On the rare occasion that I do hand-code a setter, I always use the name value as the parameter value to the setter. This eyecatcher reminds that I've hand-coded the setter. Because I usually allow Eclipse to generate getters and setters for me, when I don't there's a good reason for it. Using value as the setter's parameter value reminds me that this setter is special. (Code comments could also do that.)
Calling methods
Invoking, or calling, methods is easy. You saw in Listing 7 how to invoke the various getters of Person to return their values. Now I'll formalize the mechanics of making method calls. Method invocation with and without parameters To invoke a method on an object, you need a reference to that object. Methodinvocation syntax comprises the object reference, a literal dot, the method name, and any parameters that need to be passed:
objectReference.someMethod(); objectReference.someOtherMethod(parameter);
And here is a method invocation with parameters (accessing the Name attribute of Person):
Person p = new Person("Joe Q Author", 42, 173, 82, "Brown", "MALE");
Remember that constructors are methods, too. And you can separate the parameters with spaces and newlines. The Java compiler doesn't care. These next two method invocations are identical:
new Person("Joe Q Author", 42, 173, 82, "Brown", "MALE"); new Person("Joe Q Author", 42, 173, 82, "Brown", "MALE");
Page 30 of 57
ibm.com/developerWorks/
developerWorks
Here you are passing the return value of Person.class.getName() to the getLogger() method. Remember that the getLogger() method call is a static method call, so its syntax differs slightly. (You don't need a Logger reference to make the invocation; instead, you just use the name of the class itself as the left-hand side of the invocation.) That's really all there is to method invocation.
Strings
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 (back in Listing 5), but there are several other ways to do that. Here are a couple of ways to create a String instance with a value of hello:
String greeting = "hello"; greeting = new String("hello");
Because Strings are first-class objects in the Java language, 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. Concatenating strings You can do many things with String, and the class has many helpful methods. Without even using a method, you've already done something interesting with two Strings by concatenating, or combining, them:
l.info("Name: " + p.getName());
The plus (+) sign is shorthand for concatenating Strings in the Java language. (There is a performance penalty for doing this type of concatenation inside a loop, but for now you don't need to worry about it.)
Introduction to Java programming, Part 1: Java language basics Page 31 of 57
ibm.com/developerWorks/
Let's try concatenating Strings inside of the Person class. At this point, you have a name instance variable, but it would be nice to have a firstName and lastName. You could then concatenate them when another object requests Person's full name. The first thing you need to do is add the new instance variables (at the same location in the source code where name is currently defined):
//private String name; private String firstName; private String lastName;
You don't need name anymore; you've replaced it with firstName and lastName. Chaining method calls Now you can generate getters and setters for firstName and lastName (as shown back in Figure 6), remove the setName() method, and change getName() to look like this:
public String getName() { return firstName.concat(" ").concat(lastName); }
This code illustrates chaining of method calls. This is a technique commonly used with immutable objects like String, where a modification to an immutable object always returns the modification (but doesn't change the original). You then operate on the returned, changed value.
Operators
As you might expect, the Java language can do arithmetic, and you've already seen how to assign variables. Now I'll give you a brief look at some of the Java language operators you'll need as your skills improve. The Java language uses two types of operators: Unary: Only one operand is needed. Binary: Two operands are needed. The Java language's arithmetic operators are summarized in Table 2: Table 2. Java language's arithmetic operators
Operator + Usage a + b Description Adds a and b + +a Promotes a
Page 32 of 57
ibm.com/developerWorks/
developerWorks
to int if it's a byte , short , or char a - b Subtracts b from a * -a a * b Arithmetically negates a Multiplies a and b / a / b Divides a by b % a % b Returns the remainder of dividing a by b (the modulus operator) ++ 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 decrementing ---a Decrements
Page 33 of 57
developerWorks
a
ibm.com/developerWorks/
by 1; computes the value of a after decrementing += -= *= %= a += b a -= b a *= b a %= b Shorthand for a = a + b Shorthand for a = a - b Shorthand for a = a * b Shorthand for a = a % b
Additional operators In addition to the operators in Table 2, you've seen several other symbols that are called operators in the Java language. For instance: Period (.), which qualifies names of packages and invokes methods Parentheses (()), which delimit a comma-separated list of parameters to a method new, which (when followed by a constructor name) instantiates an object The Java language syntax also includes a number of operators that are used specifically for conditional programming; that is, programs that respond differently based on different input. You'll look at those in the next section.
ibm.com/developerWorks/
developerWorks
Page 35 of 57
developerWorks
is not evaluated) ! !a a is false & a & b a and b are both true, always evaluates b | a | b a or b is true, always evaluates b ^ a ^ b a and b are different
ibm.com/developerWorks/
The if statement
Now that you have a bunch of operators, it's time to use them. This code shows what happens when you add some logic to the Person object's getHeight() accessor:
public int getHeight() { int ret = height; // If locale of the machine this code is running on is U.S., if (Locale.getDefault().equals(Locale.US)) ret /= 2.54;// convert from cm to inches return ret; }
If the current locale is in the United States (where the metric system is not in use), then it might make sense to convert the internal value of height (in centimeters) to inches. This example illustrates the use of the if statement, which evaluates a boolean expression inside parentheses. If that expression evaluates to true, it executes the next statement. In this case, you only need to execute one statement if the Locale of the machine the code is running on is Locale.US. If you need to execute more than one statement, you can use curly braces to form a compound statement. A compound statement groups many statements into one and compound statements can also contain other compound statements.
Page 36 of 57
ibm.com/developerWorks/
developerWorks
Variable scope
Every variable in a Java application has scope, or localized namespace, where you can access it by name within the code. Outside that space the variable is out of scope, and you will get a compile error if you try to access it. Scope levels in the Java language are defined by where a variable is declared, as shown in Listing 8: Listing 8. Variable scope
public class SomeClass { private String someClassVariable; public void someMethod(String someParameter) { String someLocalVariable = "Hello"; if (true) { String someOtherLocalVariable = "Howdy"; } someClassVariable = someParameter; // legal someLocalVariable = someClassVariable; // also legal someOtherLocalVariable = someLocalVariable;// Variable out of scope! } public void someOtherMethod() { someLocalVariable = "Hello there";// That variable is out of scope! } }
Within SomeClass, someClassVariable is accessible by all instance (that is, nonstatic) methods. Within someMethod, someParameter is visible, but outside of that method it is not, and the same is true for someLocalVariable. Within the if block, someOtherLocalVariable is declared, and outside of that if block it is out of scope. Scope has many rules, but Listing 8 shows the most common ones. Take a few minutes to familiarize yourself with them.
The else statement works the same way as if, in that it executes only the next statement it runs across. In this case, two statements are grouped into a compound statement (notice the curly braces), which the program then executes. You can also use else to perform an additional if check, like so:
Introduction to Java programming, Part 1: Java language basics Page 37 of 57
developerWorks
if (conditional) { // Block 1 } else if (conditional2) { // Block 2 } else if (conditional3) { // Block 3 } else { // Block 4 } // End
ibm.com/developerWorks/
If conditional evaluates to true, then Block 1 is executed and the program jumps to the next statement after the final curly brace (which is indicated by // End). If conditional does not evaluate to true, then conditional2 is evaluated. If it is true, then Block 2 is executed, and the program jumps to the next statement after the final curly brace. If conditional2 is not true, then the program moves on to conditional3, and so on. Only if all three conditionals failed would Block 4 be executed.
If conditional evaluates to true, then statementIfTrue is executed; otherwise, statementIfFalse is executed. Compound statements are not allowed for either statement. The ternary operator comes in handy when you know you will need to execute one statement as the result of the conditional evaluating to true, and another if it does not. Ternary operators are most often used to initialize a variable (like a return value), like so:
public int getHeight() { return (gender.equals("MALE")) ? (height + 2) : height; }
The parentheses following the question mark above aren't strictly required, but they do make the code more readable.
ibm.com/developerWorks/
developerWorks
about two constructs used to iterate over code or execute it more than once: for loops and while loops.
What is a loop?
A loop is a programming construct that executes repeatedly while some condition (or set of conditions) is met. For instance, you might ask a program to read all records until the end of a file, or loop over all the elements of an array, processing each one. (You'll learn about arrays in this tutorial's Java Collections section.)
for
loops
The basic loop 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 a loop. The abstract syntax for a for loop is:
for (initialization; loopWhileTrue; executeAtBottomOfEachLoop) { statementsToExecute }
At the beginning of the loop, the initialization statement is executed (multiple initialization statements can be separated by commas). So long as loopWhileTrue (a Java conditional expression that must evaluate to either true or false) is true, the loop will be executed. At the bottom of the loop, executeAtBottomOfEachLoop is executed. Example of a for loop If you wanted to change a main() method to execute three times, you could use a for loop, as shown in Listing 9: Listing 9. A for loop
public static void main(String[] args) { Logger l = Logger.getLogger(Person.class.getName()); for (int aa = 0; aa < 3; aa++) { Person p = new Person("Joe Q Author", 42, 173, 82, "Brown", "MALE"); l.info("Loop executing iteration# " + aa); l.info("Name: " + p.getName()); l.info("Age:" + p.getAge()); l.info("Height (cm):" + p.getHeight()); l.info("Weight (kg):" + p.getWeight()); l.info("Eye Color:" + p.getEyeColor()); l.info("Gender:" + p.getGender()); } }
The local variable aa is initialized to zero at the beginning of the listing. This statement executes only once, when the loop is initialized. The loop then continues three times, and each time aa is incremented by one. As you will see later, an alternate for loop syntax is available for looping over constructs that implement the Iterable interface (such as arrays and other Java utility classes). For now, just note the use of the for loop syntax in Listing 9.
Introduction to Java programming, Part 1: Java language basics Page 39 of 57
developerWorks
ibm.com/developerWorks/
while
loops
As you might suspect, while loopWhileTrue evaluates to true, so the loop will execute. At the top of each iteration (that is, before any statements execute), the condition is evaluated. If true, the loop executes. So it is possible that a while loop will never execute if its conditional expression is not true at least once. Look again at the for loop in Listing 9. For comparison, Listing 10 codes it using a while loop: Listing 10. A while loop
public static void main(String[] args) { Logger l = Logger.getLogger(Person.class.getName()); int aa = 0; while (aa < 3) { Person p = new Person("Joe Q Author", 42, 173, 82, "Brown", "MALE"); l.info("Loop executing iteration# " + aa); l.info("Name: " + p.getName()); l.info("Age:" + p.getAge()); l.info("Height (cm):" + p.getHeight()); l.info("Weight (kg):" + p.getWeight()); l.info("Eye Color:" + p.getEyeColor()); l.info("Gender:" + p.getGender()); aa++; }
As you can see, a while loop requires a bit more housekeeping than a for loop. You must initialize the aa variable and also remember to increment it at the bottom of the loop.
do...while
loops
If you want a loop that will always execute once and then check its conditional expression, try using a do...while loop, as shown in Listing 11: Listing 11. A do...while loop
int aa = 0; do { Person p = new Person("Joe Q Author", 42, 173, 82, "Brown", "MALE"); l.info("Loop executing iteration# " + aa); l.info("Name: " + p.getName()); l.info("Age:" + p.getAge()); l.info("Height (cm):" + p.getHeight()); l.info("Weight (kg):" + p.getWeight()); l.info("Eye Color:" + p.getEyeColor()); l.info("Gender:" + p.getGender()); aa++; } while (aa < 3);
Page 40 of 57
ibm.com/developerWorks/
developerWorks
The conditional expression (aa < 3) is not checked until the end of the loop.
Loop branching
There are times when you need to bail out of a loop before the conditional expression evaluates to false. This could happen if you were searching an array of Strings for a particular value, and once you found it, you didn't care about the other elements of the array. For those times when you just want to bail, the Java language provides the break statement, as shown in Listing 12: Listing 12. A break statement
public static void main(String[] args) { Logger l = Logger.getLogger(Person.class.getName()); int aa = 0; while (aa < 3) { if (aa == 1) break; Person p = new Person("Joe Q Author", 42, 173, 82, "Brown", "MALE"); l.info("Loop executing iteration# " + aa); l.info("Name: " + p.getName()); l.info("Age:" + p.getAge()); l.info("Height (cm):" + p.getHeight()); l.info("Weight (kg):" + p.getWeight()); l.info("Eye Color:" + p.getEyeColor()); l.info("Gender:" + p.getGender()); aa++; }
The break statement takes you to the very next executable statement outside of the loop in which it's located.
Loop continuation
In the (simplistic) example in Listing 12, you only want to execute the loop once and bail. You can also skip a single iteration of a loop but continue executing the loop. For that, you need the continue statement, shown in Listing 13: Listing 13. A continue statement
public static void main(String[] args) { Logger l = Logger.getLogger(Person.class.getName()); int aa = 0; while (aa < 3) { if (aa == 1) continue; else aa++; Person p = new Person("Joe Q Author", 42, 173, 82, "Brown", "MALE"); l.info("Loop executing iteration# " + aa); l.info("Name: " + p.getName()); l.info("Age:" + p.getAge()); l.info("Height (cm):" + p.getHeight()); l.info("Weight (kg):" + p.getWeight()); l.info("Eye Color:" + p.getEyeColor()); l.info("Gender:" + p.getGender()); }
Page 41 of 57
developerWorks
ibm.com/developerWorks/
In Listing 13, you skip the second iteration of a loop but continued to the third. continue comes in handy when you are, say, processing records and come across a record you definitely don't want to process. Just skip that record and move on to the next one.
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.
Note: The square brackets in this section's code examples are part of the required syntax for Java Collections, not indicators of optional elements.
You can declare an array in one of two ways: Create it with a certain size, which is fixed for the life of the array. 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 values, and its size is fixed for the life of the array. Declaring an array In general, you declare an array like this:
new elementType [arraySize]
There are two ways to create an integer array of elements. This statement creates an array with space for five elements, but it is empty:
// creates an empty array of 5 elements: int[] integers = new int[5];
Page 42 of 57
ibm.com/developerWorks/
developerWorks
The initial values go between the curly braces and are separated by commas. Arrays the hard way A harder way to create an array would be to create it and then code a loop to initialize it:
int[] integers = new int[5]; for (int aa = 0; aa < integers.length; aa++) { integers[aa] = aa; }
This code declares the integer array of five elements. If you try to put more than five elements in the array, the Java runtime will complain and throw an exception. You'll learn about exceptions and how to handle them in Part 2. Loading an array To load the array, you loop through the integers from 1 through the length of the array (which you get by calling .length on the array more about that in a minute). In this case, you stop when you hit 5. Once the array is loaded, you can access it as before:
Logger l = Logger.getLogger("Test"); for (int aa = 0; aa < integers.length; aa++) { l.info("This little integer's value is: " + integers[aa]); }
I find the newer syntax simpler to work with, and I'll use it throughout this section. The element index Think of an array as a series of buckets, and into each bucket goes an element of a certain type. Access to each bucket is gained using an index:
element = arrayName [elementIndex];
To access an element, you need the reference to the array (its name) and the index where the element you want resides. The length method A handy method, as you've already seen, is length. It's a built-in method, so its syntax doesn't include the usual parentheses. Just type the word length and it will return as you would expect the size of the array.
Introduction to Java programming, Part 1: Java language basics Page 43 of 57
developerWorks
ibm.com/developerWorks/
Arrays in the Java language are zero-based. So, for some array named array, the first element in the array always resides at array[0], and the last resides at array[array.length - 1]. An array of objects You've seen how arrays can hold primitive types, but it's worth mentioning that they can also hold objects. In that sense, the array is the Java language's most utilitarian collection. Creating an array of java.lang.Integer objects isn't much different from creating an array of primitive types. Once again, you have two ways to do it:
// creates an empty array of 5 elements: Integer[] integers = new Integer[5]; // creates an array of 5 elements with values: Integer[] integers = new Integer[] { Integer.valueOf(1), Integer.valueOf(2) Integer.valueOf(3) Integer.valueOf(4) Integer.valueOf(5));
Each JDK class provides methods to parse and convert from its internal representation to a corresponding primitive type. For example, this code converts the decimal value 238 to an Integer:
int value = 238; Integer boxedValue = Integer.valueOf(value);
This technique is known as boxing, because you're putting the primitive into a wrapper, or box.
Introduction to Java programming, Part 1: Java language basics Page 44 of 57
ibm.com/developerWorks/
developerWorks
Similarly, to convert the Integer representation back to its int counterpart, you would unbox it, like so:
Integer boxedValue = Integer.valueOf(238); int intValue = boxedValue.intValue();
Autoboxing and auto-unboxing Strictly speaking, you don't need to box and unbox primitives explicitly. Instead, you could use the Java language's autoboxing and auto-unboxing features, like so:
int intValue = 238; Integer boxedValue = intValue; // intValue = boxedValue;
I recommend that you avoid autoboxing and auto-unboxing, however, because it can lead to code problems. The code in the boxing and unboxing snippets is more obvious, and thus more readable, than the autoboxed code, and I believe that's worth the extra effort. Parsing and converting boxed types You've seen how to obtain a boxed type, but what about parsing a String you suspect has a boxed type into its proper box? The JDK wrapper classes have methods for that, too:
String characterNumeric = "238"; Integer convertedValue = Integer.parseInt(characterNumeric);
You can also convert the contents of a JDK wrapper type to a String:
Integer boxedValue = Integer.valueOf(238); String characterNumeric = boxedValue.toString();
Note that when you use the concatenation operator in a String expression (you've already seen this in calls to Logger), the primitive type is autoboxed, and wrapper types automatically have toString() invoked on them. Pretty handy.
Lists
A List is a collection construct that is by definition an ordered collection, also known as a sequence. Because a List is ordered, you have complete control over where in the List items go. A Java List collection can only hold objects, and it defines a strict contract about how it behaves. is an interface, so you can't instantiate it directly. You'll work with its most commonly used implementation, ArrayList:
List
List<Object> listOfObjects = new ArrayList<Object>();
Page 45 of 57
developerWorks
ibm.com/developerWorks/
Note that we have assigned the ArrayList object to a variable of type List. Java programming allows you to assign a variable of one type to another so long as the variable being assigned to is a superclass or interface implemented by the variable being assigned from. We will look more at how variable assignments are affected in Part 2 in the Inheritance section. Formal type What's with the <Object> in the above code snip? It's called the formal type, and it tells the compiler that this List contains a collection of type Object, which means you can pretty much put whatever you like in the List. If you wanted to tighten up the constraints on what could or could not go into the List, you'd define it differently:
List<Person> listOfPersons = new ArrayList<Person>();
Now your List can only hold Person instances. Using Lists Using Lists is super easy, like Java collections in general. Here are some of the things you will want to do with Lists: Put something in the List. Ask the List how big it currently is. Get something out of the List. Let's try some of these out. You've already seen how to create an instance of List by instantiating its ArrayList implementation type, so you'll start from there. To put something in a List, call the add() method:
List<Integer> listOfIntegers = new ArrayList<Integer>(); listOfIntegers.add(Integer.valueOf(238));
The add() method adds the element to the end of the List. To ask the List how big it is, call size():
List<Integer> listOfIntegers = new ArrayList<Integer>(); listOfIntegers.add(Integer.valueOf(238)); Logger l = Logger.getLogger("Test"); l.info("Current List size: " + listOfIntegers.size());
To retrieve an item from the List, call get() and pass it the index of the item you want:
List<Integer> listOfIntegers = new ArrayList<Integer>(); listOfIntegers.add(Integer.valueOf(238)); Logger l = Logger.getLogger("Test"); l.info("Item at index 0 is: " listOfIntegers.get(0));
Page 46 of 57
ibm.com/developerWorks/
developerWorks
In a real-world application, a List would contain records, or business objects, and you would possibly want to look over them all as part of your processing. How do you do that in a generic fashion? You want to iterate over the collection, which you can do because List implements the java.lang.Iterable interface. (You'll learn about interfaces in Part 2.)
Iterable
If a collection implements java.lang.Iterable, it is called an iterable collection. That means you can start at one end and walk through the collection item-by-item until you run out of items. You've already seen the special syntax for iterating over collections that implement the Iterable interface, in the Loops section. Here it is again:
for (objectType varName : collectionReference) { // Start using objectType (via varName) right away... }
Iterating over a List That previous example was abstract; now here's a more realistic one:
List<Integer> listOfIntegers = obtainSomehow(); Logger l = Logger.getLogger("Test"); for (Integer i : listOfIntegers) { l.info("Integer value is : " + i); }
That little code snip does the same thing as this longer one:
List<Integer> listOfIntegers = obtainSomehow(); Logger l = Logger.getLogger("Test"); for (int aa = 0; aa < listOfIntegers.size(); aa++) { Integer I = listOfIntegers.get(aa); l.info("Integer value is : " + i); }
The first snip uses shorthand syntax: there is no index variable (aa in this case) to initialize, and no call to the List's get() method. Because List extends java.util.Collection, which implements Iterable, you can use the shorthand syntax to iterate over any List.
Sets
A Set is a collections construct that by definition contains unique elements that is, no duplicates. Whereas a List can contain the same object hundreds of times, a Set can only contain a given instance once. A Java Set collection can only hold objects, and it defines a strict contract about how it behaves.
Introduction to Java programming, Part 1: Java language basics Page 47 of 57
developerWorks
ibm.com/developerWorks/
Because Set is an interface, you can't instantiate it directly, so I'll show you one of my favorite implementations: HashSet. HashSet is easy to use and is similar to List. Here are some things you will want to do with a Set: Put something in the Set. Ask the Set how big it currently is. Get something out of the Set. Using Sets A Set's distinguishing attribute is that it guarantees uniqueness among its elements, but doesn't care about the order of the elements. Consider the following code:
Set<Integer> setOfIntegers = new HashSet<Integer>(); setOfIntegers.add(Integer.valueOf(10)); setOfIntegers.add(Integer.valueOf(11)); setOfIntegers.add(Integer.valueOf(10)); for (Integer i : setOfIntegers) { l.info("Integer value is: " + i); }
You might expect that the Set would have three elements in it, but in fact it only has two because the Integer object that contains the value 10 will only be added once. Keep this behavior in mind when iterating over a Set, like so:
Set<Integer> setOfIntegers = new HashSet(); setOfIntegers.add(Integer.valueOf(10)); setOfIntegers.add(Integer.valueOf(20)); setOfIntegers.add(Integer.valueOf(30)); setOfIntegers.add(Integer.valueOf(40)); setOfIntegers.add(Integer.valueOf(50)); Logger l = Logger.getLogger("Test"); for (Integer i : setOfIntegers) { l.info("Integer value is : " + i); }
Chances are the objects will print out in a different order than you added them in because a Set guarantees uniqueness, not order. You'll see this for yourself if you paste the code above into the main() method of your Person class and run it.
Maps
A Map is a handy collection construct because it lets you associate one object (the key) with another (the value). As you might imagine, the key to the Map must be unique, and it's used to retrieve the value at a later time. A Java Map collection can only hold objects, and it defines a strict contract about how it behaves. Because Map is an interface, you can't instantiate it directly, so I'll show you one of my favorite implementations: HashMap. Here are some of the things you will want to do with Maps:
Introduction to Java programming, Part 1: Java language basics Page 48 of 57
ibm.com/developerWorks/
developerWorks
Put something in the Map. Get something out of the Map. Get a Set of keys to the Map for iterating over it. Using Maps To put something into a Map, you need to have an object that represents its key and an object that represents its value:
public Map<String, Integer> createMapOfIntegers() { Map<String, Integer> mapOfIntegers = new HashMap<String, Integer>(); mapOfIntegers.put("1", Integer.valueOf(1)); mapOfIntegers.put("2", Integer.valueOf(2)); mapOfIntegers.put("3", Integer.valueOf(3)); // . . . mapOfIntegers.put("168", Integer.valueOf(168)); }
In this example, Map contains Integers, keyed by a String, which happens to be their String representation. To retrieve a particular Integer value, you need its String representation:
mapOfIntegers = createMapOfIntegers(); Integer oneHundred68 = mapOfIntegers.get("168");
Using Set with Map On occasion, you may find yourself with a reference to a Map, and you simply want to walk over its entire set of contents. In this case, you will need a Set of the keys to the Map:
Set<String> keys = mapOfIntegers.keySet(); Logger l = Logger.getLogger("Test"); for (String key : keys) { Integer value = mapOfIntegers.get(key); l.info("Value keyed by '" + key + "' is '" + value + "'"); }
Note that the toString() method of the Integer retrieved from the Map is automatically called when used in the Logger call. Map doesn't return a List of its keys because the Map is keyed, and each key is unique; uniqueness is the distinguishing characteristic of a Set.
developerWorks
ibm.com/developerWorks/
JARs
The JDK ships with a tool called JAR, which stands for Java Archive. You use this tool to create JAR files. Once you've packaged your code into a JAR file, other developers can simply drop the JAR file into their projects and configure their projects to use your code. Creating a JAR file in Eclipse is a snap. In your workspace, right-click the com.makotogroup.intro package and select Export. You'll see the dialog shown in Figure 8. Choose Java > JAR file. Figure 8. Export dialog box.
When the next dialog box opens, browse to the location where you want to store your JAR file and name the file whatever you'd like. The .jar extension is the default, which I recommend using. Click Finish. You'll see your JAR file in the location you selected. You can use the classes in it from your code if you put it in your build path in Eclipse. Doing that is a snap, too, as you'll see next. Using third-party applications As you grow more comfortable with writing Java applications, you will want to use more and more third-party applications to support your code. For the sake of example, let's say that you wanted to use joda-time, a JDK replacement library for doing date/time handling, manipulations, and calculations.
Introduction to Java programming, Part 1: Java language basics Page 50 of 57
ibm.com/developerWorks/
developerWorks
Let's assume you've already downloaded joda-time, which is stored in a JAR file. To use the classes, your first step is to create a lib directory in your project and drop the JAR file into it: 1. Right-click the Intro root folder in Project Explorer view. 2. Choose New > Folder and call the folder lib. 3. Click Finish. The new folder shows up at the same level as src. Now copy the joda-time .jar file into your new lib directory. For this example, the file is called joda-time-1.6.jar. (It's common in naming a JAR file to include the version number.) Now all you need to do is tell Eclipse to include the classes in the joda-time-1.6.jar file into your project: 1. Right-click the Intro project in your workspace, then select Properties. 2. In the Properties dialog box, select the Libraries tab, as shown in Figure 9: Figure 9. Properties > Java Build Path
3. Click the Add External JARs button, then browse to the project's lib directory, select the joda-time-1.6.jar file, and click OK. Once the code (that is, the class files) in the JAR file have been processed by Eclipse, they are available to reference (import) from your Java code. Notice in
Introduction to Java programming, Part 1: Java language basics Page 51 of 57
developerWorks
ibm.com/developerWorks/
Project Explorer that there is a new Folder called Referenced Libraries that contains the joda-time-1.6.jar file.
ibm.com/developerWorks/
developerWorks
If a method grows beyond one page, I refactor it. Refactoring means changing the design of existing code without changing its results. Eclipse has a wonderful set of refactoring tools. Usually a long method contains subgroups of functionality bunched together. Take this functionality and move it to another method (naming it accordingly) and pass in parameters as needed. Limit each method to a single job. I've found that a method doing only one thing well doesn't usually take more than about 30 lines of code.
Use comments
Please, use comments. The people who follow along behind you (or even you, yourself, six months down the road) will thank you. You may have heard the old adage Well-written code is self-documenting, so who needs comments? I'll give you two reasons why this is false: Most code is not well written. Your code probably isn't as well written as you'd like to think. So, comment your code. Period.
Or this one:
public static void main(String[] args) { }
Why? Well, it's standard, so most code you run across (as in, code you didn't write, but might be paid to maintain) will most likely be written that way. Having said that, Eclipse does allow you to define code styles and format your code any way you like. The main thing is that you pick a style and stick with it.
developerWorks
public void someMethod() { // Do some stuff... // Now tell all about it System.out.println("Telling you all about it:"); // Etc... }
ibm.com/developerWorks/
The Java language's built-in logging facility (refer back to Your first Java object) is a better alternative. I never use System.out.println() in my code, and I suggest you don't use it either.
What's next
In the second half of this tutorial, you will begin learning about some of the more advanced constructs of Java programming, although the overall discussion will still be introductory in scope. Java programming topics covered in that tutorial include: Inheritance and abstraction Interfaces Nested classes Regular expressions Generics Enum types
Page 54 of 57
ibm.com/developerWorks/
developerWorks
I/O Serialization Read "Introduction to Java programming, Part 2: Constructs for real-world applications."
Page 55 of 57
developerWorks
ibm.com/developerWorks/
Resources
Learn Java technology homepage: The official Java site has links to all things related to the Java platform, including the Java language specification and Java API documentation. Java 6: Learn more about JDK 6 and the tools that come with it. Javadoc homepage: Learn the ins and outs of using the Javadoc, including how to use the command-line tool and how to write your own Doclets that let you create custom formats for your documentation. Refactoring: Improving the Design of Existing Code (Martin Fowler et al., Addison-Wesley, 1999): This book is an excellent resource for learning how to write cleaner, more maintainable code. New to Java technology: Check out this compendium of developerWorks resources for beginning Java developers. 5 things you didn't know about ...: This developerWorks series provides short introductions to lesser-known (but often introductory) Java programming tips and lore. "Struts, an open-source MVC implementation" (Malcolm Davis, developerWorks, February 2001): This article introduces the model-viewcontroller design pattern as it is implemented in one of the Java platform's oldest web development frameworks. "Java theory and practice: Garbage collection and performance" (Brian Goetz, developerWorks, January 2004): This article is an overview of garbage collection, including beginner tips for writing GC-friendly classes. Eclipse IDE project resources from developerWorks: Learn what Eclipse is good for, why it is important, how you can get started with it, and where to learn more about it. The Java Tutorials: Get a comprehensive introduction to the Java language. The developerWorks Java technology zone: Hundreds of articles about every aspect of Java programming. Get products and technologies JDK 6: Download JDK 6 from Sun (Oracle). Eclipse: Download the Eclipse IDE for Java Developers. IBM developer kits: IBM provides a number of Java developer kits for use on popular platforms. Discuss Get involved in the My developerWorks community. Connect with other developerWorks users while exploring the developer-driven blogs, forums, groups, and wikis.
Introduction to Java programming, Part 1: Java language basics Page 56 of 57
ibm.com/developerWorks/
developerWorks
Page 57 of 57
In Part 1 of this tutorial, professional Java programmer J. Steven Perry introduced the Java language syntax and libraries you need to write simple Java applications. Part 2, still geared toward developers new to Java application development, introduces the more-sophisticated programming constructs required for building complex, real-world Java applications. Topics covered include exception handling, inheritance and abstraction, regular expressions, generics, Java I/O, and Java serialization. View more content in this series
developerWorks
ibm.com/developerWorks/
Objectives
The Java language is mature and sophisticated enough to help you accomplish nearly any programming task. In this tutorial, you'll be introduced to features of the Java language that you will need to handle complex programming scenarios, including: Exception handling Inheritance and abstraction Interfaces Nested classes Regular expressions Generics enum types I/O Serialization
Prerequisites
Develop skills on this topic
This content is part of a progressive knowledge path for advancing your skills. See Become a Java developer
The content of this tutorial is geared toward programmers new to the Java language who are unfamiliar with its more-sophisticated features. The tutorial assumes that you have worked through "Introduction to Java programming, Part 1: Java language basics" in order to: Gain an understanding of the basics of OOP on the Java platform. Set up the development environment for the tutorial examples. Begin the programming project that you will continue developing in Part 2.
System requirements
The exercises in this tutorial require a development environment consisting of: JDK 6 from Sun/Oracle Eclipse IDE for Java Developers Download and installation instructions for both are included in Part 1. The recommended system configuration for this tutorial is: A system supporting JDK 6 with at least 1GB of main memory. Java 6 is supported on Linux, Windows, and Solaris. At least 20MB of disk space to install the software components and examples covered.
Page 2 of 53
ibm.com/developerWorks/
developerWorks
Overloading methods
When you create two methods with the same name but with different argument lists (that is, different numbers or types of parameters), you have an overloaded method. Overloaded methods are always in the same class. At run time, the Java Runtime Environment (JRE; also known as the Java runtime) decides which variation of your overloaded method to call based on the arguments that have been passed to it. Suppose that Person needs a couple of methods to print an audit of its current state. I'll call those methods printAudit(). Paste the overloaded method in Listing 1 into the Eclipse editor view: Listing 1. printAudit(): An overloaded method
public void printAudit(StringBuilder buffer) { buffer.append("Name="); buffer.append(getName()); buffer.append(","); buffer.append("Age="); buffer.append(getAge()); buffer.append(","); buffer.append("Height="); buffer.append(getHeight()); buffer.append(","); buffer.append("Weight="); buffer.append(getWeight()); buffer.append(","); buffer.append("EyeColor="); buffer.append(getEyeColor()); buffer.append(","); buffer.append("Gender="); buffer.append(getGender()); } public void printAudit(Logger l) { StringBuilder sb = new StringBuilder(); printAudit(sb); l.info(sb.toString()); }
In this case, you have two overloaded versions of printAudit(), and one actually uses the other. By providing two versions, you give the caller a choice of how to print an audit of the class. Depending on the parameters that are passed, the Java runtime will call the correct method. Two rules of method overloading Remember these two important rules when using overloaded methods: You can't overload a method just by changing its return type. You can't have two methods with the same parameter list. If you violate these rules, the compiler will give you an error.
Introduction to Java programming, Part 2: Constructs for real-world applications Page 3 of 53
developerWorks
ibm.com/developerWorks/
Overriding methods
When a subclass of another class provides its own implementation of a method defined on a parent class, that's called method overriding. In order to see how method overriding is useful, you need to do some work on your Employee class. Once you have it set up, I'll be able to show you where method overriding comes in handy. Employee: A subclass of Person Recall from Part 1 of this tutorial that Employee might be a subclass (or child) of Person that has some additional attributes: Taxpayer identification number Employee number Hire date Salary
To declare such a class in a file called Employee.java, right-click the com.makotogroup.intro package in Eclipse. Choose New > Class..., and the New Java Class dialog box will open, as shown in Figure 1: Figure 1. New Java Class dialog
Page 4 of 53
ibm.com/developerWorks/
developerWorks
Type Employee as the name of the class and Person as its superclass, then click Finish. You will see the Employee class in an edit window. You don't explicitly need to declare a constructor, but go ahead and implement both constructors anyway. First, make sure the Employee class edit window has the focus, then go to Source > Generate Constructors from Superclass..., and you'll see a dialog that looks like Figure 2: Figure 2. Generate Constructors from Superclass dialog
Check both constructors (as shown in Figure 2), and click OK. Eclipse will generate the constructors for you. You should now have an Employee class like the one in Listing 2: Listing 2. The new, improved Employee class
package com.makotogroup.intro; public class Employee extends Person { public Employee() { super(); // TODO Auto-generated constructor stub } public Employee(String name, int age, int height, int weight, String eyeColor, String gender) { super(name, age, height, weight, eyeColor, gender); // TODO Auto-generated constructor stub } }
Page 5 of 53
ibm.com/developerWorks/
inherits the attributes and behavior of its parent, Person, and also has some of its own, as you can see in Listing 3:
Employee
Method overriding: printAudit() Now, as promised, you're ready for an exercise in overriding methods. You'll override the printAudit() method (see Listing 1) that you used to format the current state of a Person instance. Employee inherits that behavior from Person, and if you instantiate Employee, set its attributes, and invoke one of the overloads of printAudit(), the call will succeed. However, the audit that is produced won't fully represent an Employee. The problem is that it cannot format the attributes specific to an Employee, because Person doesn't know about them. The solution is to override the overload of printAudit() that takes a StringBuilder as a parameter and add code to print the attributes specific to Employee. To do this in your Eclipse IDE, go to Source > Override/Implement Methods..., and you'll see a dialog box that looks like Figure 3:
Page 6 of 53
ibm.com/developerWorks/
developerWorks
Select the StringBuilder overload of printAudit, as shown in Figure 3, and click OK. Eclipse will generate the method stub for you, and then you can just fill in the rest, like so:
@Override public void printAudit(StringBuilder buffer) { // Call the superclass version of this method first to get its attribute values super.printAudit(buffer); // Now format this instance's values buffer.append("TaxpayerIdentificationNumber="); buffer.append(getTaxpayerIdentificationNumber()); buffer.append(","); buffer.append("EmployeeNumber="); buffer.append(getEmployeeNumber()); buffer.append(","); buffer.append("Salary="); buffer.append(getSalary().setScale(2).toPlainString()); }
Notice the call to super.printAudit(). What you're doing here is asking the (Person) superclass to exhibit its behavior for printAudit(), and then you augment it with Employee-type printAudit() behavior. The call to super.printAudit() doesn't need to be first; it just seemed like a good idea to print those attributes first. In fact, you don't need to call super.printAudit() at all. If you don't call it, you must either format the attributes from Person yourself (in the Employee.printAudit() method), or exclude them altogether. Making the call to super.printAudit(), in this case, is easier.
Page 7 of 53
developerWorks
ibm.com/developerWorks/
Class members
The variables and methods you have on Person and Employee are instance variables and methods. To use them you either must instantiate the class you need or have a reference to the instance. Every object instance has variables and methods, and for each one the exact behavior (for example, what's generated by calling printAudit()) will be different, because it is based on the state of the object instance. Classes themselves can also have variables and methods, which are called class members. You declare class members with the static keyword introduced in Part 1 of this tutorial. The differences between class members and instance members 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. Adding class variables and methods When does it make sense to add class variables and methods? The best rule of thumb is to do so rarely, so that you don't overuse them. That said, it's a good idea to use class variables and methods: To declare constants that any instance of the class can use (and whose value is fixed at development time). To track "counters" of instances of the class. On a class with utility methods that don't ever need an instance of the class (such as Logger.getLogger()).
Class variables
To create a class variable, use the static keyword when you declare it:
accessSpecifier static variableName [= initialValue];
Note: The square brackets here indicate that their contents are optional. They are not part of the declaration syntax. The JRE creates space in memory to store each of a class's instance variables for every instance of that class. In contrast, the JRE creates only a single copy of each class variable, regardless of the number of instances. It does so the first time the class is loaded (that is, the first time it encounters the class in a program). All instances of the class will share that single copy of the variable. That makes class variables a good choice for constants that all instances should be able to use. For example, you declared the Gender attribute of Person to be a String, but you didn't put any constraints around it. Listing 4 shows a common use of class variables:
Introduction to Java programming, Part 2: Constructs for real-world applications Page 8 of 53
ibm.com/developerWorks/
developerWorks
Declaring constants Typically, constants are: Named in all uppercase. Named as multiple words, separated by underscores. Declared final (so that their values cannot be modified). Declared with a public access specifier (so that they can be accessed by other classes that need to reference their values by name).
In Listing 4, to use the constant for MALE in the Person constructor call, you would simply reference its name. To use a constant outside of the class, you'd preface it with the name of the class where it was declared, like this:
String genderValue = Person.GENDER_MALE;
Class methods
If you've been following along since Part 1, you've already called the static method Logger.getLogger() several times whenever you've retrieved a Logger instance to write some output to the console. Notice, though, that you didn't need an instance of Logger to do this; instead, you referenced the Logger class itself. This is the syntax for making a class method call. As with class variables, the static keyword identifies Logger (in this example) as a class method. Class methods are also sometimes called static methods for this reason. Using class methods Now you'll combine what you've learned about static variables and methods to create a static method on Employee.You'll declare a private static final variable to hold a Logger, which all instances will share, and which will be accessible by calling getLogger() on the Employee class. Listing 5 shows how:
Page 9 of 53
ibm.com/developerWorks/
public class Employee extends Person { private static final Logger logger = Logger.getLogger(Employee.class.getName()); //. . . public static Logger getLogger() { return logger; } }
Two important things are happening in Listing 5: The Logger instance is declared with private access, so no class outside Employee can access the reference directly. The Logger is initialized when the class is loaded; this is because you use the Java initializer syntax to give it a value. To retrieve the Employee class's Logger object, you make the following call:
Logger employeeLogger = Employee.getLogger();
Comparing objects
The Java language provides two ways to compare objects: The == operator The equals() method Comparing objects with == The == syntax compares objects for equality such that a == b returns true only if a and b have the same value. For objects, this means that the two refer to the same object instance. For primitives, it means that the values are identical. Consider the example in Listing 6: Listing 6. Comparing objects with ==
int int1 = 1; int int2 = 1; l.info("Q: int1 == int2? A: " + (int1 == int2));
Integer integer1 = Integer.valueOf(int1); Integer integer2 = Integer.valueOf(int2); l.info("Q: Integer1 == Integer2? A: " + (integer1 == integer2)); integer1 = new Integer(int1); integer2 = new Integer(int2); l.info("Q: Integer1 == Integer2?
Employee employee1 = new Employee(); Employee employee2 = new Employee(); l.info("Q: Employee1 == Employee2? A: " + (employee1 == employee2));
If you run the Listing 6 code inside Eclipse, the output should be:
Introduction to Java programming, Part 2: Constructs for real-world applications Page 10 of 53
ibm.com/developerWorks/
developerWorks
Apr 19, 2010 5:30:10 AM com.makotogroup.intro.Employee INFO: Q: int1 == int2? A: true Apr 19, 2010 5:30:10 AM com.makotogroup.intro.Employee INFO: Q: Integer1 == Integer2? A: true Apr 19, 2010 5:30:10 AM com.makotogroup.intro.Employee INFO: Q: Integer1 == Integer2? A: false Apr 19, 2010 5:30:10 AM com.makotogroup.intro.Employee INFO: Q: Employee1 == Employee2? A: false
In the first case in Listing 6, the values of the primitives are the same, so the == operator returns true. In the second case, the Integer objects refer to the same instance, so again == returns true. In the third case, even though the Integer objects wrap the same value, == returns false because integer1 and integer2 refer to different objects. Based on this, it should be clear why employee1 == employee2 returns false. Comparing objects with equals() equals() is a method that every Java language object gets for free, because it is defined as an instance method of java.lang.Object (which every Java object inherits from). You call equals() just as you would any other method:
a.equals(b);
This statement invokes the equals() method of object a, passing to it a reference to object b. By default a Java program would simply check to see if the two objects were the same using the == syntax. Because equals() is a method, however, it can be overridden. Consider the example from Listing 6, modified in Listing 7 to compare the two objects using equals(): Listing 7. Comparing objects with equals()
Logger l = Logger.getLogger(Employee.class.getName()); Integer integer1 = Integer.valueOf(1); Integer integer2 = Integer.valueOf(1); l.info("Q: integer1 == integer2? l.info("Q: integer1.equals(integer2)? integer1 = integer2 = l.info("Q: l.info("Q: new Integer(integer1); new Integer(integer2); integer1 == integer2? integer1.equals(integer2)?
Employee employee1 = new Employee(); Employee employee2 = new Employee(); l.info("Q: employee1 == employee2 ? A: " + (employee1 == employee2)); *l.info("Q: employee1.equals(employee2) ? A : " + employee1.equals(employee2));* Running this code produces: Apr 19, 2010 5:43:53 AM com.makotogroup.intro.Employee main INFO: Q: integer1 == integer2? A: true Apr 19, 2010 5:43:53 AM com.makotogroup.intro.Employee main INFO: Q: integer1.equals(integer2)? A: true Apr 19, 2010 5:43:53 AM com.makotogroup.intro.Employee main
Page 11 of 53
developerWorks
INFO: Q: integer1 == integer2? A: false Apr 19, 2010 5:43:53 AM com.makotogroup.intro.Employee main INFO: Q: integer1.equals(integer2)? A: true Apr 19, 2010 5:43:53 AM com.makotogroup.intro.Employee main INFO: Q: employee1 == employee2? A: false Apr 19, 2010 5:43:53 AM com.makotogroup.intro.Employee main INFO: Q: employee1.equals(employee2)? A: false
ibm.com/developerWorks/
A note about comparing Integers In Listing 7, it should be no surprise that the equals() method of Integer returns true if == returns true; but notice what happens in the second case, where you create separate objects that both wrap the value 1: == returns false because integer1 and integer2 refer to different objects; but equals() returns true. The writers of the JDK decided that for Integer, the meaning of equals() would be different from the default (which is to compare the object references to see if they refer to the same object), and would instead return true in cases in which the underlying int value is the same. For Employee, you did not override equals(), so the default behavior (of using ==) returns what you would expect, given that employee1 and employee2 do in fact refer to different objects. Basically, this means that for any object you write, you can define what equals() means as appropriate for the application you are writing. Overriding equals() You can define what equals() means to your application's objects by overriding the default behavior of Object.equals(). Again, you can use Eclipse to do this. Make sure Employee has the focus in your Eclipse IDE's Source window, then go to Source > Override/Implement Methods. The dialog box in Figure 4 will appear:
Page 12 of 53
ibm.com/developerWorks/
developerWorks
You've used this dialog before, but in this case you want to implement the Object.equals() superclass method. So, find Object in the list, check the equals(Object) method, and click OK. Eclipse will generate the correct code and place it in your source file. It makes sense that the two Employee objects are equal if the states of those objects are equal. That is, they're equal if their values last name, first name, age are the same. Autogenerating equals() Eclipse can generate an equals() method for you based on the instance variables (attributes) you've defined for a class. Because Employee is a subclass of Person, you'll first generate equals() for Person. In Eclipse's Project Explorer view, right-click Person and choose Generate hashCode() and equals() to bring up the dialog box shown in Figure 5:
Page 13 of 53
ibm.com/developerWorks/
Select all attributes (as shown in Figure 5) and click OK. Eclipse will generate an equals() method that looks like the one in Listing 8: Listing 8. An equals() method generated by Eclipse
@Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Person other = (Person) obj; if (age != other.age) return false; if (eyeColor == null) { if (other.eyeColor != null) return false; } else if (!eyeColor.equals(other.eyeColor)) return false; if (gender == null) { if (other.gender != null) return false; } else if (!gender.equals(other.gender)) return false; if (height != other.height) return false; if (name == null) { if (other.name != null) return false; } else if (!name.equals(other.name)) return false; if (weight != other.weight)
Page 14 of 53
ibm.com/developerWorks/
developerWorks
Don't worry about hashCode() for now you can keep it or delete it. The equals() method generated by Eclipse looks complicated, but what it does is pretty simple: if the object passed in is the same object as the one in Listing 8, then equals() will return true. If the object passed in is null, it will return false. After that, the method checks to see if the Class objects are the same (meaning the passed-in object must be a Person object). If that's true, then each attribute value of the object passed in is checked to see if it matches value-for-value with the state of the given Person instance. If the attribute values are null (meaning missing) then the equals() will check as many as it can, and if those match, the objects will be considered equal. You may not want this behavior for every program, but it works for most purposes. Exercise: Generate an equals() for Employee Try following the steps in Autogenerating equals() to generate an equals() for Employee. Once you have your generated equals(), add the following code above it:
public static void main(String[] args) { Logger l = Logger.getLogger(Employee.class.getName()); Employee employee1 = new Employee(); employee1.setName("J Smith"); Employee employee2 = new Employee(); employee2.setName("J Smith"); l.info("Q: employee1 == employee2? A: " + (employee1 == employee2)); l.info("Q: employee1.equals(employee2)? A: " + employee1.equals(employee2)); }
If you run the code, you should see the following output:
Apr 19, 2010 5:26:50 PM com.makotogroup.intro.Employee main INFO: Q: employee1 == employee2? A: false Apr 19, 2010 5:26:50 PM com.makotogroup.intro.Employee main INFO: Q: employee1.equals(employee2)? A: true
In this case, a match on Name alone was enough to convince equals() that the two objects were equal. Feel free to add more attributes to this example and see what you get. Exercise: Override toString() Remember the printAudit() method from the beginning of this section? If you thought it was working a little too hard, you were right! Formatting the state of an object into a String is such a common pattern that the designers of the Java language built it right into Object itself, in a method called (no surprise) toString(). The default implementation of toString() is not very useful, but every object has one. In this exercise, you'll make the default toString() a little more useful.
Introduction to Java programming, Part 2: Constructs for real-world applications Page 15 of 53
developerWorks
ibm.com/developerWorks/
If you suspect that Eclipse can generate a toString() method for you, you are correct. Go back into your Project Explorer and right-click the Employee class, then choose Source > Generate toString().... You'll see a dialog box similar to the one in Figure 5. Choose all attributes and click OK. The code generated by Eclipse for Employee is shown in Listing 9: Listing 9. A toString() method generated by Eclipse
@Override public String toString() { return "Employee [employeeNumber=" + employeeNumber + ", salary=" + salary + ", taxpayerIdentificationNumber=" + taxpayerIdentificationNumber + "]"; }
The code Eclipse generates for toString doesn't include the superclass's toString() (Employee's superclass being Person). You can fix that in a flash, using Eclipse, with this override:
@Override public String toString() { return super.toString() + "Employee [employeeNumber=" + employeeNumber + ", salary=" + salary + ", taxpayerIdentificationNumber=" + taxpayerIdentificationNumber + "]"; }
now does the heavy lifting of formatting the object's current state, and you simply stuff what it returns into the StringBuilder and return.
toString()
I recommend always implementing toString() in your classes, if only for support purposes. It's virtually inevitable that at some point you'll want to see what an object's state is while your application is running, and toString() is a great hook for doing that.
Section 3. Exceptions
No program ever works 100 percent of the time, and the designers of the Java language knew this. In this section, learn about the Java platform's built-in mechanisms for handling situations where your code doesn't work exactly as planned.
Introduction to Java programming, Part 2: Constructs for real-world applications Page 16 of 53
ibm.com/developerWorks/
developerWorks
Exception-handling basics
An exception is an event that occurs during program execution that disrupts the normal flow of the program's instructions. Exception handling is an essential technique of Java programming. In essence, you wrap your code in a try block (which means "try this and let me know if it causes an exception"), and use it to catch various types of exceptions. To get started with exception handling, take a look at the code in Listing 10: Listing 10. Do you see the error?
// . . . public class Employee extends Person { // . . . private static Logger logger;// = Logger.getLogger(Employee.class.getName()); public static void main(String[] args) { Employee employee1 = new Employee(); employee1.setName("J Smith"); Employee employee2 = new Employee(); employee2.setName("J Smith"); logger.info("Q: employee1 == employee2? A: " + (employee1 == employee2)); logger.info("Q: employee1.equals(employee2)? A: " + employee1.equals(employee2)); }
Notice that the initializer for the static variable holding the Logger reference has been commented out. Run this code and you'll get the following output:
Exception in thread "main" java.lang.NullPointerException at com.makotogroup.intro.Employee.main(Employee.java:54)
This output is telling you that you are trying to reference an object that isn't there, which is a pretty serious development error. Fortunately, you can use try and catch blocks to catch it (along with a little help from finally). try, catch, and finally Listing 11 shows the buggy code from Listing 10 cleaned up with the standard code blocks for exception handling try, catch, and finally: Listing 11. Catching an exception
// . . . public class Employee extends Person { // . . . private static Logger logger;// = Logger.getLogger(Employee.class.getName()); public static void main(String[] args) { try { Employee employee1 = new Employee(); employee1.setName("J Smith"); Employee employee2 = new Employee(); employee2.setName("J Smith"); logger.info("Q: employee1 == employee2? A: " + (employee1 == employee2)); logger.info("Q: employee1.equals(employee2)? A: " + employee1.equals(employee2)); } catch (NullPointerException npe) { // Handle...
Page 17 of 53
developerWorks
ibm.com/developerWorks/
System.out.println("Yuck! Outputting a message with System.out.println() " + "because the developer did something dumb!"); } finally { // Always executes } }
Together, the try, catch, and finally blocks form a net for catching exceptions. First, the try statement wraps code that might throw an exception. If it does, execution drops immediately to the catch block, or exception handler. When all the trying and catching is done, execution continues to the finally block, whether or not an exception has been thrown. When you catch an exception, you can try to recover gracefully from it, or you can exit the program (or method). In Listing 11, the program recovers from the error, then prints out a message to report what happened.
Page 18 of 53
ibm.com/developerWorks/
developerWorks
In this example, the NullPointerException is a child class (eventually) of Exception, so it must be placed ahead of the more general Exception catch block. You've seen just a tiny glimpse of Java exception handling in this tutorial. The topic could make a tutorial on its own. See Resources to learn more about exception handling in Java programs.
Create a driver class in Eclipse using the same procedure you used to create Person and Employee. Name the class HumanResourcesApplication, being sure to select the option to add a main() method to the class. Eclipse will generate the class for you. Add some code to your new main() so that it looks like this:
Page 19 of 53
developerWorks
public class HumanResourcesApplication { . . . private final Logger log = Logger.getLogger(Person.class); . . . public static void main(String[] args) { Employee e = new Employee(); e.setName("J Smith"); e.setEmployeeNumber("0001"); e.setTaxpayerIdentificationNumber("123-45-6789"); e.printAudit(log); } . . . }
ibm.com/developerWorks/
Now launch the HumanResourcesApplication class and watch it run. You should see this output (with the backslashes here indicating a line continuation):
Apr 29, 2010 6:45:17 AM com.makotogroup.intro.Person printAudit INFO: Person [age=0, eyeColor=null, gender=null, height=0, name=J Smith,\ weight=0]Employee [employeeNumber=0001, salary=null,\ taxpayerIdentificationNumber=123-45-6789]
That's really all there is to creating a simple Java application. In the next section, you'll begin looking at some of the syntax and libraries that will help you develop more-complex applications.
Section 5. Inheritance
You've encountered examples of inheritance a few times already in this tutorial. This section reviews some of Part 1's material on inheritance and explains in more detail how inheritance works including the inheritance hierarchy, constructors and inheritance, and inheritance abstraction.
Page 20 of 53
ibm.com/developerWorks/
developerWorks
The Person class in Listing 12 implicitly inherits from Object. Because that's assumed for every class, you don't need to type extends Object for every class you define. But what does it mean to say that a class inherits from its superclass? It simply means that Person has access to the exposed variables and methods in its superclasses. In this case, Person can see and use Object's public methods and variables and Object's protected methods and variables.
The Employee inheritance graph implies that Employee has access to all public and protected variables and methods in Person (because it directly extends it), as well as Object (because it actually extends that class, too, though indirectly). However,
Introduction to Java programming, Part 2: Constructs for real-world applications Page 21 of 53
developerWorks
ibm.com/developerWorks/
because Employee and Person are in the same package, Employee also has access to the package-private (sometimes called friendly) variables and methods in Person. To go one step deeper into the class hierarchy, you could create a third class that extends Employee:
public class Manager extends Employee { // . . . }
In the Java language, any class can have at most one superclass, but a class can have any number of subclasses. That is the most important thing to remember about inheritance hierarchy in the Java language.
Every class has at least one constructor, and if you don't explicitly define a constructor for your class, the compiler will generate one for you, called the default constructor. The preceding class definition and this one are identical in how they function:
public class Person { }
Invoking a superclass constructor To invoke a superclass constructor other than the default constructor, you must do so explicitly. For example, suppose Person has a constructor that takes the name of the Person object being created. From Employee's default constructor, you could invoke the Person constructor shown in Listing 13:
Page 22 of 53
ibm.com/developerWorks/
developerWorks
You would probably never want to initialize a new Employee object this way, however. Until you get more comfortable with object-oriented concepts, and Java syntax in general, it's a good idea to implement superclass constructors in subclasses if you think you will need them, and invoke them homogeneously. Listing 14 defines a constructor in Employee that looks like the one in Person so that they match up. It's much less confusing from a maintenance standpoint. Listing 14. Invoking a superclass homogeneously
public class Person { private String name; public Person(String name) { this.name = name; } } // Meanwhile, in Employee.java public class Employee extends Person { public Employee(String name) { super(name); } }
Declaring a constructor The first thing a constructor does is invoke the default constructor of its immediate superclass, unless you on the first line of code in the constructor invoke a different constructor. For example, these two declarations are functionally identical, so pick one:
public class Person { public Person() { } } // Meanwhile, in Employee.java public class Employee extends Person { public Employee() { } }
Or:
Introduction to Java programming, Part 2: Constructs for real-world applications Page 23 of 53
developerWorks
public class Person { public Person() { } } // Meanwhile, in Employee.java public class Employee extends Person { public Employee() { super(); } }
ibm.com/developerWorks/
No-arg constructors If you provide an alternate constructor, you must explicitly provide the default constructor, or it is not available. For example, the following code would give you a compile error:
public class Person { private String name; public Person(String name) { this.name = name; } } // Meanwhile, in Employee.java public class Employee extends Person { public Employee() { } }
This example has no default constructor, because it provides an alternate constructor without explicitly including the default constructor. This is why the default constructor is sometimes called the no-argument (or no-arg) constructor; because there are conditions under which it is not included, it's not really a default. How constructors invoke constructors A constructor from within a class can be invoked by another constructor using the this keyword, along with an argument list. Just like super(), the this() call must be the first line in the constructor. For example:
public class Person { private String name; public Person() { this("Some reasonable default?"); } public Person(String name) { this.name = name; } } // Meanwhile, in Employee.java
You will see this idiom frequently, where one constructor delegates to another, passing in some default value if that constructor is invoked. It's also a great way to add a new constructor to a class while minimizing impact on code that already uses an older constructor.
Introduction to Java programming, Part 2: Constructs for real-world applications Page 24 of 53
ibm.com/developerWorks/
developerWorks
Constructor access levels Constructors can have any access level you want, and certain rules of visibility apply. Table 1 summarizes the rules of constructor access: Table 1. Constructor access rules
Constructor access modifier public protected No modifier (package-private) private Description Constructor can be invoked by any class. Constructor can be invoked by an class in the same package or any subclass. Constructor can be invoked by any class in the same package. Constructor can be invoked only by the class in which the constructor is defined.
You may be able to think of use cases where constructors would be declared protected or even package-private, but how is a private constructor useful? I've used private constructors when I didn't want to allow direct creation of an object through the new keyword when implementing, say, the Factory pattern (see Resources). In that case, a static method would be used to create instances of the class, and that method, being included in the class itself, would be allowed to invoke the private constructor:
ibm.com/developerWorks/
There are times when you will want to create classes that only serve as abstractions and do not necessarily ever need to be instantiated. Such classes are called abstract classes. By the same token, you will find that there are times when certain methods need to be implemented differently for each subclass that implements the superclass. Such methods are abstract methods. Here are some basic rules for abstract classes and methods: Any class can be declared abstract. Abstract classes cannot be instantiated. An abstract method cannot contain a method body. Any class with an abstract method must be declared abstract.
Using abstraction Suppose you don't want to allow the Employee class to be instantiated directly. You simply declare it using the abstract keyword, and you're done:
public abstract class Employee extends Person { // etc. }
The compiler is complaining that Employee is abstract and cannot be instantiated. The power of abstraction Suppose that you need a method to examine the state of an Employee object and make sure it is valid. This need would seem to be common to all Employee objects, but would behave sufficiently differently among all potential subclasses that there is zero potential for reuse. In that case, you declare the validate() method abstract (forcing all subclasses to implement it):
public abstract class Employee extends Person { public abstract boolean validate(); }
Every direct subclass of Employee (such as Manager) is now required to implement the validate() method. However, once a subclass has implemented the validate() method, none of its subclasses need to implement it. For example, suppose you have an Executive object that extends Manager. This definition would be perfectly valid:
Introduction to Java programming, Part 2: Constructs for real-world applications Page 26 of 53
ibm.com/developerWorks/
developerWorks
When (not) to abstract: Two rules As a first rule of thumb, don't abstract in your initial design. Using abstract classes early in the design forces you down a certain path, and that could restrict your application. Remember, common behavior (which is the entire point of having abstract classes) can always be refactored further up the inheritance graph. It is almost always better to do this once you've discovered that you do need it. Eclipse has wonderful support for refactoring. Second, as powerful as they are, resist the use of abstract classes when you can. Unless your superclasses contain lots of common behavior, and on their own are not really meaningful, let them remain nonabstract. Deep inheritance graphs can make code maintenance difficult. Consider the trade-off between classes that are too large and maintainable code.
Assignments: Classes
When assigning a reference from one class to a variable of a type belonging to another class, you can do so, but there are rules. Let's look at this example:
Manager m = new Manager(); Employee e = new Employee(); Person p = m; // okay p = e; // still okay Employee e2 = e; // yep, okay e = m; // still okay e2 = p; // wrong!
The destination variable must be of a supertype of the class belonging to the source reference, or the compiler will give you an error. Basically, whatever is on the right side of the assignment must be a subclass or the same class as the thing on the left. If not, it's possible for assignments of objects with different inheritance graphs (such as Manager and Employee) to be assigned to a variable of the wrong type. Consider this example:
Manager m = new Manager(); Person p = m; // so far so good Employee e = m; // okay Employee e = p; // wrong!
While an Employee is a Person, it is most definitely not a Manager, and the compiler enforces this.
Section 6. Interfaces
Introduction to Java programming, Part 2: Constructs for real-world applications Page 27 of 53
developerWorks
ibm.com/developerWorks/
In this section, begin learning about interfaces and start using them in your Java code.
Defining an interface
An interface is a named set of behaviors (and/or constant data elements) for which an implementer must provide code. An interface specifies what behavior the implementation provides, but not how it is accomplished. Defining an interface is straightforward:
public interface interfaceName { returnType methodName( argumentList ); }
An interface declaration looks like a class declaration, except that you use the interface keyword. You can name the interface anything you want to (subject to language rules), but by convention interface names look like class names. Methods defined in an interface have no method body. The implementer of the interface is responsible for providing the method body (just as with abstract methods). You define hierarchies of interfaces, just as you do for classes, except that a single class can implement as many interfaces as it wants to. (Remember, a class can extend only one class.) If one class extends another and implements interface(s), then the interfaces are listed after the extended class, like this:
public class Manager extends Employee implements BonusEligible, StockOptionRecipient { // Etc... }
Marker interfaces An interface does not need to have any body at all. In fact, the following definition is perfectly acceptable:
public interface BonusEligible { }
Generally speaking, such interfaces are called marker interfaces, because they mark a class as implementing that interface but offer no special explicit behavior. Once you know all that, actually defining an interface is easy:
public interface StockOptionRecipient { void processStockOptions(int numberOfOptions, BigDecimal price); }
Page 28 of 53
ibm.com/developerWorks/
developerWorks
Implementing interfaces
To use an interface, you implement it, which simply means providing a method body, which in turn provides the behavior to fulfill the interface's contract. You do that with the implements keyword:
public class className extends superclassName implements interfaceName { // Class Body }
Suppose you implement the StockOptionRecipient interface on the Manager class, as shown in Listing 15: Listing 15. Implementing an interface
public class Manager extends Employee implements StockOptionRecipient { public Manager() { } public void processStockOptions (int numberOfOptions, BigDecimal price) { log.info("I can't believe I got " + number + " options at $" + price.toPlainString() + "!"); } }
When you implement the interface, you provide behavior for the method(s) on the interface. You must implement the methods with signatures that match the ones on the interface, with the addition of the public access modifier. Generating interfaces in Eclipse Eclipse can easily generate the correct method signature for you if you decide one of your classes should implement an interface. Just change the class signature to implement the interface. Eclipse puts a red squiggly line under the class, flagging it to be in error because the class doesn't provide the method(s) on the interface. Click the class name with your mouse, press Ctrl + 1, and Eclipse will suggest "quick fixes" for you. Of these, choose Add Unimplemented Methods, and Eclipse will generate the methods for you, placing them at the bottom of the source file. An abstract class can declare that it implements a particular interface, but it isn't required to implement all of the methods on that interface. This is because abstract classes aren't required to provide implementations for all of the methods they claim to implement. However, the first concrete class (that is, the first one that can be instantiated) must implement all methods the hierarchy does not.
Using interfaces
An interface defines a new reference data type, which means you can refer to an interface anywhere you would refer to a class. This includes when you declare a reference variable, or cast from one type to another, as shown in Listing 16.
Introduction to Java programming, Part 2: Constructs for real-world applications Page 29 of 53
developerWorks
ibm.com/developerWorks/
As you can see, it is perfectly valid to assign a new Manager instance to a StockOptionEligible reference, as well as to pass a new Manager instance to a method that expects a StockOptionEligible reference.
Assignments: Classes
When assigning a reference from a class that implements an interface to a variable of an interface type, you can do so, but there are rules. From Listing 16, we see that assigning a Manager instance to a StockOptionEligible variable reference is perfectly valid. The reason is that the Manager class implements that interface. However, the following assignment would not be valid:
Manager m = new Manager(); StockOptionEligible soe = m; //okay Employee e = soe; // Wrong!
Because Employee is supertype of Manager, this might at first seem okay, but it is not. Because Manager is a specialization of Employee, it is *different* and in this particular case implements an interface that Employee does not. Assignments such as these follow the rules of assignment we saw in Inheritance. And just like with classes, you may only assign an interface reference to a variable of the same type or a superinterface type.
ibm.com/developerWorks/
developerWorks
Just like member variables and methods, Java classes can also be defined at any scope including public, private, or protected. Nested classes can be useful when you want to handle internal processing within your class in an object-oriented fashion, but this functionality is limited to the class where you need it. Typically, you'll use a nested class for cases where you need a class that is tightly coupled with the class in which it is defined. A nested class has access to the private data within its enclosing class, but this carries with it some side-effects that are not obvious when you start working with nested (or inner) classes.
Just as each Manager object represents a unique human being, the DirectReports object represents a collection of actual people (employees) who report to a manager. DirectReports will differ from one Manager to another. In this case, it makes sense that one would only reference the DirectReports nested class in the context of its enclosing instance of Manager, so I've made it private. Public nested classes Because it's private, only Manager can create an instance of DirectReports. But suppose you wanted to give an external entity the ability to create instances of DirectReports? In this case, it seems like you could give the DirectReports class public scope, and then any external code could create DirectReports instances, as shown in Listing 17:
Introduction to Java programming, Part 2: Constructs for real-world applications Page 31 of 53
developerWorks
ibm.com/developerWorks/
The code in Listing 17 doesn't work, and you're probably wondering why. The problem (and also its solution) lies with the way DirectReports is defined within Manager, and with the rules of scope. The rules of scope, revisited If you had a member variable of Manager, you would expect the compiler to require you to have a reference to a Manager object before you could reference it, right? Well, the same applies to DirectReports, at least as you defined it in Listing 17. To create an instance of a public nested class, you use a special version of the new operator. Combined with a reference to some enclosing instance of an outer class, new allows you to create an instance of the nested class:
public class Manager extends Employee { public Manager() { } . . . private class DirectReports { . . . } } // Meanwhile, in another method somewhere... public static void main(String[] args) { Manager manager = new Manager(); Manager.DirectReports dr = manager.new DirectReports(); }
Note that the syntax calls for a reference to the enclosing instance, plus a dot and the new keyword, followed by the class you want to create.
ibm.com/developerWorks/
developerWorks
public class Manager extends Employee { . . . public static class ManagerComparator implements Comparator<Manager> { . . . } } // Meanwhile, in another method somewhere... public static void main(String[] args) { Manager.ManagerComparator mc = new Manager.ManagerComparator(); . . . }
In this case, you don't need an enclosing instance. Static inner classes act like their regular Java class counterparts, and they should really only be used when you need to couple a class tightly with its definition. Clearly, in the case of a utility class like ManagerComparator, creating an external class is unnecessary and potentially clutters up your code base. Defining such classes as static inner classes is the way to go.
In this example, you provide an implementation of the StockOptionEligible interface by using an anonymous inner class for instances of Employee that do not implement
Introduction to Java programming, Part 2: Constructs for real-world applications Page 33 of 53
developerWorks
ibm.com/developerWorks/
that interface. Anonymous inner classes are also useful for implementing callback methods, and in event handling, too.
Page 34 of 53
ibm.com/developerWorks/
developerWorks
* + [] ^ \d \D \s \S \w \W
Zero (0) or more of what came before One (1) or more of what came before A range of characters or digits Negation of whatever follows (that is, "notwhatever") Any digit (alternatively, [0-9]) Any nondigit (alternatively, [^0-9]) Any whitespace character (alternatively, [\n\t\f\r]) Any nonwhitespace character (alternatively, [^\n\t\f\r]) Any word character (alternatively, [a-zA-Z_0-9]) Any nonword character (alternatively, [^\w])
The first few constructs are called quantifiers, because they quantify what comes before them. Constructs like \d are predefined character classes. Any character that doesn't have special meaning in a pattern is a literal and matches itself. Pattern matching Armed with the pattern syntax in Table 2, you can work through the simple example in Listing 19, using the classes in the Java Regular Expressions API: Listing 19. Pattern matching with regex
Pattern pattern = Pattern.compile("a.*string"); Matcher matcher = pattern.matcher("a string"); boolean didMatch = matcher.matches(); Logger.getAnonymousLogger().info (didMatch); int patternStartIndex = matcher.start(); Logger.getAnonymousLogger().info (patternStartIndex); int patternEndIndex = matcher.end(); Logger.getAnonymousLogger().info (patternEndIndex);
First, Listing 19 creates a Pattern class by calling compile(), which is a static method on Pattern, with a string literal representing the pattern you want to match. That literal uses the regex pattern syntax. In this example, the English translation of the pattern is: Find a string of the form a followed by zero or more characters, followed by string. Methods for matching Next, Listing 19 calls matcher() on Pattern. That call creates a Matcher instance. When that happens, the Matcher searches the string you passed in for matches against the pattern string you used when you created the Pattern. Every Java language string is an indexed collection of characters, starting with 0 and ending with the string length minus one. The Matcher parses the string, starting at 0, and looks for matches against it. After that process completes, the Matcher contains information about matches found (or not found) in the input string. You can access that information by calling various methods on Matcher:
Introduction to Java programming, Part 2: Constructs for real-world applications Page 35 of 53
developerWorks
ibm.com/developerWorks/
matches() tells you if the entire input sequence was an exact match for the pattern. start() tells you the index value in the string where the matched string starts. end() tells you the index value in the string where the matched string ends, plus one. Listing 19 finds a single match starting at 0 and ending at 7. Thus, the call to matches() returns true, the call to start() returns 0, and the call to end() returns 8. lookingAt() vs. matches() If there were more elements in your string than the characters in the pattern you searched for, you could use lookingAt() instead of matches(). lookingAt() searches for substring matches for a given pattern. For example, consider the following string:
Here is a string with more than just the pattern.
You could search it for a.*string and get a match if you use lookingAt(). But if you use matches(), it would return false, because there's more to the string than just what's in the pattern.
You could search for wiki words in this string with a regex pattern like this:
[A-Z][a-z]*([A-Z][a-z]*)+
Page 36 of 53
ibm.com/developerWorks/
developerWorks
Run this code, and you should see the three wiki words in your console. Replacing strings Searching for matches is useful, but you also can manipulate strings once you find a match for them. You can do that by replacing matched strings with something else, just as you might search for some text in a word-processing program and replace it with other text. Matcher has a couple of methods for replacing string elements: replaceAll() replaces all matches with a specified string. replaceFirst() replaces only the first match with a specified string. Using Matcher's replace methods is straightforward:
String input = "Here is a WikiWord followed by AnotherWikiWord, then SomeWikiWord."; Pattern pattern = Pattern.compile("[A-Z][a-z]*([A-Z][a-z]*)+"); Matcher matcher = pattern.matcher(input); Logger.getAnonymousLogger().info("Before: " + input); String result = matcher.replaceAll("replacement"); Logger.getAnonymousLogger().info("After: " + result);
This code finds wiki words, as before. When the Matcher finds a match, it replaces the wiki word text with its replacement. When you run this code, you should see the following on your console:
Before: Here is WikiWord followed by AnotherWikiWord, then SomeWikiWord. After: Here is replacement followed by replacement, then replacement.
Run this code and you should get the following console output:
Introduction to Java programming, Part 2: Constructs for real-world applications Page 37 of 53
developerWorks
ibm.com/developerWorks/
Before: Here is a WikiWord followed by AnotherWikiWord, then SomeWikiWord. After: Here is a blahWikiWordblah followed by blahAnotherWikiWordblah, then blahSomeWikiWordblah.
Another approach to matching groups Listing 20 references the entire match by including $0 in the replacement string. Any portion of a replacement string of the form $int refers to the group identified by the integer (so $1 refers to group 1, and so on). In other words, $0 is equivalent to matcher.group(0);. You could accomplish the same replacement goal by using some other methods. Rather than calling replaceAll(), you could do this:
StringBuffer buffer = new StringBuffer(); while (matcher.find()) { matcher.appendReplacement(buffer, "blah$0blah"); } matcher.appendTail(buffer); Logger.getAnonymousLogger().info("After: " + buffer.toString());
Section 9. Generics
The introduction of generics in JDK 5 marked a huge leap forward for the Java language. If you've used C++ templates, you'll find that generics in the Java language are similar, but not exactly the same. If you haven't used C++ templates, then don't worry: This section offers a high-level introduction to generics in the Java language.
ibm.com/developerWorks/
developerWorks
As you can see, the ArrayList is heterogeneous: it contains two String types and one Integer type. Before JDK 5 there was nothing in the Java language to constrain this behavior, which caused many coding mistakes. In Listing 21, for example, everything is looking good so far. But what about accessing the elements of the ArrayList, which Listing 22 tries to do? Listing 22. An attempt to access elements in ArrayList
ArrayList arrayList = new ArrayList(); arrayList.add("A String"); arrayList.add(new Integer(10)); arrayList.add("Another String"); // So far, so good. *processArrayList(arrayList); *// In some later part of the code... private void processArrayList(ArrayList theList) { for (int aa = 0; aa < theList.size(); aa++) { // At some point, this will fail... String s = (String)theList.get(aa); } }
Without prior knowledge of what's in the ArrayList, you either must check the element you want to access to see if you can handle its type, or face a possible ClassCastException. With generics, you can specify the type of item that went in the ArrayList. Listing 23 shows how: Listing 23. A second attempt, using generics
ArrayList<String> arrayList = new ArrayList<String>(); arrayList.add("A String"); arrayList.add(new Integer(10));// compiler error! arrayList.add("Another String"); // So far, so good. *processArrayList(arrayList); *// In some later part of the code... private void processArrayList(ArrayList<String> theList) { for (int aa = 0; aa < theList.size(); aa++) { // No cast necessary... String s = theList.get(aa); } }
Iterating with generics Generics enhance the Java language with special syntax for dealing with entities like Lists that you commonly want to step through, element by element. If you want to
Introduction to Java programming, Part 2: Constructs for real-world applications Page 39 of 53
developerWorks
ibm.com/developerWorks/
iterate through ArrayList, for instance, you could rewrite the code from Listing 23 like so:
private void processArrayList(ArrayList<String> theList) { for (String s : theList) { String s = theList.get(aa); } }
This syntax works for any type of object that is Iterable (that is, implements the Iterable interface).
Parameterized classes
Parameterized classes really shine when it comes to collections, so that's how you'll look at them. Consider the (real) List interface. It represents an ordered collection of objects. In the most common use case, you add items to the List and then access those items either by index or by iterating over the List. If you're thinking about parameterizing a class, consider whether the following criteria apply: A core class is at the center of some kind of wrapper: that is, the "thing" at the center of the class might apply widely, and the features (attributes, for example) surrounding it are identical. Common behavior: you do pretty much the same operations regardless of the "thing" at the center of the class. Applying these two criteria, it's pretty obvious that a collection fits the bill: The "thing" is the class of which the collection comprises. The operations (such as add, remove, size, and clear) are pretty much the same regardless of the object of which the collection is comprised. A parameterized List In generics syntax, the code to create a List looks like this:
List<E> listReference = new concreteListClass<E>();
The E, which stands for Element, is the "thing" I mentioned earlier. The concreteListClass is the class from the JDK you are instantiating. The JDK includes several List<E> implementations, but you'll use ArrayList<E>. Another way you might see a generic class discussed is Class<T>, where T stands for Type. When you see E in Java code, it's usually referring to a collection of some kind. And when you see T, it's denoting a parameterized class. So, to create an ArrayList of, say, java.lang.Integer, you'd do this:
Introduction to Java programming, Part 2: Constructs for real-world applications Page 40 of 53
ibm.com/developerWorks/
developerWorks
SimpleList: A parameterized class Now suppose you want to create your own parameterized class called SimpleList, with three methods: add() adds an element to the end of the SimpleList. size() returns the current number of elements in the SimpleList. clear() completely clears the contents of the SimpleList. Listing 24 shows the syntax to parameterize SimpleList: Listing 24. Parameterizing SimpleList
package com.makotogroup.intro; import java.util.ArrayList; import java.util.List; public class SimpleList<E> { private List<E> backingStore; public SimpleList() { backingStore = new ArrayList<E>(); } public E add(E e) { if (backingStore.add(e)) return e; else return null; } public int size() { return backingStore.size(); } public void clear() { backingStore.clear(); } }
SimpleList SimpleList
can be parameterized with any Object subclass. To create and use a of, say, java.math.BigDecimal objects, you'd do this:
public static void main(String[] args) { SimpleList<BigDecimal> sl = new SimpleList<BigDecimal>(); sl.add(BigDecimal.ONE); log.info("SimpleList size is : " + sl.size()); sl.add(BigDecimal.ZERO); log.info("SimpleList size is : " + sl.size()); sl.clear(); log.info("SimpleList size is : " + sl.size()); }
Page 41 of 53
developerWorks
ibm.com/developerWorks/
Enum types
In JDK 5, a new data type was added to the Java language, called enum. Not to be confused with java.util.Enumeration, enum represents a set of constant objects that are all related to a particular concept, each of which represents a different constant value in that set. Before enum was introduced to the Java language, you would have defined a set of constant values for a concept (say, gender) like so:
public class Person { public static final String MALE = "male"; public static final String FEMALE = "female"; }
Whatever code needed to reference that constant value would have been written something like this:
public void myMethod() { //. . . String genderMale = Person.MALE; //. . . }
Defining constants with enum Using the enum type makes defining constants much more formal, and also more powerful. Here's the enum definition for Gender:
public enum Gender { MALE, FEMALE }
That just scratches the surface of what you can do with enums. In fact, enums are much like classes, so they can have constructors, attributes, and methods:
package com.makotogroup.intro; public enum Gender { MALE("male"), FEMALE("female"); private String displayName; private Gender(String displayName) { this.displayName = displayName; } public String getDisplayName() { return this.displayName; } }
One difference between a class and an enum is that an enum's constructor must be declared private, and it cannot extend (or inherit from) other enums. However, an enum can implement an interface.
Introduction to Java programming, Part 2: Constructs for real-world applications Page 42 of 53
ibm.com/developerWorks/
developerWorks
Your Gender enum could implement this interface (as well as any other enum that needed to produce a friendly display name), like so:
package com.makotogroup.intro; public enum Gender implements Displayable { MALE("male"), FEMALE("female"); private String displayName; private Gender(String displayName) { this.displayName = displayName; } @Override public String getDisplayName() { return this.displayName; } }
Files
Of all the data sources available to your Java applications, files are the most common and often the most convenient. If you want to read a file in your Java application, you must use streams that parse its incoming bytes into Java language types. is a class that defines a resource on your file system and represents that resource in an abstract way. Creating a File object is easy:
java.io.File
Introduction to Java programming, Part 2: Constructs for real-world applications Page 43 of 53
developerWorks
File f = new File("temp.txt"); File f2 = new File("/home/steve/testFile.txt");
ibm.com/developerWorks/
The File constructor takes the name of the file it will create. The first call creates a file called temp.txt in the given directory. The second call creates a file in a specific location on my Linux system. You can pass any String to the constructor of File, so long as it is a valid file name for your OS, whether or not the file that it references even exists. This code asks the newly created File object if the file exists:
File f2 = new File("/home/steve/testFile.txt"); if (f2.exists()) { // File exists. Process it... } else { // File doesn't exist. Create it... f2.createNewFile(); }
has some other handy methods that you can use to delete files, create directories (by passing a directory name as the argument to File's constructor), determine whether a resource is a file, directory, or symbolic link, and more.
java.io.File
The real action of Java I/O is in writing to and reading from data sources, which is where streams come in.
ibm.com/developerWorks/
developerWorks
StringReader/StringWriter: Read and write characters to and from Strings in memory. InputStreamReader/InputStreamWriter (and subclasses FileReader/FileWriter): Form a bridge between byte streams and character streams. The Reader flavors read bytes from a byte stream and convert them to characters. The Writer flavors convert characters to bytes to put them on byte streams. BufferedReader/BufferedWriter: Buffer data while reading or writing another stream, making read and write operations more efficient. Rather than try to cover streams in their entirety, I'll focus on the recommended streams for reading and writing files. In most cases, these are character streams. Reading from a File There are several ways to read from a File. Arguably the simplest approach is to: 1. Create an InputStreamReader on the File you want to read from. 2. Call read() to read one character at a time until you reach the end of the file. Listing 25 is an example in reading from a File: Listing 25. Reading from a File
Logger log = Logger.getAnonymousLogger(); StringBuilder sb = new StringBuilder(); try { InputStream inputStream = new FileInputStream(new File("input.txt")); InputStreamReader reader = new InputStreamReader(inputStream); try { int c = reader.read(); while (c != -1) { sb.append(c); } } finally { reader.close(); } } catch (IOException e) { log.info("Caught exception while processing file: " + e.getMessage()); }
Writing to a File As with reading from a File, there are several ways to write to a File. Once again, I'll go with the simplest approach: 1. Create a FileOutputStream on the File you want to write to. 2. Call write() to write the character sequence. Listing 26 is an example of writing to a File:
Introduction to Java programming, Part 2: Constructs for real-world applications Page 45 of 53
ibm.com/developerWorks/
Logger log = Logger.getAnonymousLogger(); StringBuilder sb = getStringToWriteSomehow(); try { OutputStream outputStream = new FileOutputStream(new File("output.txt")); OutputStreamWriter writer = new OutputStreamWriter(outputStream); try { writer.write(sb.toString()); } finally { writer.close(); } } catch (IOException e) { log.info("Caught exception while processing file: " + e.getMessage()); }
Buffering streams Reading and writing character streams one character at a time is not exactly efficient, so in most cases, you'll probably want to use buffered I/O instead. To read from a file using buffered I/O, the code looks just like Listing 25, except that you wrap the InputStreamReader in a BufferedReader, as shown in Listing 27: Listing 27. Reading from a File with buffered I/O
Logger log = Logger.getAnonymousLogger(); StringBuilder sb = new StringBuilder(); try { InputStream inputStream = new FileInputStream(new File("input.txt")); BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream)); try { String line = reader.readLine(); while (line != null) { sb.append(line); line = reader.readLine(); } } finally { reader.close(); } } catch (IOException e) { log.info("Caught exception while processing file: " + e.getMessage()); }
Writing to a file using buffered I/O is the same: you just wrap the OutputStreamWriter in a BufferedWriter, as shown in Listing 28 Listing 28. Writing to a File with buffered I/O
Logger log = Logger.getAnonymousLogger(); StringBuilder sb = getStringToWriteSomehow(); try { OutputStream outputStream = new FileOutputStream(new File("output.txt")); BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(outputStream)); try { writer.write(sb.toString()); } finally { writer.close(); } } catch (IOException e) { log.info("Caught exception while processing file: " + e.getMessage()); }
Page 46 of 53
ibm.com/developerWorks/
developerWorks
I've merely scratched the surface of what's possible with this essential Java library. On your own, try applying what you've learned about files to other data sources.
java.io.Serializable
The first step to making serialization work is to enable your objects to use the mechanism. Every object you want to be serializable must implement an interface called java.io.Serializable:
import java.io.Serializable; public class Person implements Serializable { // etc... }
The Serializable interface marks the objects of the Person class to the runtime as serializable. Every subclass of Person will also be marked as serializable. Any attributes of an object that are not serializable will cause the Java runtime to throw a NotSerializableException if it tries to serialize your object. You can manage this by using the transient keyword to tell the runtime not to try to serialize
Introduction to Java programming, Part 2: Constructs for real-world applications Page 47 of 53
developerWorks
ibm.com/developerWorks/
certain attributes. In that case, you are responsible for making sure the attributes are restored so that your object will function properly. Serializing an object Now you'll try an example that combines what you've just learned about Java I/O with what you're learning now about serialization. Suppose you create and populate a Manager object (recall that Manager is in the inheritance graph of Person, which is serializable) and then want to serialize that object to an OutputStream, in this case to a file. That process is shown in Listing 29: Listing 29. Serializing an object
Manager m = new Manager(); m.setEmployeeNumber("0001"); m.setGender(Gender.FEMALE); m.setAge(29); m.setHeight(170); m.setName("Mary D. Boss"); m.setTaxpayerIdentificationNumber("123-45-6789"); log.info("About to write object using serialization... object looks like:"); m.printAudit(log); try { String filename = "Manager-" + m.hashCode() + ".ser"; ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(filename)); oos.writeObject(m); log.info("Wrote object..."); } catch (Exception e) { log.log(Level.SEVERE, "Caught Exception processing object", e); }
The first step is to create the object and set some attribute values. Next, you create an OutputStream, in this case a FileOutputStream, and then call writeObject() on that stream. writeObject() is a method that uses Java serialization to serialize an object to the stream. In this example, you are storing the object in a file, but this same technique is used for any type of serialization. Deserializing an object The whole point of serializing an object is to be able to reconstitute, or deserialize, it. Listing 30 reads the file you've just serialized and deserializes its contents, thus restoring the state of the Manager object: Listing 30. Deserializing an object
Manager m = new Manager(); m.setEmployeeNumber("0001"); m.setGender(Gender.FEMALE); m.setAge(29); m.setHeight(170); m.setName("Mary D. Boss"); m.setTaxpayerIdentificationNumber("123-45-6789"); log.info("About to write object using serialization... object looks like:");
Page 48 of 53
ibm.com/developerWorks/
developerWorks
m.printAudit(log); try { String filename = "Manager-" + m.hashCode() + ".ser"; ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(filename)); oos.writeObject(m); log.info("Wrote object..."); ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filename)); m = (Manager)ois.readObject(); log.info("Read object using serialization... object looks like:"); m.printAudit(log); } catch (Exception e) { log.log(Level.SEVERE, "Caught Exception processing object", e); }
For most application purposes, marking your objects as serializable is all you'll ever need to worry about when it comes to serialization. In cases where you do need to serialize and deserialize your objects explicitly, you can use the technique shown in Listings 29 and 30. But as your application objects evolve, and you add and remove attributes to and from them, serialization takes on a new layer of complexity.
serialVersionUID
Back in the early days of middleware and remote object communication, developers were largely responsible for controlling the "wire format" of their objects, which caused no end of headaches as technology began to evolve. Suppose you added an attribute to an object, recompiled it, and redistributed the code to every machine in an application cluster. The object would be stored on a machine with one version of the serialization code, but accessed by other machines that might have a different version of the code. When those machines tried to deserialize the object, bad things often happened. Java serialization metadata the information included in the binary serialization format is sophisticated and solves many of the problems that plagued early middleware developers. But it cannot solve every problem. Java serialization uses a property called serialVersionUID to help you deal with different versions of objects in a serialization scenario. You don't need to declare this property on your objects; by default, the Java platform uses an algorithm that computes a value for it based on your class's attributes, its class name, and position in the local galactic cluster. Most of the time, that works fine. But if you add or remove an attribute, that dynamically generated value will change, and the Java runtime will throw an InvalidClassException. To avoid this, you should get in the habit of explicitly declaring a serialVersionUID:
import java.io.Serializable; public class Person implements Serializable { private static final long serialVersionUID = 20100515; // etc... }
Page 49 of 53
developerWorks
ibm.com/developerWorks/
I recommend using some kind of scheme for your serialVersionUID version number (I've used the current date in the example above), and you should declare it private static final and of type long. You may be wondering when to change this property. The short answer is that you should change it whenever you make an incompatible change to the class, which usually means you've removed an attribute. If you have one version of the object on one machine that has the attribute removed, and the object gets remoted to a machine with a version of the object where the attribute is expected, then things can get weird. As a rule of thumb, any time you add or remove features (meaning attributes and methods) of a class, you should change its serialVersionUID. Better to get an InvalidClassException on the other end of the wire than an application bug that's due to an incompatible class change.
Page 50 of 53
ibm.com/developerWorks/
developerWorks
Resources
Learn Java technology homepage: The official Java site has links to all things related to the Java platform, including the Java language specification and Java API documentation. Java 6: Learn more about JDK 6 and the tools that come with it. Javadoc homepage: Learn the ins and outs of using the Javadoc, including how to use the command-line tool and how to write your own Doclets that let you create custom formats for your documentation. New to Java technology: Check out this compendium of developerWorks resources for beginning Java developers. 5 things you didn't know about ...: This developerWorks series provides short introductions to lesser-known (but often introductory) Java programming tips and lore. "Try to catch me: Does exception handling impair performance?" (Tony Sintes, JavaWorld, July 2001): This article provides introductory information about exception handling in Java programs. "Exception: Don't get thrown for a loss" (Tony Sintes, JavaWorld, February 2002): Understand the difference between checked and runtime exceptions. "Regular expressions simplify pattern-matching code" (Jeff Friesen, JavaWorld.com, February 2003): Explore this extended introduction to regex. "Diagnosing Java code: Java generics without the pain, Part 1" (Eric Allen, developerWorks, February 2003): This is the first part of an article series introducing generics in Java syntax. "Java technology, IBM style: A new era in Java technology" (Chris Bailey developerWorks, April 2010): Read about upcoming changes in the official Java SE 7 release, as well as IBM value-adds focused on improving Java platform performance, reliability, and serviceability. Refactoring: Improving the Design of Existing Code (Martin Fowler et al., Addison-Wesley, 1999): This book is an excellent resource for learning how to write cleaner, more maintainable code. Design patterns: Elements of reusable object-oriented software (Erich Gammaet al., Addison-Wesley, 1994): Learn more about the Factory pattern, one of 23 design patterns that continue to define contemporary software development. Eclipse IDE project resources from IBM: Learn what Eclipse is good for, why it is important, how you can get started with it, and where to learn more about it. The Java Tutorials: Get a comprehensive introduction to the Java language. The developerWorks Java technology zone: Hundreds of articles about every aspect of Java programming. Get products and technologies JDK 6: Download JDK 6 from Sun (Oracle).
Introduction to Java programming, Part 2: Constructs for real-world applications Page 51 of 53
developerWorks
ibm.com/developerWorks/
Eclipse: Download the Eclipse IDE for Java Developers. IBM developer kits: IBM provides a number of Java developer kits for use on popular platforms. Discuss Get involved in the My developerWorks community. Connect with other developerWorks users while exploring the developer-driven blogs, forums, groups, and wikis.
Page 52 of 53
ibm.com/developerWorks/
developerWorks
Page 53 of 53