Java Types, Classes, and Objects

Types, Classes, and Objects


Preface

The situation

Many universities, colleges, and high schools (including the college
where I teach)
attempt to teach non-object-oriented (procedural)
programming fundamentals using an object-oriented programming
language such as C++, Java, or C#.  This results in some significant
problems.

The problem

The main problem is that persons who are attempting to learn programming
fundamentals are confronted with requirements to use objects, (such
as I/O objects and String objects)
without an understanding of what
they are doing when they use those objects.  This is a de-motivator
for those persons who prefer to understand what they are doing instead
of simply doing things by rote.

Help is available

This tutorial lesson is designed to help persons in this situation by
giving them an understanding of what they need to know in order to use
objects to the minimal extent necessary for courses in programming fundamentals.
 The tutorial is also designed to help those persons who are trying
to teach themselves how to become programmers.

Supplemental material

In some cases, textbooks and professors explain classes and objects away
by simply saying, “Don’t worry about why it works.  Just do
it the way I tell you to do it.”

In other cases, textbooks and professors attempt to explain classes
and objects, but fail to get the message across for a variety of reasons.

The material in this lesson is designed as a supplement to help persons
who find themselves in either of the situations listed above.

For further reading, see my extensive collection of online Java tutorials
at Gamelan.com.
A consolidated index is available at www.DickBaldwin.com.

Study hint

You may find it useful to open another copy of this lesson in a separate
browser window.  That will make it easier for you to scroll
back and forth among the different figures and listings while you
are reading about them.

Preview

Will avoid the commonly used approach

In explaining classes and objects, I will not revert to the commonly used
approach of talking about a class such as the Vehicle class, which
can be subclassed into a Car class and a Truck class, and the
further subclassing of the Car class into a SportsCar class
and a FamilyCar class.  Certainly such analogies are important
for those persons who are making a serious attempt to understand encapsulation,
inheritance,
and polymorphism, the three pillars of object-oriented
programming.  However, for those persons who simply need to understand
what it means to invoke a method on an object of the String class,
discussions of inheritance are a distraction and not a help.

Will explain from the viewpoint of type

Rather, I will explain a class and its objects from the viewpoint of a
non-primitive, user-defined type, and the operations that can be performed
on an entity of that type.  Before doing that, however, I will
explain what is commonly meant by type in type-sensitive
languages. (Every modern programmer needs to understand the concept
of type, so there is no better time to learn about type than the present.)

To lay the groundwork for the discussion of types, I will explain what
we mean by variables and objects, and the similarity between the two.

I will discuss and explain some of the common primitive types such as
short, int, double, boolean, and char,
and will then extend the concept of type to classes such as the String
class.  I will discuss some of the operations that can be performed
on entities of the primitive types, and will relate that to some of the operations
that can be performed on an entity of type String.

Standard input and output

I will also discuss and explain certain aspects of objects used to support
standard input and output.

Intended to be general in nature

To a large extent, this tutorial will be couched within the Java programming
language, because that is the language that I know best.  However,
the concepts that I will discuss are intended to be applicable to persons
using C++ and C# as well.

Variables

Let’s go back to around the ninth grade where you probably learned a little
about algebra and a little about geometry.  Hopefully, you will remember
the formula for calculating the area of a circle, as shown in Figure 1.

The area of a circle
a = pi * r * r

where: a is the area of a circle
pi is the constant 3.141592653589793
r is the radius

Figure 1 Calculating the area of a circle

The formula in Figure 1 tells us that the area of a circle is equal to
the constant pi multiplied by the radius squared.  (Note that
the true value of pi actually contains more digits than are shown in Figure
1.)

(As is the case in programming, Figure 1 uses an asterisk
(*) to indicate multiplication, as opposed to the use of an “x” or a
dot that you probably used in your algebra class.)

Constants and variables

In the formula in Figure 1, pi is a named constant whose value is
shown.  Constants in programming mean pretty much the same thing that
they mean in algebraic formulas. That is, the value of a constant never changes.

The terms a and r in the formula in Figure 1 are variables.
 Going further, your algebra teacher may have told you that a
is the dependent variable while r is an independent variable.
That is to say, the value of the area is dependent on the value that you
assign to the radius before you calculate the area.

Variables also mean pretty much the same thing in programming that they
mean in algebraic formulas.  Every variable has a name (such
as a or r)
and the value of the variable can change.
 In programming, this means that the value can change as the program
executes over time.

A variable is a named pigeonhole

You can think of a variable in an algebraic formula as the name of a value
that can change. You can think of a variable in programming as the name
of a pigeonhole in memory, which can be used to store values that can
change as the program executes.

A program named Area01

Let’s take a look at the central part of a program that calculates and
displays the area of a circle for different radius values, as shown in
Listing 1.  (The entire program is shown in Listing 9 near the
end of the lesson.)
 

    r = 1.0;
a = pi * r * r;
display(r,a);

Listing 1

The program code shown in Listing 1 begins by setting the value
of the radius variable named r to 1.0.  Then it calculates
the value of the area, and stores that value in the area variable named
a.  Finally, it displays the current values stored in the
variables named r and a.

And the answer is …

Since you can probably do the arithmetic shown in Listing 1 in your head,
it should come as no surprise to you that the output produced by the
code in Listing 1 is shown in Figure 2.

For radius = 1.0, area = 3.141592653589793

Figure 2

Calculate values for different radii

The code in Listing 2 assigns two new values, 1.5 and 2.0, to the variable
named r.  Each time a new value is assigned to the variable
r, a new area is calculated and the value is assigned to the variable
named a.  Then the values for r and a are displayed. 

    r = 1.5;
a = pi * r * r;
display(r,a);

r = 2.0;
a = pi * r * r;
display(r,a);

Listing 2

Once again, you probably won’t be surprised to learn that the value of
the area for each of the different radius values is as shown in Figure
3.

For radius = 1.5, area = 7.0685834705770345
For radius = 2.0, area = 12.566370614359172

Figure 3

Now, let’s set the concept of variables aside for a few minutes and discuss
the concept of type.

 Types

Type-sensitive languages

Java, C++, C#, and some other modern programming
languages make heavy use of a concept that we refer to as type,
or data type.  We refer to those languages as type-sensitive
languages.

Not all languages are type-sensitive languages. 
In particular, some languages hide the concept of type from the programmer
and automatically deal with type issues behind the scenes.

So, what do we mean by type?

One analogy that comes to my mind is international
currency.  For example, many years ago, I spent a little time in
Japan and quite a long time on an island named Okinawa (I believe that
Okinawa is now part of Japan).

Types of currency

At that time, as now, the type of currency used
in the United States was the dollar.  The type of currency used in Japan
was the yen, and the type of currency used on the island of Okinawa
was also the yen.  However, even though two of those currencies
had the same name, they were different types of currency, as determined
by the value relationships between them.

The exchange rate

As I recall, at that time, the exchange rate between
the Japanese yen and the U.S. dollar was 360 yen to the dollar. 
The exchange rate between the Okinawian yen and the U.S. dollar was 120
yen to the dollar.  This suggests that the exchange rate between the
Japanese yen and the Okinawian yen would have been 3 Japanese yen to the
Okinawian yen.

Analogous to different types of data

So, why am I telling you this?  I am telling
you this to illustrate the concept that different types of currency are
roughly analogous to different data types in programming.

Purchasing transactions were type sensitive

In particular, because these were three different
types of currency, the differences in the types had to be taken into
account in any purchasing transaction to determine the price in that particular
currency.  In other words, the purchasing process was sensitive to
the type of currency being used for the purchase (type sensitive).

Different types of data

Type-sensitive programming languages deal with different
types of data as well.  Some data types involve whole numbers only
(no fractional parts are allowed). We generally refer to these
as integer types.

Other data types involve numbers with fractional
parts.  We generally refer to these types as floating point
types, because a decimal point can float back and forth, separating the
whole number part from the fractional part. (After a while, we get lazy
and refer to these simply as floating types.)

An example of integer and floating-point types

Figure 4 contains a description of a problem involving both integer types
and floating-point types.

Consider the problem of determining the number of cans
of paint that must be purchased in order to paint all 15 tables in a
restaurant. The number of tables is an integer type.  We don’t want
to paint 14.6 tables or 15.7 tables.  We want to paint exactly 15
tables. (We don’t want to have a fractional part of a table left
unpainted.)

We know that one can of paint will cover 3.6 tables. The number
of tables that can be painted with one can of paint is a floating-point
value because it contains a whole number part and a fractional part.

A little arithmetic tells us that 4.167 cans of paint will
be required to paint all 15 tables.  This is also a floating-point
value, because it has a whole number part and a fractional part.

However, the man at the hardware store is unwilling to sell us 4.167
cans of paint.  He requires us to specify the number of cans of paint
as an integer value. In this case, we will need to purchase 5 cans of
paint in order to have enough paint available to paint all 15 tables, with
a little paint left over at the end.

Figure 4 Calculating the number of cans of paint
to purchase

Other data types

Although all data in a computer is stored in
numeric format, some data types conceptually have nothing to do with numeric
values, but deal only with the concept of true or false
or with the concept of the letters of the alphabet and the punctuation characters.

 I will have more to say about these types later.

Type specification

For every different type of data used with a particular
programming language, there is a specification somewhere that defines
two important characteristics of the type:

1.  The set of all possible data values that
can be stored in an instance of the type (we will learn some other
names for instance later)
?

2.  The operations that you can perform
on that instance alone, or in combination with other instances? (For
example, operations include addition, subtraction, multiplication, division,
etc.)

What do I mean by instance?

 
Think of the type specification as being analogous to the plan or
blueprint for a model airplane.  Assume that you build three model
airplanes from the same set of plans.  You will have created three
instances of the single set of plans.

We might say that an instance is the physical manifestation of a plan or
a type.

Using mixed types

Somewhat secondary to this specification, but also
extremely important, is a set of rules that defines what happens when
you perform an operation involving mixed types (such as making a purchase
using some yen currency in combination with some dollar currency).

 However, that is beyond the scope of this tutorial, so I won’t get
into it here.

An example, the short data type

There is a data type in Java, C++, and C# known
as short.  If you have an instance of the short type
in Java, the set of all possible values that you can store in that instance
is the set of all whole numbers ranging from -32,768 to +32,767.

This constitutes a set of 65,536 different values,
including the value zero.  No other value can be stored in an instance
of the type short.  For example, you cannot store the value
35,000 in an instance of the type short in Java.  If you need
to store that value, you will have to use some type other than short.

Sort of like an odometer

The short type is an integer type. 
Integer types are somewhat analogous to the odometer in your car (the
thing that records how many miles the car has been driven).
 
For example, depending on the make and model of car, there is a specified
set of values that can appear in the odometer.  The value that
appears in the odometer depends on how many miles your car has been
driven.

It is fairly common for an odometer to be able to
store and to display the set of all positive values ranging from zero
to 99,999.  If your odometer is designed to store that set of values
and if you drive your car more than 99,999 miles, it is likely that the
odometer will roll over and start back at zero after you pass the 99,999-mile
mark.  In other words, that particular type of odometer does not
have the ability to store a value of 100,000 or more miles.  Once
you pass the 99,999 mark, the data stored in the odometer is corrupt. 
It no longer represents the true number of miles for which the car has been
driven.

A word or two about operations

Assume that you have two instances of the type short
in a Java program.  Here are some of the operations that you can
perform on those instances:

  • You can add them together.
  • You can subtract one from the other.
  • You can multiply one by the other.
  • You can divide one by the other.
  • You can compare one with the other
    to determine which is algebraically larger.

There are other operations that are allowed
as well.  In fact, there is a well defined set of operations that
you are allowed to perform on those instances, and that set of operations
is defined in the specification for the type short.

What if you want to do something different?

If you want to perform an operation that is not
allowed by the type specification, then you will have to find another
way to accomplish that purpose.

For example, some programming languages allow you
to raise whole-number types to a power (example:  four squared,
six cubed, nine to the fourth power, etc.).
  However, that operation
is not allowed by the Java specification for the type short
If you need to do that operation with a data value of the Java short
type, you must find another way to do it.

Two major categories of type

Java data types can be subdivided into two major
categories:

  • Primitive types
  • User-Defined (class) types

These two categories are discussed in more
detail in the following sections.

Primitive Types

Java, C++, and C# are extensible programming
languages

What this means is that there is a core component to the language that
is always available.  Beyond this, individual programmers can extend
the language to provide new capabilities.  The primitive types discussed
in this section are the types that are part of the core language. 
A later section will discuss user-defined types that become available when
a programmer extends the language.

Four categories of primitive types

It seems that when teaching programming, I constantly find myself subdividing
topics into sub-topics.  I am going to subdivide the topic of primitive
types into four categories:

  • Whole-number (integer) types
  • Floating-point types
  • The character type
  • The boolean type

Whole-number (integer) types

Hopefully this categorization will make it possible for me to explain
these types in a way that is easy for you to understand.

The whole-number types, often called integer types, are probably the easiest
to understand.  These are types that can be used to represent data
without fractional parts.

Purchasing applesauce and hamburger

For example, consider purchasing applesauce and hamburger.  At the
grocery store where I shop, I am allowed to purchase applesauce by the
jar, only in whole-number or integer quantities.

For example, the grocer is happy to sell me one jar of applesauce and is
even happier to sell me 36 jars of applesauce.  However, she would be
very unhappy if I were to open a jar of applesauce in the store and attempt
to purchase 6.3 jars of applesauce.

A count of the number of jars of applesauce that I purchase is somewhat
analogous to the concept of whole-number data types in Java.  Applesauce
is not available in fractional parts of jars, at least not at the store
where I purchase my groceries.

On the other hand, the grocer is perfectly willing to sell me 6.3 pounds
of hamburger.  This is somewhat analogous to floating-point data
types in Java.

Accommodating applesauce and hamburger in a program

Therefore, if I were writing a program dealing with quantities of applesauce
and hamburger, I might elect to use a whole number type to represent
jars of applesauce and to use a floating-point type to represent pounds of
hamburger.

Different whole-number types

Four different whole-number types are built into the Java language:

  • byte
  • short
  • int
  • long

The four types differ primarily in terms of the range of values
that they can accommodate and the amount of computer memory required
to store instances of the types.  (Note that similar types exist
in C++ and C#, with similar, but not identical characteristics.)

Differences in operations

Although there are some subtle differences among these four types in terms
of the operations that you can perform on them, I will defer a discussion
of those differences until a more advanced lesson (for example some
operations require instances of the byte and short types
to be converted to type int before the operation takes place).

Algebraically signed values

All four of these types can be used to represent algebraically signed values
ranging from a specific negative value to a specific positive value.

Range of the byte type

For example, the byte type can be used to represent the set of whole
numbers ranging from -128 to +127 inclusive.  (This constitutes a set of 256 different values, including
the value zero.)

The byte type cannot be used to represent
any value outside this range.  For example, the byte type
cannot be used to represent either -129 or +128.

No fractional parts allowed by byte type

Also, the byte type cannot be used to represent fractional values
within the allowable range.  For example, the byte type cannot
be used to represent the value of 63.5 or any other value that has a
fractional part.

Like a strange odometer

To form a crude analogy, the byte type is sort of like a strange
odometer in a new (and unusual) car that shows a mileage value
of -128 when you first purchase the car.  As you drive the car, the
negative values shown on the odometer increment toward zero and then pass
zero.  Beyond that point they increment up towards the value of +127.

Oops, numeric overflow!

When the value passes (or attempts to pass) +127 miles, something
bad happens.  (The value rolls over and starts at -128 again.)
From that point forward, the value shown on the odometer is not a reliable
indicator of the number of miles that the car has been driven.

Ranges for each of the whole-number types

The table in Figure 5 shows the range of values that can be accommodated
by each of the four whole-number types supported by the Java programming
language.


byte

-128 to +127

short
-32768 to +32767

int
-2147483648 to +2147483647

long
-9223372036854775808 to +9223372036854775807

Figure 5 Ranges for integer types

Can represent some fairly large values

As you can see, the int and long types can represent some
fairly large values.  However, if your task involves calculations
such as distances in interstellar space (or the U.S. national debt),
these ranges probably won’t accommodate your needs.  This will lead
you to consider using the floating-point types discussed in the upcoming
sections.

Operations on integer types

As mentioned earlier, some of the operations that can be performed on integer
types are as follows:

  • You can add them together.
  • You can subtract one from the other.
  • You can multiply one by the other.
  • You can divide one by the other.
  • You can compare one with the other to
    determine which is algebraically larger.

I will discuss other operations that can be performed on whole-number types
in a subsequent lesson.

Floating-point types

Floating-point types are a little more complicated than whole-number types. 
I found the following definition of floating-point in the Free On-Line
Dictionary of Computing
at this URL:

A number representation consisting of a mantissa, M, an exponent,
E, and an (assumed) radix (or “base”) . The

number represented is M*R^E where R is the radix – usually
ten but sometimes 2.

So what does this really mean?

Assuming a base or radix of 10, (which is probably what you have been
using since your first kindergarten class),
I will attempt to explain
this concept using an example.

Consider the following value:  623.57185

I can represent this value in any of the following different ways (where
* indicates multiplication):

.62357185*1000
6.2357185*100
62.357185*10
623.57185*1
6235.7185*0.1
62357.185*0.01
623571.85*0.001
6235718.5*0.0001
62357185.*0.00001

Figure 6 Different ways to represent a value

In other words, I can represent the value as a mantissa (62357185)
multiplied by a factor where the purpose of the factor is to represent
a left or right shift in the position of the decimal point.

Now consider the factor

Each of the factors shown above represents the value of ten raised to some
specific power, such as ten squared, ten cubed, ten raised to the fourth
power, etc.

Exponentiation

If we allow the symbol ^ to represent exponentiation (raising to a
power)
and allow the symbol / to represent division, then we can write
the values for the above factors in the ways shown in Figure 7. Note in
particular the numbers in blue, which I will refer to later as the exponents.

1000 = 10^+3 = 1*10*10*10

100 = 10^+2 = 1*10*10
10 = 10^+1 = 1*10
1 = 10^+0 = 1
0.1 = 10^-1 = 1/10
0.01 = 10^-2 = 1/(10*10)

0.001 = 10^-3 = 1/(10*10*10)

0.0001 = 10^-4 = 1/(10*10*10*10)

0.00001 = 10^-5 = 1/(10*10*10*10*10)

Figure 7 Representation of the factor

For example, in the above notation, the term 10^+3 means 10 raised to the
third power.

The zeroth power

By definition, the value of any value raised to the
zeroth power is 1.  (Check this out in your old high school algebra
book.)

The exponent and the factor

Hopefully, at this point you will understand the relationship between the
value shown in blue (the exponent) and the factor introduced earlier.

Different ways to represent the same value

Having reached this point, by using substitution, I can rewrite the original
set of representations of the value 623.57185 in the different ways
shown in Figure 8. (Compare Figure 8 with Figure 6.)

.62357185*10^+3
6.2357185*10^+2
62.357185*10^+1
623.57185*10^+0
6235.7185*10^-1
62357.185*10^-2
623571.85*10^-3
6235718.5*10^-4
62357185.*10^-5

Figure 8 Different ways to represent a value

It is very important to for you to understand that these are simply different
ways to represent the same value.

A simple change in notation

Finally, by making a simplifying change in notation where I replace *10^
by E I can rewrite the different representations of the value
of 623.57185 in the ways shown in Figure 9.

.62357185E+3
6.2357185E+2
62.357185E+1
623.57185E+0
6235.7185E-1
62357.185E-2
623571.85E-3
6235718.5E-4
62357185.E-5

Figure 9 Different ways to represent a value

This is a relatively standard way to display floating-point values, using
exponential notation (the letter E indicates an exponent).

Getting the true value

Floating-point types represent values as a mantissa containing a decimal
point along with an exponent value, which tells how many places to shift
the decimal point to the left or to the right in order to determine the
true value.

Positive exponent values mean that the decimal point should be shifted
to the right.  Negative exponent values mean that the decimal point
should be shifted to the left.

Advantages of floating-point types

One advantage of floating-point types is that they can be used to maintain
fractional parts in data values.

Another advantage of floating-point types is that a very large range of
values can be represented using a reasonably small amount of computer
memory for storage of the values. For example (assuming that I counted
the number of digits correctly)
Figure 10 shows how to represent a very
large value and a very small value as a floating-point type.

The very large value:

62357185000000000000000000000000000000.0

can be represented as 6.2357185E+37

The very small value:

0.0000000000000000000000000000062357185

can be represented as 6.2357185E-30

Figure 10 Representing a large range of values

When would you use floating-point?

If you happen to be working in an area where you need to keep track of
fractional parts (such as the amount of hamburger in a package),
have to work with extremely large numbers (distances between galaxies),
or have to work with extremely small values (the size of atomic particles),
then you will need to use the floating-point types.

Two floating-point types

Java supports two different floating-point types:

  • float
  • double

These two types differ primarily in terms of the range of values
that they can support and the number of significant digits used in the
representation of those values.  Figure 11 shows the smallest and largest
values that can be accommodated by each of the floating-point types. 
Values of either type can be either positive or negative.



float
1.4E-45 to 3.4028235E38

double
4.9E-324 to 1.7976931348623157E308

Figure 11 Range for floating-point
types

Operations on floating-point types

Floating-point types support the typical arithmetic and comparison operations.
 I will discuss the operations that can be performed on floating-point
types in more detail in a subsequent lesson.

The character type

Computers deal only in numeric values.  They don’t know how to deal
directly with the letters of the alphabet and punctuation characters.

The purpose of the character type is to make it possible to represent the
letters of the alphabet, the punctuation characters, and the numeric characters
internally in the computer.  This is accomplished by assigning a numeric
value to each character, much as you may have done to create secret codes
when you were a child.  (For example, in Java an upper-case A character
is represented by the numeric value 65, whereas the upper-case B is represented
by the numeric value 66.)

A single character type

Java supports a single character type named char.  The char
type uses a standard character representation known as Unicode to represent up to 65,535 different
characters.

Why so many characters?

The reason for the large number of possible characters is to make it possible
to represent the characters making up the alphabets of many different
countries and many different languages.

Representing a character symbolically

Although each character is represented internally by a numeric value,
as long as the characters that you use in your program appear on your keyboard,
you usually don’t have a need to know the numeric values associated with
the different characters.

In Java, you usually represent a character to the program by surrounding
it with apostrophes as follows:  'A'.

The Java programming tools know how to cross-reference that specific character
symbol against the Unicode table to obtain the corresponding numeric
value.  (A discussion of the use of the char type to represent
characters that don’t appear on your keyboard is beyond the scope of
this lesson.)

The char type supports the typical arithmetic and comparison operations.
I will discuss the operations that can be performed on the char
type in more detail in a subsequent lesson.

The boolean type

The boolean type is the simplest type supported by Java.  It
can have only two values:

  • true
  • false

Generally speaking, about the only operations that can be applied
to an instance of the boolean type are to change it from true
to false, and vice versa.  The boolean type is commonly used in some
kind of a test to determine what to do next, such as that shown in Figure
12.

if some test
returns true, then
    do one thing
otherwise
    do something else

Figure 12 Typical use of boolean type

I will discuss the operations that can be performed on the boolean
type in more detail in a subsequent lesson.

User-Defined
Types

Extending the language

As mentioned earlier, Java is an extensible programming
language.  There is a core component to the language that is always
available.  Beyond this, different programmers can extend the language
in different ways to meet their individual needs.  (In addition,
the programmers at Sun have created a large library of user-defined types,
which you can use if you have access to the library.)

Creating new types

One of the ways that individual users can extend
the language is to create new types.  As indicated earlier,
for every different type of data used with a particular
programming language, there is a specification somewhere that defines
two important characteristics of the type:

1.  The set of all possible data values that
can be stored in an instance of the type
.
2.  The operations that you can perform
on that instance alone, or in combination with other instances.

Thus, when creating or defining a new type, the
user must define the set of values that can be stored in an instance of the
type, as well as the operations that can be performed on instances of the
type.  From the viewpoint of this tutorial lesson, the purpose of a
class definition is to create a new type, and to define the attributes of
the type listed above.

No magic involved

While this might initially seem like magic, once
you get to the heart of the matter, it is relatively straightforward. 
Instances of new types are created by combining instances of primitive types
and instances of other user-defined new types.

An example

For example, the String type, which can be
used to represent a person’s last name, is just a grouping of a bunch of
instances of the primitive char type (plus a specification of
the operations that can be performed on an instance of the new String
type).

A new Person type, which could be used to
represent a person’s first name and last name, might simply be a grouping
of two instances of the user-defined String type.

The company telephone book

A programmer responsible for producing the company
telephone book might create a new Employee type that could be
used to store the first and last names along with the telephone number
of an individual.  Using this new type, the programmer could create
an instance of the new type for each employee in the company.

(At this point, let me sneak a little jargon
in and tell you that we normally refer to such instances as objects
More specifically, we refer to instances of primitive types as variables,
and we refer to instances of user-defined types as objects.)

A comparison operation

This programmer might define one of the allowable
operations for the
Employee type to be a comparison between two objects of the Employee type to determine
which is greater in an alphabetic sorting sense.  This operation could
be used to sort the set of objects representing all of the employees into
alphabetical order.  The set of sorted objects could then be used
to print a new telephone book.

A name-change operation

Another allowable operation that the programmer might
define for the
Employee type would be the ability to change the name stored in an
object representing an employee.

For example when Suzy Smith marries Tom Jones, she
might elect to thereafter be known as Suzy Jones, Suzy Smith-Jones, or Suzy
Jones-Smith.  In this case, there would be a need to modify the object
that represents her to reflect her newly-elected surname.  (Or perhaps
Tom Jones might elect to thereafter be known as Tom Smith, in which case it
would be necessary to modify the object that represents him.)

An updated telephone book

The programmer could use the name-changing operation
to modify the object, use the sorting operation to re-sort the set
of objects, and then print and distribute a modified version of the telephone
book.

Many user-defined types already exist

Unlike the primitive types which are predefined,
I am unable to give you much in the way of specific information about
user-defined types, simply because they don’t exist until the user defines
them.

I can tell you, however, that when you obtain the
Java programming tools from Sun, you not only receive the core language
containing the primitive types, you also receive a large library containing
several thousand user-defined types that have already been defined. 
A large documentation package is available from Sun to help you determine
the individual characteristics of these user-defined types.

The most important thing

At this stage in your development as a programmer,
the most important thing for you to know about user-defined types is
that they are possible in Java, C++, and C#, as well as in some other
object-oriented programming languages as well.

Unlike earlier procedural programming languages
such as C and Pascal, when you use an extensible object-oriented programming
language, you are no longer forced to adapt your problem to the available
tools.  Rather, you now have the opportunity to extend the tools
to make them better suited to solve your problem.

The class definition

The specific Java mechanism that makes it possible
for you to define a new type is a mechanism known as the class definition.
 In Java, whenever you define a new class, you are at the same
time defining a new type.  Your new type can be as simple, or as
complex as you want it to be.

An object (instance) of your new type can
contain a very small amount of data, it can contain a very large amount
of data, or it may contain no data at all.

The operations that you allow to be performed on
an object of your new type can be rudimentary, or they can be very powerful,
or they may fall somewhere in between.

It is all up to you

Whenever you define a new class (type) you
not only have the opportunity to define the data definition and
the operations, you also have a responsibility to do so.

Much to learn and much to do

If you belong to the population for which this lesson
is intended, you still have much to learn and much to do before you will
need to define new types. There are a lot of fundamental programming concepts
that you will need to learn before you seriously embark on a study involving
the definition of new types.

However, in addition to learning fundamental programming
concepts, you will probably also need to learn how to create and use objects
of a few existing classes, such as the String class, and the classes
used for keyboard input and screen output.  In order to use objects
of these classes, you simply need to understand how to create the objects,
and how to perform the operations defined for objects of those classes.

For the present then, simply remember that the capability
to create new types is available. If you work to expand your knowledge
of Java programming one small step at a time, when you reach the point
where you need to define new types, you will be ready and eager to do
so.
 

Using the
String and I/O Classes

As stated earlier, the purpose of this lesson is to help persons in fundamental
programming courses by giving them an understanding of what they need to
know in order to use objects to the minimal extent necessary in such
courses.  Hopefully by now, you know what a class is and what an object
is.  I will show you a couple of examples of creating and using common
objects by invoking operations (methods) on those objects.

Creating an object of the String class

The following listings, beginning with Listing 3, show the central code
for a program named Basics01, which illustrates the use of an object
of the String class, as well as the use of objects of the PrintStream
class and the InputStream class.  A complete listing of the
program is shown in Listing 10 near the end of the lesson.

One of the predefined classes in the Java standard class library is a class
named String. The purpose of the String class is to define
a type of object capable of encapsulating a string of characters (such
as a person’s name, for example),
and to provide operations that can
be performed on an object of the class.

Listing 3 shows one way to create a new String object containing
the characters shown within the quotation marks, and to save a reference
to that new object in a variable named aString.

    aString = "Enter a character";

Listing 3

In Java, all that is necessary to create a new object of the type String
is to include characters within a pair of matching quotation marks.

Saving a reference to the object

If you are going to use the object later, you will need to save a reference
to the object in a variable as illustrated in Listing 3. Once you have a
reference to the object, you can use that reference to perform operations
on the object.  (Performing operations on the String object
will be illustrated later in this program.)

Displaying information on the screen

The code in Listing 4 causes the String object created above
to be displayed on the computer screen.

    System.out.println(aString);

Listing 4

Without getting into some rather complex details, let me simply state
that System.out in Listing 4 represents an object of the predefined
class named PrintStream.  This object, which is automatically
created when a Java program starts running, makes it possible to cause information
to be displayed on the computer screen.

(Technically, the information is displayed on the standard
output device, but I won’t get into that level of detail here.)

Performing operations on objects

The standard way to perform operations on objects in Java is to invoke
methods, (such as the println method shown in Listing 4), on
those objects.  An object of the PrintStream class supports
several different operations or methods, including the println method
shown in Listing 4, and a method named print, which you will see later.

You invoke the println method using the syntax shown in Listing
4, passing the information to be displayed as a parameter to the method
within the parentheses.  The code in Listing 4 causes the line of text
shown in Figure 13 to be displayed on the computer screen.  (Compare
the text on the screen with the characters represented by the String object
in Listing 3.)

Enter a character

Figure 13

Getting a character from the keyboard

For this program, the line of text shown in Figure 13 is actually a
prompt asking the user to press a character key on the keyboard and then
to press the Enter key on the keyboard.  When the user does
this, the code in Listing 5 will capture the character and save it in a
variable named aCharacter.
 

    aCharacter = (char)System.in.read();

Listing 5

The System.in term in Listing 5 represents an object of the predefined
class named InputStream.  This object, which is automatically
created when a Java program starts running, makes it possible to capture
information entered by the user at the keyboard. 

(Technically, the information is captured from the standard input
device, but I won’t get into that here.)

Thus the code in Listing 5 captures the keyboard character as type int,
forces the type to be converted from type int to type char,
and saves the result in a variable named aCharacter.

(The type conversion is accomplished by (char), which is commonly
referred to as a cast.  The reason why this conversion is necessary is
beyond the scope of this tutorial.)

Performing three operations on the PrintStream
object

The code in Listing 6 performs three consecutive but separate
operations on the PrintStream object to display the character on
the computer screen.
 

    System.out.print(aCharacter);
System.out.print(aCharacter);
System.out.println(aCharacter);

Listing 6

The first two operations in Listing 6 invoke the print method on
the PrintStream object, passing the single character previously
captured from the keyboard as a parameter to the method.  The third
operation invokes the println method on that same object, passing
the same character as a parameter.

Difference between print and println

The difference between the print operation and the println
operation is as follows.  When the print operation is performed,
the method displays the information and then leaves the screen cursor immediately
to the right of that information ready to display additional information
at that location on the screen.

When the println operation is performed, the method displays the
information, and then moves the screen cursor down to the left side of
the next line on the screen, ready to display additional information on
the next line.

Assuming that the user entered the character z when requested above,
the code in Listing 6 causes that character to be displayed three times
in succession, as shown in Figure 14, and then moves the screen cursor down
to the next line on the screen.

zzz

Figure 14

Performing another operation on the String object

The Sun documentation for the String class identifies more
than fifty different operations that can be performed on an object of the
String class.  One of those operations is to extract a substring
from the data encapsulated in the String object, and to return the
substring as a new String object.  This is illustrated by the
code in Listing 7, which gets a substring extending from character number
3 to character number 12, and saves a reference to the new String
object in a variable named bString.
 

    bString = aString.substring(3,12);

Listing 7

Display the substring

Finally, the code in Listing 8 uses the println method
discussed earlier to display the substring on the computer screen.
 

    System.out.println(bString);

Listing 8

This produces the screen output shown in Figure 15.  Compare this
output with that shown in Figure 13, and you should be able to see how the
substring relates to the original string. (When counting characters, start
counting with zero.  In other words, the first character in Figure 13
is character number 0.)

er a char

Figure 15

Run the Programs

At this point, if you are already set up to compile and execute Java programs,
you may want to paste the code from Listing 9 and Listing 10 into your text
editor, save the two programs in files named Area01.java and Basics01.java,
and then compile and execute the two programs.  Experiment with the
two programs, making changes and observing the results.

Summary

In this lesson, I have explained the concept of a class and its objects
from the viewpoint of a non-primitive, user-defined type, and the operations
that can be performed on an entity of that type.

I explained what is commonly meant by type in type-sensitive
languages.

I explained what we mean by variables and objects,
and the similarity between the two.

I discussed and explained some of the common primitive types, and extended
the concept of type to classes such as the String class.  I discussed
some of the operations that can be performed on variables of the primitive
types, and related that to some of the operations that can be performed on
an object of type String.

I also discussed and explained certain aspects of objects used
to support standard input and output.

Complete Program Listings


Complete listings of the two programs discussed in this lesson are shown
in Listing 9 and Listing 10 below.
 

/*File Area01.java
Copyright 2003 R.G.Baldwin

Illustrates the use of constants and variables
in Java

Tested using SDK 1.4.1 under WinXP
************************************************/
import java.util.*;

public class Area01{
public static void main(String[] args){
double a;
double r;
final double pi = Math.PI;

r = 1.0;
a = pi * r * r;
display(r,a);

r = 1.5;
a = pi * r * r;
display(r,a);

r = 2.0;
a = pi * r * r;
display(r,a);

}//end main
//-------------------------------------------//

static void display(double r, double a){
System.out.println("For radius = " + r +
", area = " + a);
}//end print
}//end Area01 class
//=============================================//

Listing 9

 

/*File Basics01.java
Copyright 2003 R.G.Baldwin

Illustrates the basics of strings and
standard I/O.

For an input character of z, this program
produces the following output:

Enter a character
z
zzz
er a char

Tested using SDK 1.4.1 under WinXP
************************************************/
import java.util.*;
import java.io.*;

public class Basics01{
public static void main(String[] args)
throws IOException{
char aCharacter;
String aString;
String bString;

//Create a String object
aString = "Enter a character";

//Display the String object
System.out.println(aString);

//Get and save a character from the keyboard
aCharacter = (char)System.in.read();

//Display the character three times
// in succession
System.out.print(aCharacter);
System.out.print(aCharacter);
System.out.println(aCharacter);

//Get a substring from the string
bString = aString.substring(3,12);

//Display the substring
System.out.println(bString);

}//end main
}//end class Basics01

Listing 10


Copyright 2003, Richard G. Baldwin.  Reproduction in whole or in part
in any form or medium without express written permission from Richard Baldwin
is prohibited.

About the author

Richard Baldwin
is a college professor (at Austin Community College in Austin, TX)
and private consultant whose primary focus is a combination of Java,
C#, and XML. In addition to the many platform and/or language independent
benefits of Java and C# applications, he believes that a combination of
Java, C#, and XML will become the primary driving force in the delivery
of structured information on the Web.

Richard has participated in numerous consulting projects, and he frequently
provides onsite training at the high-tech companies located in and
around Austin, Texas.  He is the author of Baldwin’s Programming
Tutorials, which has gained a worldwide
following among experienced and aspiring programmers. He has also
published articles in JavaPro magazine.

Richard holds an MSEE degree from Southern Methodist University and
has many years of experience in the application of computer technology
to real-world problems.

[email protected]

-end-
 


Latest Posts

Related Stories