MENU
Webucator was an excellent introduction to project management and prep for the exam. I am confident that as a result...More Testimonials »

Java Objects

In this lesson, you will learn all about object-oriented programming within Java.

Lesson Goals

  • Learn general Object-Oriented concepts
  • To declare object classes, defining methods and fields
  • To create instances of objects and use them in a program
  • To access objects using reference variables
  • To define different protection levels for elements
  • To define constructors
  • To overload methods and constructors
  • To use the this self-reference
  • To declare and use static elements
  • To create packages of classes

Objects

In general, the concept of an object is: a collection of data along with the functions to work with that data

  • as opposed to purely procedural languages where the data and functions are separate, and the data is passed to the function to work with

In Java, all code must be written inside object definitions

  • a class is an object definition, containing the data and function elements necessary to create an object
  • an instance of an object is one object created from the class definition (the process is known as instantiation)
  • the data and function elements are known as members
  • data members are also known as fields, properties, or attributes, and function members as methods

Methods can work with any fields of an object, as well as any other method

A constructor is a function that defines the steps necessary to instantiate one object of that class

  • if you do not explicitly create one, a default constructor that does nothing will be created automatically

Each class should generally be defined in its own file if it is to be used by other classes

  • the file name must match the name of the class, plus a .java extension

The data members of a class may be any type of data, objects or primitives

It is traditional in Java that only class names begin with capital letters

  • variable names, both object and primitive, begin with lowercase letters, as do method names
  • multi-word names, like first name, are done in Camel-case (as in firstName)

Object-oriented Languages

For a whirlwind tour of Object-Oriented Programming (OOP), an object-oriented language isĀ supposed to include three major concepts:

Encapsulation - that data and the functions to work with that data are contained within the same entity; encapsulation includes another concept: data hiding or protection - where direct access to data is restricted in favor of methods that manipulate the data

Polymorphism - that there can be different forms of the same thing, so that you could have one variable that could store several different types of objects; or a method that accepts no arguments and another method with the exact same name that accepts an integer argument (and maybe another that accepts a string and a double-precision argument, etc.).

Inheritance - that an object definition can use another object definition as a starting point and build upon that base object (in which case the original object definition is called the base class, parent class, or superclass and the new class is called the derived class, the child class, or subclass, respectively) - note that a derived class can be used as a base for further inheritance, creating a chain

Java uses all three of these concepts

Java does not allow any code that is not part of an object - all code in your program must be inside a class definition

Java forces inheritance on every class - if you do not explicitly inherit from a class, then by default Java assumes that you are inheriting from the class called Object (which does not do much, but does have several useful methods)

  • if you think about this, it implies that every class is descended from Object, since whatever class you inherit from must have inherited from something, which would be either Object or something that inherited from something, etc.
  • the concept of polymorphism implies that you could store any type of object in a variable whose type was Object

Object-oriented Programs

An object-oriented program replaces the concept of a linear sequence of steps with steps that create instances of classes, connect them together, and set them to communicate with each other

For example, in an HR/Payroll application, we might have object classes that represent employees, dependents, checks, departments, financial accounts, etc

The act of creating a check for an employee could trigger a number of actions:

  • update the employee's accumulated pay, etc., fields - done in an employee object
  • update accumulated department totals - performed in a department object
  • debit a financial account - done in an account object
  • cause a physical check to be printed - a check object would be created encapsulating the information, and sent to a printing module (in some sort of object dedicated to similar generic operations)
  • upon successful printing of the check, notifications would be sent to the above objects to finalize their operations

Encapsulation is the concept that the data and operations that work on or with that data are bundled together into one unit - the object

Objects are said to have state and behavior

  • the state is the current set of values for all the data values
  • terms commonly used to describe the state values are:
    • data members
    • fields
    • properties
    • attributes
  • the behavior is the set of operations that the object can perform (the functional code for the object)
    • commonly called methods, but also sometimes called functions

Encapsulation

In a procedural program, the executable code is usually kept in a separate area from the data values.

  • in general, the programmer will call the code in the entity where its data is located

In an object-oriented program, it will appear to the programmer that the data values are kept with the executable code.

  • each object has a memory area, which can be viewed as having a data block and a code block (note that this is a dramatic simplification - in practice the data is indeed kept separate from the data, but the compiler and/or runtime environment handles the details necessary to match an object's code with its data)
  • an entity's code automatically knows where its data is

Memory organization comparison

Encapsulation is often said to include the concept of data hiding, where the data elements of an object are not directly visible to the outside world. Instead, methods are provided to retrieve and set the values of the data elements.

  • forcing external code to go through code inside the class allows for validation of data; for example, if a field must have a value that is greater than or equal to zero, the method to set that field can perform a test on incoming data to ensure that it is valid

OOP as a Messaging System

One way to view an object-oriented environment is as a messaging system. Objects can send each other messages by calling their methods. In the following example, the code sends object p three messages, first to set its first name value to "James", then to set its last name to "Berger", and finally to ask it what the full name is (the result of which it passes on in a message to System.out, requesting that it be printed).

Person p = new Person();
p.setFirstName("James");
p.setLastName("Berger");
System.out.println(p.getFullName());

Creating and Using an Instance of an Object

This discussion applies to object types that have already been defined; we will look at how to define object types later.

When an object is actually created and stored, that is known as an instance of the class

  • the process is often called instantiating an object

The keyword new is almost always used to instantiate an object:

StringBuffer sb = new StringBuffer("Hello World");
  • the variable sb now holds an instantiated StringBuffer object (StringBuffer is a class from the Java API)
  • creating a String does not require the new keyword, text in quotes automatically becomes a String object:
String s = "Hello World";

To use an object, we need an expression that provides a reference to the object:

  • the dot operator ( . ) following an object reference gives access to the elements of that object
  • so, if we know that the String class has a method toUpperCase() that returns an uppercase version of the text, we can now call that method to get our message in uppercase:
s.toUpperCase();
  • we could use this in our Hello.java program as follows:
System.out.println(s.toUpperCase());
  • go ahead and modify Hello.java to print the message in uppercase

Object Definition

Duration: 5 to 15 minutes.

We would like to develop a simple employee database system that will include the ability to pay an employee (print a check for them, and processing necessary for bookkeeping). As a first step, we will define a type of object to represent an employee. Note that we are not trying to create an actual Java class. We are only interested in the sorts of things we think represent an employee. This is called pseudo code.

  1. Make a list of what data fields we would be likely to need (names and the type of data they would hold)
  2. In a separate list, what methods might be associated with an employee's data?

Solution:

Solutions/Objects00/Employee.java
public class Employee {
	
/*
Possible elements of an employee class include: 

Data fields:

	String first name
	String last name
	String middle name or initial
	String social security number
	int employee id
	Date hire date
	Date date of birth
	char? byte? gender
	char? byte? employee type
	int department
	double periodic pay amount
	double ytd amounts for pay and all withholdings
	char? byte? benefits plan selection
	String employee review information


Methods:

	get and set values for all properties
	pay them
	print various reports or report lines


	*/

}

The above is only a partial listing of what could very likely be a large and complex class in a real application.

References

When you create an object, it occupies some memory location that the JVM allocates at runtime.

If you then have a reference and set it equal to that object, the variable's memory location stores the numeric address of the memory location where the object is stored (often it is said that it points to the object's memory location; it is more appropriate to say that it references the object).

If you set one object variable equal to another, you do not have two copies of the object - you have one object and two variables that point to that object.

If you had the following:

MyClass myObject = new MyClass();
MyClass yourObject = myObject;

You would not end up with two copies of the same data, you would end up with two references to the same object (since all you really did was store another copy of the original object's memory address)

So, if you then changed the properties of myObject, the properties as seen through yourObject would change as well.

References and variables

Note: there is a clone() method available to copy an object's contents to a new object.

Although this is a technicality, it is an important one: certain Java classes are immutable, or final; that is, they cannot be changed once they are created. Note that:

  • String objects are immutable, as are the wrapper classes that hold primitive data types inside objects.
  • You can still set a String reference variable to a new value, but what you are doing is setting the reference to point to a different object.
  • The important point to note is that if there were other references to the original String, they would be unchanged - this follows from how references work, but is counter to how some languages work.

Reference Example

In the following code, the new keyword creates an object, in this case a new StringBuffer object.

Code Sample:

Java-Objects/Demos/References.java
public class References {
  public static void main(String[] args) {
    StringBuffer sb1 = new StringBuffer("Hello");
    StringBuffer sb2 = null;
    sb2 = sb1;
    sb2.append(" World");
    System.out.println(sb1);
  }
}
  • The first line creates a StringBuffer object containing the text Hello, then creates a reference variable called sb1 that points to that new object.
  • The second line creates a reference variable called sb2 that points to nothing (it is set to null).
  • The third line then sets sb2 to point to the same object that sb1 points to.

References

  • the last line changes sb2; the effect of this change is seen via sb1 as well

Effect of Multiple References to One Object

Reference Expressions

A reference is a simple form of expression. References that result from more complex expressions may be used as well. For, example, with an array of String references called names, a method for one element can be called in this manner:

names[1].toUpperCase()

Similarly, if a method returns a reference, a method may be called directly on the returned value.

Code Sample:

Java-Objects/Demos/ReferencesChained.java
public class ReferencesChained {
  public static void main(String[] args) {
    String ending = "World";
    StringBuffer sb1 = new StringBuffer("Hello");
    StringBuffer sb2 = null;
    sb2 = sb1;
    sb2.append(" ").append(ending);
    System.out.println(sb1);
  }
}

The StringBuffer.append method returns a reference to the same StringBuffer object, so that another call to append or any other method can be chained to the result.

Defining a Class

The basic syntax for defining a class is:

[modifiers] class ClassName {
	(field definitions, constructors, and methods go here)
}

Example (for overall format only, we will examine the pieces later):

Code Sample:

Java-Objects/Demos/BookBasic.java
public class BookBasic {
  private int itemCode;
  private String title;
  public int getItemCode() {
    return itemCode;
  }
  public void setItemCode (int newItemCode) {
    if (newItemCode > 0) itemCode = newItemCode; 
  }
  public String getTitle() {
    return title;
  }
  public void setTitle (String newTitle) {
    title = newTitle;
  }
  public void display() {
    System.out.println(itemCode + " " + title);
  }
}

Normally the access term for the class should be public, which means that the class can be used in other classes.

  • A public class definition must be in a file with a name that matches the class name, but classes with other access levels do not have that requirement.

Access to Data

Class definitions may have different access levels - this determines where an object of the class may be instantiated or otherwise used.

  • Class definitions are usually made freely available to all code, defined as public.

All data and function members in a class are available to any code written for that class.

  • Note that the normal scope rules for local variables apply to Java - that is, a method's internal variables are never available to other methods.

There are four possible access states. Three are declared with access keywords:

  1. public - the member may be freely accessed by code from any other class.
  2. protected - the member may not be accessed by code from any other class, except:
    • Classes that inherit from this class.
    • Classes in the same package (a package is a collection of classes - basically all the classes stored in the same directory).
  3. private - the member may not be accessed by code in any class, including classes that inherit from this one.

Then there is the fourth possibility, the default access used if you use no access word at all. Note that:

  • Any code in the same package (the same directory) can access the member.
  • This type of access is often called package access, and is also sometimes called default or friendly access.

Example - the example shown previously defines a class that will be publicly available, so that any class could instantiate one. The class listed below can use that class in its code, but cannot directly access the title or itemCode fields, so it must call the setTitle and setItemCode methods to set property values, and getTitle and getItemCode to retrieve values:

Code Sample:

Java-Objects/Demos/UseBookBasic.java
public class UseBookBasic {
  public static void main(String[] args) {
    BookBasic b = new BookBasic();
    b.setItemCode(5011);
    b.setTitle("Fishing Explained");
    System.out.println(b.getItemCode() + " " + b.getTitle());
    System.out.print("From display: ");
    b.display(); 
  }
}

More on Access Terms

What the access terms control is where the name of the item may be written in your code. Note that:

  • If the item is public, then the name of the item may be written within code for any other class.
  • If the item is private, then the name of the item may not be written within the code for any other class.
  • Package access is the default if no access term is specified; this allows the name of the item to be written within code for any class in the same package (which maps to the same directory).
  • If the item is protected, then the name of the item may not be written within the code for any other class, except one that extends this class, or a class in the same package.

For example:

  • If a class definition is public, then you can write the name of the class in code in some other class (so you can use the class).
  • If it is private, then you can't write the name in any other class, so you couldn't use it (in fact, a private class definition can only be used in one very special situation, to be covered later).

For another example:

  • If the class is public, and contains a public element, you can write the name of the class in another class, and you can write the name of the element in that class as well - and if the element is private, then you can't write its name, so you can't use it.
  • So, the UseBookBasic class code, which has a BookBasic variable b, can use b.getItemCode() and b.setItemCode(5011), but not b.itemCode.

Note: this does not mean that a private item cannot be affected by the code in another class; it just can't have its name written in that other class.

  • the BookBasic example shows how public methods can provide controlled access to a private element - in this case to ensure that the value of itemCode is never set to a negative value.

Adding Data Members to a Class

Data members are added using an access specifier, a data type keyword, and the field name.

[modifiers] class ClassName {
	[modifiers] dataType fieldName1, fieldName2, . . .;
	modifiers] dataType fieldName3 = value, . . .;
	(etc.)
}
  • Note that multiple members of the same type may be defined with one statement.
  • Variables may be given initializing values when declared.
  • Primitive data elements are initialized to 0 unless explicitly set to a value (and remember that for a boolean value a 0 bit means false) .
  • Member elements that are object reference variables are automatically set to null if not given a specific initial value (null is a keyword).

Revisiting our BookBasic example:

class BookBasic {
	private int itemCode;
	private String title;
	. . .
}
  • This class has an integer variable itemCode and a reference to a String object called title.
  • Neither of these can be directly accessed from outside the class definition, since they are private.

Adding Function Members (Methods) to a Class

Methods may be added in a similar manner. Nnote that in Java, unlike C++, the method body must be defined when the function is declared - you can't postpone that until later.

[modifiers] class ClassName {

	[modifiers] dataType fieldName1, fieldName2, . . . ;
	[modifiers] dataType fieldName3 = value, . . . ;
	
	[modifiers] returnType methodName(paramType paramName, . . . ) {
	
	[method body code goes here]
	
	}
	
		[more methods here]

Methods may freely access all data and function members of the class.

Standard Practices for Fields and Methods

It is considered a good practice to have fields be private, with access provided by "set and get" methods that are public.

Java has a naming convention that, for a field named value, there should be methods setValue (accepting a parameter of the same type as value) and getValue() which would return a value whose type is the same as value). Note that:

  • Get methods do not take any parameters; set methods take one parameter whose type matches the type of the field.
  • For boolean true/false values, the convention also uses is and has as prefixes for the get methods (as in isEnabled() for a field called enabled).

It is also considered a good practice to reuse existing methods for any steps that work with data in a way other than simple retrieval. If we had, for example, a method that accepted both itemCode and title, we would call the setItemCode() and setTitle() methods rather than access those fields directly. That way if our approach to setting a value changes, the effect is automatically applied through all methods that do that.

public void setItemCodeTitle (int newItemCode, String newTitle) {
	setItemCode(newItemCode);
	setTitle(newTitle);

}

Order of Elements within a Class

There is no requirement that the elements of a class be in any specific order. Any field or method can be accessed from a method, even if the referenced element is declared later. The compiler makes a pass through the code to identify all the elements, and then goes back through the code again armed with that information.

The most common practice is to list fields first, then constructors, then get/set methods, and then all the other methods. (There is a less commonly held belief that fields should be listed last.) Generally the static fields are listed before the non-static ones.

If there are dependencies between fields due to the value of one field being used to initialize another field, then that would impose an order on those fields.

private int range = 100;
private Random r = new Random();
private int answer = r.nextInt(range);
// answer must come after range and r,
// since it depends on them

Java Beans

If you follow the standard naming conventions, you are most of the way to creating a Java Bean class. Note that:

  • A bean is an object that can be created and used by various tools, both at development time and at run time.
  • Java has a mechanism known as reflection, inspection, or introspection that allows for classes to be instantiated and used without knowing at compile time what the class name is.
    • A bean can be instantiated using a String variable containing the class name.
    • Or a bean can be delivered to an application, for example, across a network connection.
    • At runtime, for any object, its methods and fields can be determined by the JVM, as it inspects the object.
    • Tools can then match field names to get and set methods and treat the fields as properties (to this point, we have used the terms fields and properties more or less interchangeably - in bean-speak fields are data values and properties are private fields that can be accessed by set and/or get methods).
  • Example uses of beans include:
    • Java-based GUI development tools can create instances of GUI component beans such as text fields, radio buttons, etc., and create a properties table where the programmer can enter values into text fields for values such as background color, foreground color, text value, etc. (keep in mind that the environment's JVM can inspect the component objects).
    • Java Server Pages can instantiate a bean and set and retrieve properties to display in a dynamic web page .
  • To be a bean, a class must:
    • Have a constructor that takes no parameters (the object will be created empty, and populated from the set methods - if there was a Rectangle class, for example, with height and width properties that were int, the tool wouldn't know how to call a constructor that took both parameters (which is the height and which is the width?).
    • Follow the naming convention stated above.
    • A bean can also have additional methods that code with advance knowledge of the class can call.

Bean Properties

Properties are defined by public get and set methods, and usually map to private fields.

  • A read-only property would have a get method, but no method to set the value - the property might or might not be backed by a field.
    public class Rectangle {
    
      private int height;
      private int width;
      
      public Rectangle() { }
    
      public int getHeight() {
        return height;
      }
    
      public void setHeight(int newHeight) {
        height = newHeight;
      }
    
      public int getWidth() {
        return width;
      }
    
      public void setWidth(int newWidth) {
        width = newWidth;
      }
    
      public int getArea() {
        return height * width;
      }
    }
    

    The class has a constructor that takes no parameters, plus methods to get and set the height and width. The area is a read-only property that is not backed by a field - it is calculated each time it is needed.

    public class UseRectangle {
    
      public static void main(String[] args) {
        Rectangle r = new Rectangle();
        r.setHeight(100);
        r.setWidth(250);
    
        System.out.println(
          "Rectangle of height " + r.getHeight() +
          " and width " + r.getWidth() +
          " has an area of " + r.getArea());
      }
    
    }

    This is a simple main class to create and use a Rectangle. A Java Server Page might use a similar approach to populate the height and width from values submitted from a form, and send back a page showing the area.

  • Less often, you might encounter a write-only property - a simplistic example might be a security bean that uses a key to encrypt and decrypt messages: you could set the key, but not retrieve it (and other methods would set an incoming encrypted message and retrieve the decrypted version, or set an outgoing message and retrieve the encrypted result).

Payroll01: Creating an Employee Class

Duration: 10 to 15 minutes.

After review with management, it is decided that we will store the following information for each employee:

  • Employee ID (an integral number, automatically assigned when a new employee is added).
  • First name and last name.
  • Department number (an integral value, call it dept).
  • Pay rate (a floating-point value, using a double).

In addition, we would like:

  • A method called getPayInfo() that will return a sentence with the employee's name, id, department, and pay rate amount.
  • A method called getFullName() that will return the first and last names separated by a space.
  1. Define a class called Employee with these characteristics, using standard practices for limiting data access and for method naming .
  2. In order to be useful, there should be methods to set and get all properties (except setting the employee id, which will happen automatically in a manner to be determined later; for now, just let it default to 0, but still provide a method to retrieve it).
  3. Create another class called Payroll with a main method, which should:
    • Instantiate an Employee object.
    • Set values for all the properties (name, department, and pay rate).
    • Call the getPayInfo() method to see the results.

Solution:

Solutions/Payroll01/Employee.java
public class Employee {
  private int id = 0;
  private String firstName;
  private String lastName;
  private int dept;
  private double payRate;

  public int getId() {  return id;  }

  public String getFirstName() { return firstName; }

  public void setFirstName(String fn) {
    firstName = fn;
  }

  public String getLastName() { return lastName; }

  public void setLastName(String ln) {
    lastName = ln;
  }

  public int getDept() { return dept; }

  public void setDept(int dp) {
    dept = dp;
  }


  public double getPayRate() { return payRate; }

  public void setPayRate(double pay) {
    payRate = pay;
  }

  public String getFullName() {
    return firstName + " " + lastName;
  }

  public String getPayInfo() {
    return "Employee " + id + " dept " + dept + " " +
           getFullName() + " paid " + payRate;
  }

}

Solution:

Solutions/Payroll01/Payroll.java
public class Payroll {  
  public static void main(String[] args) {
    Employee e1 = new Employee();
    e1.setFirstName("John");
    e1.setLastName("Doe");
    e1.setPayRate(6000.0);
    e1.setDept(2);
    System.out.println(e1.getPayInfo());
  }
}

Constructors

Every class should have at least one method: the constructor. This specifies code that must run to instantiate (usually to initialize) a new object from the class definition.

The constructor is a function with the same name as the class.

[modifiers] class ClassName {
		[modifiers] ClassName(paramType paramName, . . . ) {
			[code goes here]
		}
	}
  • The name of the constructor is the the same as the name of the class.
  • Constructors have no return type and do not return a value.
  • They make take input parameters, usually to populate the fields for the new object.
  • They may use the standard access keywords - usually they are public (but in some circumstances involving inheritance they are sometimes protected or private).

The methods in an object are available at construction time. While a constructor could set field values directly, it is considered a better practice to call the set methods to store any values. That way, any validation necessary on the data can be accomplished without duplicating the validating code.

Code Sample:

Java-Objects/Demos/BookWithConstructor.java
public class BookWithConstructor {

  private int itemCode;
  private String title;

  public BookWithConstructor(int newItemCode, String newTitle) {
    setItemCode(newItemCode);
    setTitle(newTitle);
  }
  public int getItemCode() {
    return itemCode;
  }
  public void setItemCode (int newItemCode) {
    if (newItemCode > 0) itemCode = newItemCode; 
  }
  public String getTitle() {
    return title;
  }
  public void setTitle (String newTitle) {
    title = newTitle;
  }
  public void display() {
    System.out.println(itemCode + " " + title);
  }
}

Note how the constructor can make use of the existing methods.

Code Sample:

Java-Objects/Demos/UseBookWithConstructor.java
public class UseBookWithConstructor {
  public static void main(String[] args) {
    BookWithConstructor b = 
        new BookWithConstructor(5011, "Fishing Explained");
    b.display();
  }
}

Instantiating Objects Revisited

You instantiate an object from a class using the new keyword.

objectReferenceVariable = new ClassName(parameters);
  • Note that the type of the object reference variable must match the type of the class being instantiated (or, for later, match a parent of that class).
  • You can pass parameters as long as the parameter list matches a constructor for that class.
  • Note that while the object reference is often stored in a variable, it does not have to be - an object can be instantiated in any situation where an object reference could be used, for example:
new BookWithConstructor(1234, "Help is on the Way").display();
System.out.println(new Integer(5));

When a new instance is created, the following sequence of events takes place (note that this includes only concepts we have covered thus far; later, we will expand the sequence as we cover additional topics):

  1. Memory is allocated in an appropriately sized block.
  2. The entire block is set to binary zeros (so every field is implicitly initialized to 0).
  3. Explicit initializations of fields take place.
  4. The constructor runs.
  5. The expression that created the instance evaluates to the address of the block.

Important Note on Constructors

If you create no constructors at all, you will get a default constructor that accepts no arguments and does nothing (but at least you can use it to instantiate an object).

If you write a constructor that accepts arguments, then you lose the default constructor. Note that:

  • If you still want to have a no-argument constructor that does nothing, you must explicitly write it.
  • Try modifying UseBookWithConstructor.java to add line like BookWithConstructor c = new BookWithConstructor();.

Payroll02: Adding an Employee Constructor

Duration: 10 to 15 minutes.
  1. Modify your Employee class to use a constructor that accepts parameters for the first and last names, department, and pay rate.
  2. Also add a constructor that takes no parameters and does nothing (as we discussed above).
    public Employee() { }
  3. In Payroll, modify main to add another Employee variable that receives an instance created using this constructor, then print their pay info.

Solution:

Solutions/Payroll02/Employee.java
public class Employee {
  private int id = 0;
  private String firstName;
  private String lastName;
  private int dept;
  private double payRate;
  
public Employee() { }
  
  public Employee(String firstName, String lastName, 
  			int dept, double payRate) {
    setFirstName(firstName);
    setLastName(lastName);
    setDept(dept);
    setPayRate(payRate);
  }


---- C O D E   O M I T T E D ----
}

Solution:

Solutions/Payroll02/Payroll.java
public class Payroll {  
  public static void main(String[] args) {

    Employee e1 = new Employee();
    e1.setFirstName("John");
    e1.setLastName("Doe");
    e1.setPayRate(6000.0);
    e1.setDept(2);
    System.out.println(e1.getPayInfo());

    Employee e2 = new Employee("Jane", "Smith", 15, 6500.0);
    System.out.println(e2.getPayInfo());
  }
}

Method Overloading

Methods can be overloaded to have several versions, all with the same name. Note that:

  • The difference is in the parameter lists, also known as the function signature.
  • Differences in return types are not enough to create a second version of a function, the parameter lists must be different.
[modifiers]returnType methodName(paramList) { . . . }
[modifiers]returnType methodName(differentParamList) { . . . }

Constructors are often overloaded, so that an object can be instantiated from different combinations of parameters.

This is an example of polymorphism - the concept that the same name may have several forms. Method overloading produces a functional polymorphism, since the same method name can be used in different ways. While polymorphism is mainly used to describe having one variable that can store different but related types due to inheritance, the concept also applies to overloaded methods at a smaller scale.

The combination of the method name and pattern of types in the parameter list is called the function signature. Within a class, every method and constructor must have a unique signature. Note that the return type is not considered part of the signature.

It is not required to keep the same return type for different versions of the same method. For example, the Math class has multiple max methods to determine the maximum of two parameters passed in. As you might expect, they are:

public static double max(double a, double b)
public static float max(float a, float b)
public static int max(int a, int b)
public static long max(long a, long b)

While technically this is not considered overloading, that is a minor issue of semantics.

Continuing our example:

Code Sample:

Java-Objects/Demos/BookMultiConstructor.java
public class BookMultiConstructor {
  private int itemCode;
  private String title;
  public BookMultiConstructor(int newItemCode, String newTitle) {
    setItemCode(newItemCode);
    setTitle(newTitle);
  }
  public BookMultiConstructor(String newTitle) {
    setItemCode(0);
    setTitle(newTitle);
  }

---- C O D E   O M I T T E D ----

}

Code Sample:

Java-Objects/Demos/UseBookMultiConstructor.java
public class UseBookMultiConstructor {
  public static void main(String[] args) {
    BookMultiConstructor b =
        new BookMultiConstructor(5011, "Fishing Explained");
    b.display();
    // note the immediate call to a method on a new instance
    new BookMultiConstructor("Dogs I've Known").display();
  }
}

Payroll03: Overloading Employee Constructors

Duration: 10 to 15 minutes.
  1. Add more Employee constructors:
    • One that takes values for first and last names only.
    • One that takes values for first name, last name, and department.
    • One that takes values for first name, last name, and pay rate.
    • Note that, in practice, you can use the parameter lists for constructors to help enforce what you would consider valid combinations of properties - for example, if you would not want an employee to exist in a state where they had name and department information, but no pay rate, then the absence of that particular constructor would help ensure that
    • judicious use of copy-paste-edit can speed up this process, but be careful to make every necessary edit if you do this!
  2. You will find yourself writing somewhat repetitive code, setting the same values the same way in several different constructors - we will address this in a few pages.
  3. In Payroll, create and pay additional instances using these constructors.

Solution:

Solutions/Payroll03/Employee.java
public class Employee {
  private int id = 0;
  private String firstName;
  private String lastName;
  private int dept;
  private double payRate;
  
  public Employee() {  }

  public Employee(String firstName, String lastName) {
    setFirstName(firstName);
    setLastName(lastName);
  }

  public Employee(String firstName,String lastName, int dept) {
    setFirstName(firstName);
    setLastName(lastName);
    setDept(dept);
  }

  public Employee(String firstName, String lastName, double payRate) {
    setFirstName(firstName);
    setLastName(lastName);
    setPayRate(payRate);
  }

  public Employee(String firstName, String lastName, 
  			int dept, double payRate) {
    setFirstName(firstName);
    setLastName(lastName);
    setDept(dept);
    setPayRate(payRate);
  }

---- C O D E   O M I T T E D ----
}

We now have five constructors, one for each of the combinations of parameters we will accept.

Solution:

Solutions/Payroll03/Payroll.java
public class Payroll {  
  public static void main(String[] args) {
    Employee e1 = new Employee();
    e1.setFirstName("John");
    e1.setLastName("Doe");
    e1.setPayRate(6000.0);
    e1.setDept(2);
    System.out.println(e1.getPayInfo());

    Employee e2 = new Employee("Jane", "Smith", 15, 6500.0);
    System.out.println(e2.getPayInfo());

    Employee e3 = new Employee("Bob", "Jones", 5400.0);
    System.out.println(e3.getPayInfo());

    Employee e4 = new Employee("Bill", "Meelater", 4);
    System.out.println(e4.getPayInfo());

  }
}

Employee e1 uses the default constructor (the one that takes no parameters, so that the set methods must be called to populate the object's data fields). e2 uses the full constructor. e3 uses the constructor that omits department - since we didn't set it, it prints as 0. e4 leaves off the pay rate, and, again, since we didn't set it, it prints as 0.0.

The this Keyword

The this keyword provides a reference to the current object. It is used to resolve name conflicts.

If you have a method that receives an input parameter String s, but s is already a member variable String reference.

  • class X {
    	String s;
    	public void setS(String s) {
    		this.s = s;
    	}
    }
    • Within the function, the parameter s is a local variable that will disappear when the function ends.
    • It hides the existence of the s member field.
    • But, we can pass the local variable's value into the field s by using the this reference to resolve s as the one that belongs to this object.
    • Some programmers always use this approach with constructor functions and set methods - the benefit is that the code is more self-documenting, since it would use the most appropriate variable name in both the class definition and in the method argument.

The this keyword is also used when the object's code needs to pass a reference to itself to the outside. Say you are creating MyClass. Some other class, YourClass, has a public method called useMyClass that needs a reference to a MyClass object; in other words, something like:

public void useMyClass(MyClass x) { . . . }

Your code for MyClass could do:

YourClass yC = new YourClass(); yC.useMyClass(this);

The this keyword is also used in a constructor function to call another constructor from the same class.

Using this to Call Another Constructor

As we have seen, to avoid duplicating code in different functions, any function in a class may call other functions in that class.

  • A function that is not a constructor can only call other functions that are not constructors (although it can still cause a constructor to run by instantiating an object).
  • A constructor may only call one other constructor, and it must be done as the first line in the function.
  • The this keyword is used instead of the name of the constructor, then arguments are passed in a normal fashion.

The following example uses this to provide the object with a reference to itself, as well as to chain to another constructor:

Code Sample:

Java-Objects/Demos/BookUsingThis.java
public class BookUsingThis {

  private int itemCode;
  private String title;

  public BookUsingThis(int itemCode, String title) {
    setItemCode(itemCode);
    setTitle(title);
  }
  public BookUsingThis(String title) {
    this(0, title);
  }


---- C O D E   O M I T T E D ----
}

The constructor that only accepts the title calls the other constructor, passing 0 as the item code. It would also be possible to set up the chain of constructors in the reverse direction, as in:

public BookUsingThis(int itemCode, String title) {
	this(title);
	setItemCode(itemCode);
}
public BookUsingThis(String title) {
	setTitle(title);
}

This approach is good if the default values of the fields (either zero or as set by an initializer) are acceptable - note that the constructor that accepts only title never sets the item code.

Code Sample:

Java-Objects/Demos/UseBookUsingThis.java
public class UseBookUsingThis {
  public static void main(String[] args) {
    BookUsingThis b = new BookUsingThis(5011, "Fishing Explained");
    b.display();
    new BookUsingThis("Dogs I've Known").display();
  }
}

The second book uses the constructor that accepts only the title. Since the new operator results in a reference to the book, we can chain a call to display to that result.

Payroll04: Using the this Reference

Duration: 10 to 15 minutes.
  1. Now we can clean up our Employee class definition.
  2. Modify the set methods to use the same name for each parameter as the associated field.
  3. Modify the constructors to eliminate redundant code - for example:
    public Employee(String firstName, String lastName, int dept) {
    	this(firstName, lastName); setDept(dept);
    }
  4. If you haven't already done so, make use of getFullName in getPayInfo.

Solution:

Solutions/Payroll04/Employee.java
public class Employee {
  private int id = 0;
  private String firstName;
  private String lastName;
  private int dept;
  private double payRate;
  
  public Employee() {
  }
  public Employee(String firstName, String lastName) {
    setFirstName(firstName);
    setLastName(lastName);
  }
  public Employee(String firstName,String lastName, int dept) {
    this(firstName, lastName);
    setDept(dept);
  }
  public Employee(String firstName, String lastName, double payRate) {
    this(firstName, lastName);
    setPayRate(payRate);
  }
  public Employee(String firstName, String lastName, 
  			int dept, double payRate) {
    this(firstName, lastName, dept);
    setPayRate(payRate);
  }

  public int getId() {  return id;  }

  public String getFirstName() { return firstName; }

  public void setFirstName(String firstName) {
    this.firstName = firstName;
  }

  public String getLastName() { return lastName; }

  public void setLastName(String lastName) {
    this.lastName = lastName;
  }

  public int getDept() { return dept; }

  public void setDept(int dept) {
    this.dept = dept;
  }


  public double getPayRate() { return payRate; }

  public void setPayRate(double payRate) {
    this.payRate = payRate;
  }

  public String getFullName() {
    return firstName + " " + lastName;
  }

  public String getPayInfo() {
    return "Employee " + id + " dept " + dept + " " +
           getFullName() + " paid " + payRate;
  }

}

static Elements

The keyword static states that one and only one of this element should be available at all times - whether there is one object of the class instantiated, many objects, or none at all

[modifiers] static dataType fieldName;
[modifiers] static returnType methodName(paramType paramName, . . .)

If a data member is static, there is one memory location that is shared among all instances of a class.

  • This allows the separate instances of one class a way of sharing data with each other.
  • That memory location exists even if no objects of the class exist - this is useful for creating constants.
  • Any initialization of this value occurs just once, when the class is loaded into the JVM.

If a method is static and public, then it is available even when no objects have been instantiated.

  • If a class has static fields, you may want to have methods to work with those fields. It would make sense that those methods be static as well.
  • Utility functions, like the random number and trigonometric functions in Math - they have to be part of a class, but you wouldn't want to have to instantiate some special object just to generate a single random number or calculate a cosine.
    • For more sophisticated random number generation, where repeatability of a sequence of random values might be desirable, there is a Random class.
  • One caveat with this approach is that the function cannot access any of the other elements (data members or methods) that are not also static, since it is not guaranteed that they will exist when the function is called.
  • Such an element is referenced using the name of the class, a dot, then the element name:
double x = Math.random(); double y = Math.sqrt(x);

The main Method

Now we can see that main, as a static method, can be run without any instance of the class. In fact, no instance is created automatically - the JVM only needs to load the class into memory.

If you create a class that has fields or methods not marked as static, and try to access them from main, you will get a rather cryptic error message about a non-static element referenced from a static context. The problem is that there isn't necessarily an instance of your class in memory, or there may be more than one. To call a non-static element, you must first create an instance and access the element for that instance.

The following example draws from the GUI world, where the JFrame class has a setVisible method that this class inherits. The main method creates an instance of its own class to display.

import javax.swing.*;
public class MyGUI extends JFrame{
	public static void main(String[] args) {
		MyGUI gui = new MyGUI();
		gui.setVisible(true);
	}
}

Payroll05: A static field in Employee

Duration: 5 to 10 minutes.
  1. Add a private and static integer field called nextId to the Employee class (give it an initial value of 1).
  2. Modify the declaration of the id field as follows:
    private int id = nextId++;
  3. What happens when each new Employee gets instantiated?
  4. Run and test the application - you should see incrementing employee ids.
  5. Now add the following to Employee.java:
    public static int getNextId() {
    	return nextId;
    }
    public static void setNextId(int nextId) {
    	Employee.nextId = nextId;
    }
    Notice the syntax we use to resolve the name conflict between the nextId parameter and the static nextId field.
  6. In Payroll, before we create the first employee, call Employee.setNextId(22) to set the value that will be used for the first employee.

Solution:

Solutions/Payroll05/Employee.java
public class Employee {
  private static int nextId = 1;
  private int id = nextId++;

  public static void setNextId(int nextId) {
    Employee.nextId = nextId;
  }
  public static int getNextId() {
    return nextId;
  }

  private String firstName;
  private String lastName;
  private int dept;
  private double payRate;

---- C O D E   O M I T T E D ----
}

When the Employee class is loaded, the nextId field is initialized to 1. This initialization only happens once, when the class is loaded.

Then, when each employee is created, the explicit initializer for that employee's id reads the current value of nextId, and then increments nextId to be ready for the next employee.

Garbage Collection

Java takes care of reclaiming memory that is no longer in use.

Your program is not making memory-allocation calls directly to the operating system - the JVM requests a fairly large block at start time, and handles memory allocation from that block. Note that:

  • When necessary, it can request additional blocks from the OS.
  • There are command line options to set the initial and maximum sizes.

When an object is no longer reachable through your code, it becomes subject to garbage collection. Note that:

  • The JVM has a low-priority thread that checks the graph of objects to find orphaned objects.
  • Those that are found are marked as available to be collected.
  • A separate process will run when necessary to reclaim that memory.
  • If your program has small memory requirements, it is possible that garbage collection will never run.

You can request garbage collection by calling System.gc(). Note that this may not have any effect; it is not guaranteed to run when you ask - the call is merely a request.

You can specify code to run when an object is collected by writing a finalize() method in a class. Note that:

  • There is no guarantee that this method will ever run.
  • When the program ends, any objects still reachable will not be collected, nor will any objects marked for collection but not yet collected - thus finalization will not occur for those objects.

Java Packages

As you have seen, Java programs may involve a large number of files.

Packages help organize files within a program, as well as to collect objects into groups to improve reusability of code.

A package structure is a tree structure that maps to a folder/directory structure. Note that:

  • Package names correspond to directory names.
  • A dot is used between levels of a multilevel structure.
  • Searching for files starts at the root level of the structure during compiling or class-load at runtime.
  • You can't use packages or directory structures without explicitly placing a class in a package via your code.
  • Classes are always referenced from the root of the structure, and the fully-qualified name of the class contains the relative path elements to reach the class from the root point, separated by dot characters.

To assign a class to a package, use a package statement as the first non-blank, non-comment line of code.

Code
Effect
package test;

Puts this class in the package called test in order to be found, the class file must be in a directory named test that directory must be located in one of the classpath locations.

package test.util;

Puts this class in the package called test.util in order to be found, the class file must be in a subdirectory of the test directory named util; test must be located in one of the classpath locations.

The accepted convention is that package names be all lowercase.

Example

package test;
public class XYZ { . . . }
  • XYZ.java should be in a directory called test that is located within a directory listed in our CLASSPATH.
  • The fully-qualified name of the class is test.XYZ.

When the compiler and JVM look for the files, they will search every CLASSPATH entry for a directory called test containing XYZ.java (or XYZ.class, if that is what is needed).

Compiling and Executing with Packages

The default behavior of the compiler in JDK 1.4 and later is to compile source code in a directory structure into class files in the same structure. Note that:

  • You can use the -d option with javac to put the class files into a separate, parallel directory structure.
    javac -d rootofstructure *.java
  • It will start the structure at the directory called rootofstructure.
  • It will create the package directories if they don't already exist.
    • In earlier versions of the JDK, all the source code, regardless of package, had to be in the same directory for that to work.

To run an executable Java class that belongs to a package, your command prompt should be at the directory level that matches the root of the package structure. You would then treat the class name as packagename.ClassName.

Example - the directory C:\MyProject is the root level of a package structure, and the main class, XYZ.class, is in the package called test (therefore test is a subdirectory of MyProject).

To run XYZ.class, the command line would look like the following:

C:\MyProject>java test.XYZ

Working with Packages

Once a package structure is created, all classes in it must be referenced with respect to the root of the structure. You have two choices:

  • Explicitly use the fully-qualified name of the class.
  • import either that specific class or the entire package.

Say your directory C:\Bookstore is the root of your project.

  • Iit contains UseBookInPackage.java (the main class).
  • Within that is a directory called types, which contains BookInPackage.java.

Declaring a package

The BookInPackage class would start like this:

package types;
public class BookInPackage {
	. . .
}

The package statement must be the first non-comment, non-blank line in the file.

Referencing Packaged Classes - Importing and Fully Qualified Names

In UseBookInPackage.java, you could import the entire types package, or just the class(es) you need

import types.*;

or

import types.BookInPackage;

then

public class UseBookInPackage {
	public static void main(String[] args) {
		BookInPackage b = new BookInPackage("My Favorite Programs");
		. . .
	}
}

Or, instead of importing, you could explicitly reference the class by its full name, which precedes the class name with the package structure (using a dot character as the directory separator)

public class UseBookInPackage {
	public static void main(String[] args) {
		types.BookInPackage b = new types.BookInPackage("My Favorite Programs");
		. . .
	}
}

This approach is necessary if your program uses two classes with the same name (such as java.sql.Date and java.util.Date).

All classes in the Java API are in packages. We have not had to deal with imports to this point because the package java.lang is special - it does not need to be imported. (This is where System, String, the numeric wrapper classes like Integer, and a few more generally useful classes reside.)

The package at the root level is called the default package.

When the main Class is in a Package

If your main class is in a package, then it must be compiled and run from the root of the structure.

For example, say your directory C:\Bookstore is the root of your project, but it contains no classes.

It contains a subdirectory called bookstore, which contains Bookstore.java (the main class)

package bookstore; public class Bookstore { . . . }
  • If that directory contained the types directory, the remainder of the code would be as before - we could import our types.Book class.

To compile the program from the C:\Bookstore directory, use the standard operating system directory separator character:

javac bookstore\Bookstore.java

To run the program, you must be in the C:\Bookstore directory, use the dot character as the separator character:

java bookstore.Bookstore

Note that the preferred practice is to have no classes in the default package.

  • Because the package has no name, there is no way to access it or any of its classes from any other package!
  • Because no other class needs to reference the main class, for now we will leave that in the default package.

static Imports

Java 5 introduced the static import, which enables you to import the static elements of a class. This can be used both for fields (usually constants) and methods. The syntax is:

import static packages.Class.element;
import static packages.Class.*;

These will import the specified elements so that they may be used within the current class, not only without the fully qualified package structure, but without even needing the class name.

Code Sample:

Java-Objects/Demos/TestStaticImport.java
import static java.lang.Math.*;

public class TestStaticImport {

  public static void main(String[] args) {
    double sin90 = sin(PI / 2);
    double sqrt = sqrt(4);
    
    System.out.println("Sine of 90 degrees is " + sin90);
    System.out.println("Square root of 4.0 is " + sqrt);
  }

}

We have imported every static element from java.lang.Math, giving us access to Math.PI, Math.sin(double), and Math.sqrt(double). Notice that the local variable sqrt does not conflict with the imported method, since without the parentheses for the parameter list, we are using the variable, with parentheses we are calling the method. If we did write a sqrt method in this class, that method would take precedence over the imported method; calling sqrt would invoke our local method, and we would need to qualify the call to sqrt as Math.sqrt in order to use the Math version.

Other Package Considerations

It is a recommended convention that classes created for external distribution use a package structure that begins with the creating organization's domain name in reverse order (for example, there are a few non-standard classes in the API provided by Sun that are in the com.sun package).

Sun recommends that package names be in all lowercase.

Package access does not necessarily limit you to working in the same directory; you could have the same directory structure from a different root point in the classpath.

Variable Argument Lists (varargs)

Java 5 added a new syntax for variable argument lists, called varargs. This enables you to write methods that take a variable number of arguments, in addition to any fixed arguments, as long as the variable arguments are all of the same type.

  • There may be 0 or more fixed parameters at the beginning of the parameter list, as in a regular parameter list.
  • There may be 0 or 1 varargs entries at the end of the parameter list; this single entry supports 0 or more parameters of the specified type.
  • The varargs parameter is specified as type..., as in int...
  • Within the method, the varargs entry is treated as an array.

Code Sample:

Java-Objects/Demos/Varargs.java
public class Varargs {
	
	public static void showDataOnly(int... values) {
		for (int i = 0; i < values.length; i++) {
			System.out.println("  " + values[i]);
		}
	}

	public static void showDataWithMessage(String message, int... values) {
	  System.out.println(message);
		showDataOnly(values);
	}
	
	public static void main(String[] args) {

		System.out.println("Below printed from showDataOnly");
		showDataOnly(2, 4, 5, 1, 7);
		
		showDataWithMessage(
				"This printed with showDataWithMessage", 2, 4, 5, 1, 7);
		
		showDataWithMessage("No values this time");
		
		int[] data = { 4, 5, 1, 3 };
		showDataWithMessage("This time we passed an array", data);
	}	
}

There are two varargs methods. The first has only the one varargs parameter, values. Within its code, values can be treated as an array. When we call the method, we can pass it any number of integer values, 0 or more.

The second method takes one String, followed by the varargs parameter. When we call it, we must pass at least the message, and can pass any number of integers after that. Note that the second method prints the message, and then delegates printing the values to the first method.

In main, we also try another approach, which is to pass an array for the varargs parameter. This also works. Because of this, it is not legal to write a method overloads like myMethod(int[] x) and myMethod(int... x), since they could both be invoked by passing an array, even though the array version could not be invoked by passing a comma-separated list of values!

Keyboard I/O Using the Console Class from Java 6

The Console class provides an easy way to access the keyboard. Since not all hardware running Java might actually have a console, or a program might have been launched using some automated process like a background job scheduler for which a console would not be available, earlier versions did not supply this. Starting with Java 6, if a console is available to the JVM, the System.console() method will return an instance of the Console class, otherwise, it will return null. (Unfortunately, Eclipse is one environment for which a console is not available, due to the way that your programs are launched within it.)

Console Class Methods
void flush() Flushes the console and forces any buffered output to be written immediately.
Console format(String format, Object...args) Writes a formatted string to this console's output stream using the specified format string and arguments. Returns this instance so that method calls may be chained.
Console printf(String format, Object...args) A convenience method to write a formatted string to this console's output stream using the specified format string and arguments. Returns this instance so that method calls may be chained.
Reader reader() Retrieves the unique Reader object associated with this console.
String readLine() Reads a single line of text from the console.
String readLine(String format, Object...args) Provides a formatted prompt, then reads a single line of text from the console.
char[] readPassword() Reads a password or passphrase from the console with echoing disabled.
char[] readPassword(String format, Object...args) Provides a formatted prompt, then reads a password or passphrase from the console with echoing disabled.
PrintWriter writer() Retrieves the unique PrintWriter object associated with this console.

String and PrintStream Methods

The format method listed above is also available as a static method in the String class, and, for convenience, in the PrintStream class (of which System.out is an instance). String.format returns the formatted String result, while the PrintStream version prints the formatted result and returns the PrintStream reference for chaining.

PrintStream also has the printf method, which again returns its own reference after printing the output.

The discussion below is a brief overview of formatting strings. The String class documentation of the format method has a link to a full discussion of all the formatting options.

Format Strings

The methods that accept a format string use the varargs (variable argument list) syntax added in Java 5. The format string contains placeholders, indicated by the % signs, at which values will be substituted. The values used will be any additional parameters you pass to the method as indicated by the Object... parameter. The percent sign is followed by one or more symbols that determine the type and format of the output for that value. Format specifiers are of the form:

%[argument_index$][flags][width][.precision]conversion

Where (in order of importance/frequency of use):

  • Conversion is a one or two letter code indicating the type of data - some conversion codes are:
    • d: integer
    • f: floating point
    • e: floating point in scientific notation
    • s: string
    • b: boolean (using true and false as output strings)
  • There is a large selection of two-letter codes for date/time values, some of which are:
    • tY:a four-digit year
    • ty: a two-digit year
    • tm: a two digit month number
    • tB: a full month name
    • tb: a three-letter month name abbreviation
    • td: a two-digit day of month (with leading zero if necessary)
    • te: a one or two digit day of month
    • tD: a date formatted as month/day/year (as in 02/05/2010)
    • tr: a time formatted as hours:minutes:seconds AM/PM (as in 09:05:00 AM)
  • Width is the minimum character width of the field.
  • Precision is the number of digits after the decimal point (these characters and the decimal point are included in the character count toward the width).
  • Flags modify the output , they include:
    • -: indicates left-alignment within a field, without this all values, even strings, are right-aligned
    • 0: pad a numeric field with 0 characters
    • (space): leave a space for a sign in positive numbers
    • ,: include grouping characters every three digits left of the decimal point
    • (:indicate negative values in parentheses
    • +: always include a sign on a number
  • Argument_index is the position of the argument in the parameter list, overriding the default of using the arguments in order - this is particularly useful with dates and times, where several pieces of the same value may be printed (day, month, year, etc.).
    • A dollar sign follows the argument index, in order to distinguish from a width number.
    • Note that the first value parameter is 1$.
  • %% prints a single percent sign.
  • %n produces a platform-specific end-of line sequence.

Note that many of the controls are locale-specific, so that month names, day names, the use of comma and dot for thousands or decimal separator, etc., will be determined by the machines locale setting.

Code Sample:

Java-Objects/Demos/TestConsole.java
import java.io.Console;
import java.util.Date;

public class TestConsole {
  public static void main(String[] args) {
    Console console = System.console();
    
    // using unformatted I/O
    String name = console.readLine("What is your name? ");
    console.printf("Hello, " + name + "\n\n");

    // using formatted I/O
    int age = Integer.parseInt(
        console.readLine("Tell me %s, how old are you? ", name));
    console.printf("Wow, you don't look a day over %d!%n%n", age - 10);
    
    // using formatted date/time
    console.printf("Today is %tD%nand it is now %tr%n%n",
        new Date(), new Date());
    
    // using argument order indicators
    console.printf("Today is %1$tD%nand it is now %1$tr%n%n",
        new Date());
    
  }
}

The first output concatenates strings to substitute values. The second uses a formatting string with a series of substitution values instead.

The third output line supplies the same date twice, in order to print two different parts of it, while the fourth output uses the parameter index so that we need only supply the date once.

So, in the formatting method parameter list, String format, Object...args, the format string is the first parameter, and the values to be formatted are the rest. Because their type is Object, they can actually be any type of object. But, how come it accepts primitives like integers? Hold onto that thought for just a while longer, we will cover that later in this lesson.

Keyboard Input Prior to Java 6

Java input is very "low-level" - it deals with raw bytes of data, but, we can use classes designed to make input easier.

Also, in general, input is error-prone, requiring our program to handle exceptions, situations requiring special attention. Even though the keyboard will never throw an exception, as an instance of input stream, its methods are marked as throwing IOException.

Since it would be nice to have interactive programs, the following code gives an example of reading data from the keyboard.

Code Sample:

Java-Objects/Demos/InputTest.java
import java.io.*;
public class InputTest {
  public static void main(String[] args) 
                     throws Exception {
    BufferedReader in = 
            new BufferedReader(
                    new InputStreamReader(System.in));
    System.out.println("Enter a string");
    String s = in.readLine();
    System.out.println("Enter an integer");
    int i = Integer.parseInt(in.readLine());
    System.out.println("Enter a character");
    char c = (char) in.read();
    System.out.println("s=" + s + " i=" + i + " c=" + c);
  }
}

Briefly:

  • The import statement enables you to use classes not in the default set (in this case the input-related classes) - the API documentation lists the package for each class.
  • The raw input class System.in is filtered using an InputStreamReader, which allows it to recognize input as text characters (the basic InputStream is plugged into a more complex object that does additional processing).
  • Then, the resulting InputStreamReader is plugged into a BufferedReader that buffers keyboard input until it recognizes the Enter keystroke.
  • Lines can now be read using readLine(), and characters can be read using read() (note that they are read as integers, thus the typecast).
  • A sequence of digits can be parsed using a static method of the Integer class.
  • Since the whole input and parsing process is error-prone, we will avoid issues by having mainthrow an exception (much more on exceptions later).

A Keyboard Reader Class

The following class, KeyboardReader.java, can be used to read from the keyboard:

Code Sample:

Java-Objects/Demos/util/KeyboardReader.java
package util;
import java.io.*;

public class KeyboardReader {

  private static BufferedReader in =
      new BufferedReader(new InputStreamReader(System.in));
  
  public KeyboardReader() { }
  
  public static String getPromptedString(String prompt) {
    String response = null;
    System.out.print(prompt);
    try {
      response = in.readLine();
    } catch (IOException e) {
      System.out.println("IOException occurred");
    }
    return response;
  }
  
  public static char getPromptedChar(String prompt) {
    return getPromptedString(prompt).charAt(0);
  }
  
  public static int getPromptedInt(String prompt) {
    return Integer.parseInt(getPromptedString(prompt));
  }
  
  public static float getPromptedFloat(String prompt) {
    return Float.parseFloat(getPromptedString(prompt));
  }
  public static double getPromptedDouble(String prompt) {
    return Double.parseDouble(getPromptedString(prompt));
  }
}

The approach shown above is used in a class with all static elements. A static BufferedReader is created when the class is loaded, and used by a set of static methods that retrieve specific types of data from the keyboard (String, char, int, float, and double).

The getPromptedString method prints the prompt and retrieves the String entered in response. It handles the IOException that readLine is specified as throwing (even though the keyboard can never actually throw that exception, the methods in the classes involved, like BufferedReader and InputStream (used behind the scenes), can throw those exceptions when used with other types of streams, like those coming from files. This is one case where inheritance can actually get in the way, by adding a bit of complexity as the cost of having a system that will work for all types of input streams.

Since all keyboard input starts as a String, the methods to retrieve other types of data reuse getPromptedString, and then process the result to obtain the required type of data. One benefit of this approach is that the potential for an exception has been handled, so we don't have to worry about that again in these methods.

Since many organizations have not yet migrated to Java 6, the remainder of the demos and exercises use KeyboardReader. (Plus, it has a few extras that Console does not, like the parsing of numeric values.) Feel free to modify these to use Console I/O if you are an a Java 6 environment - you might want to create a revised KeyboardReader that uses the Console methods, for example.

Payroll06: Creating an employees package

Duration: 5 to 10 minutes.
  1. Create a package called employees; put the Employee.java file into this package (leave Payroll where it is).
  2. This will require not only creating the directory and moving the file, but also adding an import statement to Payroll and a package statement to Employee.
  3. Also, if Employee.class still exists in the original directory, it's presence may cause compilation problems - delete Employee.class (the IDE's call this doing a clean) .
  4. To compile, start in the project root directory (the directory containing Payroll). If you compile Payroll as usual, it will also find and compile Employee. To compile just Employee, you would still work in the project root directory, but execute.
    javac employees\Employee.java
  5. Run Payroll in the usual fashion.

Solution:

Solutions/Payroll06/employees/Employee.java
package employees;

public class Employee {

---- C O D E   O M I T T E D ----

}

Solution:

Solutions/Payroll06/Payroll.java
import employees.*;

public class Payroll {  
  public static void main(String[] args) {
    Employee e1 = new Employee();
    e1.setFirstName("John");
    e1.setLastName("Doe");
    e1.setPayRate(6000.0);
    e1.setDept(2);
    System.out.println(e1.getPayInfo());

    Employee e2 = new Employee("Jane", "Smith", 15, 6500.0);
    System.out.println(e2.getPayInfo());

    Employee e3 = new Employee("Bob", "Jones", 5400.0);
    System.out.println(e3.getPayInfo());

    Employee e4 = new Employee("Bill", "Meelater", 4);
    System.out.println(e4.getPayInfo());

    Employee e5 = new Employee("Della", "Kitessen");
    System.out.println(e5.getPayInfo());

  }
}

Payroll07: Using KeyboardReader in Payroll

Duration: 10 to 20 minutes.
  1. Add another employee to your payroll, this time using the KeyboardReader class to prompt the user for all data; for example, to read a double-precision value for a width:
    double width = KeyboardReader.getPromptedDouble("Please enter the width: ");
  2. KeyboardReader is in the util package (which should already be present under Exercises); you will need to import the class in Payroll.java.
  3. There are no changes to Employee.java.

Solution:

Solutions/Payroll07/Payroll.java
import employees.*;
import util.*;

public class Payroll {  
  public static void main(String[] args) {
    String fName = null;
    String lName = null;
    int dept = 0;
    double payRate = 0.0;

    fName = KeyboardReader.getPromptedString("Enter first name: ");
    lName = KeyboardReader.getPromptedString("Enter last name: ");
    dept = KeyboardReader.getPromptedInt("Enter department: ");
    payRate = KeyboardReader.getPromptedDouble("Enter pay rate: ");
    Employee e1 = new Employee(fName, lName, dept, payRate);

    fName = KeyboardReader.getPromptedString("Enter first name: ");
    lName = KeyboardReader.getPromptedString("Enter last name: ");
    dept = KeyboardReader.getPromptedInt("Enter department: ");
    payRate = KeyboardReader.getPromptedDouble("Enter pay rate: ");
    Employee e2 = new Employee(fName, lName, dept, payRate);

    fName = KeyboardReader.getPromptedString("Enter first name: ");
    lName = KeyboardReader.getPromptedString("Enter last name: ");
    dept = KeyboardReader.getPromptedInt("Enter department: ");
    payRate = KeyboardReader.getPromptedDouble("Enter pay rate: ");
    Employee e3 = new Employee(fName, lName, dept, payRate);

    System.out.println(e1.getPayInfo());
    System.out.println(e2.getPayInfo());
    System.out.println(e3.getPayInfo());

  }
}

This code goes a bit beyond the requested changes, creating three employees instead of just one.

String, StringBuffer, and StringBuilder

Java has three primary classes for working with text.

The String class is optimized for retrieval of all or part of the string, but not for modification (it is assumed to be written once and read many times). Note that:

  • This comes at the expense of flexibility.
  • Once created, a String object is immutable, that is, its contents cannot be changed.
  • Methods like toUpperCase() that seem like they would change the contents actually return a brand-new String object with the new contents.

The StringBuffer and StringBuilder classes are optimized for changes. Note that:

  • They are essentially the same as far as available methods, but StringBuilder is not safe for multithreaded applications while StringBuffer is (but StringBuilder is faster because of that).
  • They are useful for building a long string from multiple pieces, or for text-editing applications.
  • Among the useful methods are:
    • append(String s) and other forms accepting various primitive data types and a form that accepts another StringBuffer object.
    • insert(int position, String s) to insert text at (in front of) the specified position; again, various forms for different types of data.
    • delete(int start, int end) to delete characters from the start position up to but not including the end position.
    • toString() will convert the contents of the object to a String object.
  • Note that the modifying methods listed above modify the contents of the object, but also return a this reference, (a reference to the same object) - this enables chaining of method calls:
    StringBuffer sb = new StringBuffer();
    sb.append("Hello").append(" World");
    System.out.println(sb.toString());

Creating Documentation Comments and Using javadoc

Java documentation can be created as part of the source code. If you embed javadoc comments in your code the javadoc documentation engine supplied with the jdk will create a set of interlinked HTML pages, one for each class.

Javadoc Comments

A javadoc comment is a block comment beginning with /**, and ending with */

  • They may be used before any documentable item: (classes, fields, constructors, and methods - if used in other places they will be ignored by the documentation engine).
  • Use them to provide descriptions of the item.
  • Many HTML tags are valid within the comment - in particular, formatting tags like <em> and <code> are often used, as well as lists.
  • There are a number of special block tags that begin with @, such as:
    • @param documents a method or constructor parameter.
    • @return documents a return value.
    • {@link BookWithJavadoc} creates a hyperlink to the specified class page.
javadoc options filelist

Command line options include:

  • -d destinationdirectory to produce the output in a specific directory, which need not already exist (default is current directory).
  • Options for access levels to document (each option generates documentation for that level and every more accessible level - e.g., protected documents public, package, and protected elements)
    • -private
    • -package
    • -protected
    • -public

The file list can use file names, package names, wildcards, like *.java (separate multiple elements with spaces).

The following command places the output in a subdirectory docs of the current directory, and documents the "default package" and the employees package (use this in the upcoming exercise):

javadoc -d docs -private employees *.java

See Sun's How To reference for more information.

Code Sample:

Java-Objects/Demos/BookWithJavadoc.java
/**
 *  Represents a Book in inventory,with an item code and a price
 */
public class BookWithJavadoc {

  /**
  * The book's item code
  */
  private int itemCode;

 /**
 * The title of the book
 */
  private String title;

 /**
 * Creates a book instance.
 * @param itemCode the book's item code.  It is expected
 *  that the value will be non-negative; a negative value
 *  will be rejected.
 * @param title the title of the book
 */
  public BookWithJavadoc(int itemCode, String title) {
    setItemCode(itemCode);
    setTitle(title);
  }

 /**
 * Creates a book instance,  The item code will be set to 0.
 * @param title the title of the book
 */
  public BookWithJavadoc(String title) {
    setItemCode(0);
    setTitle(title);
  }

 /**
 * Retrieves the item code for the book.
 * @return the book's item code
 */
  public int getItemCode() {
    return itemCode;
  }

 /**
 * Sets the item code for the book.  It is expected that the value will
 *  be non-negative; a negative value will be rejected.
 * @param itemCode the book's item code
 */
  public void setItemCode (int itemCode) {
    if (itemCode > 0) this.itemCode = itemCode; 
  }

 /**
 * Retrieves the title of the book.
 * @return the title of the book
 */
  public String getTitle() {
    return title;
  }

 /**
 * Sets the title of the book.
 * @param title the title of the book
 */
  public void setTitle (String title) {
    this.title = title;
  }
  public void display() {
    System.out.println(itemCode + " " + title);
  }
}

Even though some of the documentation will often be trivial, parameters and return values should always be documented. Any restrictions on parameter values, such as the non-negative parameters, should be mentioned.

Code Sample:

Java-Objects/Demos/UseBookWithJavadoc.java
/**
* Tests the {@link BookWithJavadoc} class.
*/
public class UseBookWithJavadoc {
  
  /**
   * Tests the {@link BookWithJavadoc} class.
   */
  public static void main(String[] args) {
    BookWithJavadoc b = new BookWithJavadoc(5011, "Fishing Explained");
    b.display();
  }
}

Note the use of the {@link} tag, since nothing else (parameter or return types) would mention the BookWithJavadoc class otherwise.

Payroll08: Creating and Using javadoc Comments

Duration: 5 to 10 minutes.
  1. Add javadoc comments to all the fields and methods in Employee. Don't worry about making them completely descriptive, but do document parameters and return values for the methods. Note that judicious use of copy and paste can speed up the process.
  2. Run javadoc and view index.html in a browser.

Solution:

Solutions/Payroll08/employees/Employee.java
package employees;
/**
  Represents an Employee
*/
public class Employee {
/**
Holds the id of the next instance
*/
  private static int nextId = 1;
/**
Stores the current next id value in this instance and
increments <code>nextId</code>
*/
  private int id = nextId++;
/**
Stores the first name
*/
  private String firstName;
/**
Stores the last name
*/
  private String lastName;
/**
Stores the department number
*/
  private int dept;
/**
Stores the pay rate
*/
  private double payRate;
  
/**
Constructs an empty Employee
*/
  public Employee() {
  }
  
/**
Constructs an Employee with first and last names
@param  firstName the first name
@param lastName the last name
*/
  public Employee(String firstName, String lastName) {
    setFirstName(firstName);
    setLastName(lastName);
  }
  
/**
Constructs an Employee with first and last names, and department number
@param  firstName the first name
@param lastName the last name
@param dept the department number
*/
  public Employee(String firstName,String lastName, int dept) {
    this(firstName, lastName);
    setDept(dept);
  }
  
/**
Constructs an Employee with first and last names, department number,
and pay rate
@param  firstName the first name
@param lastName the last name
@param payRate the pay rate
*/
  public Employee(String firstName, String lastName, double payRate) {
    this(firstName, lastName);
    setPayRate(payRate);
  }
  
/**
Constructs an Employee with first and lsst names, department number,
and pay rate
@param firstName the first name
@param lastName the last name
@param dept the department number
@param payRate the pay rate
*/
  public Employee(
  	String firstName, String lastName, int dept, double payRate) {
    this(firstName, lastName, dept);
    setPayRate(payRate);
  }

/**
Retrieves the next id
@return the nextId value
*/
  public static int getNextId() {
    return nextId;
  }
  
/**
Retrieves the next id
@return the nextId value
*/
  public static void setNextId(int nextId) {
    Employee.nextId = nextId;
  }
  
/**
Retrieves the id
@return the id
*/
  public int getId() {  return id;  }

/**
Retrieves the first name
@return the first name
*/
  public String getFirstName() { return firstName; }

/**
Sets the first name
@param firstName the first name
*/
  public void setFirstName(String firstName) {
    this.firstName = firstName;
  }

/**
Retrieves the last name
@return the last name
*/
  public String getLastName() { return lastName; }

/**
Sets the last name
@param lastName the last name
*/
  public void setLastName(String lastName) {
    this.lastName = lastName;
  }

/**
Retrieves the department
@return the department
*/
  public int getDept() { return dept; }

/**
Sets the department
@param dept the department
*/
  public void setDept(int dept) {
    this.dept = dept;
  }


/**
Retrieves the pay rate
@return the pay rate
*/
  public double getPayRate() { return payRate; }

/**
Sets the pay rate
@param payRate the pay rate
*/
  public void setPayRate(double payRate) {
    this.payRate = payRate;
  }

/**
Retrieves the full name as first name and last name separated by a space
@return the full name
*/
  public String getFullName() {
    return firstName + " " + lastName;
  }

/**
Retrieves a pay information string
@return a String with the pay information
*/
  public String getPayInfo() {
    return "Employee " + id + " dept " + dept + " " +
           getFullName() + " paid " + payRate;
  }

}

Solution:

Solutions/Payroll08/Payroll.java
import employees.*;
import util.*;

/**
* Tests the {@link Employee} class.
*/

public class Payroll {  

  public static void main(String[] args) {
    String fName = null;
    String lName = null;
    int dept = 0;
    double payRate = 0.0;

    fName = KeyboardReader.getPromptedString("Enter first name: ");
    lName = KeyboardReader.getPromptedString("Enter last name: ");
    dept = KeyboardReader.getPromptedInt("Enter department: ");
    payRate = KeyboardReader.getPromptedDouble("Enter pay rate: ");
    Employee e1 = new Employee(fName, lName, dept, payRate);

    fName = KeyboardReader.getPromptedString("Enter first name: ");
    lName = KeyboardReader.getPromptedString("Enter last name: ");
    dept = KeyboardReader.getPromptedInt("Enter department: ");
    payRate = KeyboardReader.getPromptedDouble("Enter pay rate: ");
    Employee e2 = new Employee(fName, lName, dept, payRate);

    fName = KeyboardReader.getPromptedString("Enter first name: ");
    lName = KeyboardReader.getPromptedString("Enter last name: ");
    dept = KeyboardReader.getPromptedInt("Enter department: ");
    payRate = KeyboardReader.getPromptedDouble("Enter pay rate: ");
    Employee e3 = new Employee(fName, lName, dept, payRate);

    System.out.println(e1.getPayInfo());
    System.out.println(e2.getPayInfo());
    System.out.println(e3.getPayInfo());

  }
}

Primitives and Wrapper Classes

Since it is sometimes useful to treat a primitive value as if it were an object, the API provides a set of wrapper classes that store a primitive value inside an object, and also have useful methods for that type of primitive. Note that:

  • There is a matching wrapper class for each primitive type.
  • There are also two additional wrapper classes that allow for arbitrarily large values for floating point values and integer values, respectively: BigDecimal and BigInteger.
  • The wrapper classes each have a constructor that take a value of the associated primitive type.
  • All of the numeric wrapper classes have all of the following methods to retrieve numeric values (they are inherited from the Number class that all this group of classes extend):
    • public byte byteValue()
    • public short shortValue()
    • public int intValue()
    • public long longValue()
    • public float floatValue()
    • public double doubleValue()
  • The other wrapper classes each have one method for retrieving the value:
    • Boolean has public boolean booleanValue()
    • Character has public char charValue()
Primitives and Wrapper Classes
Primitive Type
Storage Size
Wrapper Class
Comments
boolean 1 bit Boolean Not usable mathematically, but can be used with logical and bitwise operators
char 16 bits Character Unsigned, not usable for math without converting to int
byte 8 bits Byte Signed
short 16 bits Short Signed
int 32 bits Integer Signed
long 64 bits Long Signed
float 32 bits Float Signed
double 64 bits Double Signed
void None
N/A object reference BigInteger Can store arbitrarily large or small values - cannot be used with math operators like +, -, etc., but have methods to perform those operations.
BigDecimal

Autoboxing and Unboxing

Up through Java 1.4, you were required to explicitly code the construction of a wrapper class object from your primitive value, and to explicitly retrieve the primitive value:

int x = 33;
Integer xObj = new Integer(x);
// then, later on
int y = xObj.intValue();

While you can still take this approach, Java 1.5 added Autoboxing, where the primitive to object conversion and the reverse conversion will occur automatically (the compiler will put in the necessary steps)

int x = 33;
Integer xObj = x;
// then, later on
int y = xObj;

Now, back to the formatting methods and the Object..., parameter. Because of autoboxing, it can accept primitives, which will be boxed into wrapper objects. Hence, an int can now be used as an Object value for the varargs parameter - the compiler will add code to box the int into an Integer, which is then acceptable as the Object expected by the method.

While a useful coding convenience, there are some tricky rules regarding autoboxing when combined with implicit typecasting and method overloading, which are beyond the scope of this course.

Comparisons and Flow Control Structures → ← Java Basics

Client Success
  1. Compare Us
  2. Client List
  3. Testimonials
Join The Team
  1. Learn how you can become a Webucator Trainer
  2. Career Opportunities
Locations
© Webucator, Inc. All rights reserved. |Toll Free: 1-877-932-8228Toll Free: 1-877-932-8228 |Outside the USA: 315-849-2724|Fax: 315-849-2723