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
.
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.
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.
main
method. In general, this design
is a good one to follow. One class will contain only the main
method. The other classes will do the actual work.
main
method creates a number of objects in different
contexts (not only a Plane
, but also Point
s).
plot
requires a Point
, we can create one on
the fly.
Plane
in order to use it successfully. All we need to know is that we can
add points with plot
.
In Experiment J2.2
you will consider how one uses the Point
and Plane
classes together, using the
JustPlaneFun
class to coordinate the two.
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:
public
, which indicates that the method is
generally accessible. (That is, any program that creates an appropriate
object can use this method of the object.)
void
.
SimpleOutput
object. (It also uses the
point to be printed, but that will be handled implicitly.) A method that
reads in a point will need a SimpleInput
object.
Each formal parameter is described with
(1) a type and (2) a name. Note that the formal parameter
gives a name to the parameter for use within the method. The names of the formal
and actual parameters do not need to match.
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.
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.
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.
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.
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.
One of the more confusing aspects of Java is the treatment of variables
that store references to objects. Objects, like
Point
s and String
s, 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.
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
paintBrush.drawOval(int left, int top, int width, int height)
paintBrush.drawRect(int left, int top, int width, int
height)
paintBrush.fillRect(int left, int top, int width, int height)
paintBrush.drawLine(int x1, int x2, int y1, int y2)
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.
[Front Door] [Introduction] [Code]
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