Experiments in Java


Session J2: Objects and Methods

Objects

You may have heard that Java is an object-oriented language. What does that mean? Being an object-oriented language means that Java programs are collections of objects that communicate with each other. An object can be almost anything, from a number to a virtual person. Each object has certain attributes, such as the value of the number or the eye color of the virtual person. In addition, each object has certain abilities, which are provided by methods. For example, a number might be able to add itself to another number, a virtual person might be able to blink, and an electronic book might be able to tell you where to find information on a particular topic.

In Java, objects are grouped into classes. A class specifies the methods each object provides and the kinds and names of attributes each object in the class has. Objects differ in the particular values associated with attributes. In addition, objects with different values for their attributes will most likely work differently. For example, adding the number 2 is different than adding the number 3.

What does this mean in terms of the nitty-gritty details? In particular, how do you use objects when you are programming? As you've seen, Java programs are based on a main method. You can use this method to create objects and then tell them to do things (you ask them to execute methods). In fact, in the experiments you did in the previous lab you may have created SimpleInput and SimpleOutput objects. You then used the SimpleOutput objects to write output to the computer screen and the SimpleInput objects to read input from the keyboard. You've also used Java's built-in String objects.

What objects can you use other than these three objects? Java comes with thousands of built-in objects. More importantly, you can define your own objects. Today, you will use and extend an existing class, Point. You will also build your own very simple classes, PointPrinter and PointReader.

Creating objects

To use an object in Java, you must first create the object and make a variable refer to that object. For example, to create a new SimpleOutput object and have out refer to it, we use

SimpleOutput out = new SimpleOutput();

As this suggests, you need the new command to create objects. In addition, you need to construct the object you are creating. A constructor is a method used to create new objects that initializes the values of the object.

For example, suppose we had a class of objects representing dates, with corresponding year, month, and day. In order to create a new date, we'd need to specify the year, month, and day. Hence, the constructor would need three values. Here's an example in which we create the date April 1, 1998.

SimpleDate date = new SimpleDate(1998, 4, 1);

Note that the year, month, and day are given as values in parentheses following the name of the class. These are called the parameters to the constructor. (You may also see them referred to as arguments, actual parameters, or actuals.)

How did I know which order to use when building the object? I looked at the documentation. The person who designs a class determines the available constructors and the order in which parameters to constructors are supplied. The clients of a class must consult documentation to determine which constructors are available and how they should be used.

Similarly, if we were to create the point (2,3) in the plane, we might write

Point pt = new Point(2,3);

Note that we didn't need any parameters when creating a new SimpleOutput object. That's because the default for these objects is to print to the screen and they don't need any other information. There is also a no parameter constructor for Point that creates a point at (0,0).

What can you do with a point once you've created one? It's somewhat up to the designer. You might move it left, right, up, or down. You might ask for its value or its distance from the origin.

The following is a simple program that creates a point, moves it a little, and then prints out a little bit of information about it.


import Point;

/**
 * Build a point and move it around.
 *
 * @version 1.1 of September 1998
 * @author Samuel A. Rebelsky
 */
public class PointFun {
  /**
   * Build a point and move it around.
   */
  public static void main(String[] args) {
    // We'll be generating some output.
    SimpleOutput out = new SimpleOutput();
    // The point we'll be playing with.
    Point pt = new Point(2,3);
    // Print some basic information.
    out.println("(" + pt.getX() + "," + pt.getY() + ")");
    out.println("  distance from origin: " + 
                pt.distanceFromOrigin());
    // Move it right a little bit.
    out.println("Shifting right by 0.7");
    pt.shiftRight(0.7);
    // Print current information.
    out.println("(" + pt.getX() + "," + pt.getY() + ")");
    out.println("  distance from origin: " + 
                pt.distanceFromOrigin());
    // Move it up a little bit.
    out.println("Shifting up by 2.5");
    pt.shiftUp(2.5);
    // Print current information.
    out.println("(" + pt.getX() + "," + pt.getY() + ")");
    out.println("  distance from origin: " + 
                pt.distanceFromOrigin());
    // Move it left a little bit.
    out.println("Shifting left by 10.2");
    pt.shiftLeft(10.2);
    // Print current information.
    out.println("(" + pt.getX() + "," + pt.getY() + ")");
    out.println("  distance from origin: " + 
                pt.distanceFromOrigin());
  } // main(String[])
} // class PointFun


What should you observe about this? First, that whenever we need information from an object we need to request it by calling a method. Second, that the ability to get information does not tell us much about how the object is designed (we'll come back to this later). For example, is the distance of the point from the origin stored within the point or only computed on demand?

In Experiment J2.1 you will consider some issues in using and printing points.

Coordinating objects

While it is certainly useful to deal with points numerically, many times we want to see them plotted on the plane. If we were given a Plane class, how might we plot some points?

Build a new Plane object.
For each point,
  Build a new point.
  Plot it.

Here is a simple class which does just that.


import Plane;

/**
 * A simple attempt to play with the Plane and Point classes.
 *
 * @version 1.0 of May 1998
 * @author Samuel A. Rebelsky
 */
public class JustPlaneFun {
  /**
   * Build a plane and display it.
   */
  public static void main(String[] args) {
    // Build the plane.
    Plane plane = new Plane();
    // Plot a few points.
    plane.plot(new Point(1,3));
    plane.plot(new Point(-2.5,4));
    plane.plot(new Point(4,-5.2));
  } // main(String[])
} // class JustPlaneFun


You should observe a number of things about this class.

In Experiment J2.2 you will consider how one uses the Point and Plane classes together, using the JustPlaneFun class to coordinate the two.

Building your own methods

Note that in our experiments and programs above, we had a number of operations in common. In PointFun.java, we printed the point in the same way again and again and again. If we made a change in the way we decided to print points, we'd need to modify a great deal of code. It would be useful to write the instructions once and then simply say something like ``print out this point''.

Similarly, in the experiments we needed to write instructions for reading in a point in different programs (both in PointFun.java and JustPlaneFun.java). Here it would be useful to just write ``read in a point''.

Can we create such shorthands? Yes! We will make a copy of Point.java and develop methods that provide the shorthands.

What is involved in a method definition? Four things:

It is also helpful to begin each method with a short comment that describes what the method does.

We now have the tools to build a method that prints out a point and its distance from the origin. We will add this method to the Point class. What parameters will this method need? The SimpleOutput object that does the printing. Why is there no parameter for the point to print? Because Java's object-oriented design suggests that each point will provide this method, hence the point to print is implicit. (Just as we write pt.getX(), we'll write pt.print(out).)

The method header therefore begins

public void print(SimpleOutput out) {

What does the body of the method look like? More or less like the code we used elsewhere.

  out.println("(" + this.getX() + "," + this.getY() + ")");
  out.println("  distance from origin: " + 
              this.distanceFromOrigin());

The this refers to the current object. Since getX and getY must be called for a particular object, we need to specify that object. Since we want to print the current object, we use this.

As always, you must end the method with a right curly brace.

How do we use our new method? Just like the methods we've used in the past. We create the object and then call the method.

Point pt = new Point(2,3);
pt.print(out);
Point another = new Point(4,2);
another.print(out);

What should you observe about this new method? First, we've added a name to some common code and parameterized that common code, which means that we can use it even with points with different names.

In Experiment J2.3 you will add the new print method to the Point class.

Creating your own classes

What if you'd like to write print, but you do not have access to the source code for Point? (In commercial settings, you will rarely have access to the source code for the classes you use.) As you will see in a later session, you can extend existing classes. However, that is beyond this introductory session. For now, the best solution may be to create your own new class that knows how to print points. Whenever you want to print a point, you create an object in this class. Some programmers consider such helper classes an odd design tactic. However, at this point in your career you will find many benefits from using them.

How do we begin a new class? As in past experiments, begin with

public class PointPrinter {

In this case, the print method will need to take two parameters: not just the output object, but also the explicit point to print. The method will begin

public void print(SimpleOutput out, Point pt) {

What does the body of the method look like? More or less like the code we used elsewhere.

    out.println("(" + pt.getX() + "," + pt.getY() + ")");
    out.println("  distance from origin: " + 
                pt.distanceFromOrigin());

And, as always, we end our method with a right curly brace. Putting it all together and adding a few short comments, we get


import Point;
import SimpleOutput;

/**
 * Some utilities for printing points.
 */
public class PointPrinter {

  // +---------+--------------------------------------------------
  // | Methods |
  // +---------+

  /**
   * Print a point using a particular output object.
   */
  public void print(SimpleOutput out, Point pt) {
    out.println("(" + pt.getX() + "," + pt.getY() + ")");
    out.println("  distance from origin: " + 
                pt.distanceFromOrigin());
  } // print(SimpleOutput, Point)
} // class PointPrinter


How do we use our new class? We create a new object in the class and then call its method, as in

    PointPrinter printer = new PointPrinter();
    Point pt = new Point(2,3);
    printer.print(out,pt);
    Point another = new Point(4,2);
    printer.print(out,another);

What should you observe about this new class? First, we've once again added a name to some common code and parameterized that common code, which means that we can use it even with points with different names. Second, in order to use the new method, we need to create an object that provides that method. For now, this will be your model for programming in Java: when you need a new method, you'll need to put it in a class and create an object that provides the method, which fits in well with Java's object-oriented paradigm.

In Experiment J2.4 you will experiment with this newly-created class.

Return values

So far, all of the methods we've created have modified the object or printed output. At times, we want to write methods that return values. For example, we might want to write methods that give the x coordinate of a point or the distance of a point from the origin.

How do we write methods that return values? It turns out that it's not much different than writing methods that do not return values. First, you need to determine what type will be returned (e.g., integer, string, etc.). In the declaration for the method, you need to write that type, as in

  public type method(...) {
    ...
  } // method(...)

For example, we might declare getX as

  public int getX() {
    ...
  } // getX()

Next, we need to specify which value to return from the method. We do this with a return statement, as in

  public int getX() {
    return xcoordinate;
  } \\ getX()

Similarly, to return the average of two numbers, you might write

  /**
   * Compute the average of two doubles.
   */
  public double average(double a, double b) {
    double ave (a + b) / 2;
    return ave;
  } // average(double,double)

In Experiment J2.5 you will create a simple class that can compute averages.

Static methods

You may have found it awkward to need to create a new helper object (in this case PointPrinter) every time you needed a helper method (in this case, print). This may be especially true if you've worked in other languages in which it is possible to define methods separate from objects and classes.

If you are sure that you need an object-independent method, Java permits you to create so-called static methods (also called class methods). Such methods are associated with particular classes, but do not require the creation of objects to use them.

For example, let's suppose that we wanted to write a method that reads in x and y coordinates and returns a point. Here's a sketch of the body of that method.

    float x;  // The x coordinate  
    float y;  // The y coordinate
    out.print("Enter the x coordinate: ");
    x = in.readFloat();
    out.print("Enter the y coordinate: ");
    y = in.readFloat();
    return new Point(x,y);

We'll place this method in a class called PointReader. Now, suppose that we want to be able to use that method without creating a new PointReader. We can declare the method as static, as in

static public Point read(SimpleOutput out, SimpleInput in) {

If you place this method within PointReader, you can refer to it as PointReader.read. If you create an object, reader, of class PointReader, you can also refer to this method as helper.read.

Experience shows that static methods often lead to problems for beginning programmers. Hence, static methods will be used rarely, if at all, in subsequent exercises.

In Experiment J2.6 you will consider some issues pertaining to static methods.

Detour: The main method

By now, you may have realized that all of those main methods you've been creating are, in fact, static methods. As you might guess, Java's main is static so that you do not need to create a new object in order to run it. In fact, when you execute a class, you are actually telling the Java virtual machine to call the main method of that class.

Assigning objects

One of the more confusing aspects of Java is the treatment of variables that store references to objects. Objects, like Points and Strings, are treated differently than primitive values, like integers. In particular, when you assign integer variables, the value is copied from one to another. However, when you assign objects, a reference is copied. In effect, Java allows multiple variables to refer to the same object.

For example, consider the following code:

SimpleOutput out = new SimpleOutput();
int a;
int b;
a = 3;
b = a;
a = 2;
out.print("a = " + a);
out.print(", ");
out.println("b = " + b);
b = 10;
out.print("a = " + a);
out.print(", ");
out.println("b = " + b);

This code will print out

a = 2, b = 3
a = 2, b = 10

This suggests that the value stored in variable a is copied to variable b in the line

b = a

but that no relationship between the two is maintained.

As a contrast, consider the following code:

    SimpleOutput out = new SimpleOutput();
    Point p1 = new Point(2,3);
    Point p2 = p1;
    out.println("Originally");
    out.println("p1: (" + p1.getX() + "," + p1.getY() + ")");
    out.println("p2: (" + p2.getX() + "," + p2.getY() + ")");

    p1.shiftLeft(4);
    out.println("After shifting p1 left 4");   
    out.println("p1: (" + p1.getX() + "," + p1.getY() + ")");
    out.println("p2: (" + p2.getX() + "," + p2.getY() + ")");

    p2.shiftUp(3);
    out.println("After shifting p2 up 3");
    out.println("p1: (" + p1.getX() + "," + p1.getY() + ")");
    out.println("p2: (" + p2.getX() + "," + p2.getY() + ")");

This code will print out

Originally
p1: (2,3)
p2: (2,3)
After shifting p1 left 4
p1: (-2,3)
p2: (-2,3)
After shifting p2 up 3
p1: (-2,6)
p2: (-2,6)

In this case, both p1 and p2 refer to the same Point object. Hence, a change made to that object through p1 is reflected in p2 and vice-versa.

In many ways, this corresponds to our normal sense of objects. Each ``real world'' object has many names. For example, you might call your teacher ``Professor Smith'' and one of your fellow students might call your teacher ``Teach''. If you give ``Professor Smith'' an apple, then your fellow student will still be able to note that ``Teach'' has an apple.

But this association is not permanent. You are free to change which object a variable refer to. For example, consider the following code

    Point p1 = new Point(2,3);
    Point p2 = p1;
    out.println("(" + p1.getX() + "," + p1.getY() + ")");
    out.println("(" + p2.getX() + "," + p2.getY() + ")");
    p2 = new Point(1,1);
    out.println("(" + p1.getX() + "," + p1.getY() + ")");
    out.println("(" + p2.getX() + "," + p2.getY() + ")");

This code will print

(2,3)
(2,3)
(2,3)
(1,1)

This output happens because the assignment to p2 changed the reference and not the object. Again, this effect corresponds to our normal sense of naming. If you take computer science from Professor Smith and mathematics from Professor Jones, then you might use ``Teach'' to refer to Professor Smith during computer science and Professor Jones during mathematics. When you ask ``Teach'' a question, who you ask depends on who ``Teach'' currently refers to.

In Experiment J2.7 you will consider some issues relating to assigning objects.

Fonts and graphics for applets

This section is optional and is intended for classes emphasizing applets or pursuing a simultaneous discussion of applications and applets.

The applets you created in the first laboratory session were relatively primative. All they did was print a simple message, in some font that the applet viewer or Web browser selected. Obviously, you'd like more abilities and control. In particular, most programmers expect to be able to draw pictures in their applets and to select the color and typeface for text.

As you might expect, much of this control has to do with objects and methods. In particular, there is a Font class (more particularly, java.awt.Font) that lets you create new objects for controlling the appearance of text. In addition, objects in the java.awt.Graphics class provide a number of methods for drawing other objects.

When you want to write text in a new font, you must first create a new Font object (or reuse an existing Font object) that specifies basic details of the font: the logical name (SansSerif, Serif, Monospaced, etc.), the size (10pt, 12pt, etc.), and the style (plain, bold, italic, or bold-italic). The command to create a new Font is

  Font myFont = new Font(name, style, size)

where the style must be selected from Font.PLAIN, Font.BOLD, Font.ITALIC, or Font.BOLD+Font.ITALIC. For example,

    Font myFont = new Font("Serif", Font.BOLD, 24);

Once you have selected a font, you must tell the graphics object to use that font with setFont. For example, the following paint method paints the string ``Hello World'' in a 24-point, bold, serif font.

  public void paint(Graphics paintBrush) {
    Font myFont = new Font("Serif", Font.BOLD, 24);
    paintBrush.setFont(myFont);
    paintBrush.drawString("Hello World", 10, 40);
  } // paint(Graphics)

Note that you cannot specify color as part of a font selection. Color is determined by the graphics object that does the actual drawing. You tell the graphics object what color to use with the setColor method. What colors can you use? The basic set consists of: Color.black, Color.blue, Color.cyan, Color.darkGray, Color.gray, Color.green, Color.lightGray, Color.magenta, Color.orange, Color.pink, Color.red, Color.white, and Color.yellow. For example, to draw the word ``Hello'' in green and ``Goodbye'' in red, I might write

    paintBrush.setColor(Color.green);
    paintBrush.drawString("Hello", 10, 30);
    paintBrush.setColor(Color.red);
    paintBrush.drawString("Goodbye", 15, 60);

What are all these Color.xxx things? They are predefined objects, specified within the library class java.awt.Color. Hence, in order to use any of these colors, you must import that class.

But these colors do not give you a very rich pallet. What if you want more colors? You can create your own colors. That is, you can create your own objects that are members of java.awt.Color and that give you more precise control than the basic colors. The easiest way to create a new color is by specifying red, green, and blue components (each a value between 0 and 255). For example,

   Color myPurple = new Color(...);

You are encouraged to experiment with these components to see how they are used to form different colors.

But what about drawing something other than pieces of text? Java's Graphics objects provide a number of methods to support drawing shapes, including

In Experiment J2.8, you will make use of Java's Font and Color classes. In Experiment J2.9, you will test the various drawing primitives.


Copyright (c) 1998 Samuel A. Rebelsky. All rights reserved.

Source text last modified Mon Oct 25 16:16:22 1999.

This page generated on Tue Oct 26 15:38:26 1999 by Siteweaver.

Contact our webmaster at rebelsky@math.grin.edu