Types, Classes, and Objects

Baldwin explains the concept of a class and its objects from the viewpoint of a user-defined type.  He contrasts user-defined types with primitive types, explains what is commonly meant by type in type-sensitive languages, and explains the similarity between variables and objects.

Published:  July 1, 2003
By Richard G. Baldwin

Java Programming Notes # 905


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:

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:

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

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:

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:

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:

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: 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.

Baldwin@DickBaldwin.com

-end-