Sunday, December 9, 2007

Data,Operations and Methods

Data Types

Imagine what would happen if SimCom accidentally treated bytes of data as if they were instructions, or instructions as if they were data. In the first case, the virtual machine would execute a random series of opcodes, producing nothing of value. In the second case, instructions would likely be modified (added to or subtracted from one another), again producing nothing of value.

The point is that SimCom uses memory for two different purposes, instructions and data, and each memory type must be treated appropriately. There are no facilities built into SimCom to guarantee appropriate treatment. You just have to be a careful programmer.

This distinction between memory uses is also found in Java and all other high-level languages. Fortunately, Java makes it impossible to execute data or do arithmetic on opcodes.

SimCom has no facilities for dealing with fractions, characters, or very large numbers, and negative numbers are mysterious. Java supports all these different characteristics of numbers. It does this by providing different data types. For now, you can think of a data type as a way of using memory to represent data. SimCom uses an eight-bit base-2 representation. Java provides several base-2 representations: two representations for numbers that might contain fractions, one for characters, and one for logical (true/false) values.

Processing a Java data type as if it were a different type would produce worthless results. Java protects you from this kind of problem by requiring you to declare all data types; the compiler enforces the integrity of your declarations. Of course, this will make much more sense later in this chapter, after we discuss declarations. Right now, let's look at Java's data types. Later on, you'll see how they're used.

Integer Data Types


Name

Size

Minimum Value

Maximum Value

byte

8 bits

-128

127

short

16 bits

-32768

32767

int

32 bits

-2147483648

2147483647

long

64 bits

-9223372036854775808

9223372036854775807


Floating-Point Data Types


Name

Size

Minimum Value

Maximum Value

Smallest-Magnitude Positive Value

float

32 bits

-3.4 x 1038

3.4 x 1038

1.4 x 10-45

double

64 bits

-1.8 x 10308

1.8 x 10308

4.9 x 10-324


Declaring and Assigning

In a sense, computer programming is the art of assigning the right value to the right data at the right time. In Java, as in many other languages, you have to declare your data before you use it. Declaring means telling the compiler the types of data you will be using. In this section you will see how to declare and assign data, and will look at your first complete Java program.

When you programmed SimCom, you had to specify the address of each data operand. That meant you had to remember what you were using the different memory bytes for. For example, the Times 5 program used byte #29 as a loop counter and byte #30 for storing the result. Yet, when you look at the program for the first time, it's very difficult to tell what's going on.

In Java, you never have to remember which memory location is being used for which purpose. In fact, there is no way to even know which memory location is being used for which purpose. You pick a name for each memory location you want to use, and you refer to memory locations by name rather by address. The compiler assigns the addresses. All you have to do is tell the compiler the names you will be using, and the data type associated with each name.

For example, if you wanted to use a byte as a loop counter, it would be reasonable to choose the name loopCounter. Then you would declare as follows:

byte loopCounter;

A piece of memory that is declared and named in this way is known as a variable, so we will use that term from here on.

A declaration has three parts: a data type, a name, and a semicolon.

The data type (for now) is one of the eight primitive types: byte, short, int, long, float, double, char, and boolean. Later we will introduce some other types.

The name has to begin with a letter, an underline (_), or a dollar sign ($). The rest of the name can consist of letters, underlines, dollar signs, or digits. It is good programming practice to use variable names that begin with lowercase letters. If the name consists of more than one word, the second word and all subsequent words begin with uppercase letters. This is what we have done with loopCounter. Later in this book, you will see that there are other entities besides variables for which you will assign names (including classes and interfaces). These entities use different naming conventions. Following the conventions helps make source code easy to read.

The semicolon is a vital part of a declaration. A declaration is a kind of statement. A statement is a single instruction. All statements must end with a semicolon. Otherwise, the compilation will fail and the compiler will print out an error message with the line number where it ran into trouble.

Be aware that it is inherently impossible to create a compiler that produces consistently helpful error messages. Imagine someone running along a rough cobblestone road. If his foot slips on a stone, he might stagger for a few steps before falling. Similarly, if the compiler slips on an ungrammatical line, it might stagger over a few more lines before crashing and printing a message.

For the sake of convenience, you can declare multiple variables in a single statement, as long as the variables are all of the same type. So the following:

double mass, velocity, energy;

is equivalent to the following:

double mass;
double velocity;
double energy;

After you declare a variable, you can assign values to it. The following two lines declare and assign a variable called velocity:

double velocity;
velocity = 123.456;

Notice that the assignment statement, like the declaration statement, ends with a semicolon. An assignment statement has the form variable = value semicolon. (In the next chapter, you will see how the value can be a complicated mathematical formula. For now, the value will be a simple literal number.) Be aware that the equal sign is just a symbol, and its meaning is not exactly the same as its meaning in a mathematical context. In geometry, when we say Area = πr2, the equal sign means "is, always has been, and always will be." In Java, the

equal sign means "store the value to the right of the equal sign in the variable to the left of the equal sign."

When you assign to a char variable, the easiest approach is to enclose the value in single quotes, like this:

char ch;
c = 'w';

After execution, the variable ch contains the Unicode representation for the letter w. The single quotes can also contain special codes, called escape codes, that encode special characters. The most useful of these are

  • '\n' – Newline

  • '\t' – Tab


Arithmetic Operations

Java's arithmetic operations fall into two categories: basic arithmetic (addition, subtraction, multiplication, and division), and some more exotic operations such as modulo and shifting. We will begin by looking at the simple operations

Boolean Operations

So far, all the operations we have looked at have dealt with numbers. Now we turn our attention to operations that work on boolean data. Some of these operations share symbols with similar numeric operations (|, for example). However, the boolean versions are essentially different from their numeric counterparts.

Most of Java's boolean operations are binary, and both operands must be of boolean type.

Compound Assignment

Operator

Example

Equivalent

+=

x += 5;

x = x+5;

-=

x -= 5;

x = x-5;

*=

x *= 5;

x = x*5;

/=

x /= 5;

x = x/5;

%=

x %= 5;

x = x%5;

<<=

x <<= 5;

x = x<<5;

>>=

x >>= 5;

x = x>>5;

>>>=

x >>>=5;

x = x>>>5;

&=

b &= false;

b = b&false;

|=

b |= false;

b = b|false

^=

b ^= false;

b = b^false;

Compound assignments provide no new functionality. They just provide a convenient way to abbreviate.


Method Structure

The easiest way to introduce methods is with an example. Suppose you want to print out the fifth powers of the numbers 5 through 9. The following code, which doesn't use methods, does the job in a clumsy, inelegant way:

 1. public class NoMethods
2. {
3. public static void main(String[] args)
4. {
5. int n = 5;
6. int n5th = n*n*n*n*n;
7. System.out.println(n + " >=> " + n5th);
8. n = 6;
9. n5th = n*n*n*n*n;
10. System.out.println(n + " >=> " + n5th);
11. n = 7;
12. n5th = n*n*n*n*n;
13. System.out.println(n + " >=> " + n5th);
14. n = 8;
15. n5th = n*n*n*n*n;
16. System.out.println(n + " >=> " + n5th);
17. n = 9;
18. n5th = n*n*n*n*n;
19. System.out.println(n + " >=> " + n5th);
20. }
21. }

The application's output is

5 >=> 3125
6 >=> 7776
7 >=> 16807
8 >=> 32768

Scope

When you write a method declaration, you can choose almost any argument names you like. Of course, the names have to be legal (beginning with a letter, underscore, or dollar sign, and continuing with the same plus digits). Moreover, the name should be indicative of the argument's meaning. But beyond these considerations, you have complete latitude. In particular, you are allowed to reuse a variable name that has been used elsewhere in your program.



9 >=> 59049

Arrays.Classes and Inheritance

Declaring Arrays

Array declaration, like primitive declaration, associates a variable name with a data type. There are two ways to declare an array. The preferable format is

Component_type[] name; 

Note the square brackets after the component type. An example of this format is

float[] temperaturesCelsius;

This declaration says that temperaturesCelsius is the name of an array whose components are all of type float. The number of components will be specified later, when the array is created. Note the plural in the name, indicating that the variable relates to more than one temperature.

The alternative format for array declaration is

Component_type name[]; 

The only difference is that the empty square brackets now come after the name, rather than after the component type. This format is included as a holdover from older programming languages (C and C++). It is considered less readable than the first format, and we will not use it in this book.

Creating Arrays

At some point after you declare an array, you need to create it. This is new. With primitives, all you had to do was declare a variable's type, and the variable came into existence. Arrays, as well as all other kinds of objects, are different: You have to create them explicitly. This is done with the keyword new.

The format of an array creation statement is

name = new component_type[number_of_components]; 

you declared a variable named temperaturesCelsius to be an array whose components have float type. From this point on, we will say this more briefly: temperaturesCelsius is an array of floats. When you want to create the array, you first have to decide how many components it will have. If you want 10 components, for example, you would create the array like this:

temperaturesCelsius = new float[10];

The array size does not have to be a literal int. A variable is acceptable. Suppose you have a method called getNTempReadings, which returns the number of temperatures available to the program. Then you might do the following:

int nTemps = getNTempReadings();
temperaturesCelsius = new float[nTemps];

You could even get more terse:

temperaturesCelsius = new float[getNTempReadings()];

Arrays and Loops

Loops, and especially for loops, are ideal for processing arrays. No matter what you want to do to the array, you typically use a for loop with a loop counter that ranges from 0 through array-length-minus-1. Within the loop's body you perform whatever processing you want, using the loop counter as an array index.

For example, the following code computes the product of all the values in an array called measurements:

double product = 1; for (int i=0; iNote that this code works on arrays of all sizes, because it reads the array size from measurements.length.

Multi-Dimensional Arrays

The arrays we have looked at so far have been one-dimensional. This means that each component is specified by a single unique index. Java also supports multi-dimensional arrays, in which each component is specified by a unique sequence of indices. The number of indices in the sequence is the array's dimension. In a two-dimensional array, for example, each component has two indices. some one-dimensional arrays as columns of boxes. We can portray a two-dimensional array as a lattice or matrix, where each component is identified by its row and column

for (int i=0; i<3; i++)

for (int j=0; j<2; j++)

twoDimInts[i][j] = 39;

for (int hour=0; hour<24; hour++)
{
float tempTotal = 0;
for (int stn=0; stn<50; stn++)
tempTotal += temps[hour][stn];
float tempAvg = tempTotal / 50;
System.out.println("Average temp at time " + hour +
" = " + tempAvg);
}

On the other hand, you might want the average temperature over the entire day for each station. For that, you would use the following code:

for (int stn=0; stn<50; stn++)
{
float tempTotal = 0;
for (int hour=0; hour <24; hour++)
tempTotal += temps[hour][stn];
float tempAvg = tempTotal / 24;
System.out.println("Average temp at station " + stn +
" = " + tempAvg);
}

Classes

Plato would have approved of the concept of classes.

The easiest way to learn about classes is to step outside the domain of object-oriented programming for a moment and look at the real world. (Plato might not have approved of calling it "real.") We experience things in the world, and we create categories in our minds so that we can think about those things collectively.

Objects and Their Data

You have already learned that objects, like arrays, contain clusters of data. You have also learned that the data in an object, unlike the data in an array, can be of differing types. The number, types, and names of an object's data elements are all defined in the object's class definition.

Let's look at a very simple example. Here is a very simple class definition:

public class Person { int age; short weight; }

This is our first example of a class that does not contain a method called main. In fact, this class definition has no methods at all. The class just defines a bundle of data. The bundle contains an int called age and a short called weight.

To create an individual instance of this class, you would use the following code:

Person keara; keara = new Person();

Multiple Objects

You have learned that a class is a kind of stencil or cookie cutter for creating objects. Cookie cutters are especially useful if you're going to make lots of cookies. Similarly, classes are really useful because you can use a class to make multiple instances of that class. Different cookies made from the same cutter have the same outline, but they can be frosted or decorated differently. Similarly, different instances of the same class can have different data values.

Objects and Their Methods

In addition to containing data, objects can also contain methods. You might have suspected as much, because all the application classes presented in this book have contained at least one method (public static void main(String[] args), and sometimes more than one. All those methods have had the keyword static in their declarations. Later in this chapter you will see that static means, in a sense, "not object-oriented." The methods had to be static because we had not yet introduced objects. Now it is time to present genuinely object- oriented methods.

Let's add a method to the Person class:

public class Person
{
int age;
short weight;

int ageInNYears(int n)
{
return age + n;
}
}

The method computes how old the person will be in n years. It has a lot in common with the methods you have already seen. It has a declaration that specifies the method's return type, name, and argument list. It has a body enclosed in curly brackets, and it returns a value.

There are two major differences between this method and the ones you have looked at in previous chapters:

  • There is no static in the declaration.

  • The method refers to a field (age) of the class where the method is defined.

To call a method of an object, you again use the "reference-dot" notation. The following code shows how this is done:

1. Person ed = new Person();
2. ed.age = 62;
3. ed.weight = 220;
4. int n = ed.ageInNYears(3);

5. System.out.println("Ed will be " + n +" in 3 years.");

Construction and Constructors

When you invoke new to construct a new instance of a class, automatically a call is made to a piece of code in that class, called its constructor. This may come as a surprise, because for the past two chapters you have constructed objects without ever writing constructors, or even knowing about them. In a moment you will see how this works out.

A constructor looks like a method. In fact, there are only two differences between a constructor and a method:

  • A constructor has no return type.

  • The constructor's name is the same as the class's name.

Like a method, a constructor has a body, enclosed in curly brackets. The code in the body can initialize the newborn object. In fact, this initialization is the basic job of constructors. A constructor can always assume that the object's variables and methods (whether declared in the class or inherited) exist and are accessible for reading, writing, and calling.

Let's add a constructor to the Worker class. Assume that all workers in the company have the same salary: $34,567.89. The Worker class (with a different main() method) becomes the following:

1. public class Worker extends Employee 2. { 3. boolean getsOvertime; 4. 5. Worker() 6. { 7. salary = 34567.89f; 8. } 9. 10. void dumpSalary () 11. { 12. System.out.println("Salary = " + salary); 13. } 14. 15. public static void main(String[] args) 16. { 17. Worker dagwood = new Worker(); 18. dagwood.dumpSalary(); 19. } 20. }

The constructor is lines 5-8. Constructors, variables, and methods can appear in any order in the body of a class definition. However, it is common practice to have variables come first, followed by constructors, followed by methods. Usually, the main() method comes at the end. When this application is run, line 17 constructs a new instance of Worker. First, space for all variables is allocated. Then the constructor code is called. Line 7 of the constructor initializes salary to 34567.89, so the call to printSalarydumpSalary() prints out Salary = 34567.89.

Overloading Constructors

It is not especially realistic to expect every worker in a company to have the same salary. Fortunately, you can pass arguments into a constructor the same way you pass them into a method. As with a method, you can put an argument list inside the parentheses that follow the constructor name. Those arguments are accessible within the method. The next version of Worker has a constructor that accepts an argument.

 1. public class Worker extends Employee
2. {
3. boolean getsOvertime;
4.
5. Worker(float sal)
6. {
7. salary = sal;
8. }
9.
10. void dumpSalary ()
11. {
12. System.out.println("Salary = " + salary);
13. }
14.
15. public static void main(String[] args)
16. {
17. Worker dagwood = new Worker(55555.55f);
18. dagwood.dumpSalary();
19. }
20. }

The constructor now takes a float argument, and the invocation on line 17 passes a float. The output is Salary = 34567.89.

In Chapter 4, "Methods," you learned that methods are polymorphic. That is, different methods within a class may share a common name, as long as their argument lists are different. The practice of reusing a method name in a class is called overloading. (The term has a negative connotation in real life. When people or bridges are overloaded, that's bad. But in programming, there is nothing bad about overloading.) You can also overload a class's constructor so that the class has multiple constructors, as shown here:

public class Manager extends Employee
{
Worker[] workers;

Manager(int nWorkers)
{
workers = new Worker[nWorkers];
}

Manager(float sal, int nWorkers)
{
salary = sal;
workers = new Worker[nWorkers];
}
}

This class has two constructors. In both versions, you specify the number of workers. In the second version, you also specify the manager's salary.

Default Constructors

This section answers an important question: In the previous chapter and the first part of this one, how was it possible to construct objects in classes that didn't have any constructors? To understand the answer, you have to know what a no-args constructor is. It's just a constructor with an empty argument list.

When you create a class with no constructors, the compiler creates a no-args constructor automatically. A no-args constructor that is created automatically is called a default constructor. You only get a default constructor if your class does not explicitly have any constructors. If your class has constructors, no matter how many, no-args or otherwise, no default constructor is created for you.

This mechanism assures that every class has at least one constructor, even if the class was written by someone who has never heard of constructors!

Overriding

You have already seen method overloading, where a method name is reused in a class definition. Java also supports method overriding, which looks like it ought to be similar to overloading because the spellings are so similar. In fact, overriding is indeed a kind of method name reuse. In this case, the reuse is within an inheritance hierarchy.






Packages,Access,Interfaces

Packages

A Package is a named group of classes. Generally, the classes of a package are collected together into a directory. It is possible for package classes to appear in more than one directory, but it's hard to imagine when this would be helpful. So a package looks a lot like a directory, even though they aren't exactly the same thing.

There is another similarity: Just as a directory can contain files and subdirectories, a package can contain classes and subpackages. For example, a package called acmeproducts might contain two classes named Database and Connection. The package might also contain a subpackage called utilities, which contains three classes named ThreadPool, Mailbox, and UserProfile.

Access

Java's access control is based on the idea that certain features of a class should not be usable by other classes. Before you learn the details of access control, let's look at why this idea is sound.

To support this data-hiding approach, object-oriented languages provide facilities to let you restrict access to a class's data and methods. In Java, this is done with access modifier keywords. Java has three access modifiers:

  • public

  • private

  • protected


Final and Abstract

This section will look at two more modifiers: final and abstract. They aren't access modifiers, but this is still a good place to present them.

Final

Classes, methods, and data may be designated final. A final class may not be subclassed. A final method may not be overridden. A final variable may not be modified after it is initialized.

Final data is useful for providing constants. For example, you might have a Zebra class that provides the zebra's weight in pounds or kilograms:

class Zebra extends Mammal
{
private double weightKg;

public double getWeightKg()
{
return weightKg;
}

public double getWeightLbs()
{
return weightKg * 2.2;
}
}

This class uses appropriate data hiding. A zebra's weight is stored in kilos (the variable name leaves no doubt there), but users of the class never need to know that. Let's assume that eventually the class will have many methods that convert back and forth between kilos and pounds. There will be a lot of multiplying and dividing by 2.2. The standard approach to this situation is to declare a constant:

class Zebra extends Mammal
{
static private final double KGS_TO_LBS = 2.2;

private double weightKg;

public double getWeightKg()
{
return weightKg;
}

public double getWeightLbs()
{
return weightKg * KGS_TO_LBS;
}
}

The constant is called KGS_TO_LBS. It is static because its value is the always going to be the same for all instances of the class, so there is no benefit in giving each instance its own non-static copy. It is private because it is only for use inside the class. It is final because its value should never change under any circumstances. Constants require a little extra typing, but they are well worth the effort for three reasons:

  • They explain what they do. Someone reading the code, especially someone who doesn't recognize 2.2 as the kilogram-to-pounds conversion factor, will instantly understand the intention of a constant named KGS_TO_LBS.

  • They eliminate the need to look up or memorize conversion factors and similar values.

  • They provide protection against typos.

The third point requires an example. Suppose you aren't using constants, and it's late at night, and you're tired. Somewhere in the Zebra source code, which is now thousands of lines in length, your finger slips and you accidentally type 3.3 instead of 2.2. It could take a long time for the error to manifest itself, and when it does, you will have to soft through thousands of lines of code to find the problem.

On the other hand, suppose you are committed to using the constant. It is still late at night, and your finger slips, and you accidentally multiply by KGS_TO_LBX instead of KGS_TO_LBS. The next time you compile your code, the compiler will complain that variable KGS_TO_LBX does not exist. When you use constants, the compiler finds your typos for you.

Abstract

Classes and methods may be designated abstract; data may not.

An abstract method has no method body. All the code from the opening curly bracket through the closing curly bracket is gone, replaced with a single semicolon (;). Here is an example of an abstract method:

abstract protected double getAverage(double[] values);

The abstract keyword may be combined with the public and protected access modifiers. Here you see a method that is both abstract and protected. There is nothing unusual about the declaration part of the method. It only gets strange after the parenthesis that closes the argument list. Where you would expect to find the method body, there is only a semicolon.

When a class has an abstract method, that method's implementation will be found in the class's subclasses. In a moment you'll see an example, but first let's cover a few rules governing abstract classes and methods.

An abstract class may not be instantiated. That is, you are not allowed to call any constructor of any abstract class. Also, if a class contains any abstract methods, the class itself must be abstract. You might say that an abstract class is one that is incomplete: It lacks one or more method implementations.

Suppose you want to create several classes, all of which share some functionality and model similar real-world things. This strongly indicates that the classes should extend a common superclass, which should contain the shared functionality. Every subclass will inherit the common methods, so this is a good object-oriented design. It would not be unusual at this point to realize that there is some functionality that every subclass must have, but that every subclass should do in its own unique way.

Data in Interfaces

An interface is allowed to contain data, provided the data is public, final, and static. This provides an easy way to define constant data.

Extending Interfaces

An interface is allowed to extend another interface. The syntax is

interface  extends 
{
// Declarations and data
}

The new interface consists of all the methods and data defined in the parent interface. For example, you might define the following:

package nature;

interface SingingTalker extends Talker
{
public void sing(String song);
}

This interface consists of the two methods defined in Talker (say() and repeat()), as well as sing(). A class that wants to implement this interface must use all three methods.

A class can extend only a single parent class, but an interface can extend multiple parent interfaces. For example, if InterA, InterB, and InterC are all interfaces, the following is legal:

interface ManyParents extends InterA, InterB, InterC
{
public int anotherMethod(double d, char ch);

}

This interface consists of all the constants and methods of all three parents, plus the method defined explicitly in the source code.


The API Pages

There are more than 1,000 core Java classes, and every one of them has been described in detail by the Java creators. Unlike a lot of manufacturers' technical specifications, these descriptions are well-written, accurate, and helpful. They are provided as a set of interconnected HTML pages that you can download to your hard drive and view with the Web browser of your choice. Like Java itself, they are freely downloadable. If you have not already done so, please download them before continuing with this chapter.

Digression: A Personal Anecdote

Quite a while ago, before the birth of the World Wide Web, I worked for a company that made computers. These computers used a programming language that was a bit like C, but it was object-oriented. In addition to the language, there were a number of classes that supported I/O, graphical user interfaces, math, and so on. If you saw it today, you would probably be reminded of Java, but with fewer supplied classes.

There were several dozen of these classes. Their documentation consisted of two manuals that listed the classes in alphabetical order. For each class, the manuals listed the inheritance hierarchy, the data, and the methods.

Those of us who wrote programs for this system each had our own copy of the manuals. You could tell how long someone had been working there by the shape their manuals were in. Those books took a beating. We were always flipping back and forth. If I wanted to remind myself what a certain method of a certain class did, I might find that the method wasn't explained where I expected an explanation, because the class I was reading about inherited the method from its superclass. So I would look up the superclass (which might be in the other volume), and I would see that it returned an object reference, and I would have to look up that object's explanation because I hadn't seen it before.

I flipped a lot of pages because a lot of information for one class was (quite rightly) presented in the description of a different class. For example:

  • The class's superclass

  • The type of a non-primitive variable

  • The type of a non-primitive method argument

  • The type of a non-primitive method return value

  • Any class, method, or variable mentioned for any reason in the description I was reading

A set of HTML documents would have eliminated all that page-turning. The only problem was, this was 1988 and there was no HTML. The Web was just a glimmer in the eyes of a few people in Switzerland, and hypertext was an idea that wasn't discussed much outside of universities. We programmers would often wonder, "Couldn't we fix it so I could read all this on my screen, and somehow click on names of classes and data and methods to read their explanations?" But we didn't invent the World Wide Web.

File Input and Output & Painting & Components

Writing and Reading Bytes

Document files don't really contain text. Image files don't really contain pictures. MP3 files don't really contain music, and MPEG files don't really contain movies. They all contain bytes, because all files just contain bytes. The bytes encode information; they are decoded by software appropriate to the encoded content. This is why filename extensions are so important. They tell the computer what decoding software to use. If you take an image file that encodes a really beautiful picture and you change filename extension to .mp3, it's probably going to sound terrible.

All files are sequences of bytes. Before we look at decoding and encoding the information represented by the files, you need to learn how to write and read plain ordinary bytes. You will make extensive use of two of the classes in the java.io package:

  • FileOutputStream

  • FileInputStream

A file output stream writes bytes to a file; a file input stream reads bytes from a file. Our purpose here is not to present both classes in their entirety. Here you will learn more than enough to be able to use them well. Whenever you want to complete your understanding, you can refer to the API documentation.

Backslashes in Filenames

If you want to write bytes to a file in the current working directory called xyz, you can construct a file output stream like this:

FileOutputStream fos;
fos = new FileOutputStream("xyz");

Of course, you can create a file output stream in a similar way. Ignoring for the moment the issue of what you can actually do with those streams, you have to deal with the question of what happens when you want to specify a full pathname on a Windows system. For example, what if you want to write to a file whose full pathname is C:my_files\photos\abc? The following code will not do what you want:

FileOutputStream fos;

fos = new FileOutputStream("C:my_files\photos\abc");

Surprisingly, this code will not compile! The compiler error says that there is an invalid escape character, whatever that means.

Actually, the problem has nothing to do with file output streams. It has to do with backslashes in literal strings. You would get the same compilation error if you tried the following:

String s = "C:my_files\photos\abc";

Writing and Reading Data

At this point, you might be asking yourself, "When would I ever want to write or read bytes?" After all, one of the huge disadvantages of SimCom, as compared to the JVM, is that it deals only in bytes, while Java supports eight primitive data types and limitless class types.

The answer, fortunately, is that you never have to write or read bytes if you don't want to. You still have to create file input and output streams, and you still have to close them when you're done using them, but you don't have to write to them or read from them. Not directly, anyway. The writing and reading can be done by two very useful classes in the java.io package:

  • DataOutputStream

  • DataInputStream

Painting


Painting

There are many reasons why a frame's contents may need to be redrawn:

  • The frame has become visible for the first time.

  • The contents have changed due to user input (as when you moved a scrollbar in Color Lab).

  • The frame has deiconified.

  • The frame has moved to the front of the desktop, after having been covered or partially covered by another frame.

When any of these occur, the underlying windowing software notifies the frame, and a call is made to the frame's paint() method. The great thing about this arrangement is that you never have to detect these changes to the frame. The environment takes care of all that for you. All you have to do is subclass Frame and provide a paint() method that draws the frame's interior. In other words, you have to think about what to paint, but you don't have to think about when to paint.

Drawing and Filling with a Graphics Object

The shapes that you can draw with the Graphics class include the following:

  • Lines

  • Squares and rectangles

  • Circles and ovals


1. import java.awt.*;
2.
3. public class BlackLineOnWhite extends Frame
4. {
5. BlackLineOnWhite()
6. {
7. setSize(150, 180);
8. }
9.
10. public void paint(Graphics g)
11. {
12. g.setColor(Color.black);
13. g.drawLine(60, 115, 120, 70);
14. }
15.
16. public static void main(String[] args)
17. {
18. BlackLineOnWhite blonw = new BlackLineOnWhite();
19. blonw.setVisible(true);
20. }
1. import java.awt.*;
2.
3. public class BlueRect extends Frame
4. {
5. BlueRect ()
6. {
7. setSize(150, 180);
8. }
9.
10. public void paint(Graphics g)
11. {
12. g.setColor(Color.blue);
13. g.drawRect(25, 50, 100, 35);
14. }
15.
16. public static void main(String[] args)
17. {
18. BlueRect br = new BlueRect();
19. br.setVisible(true);
20. }
1. import java.awt.*;
2.
3. public class FontAndBaseline extends Frame
4. {
5. FontAndBaseline()
6. {
7. setSize(200, 150);
8. }
9.
10. public void paint(Graphics g)
11. {
12. int x = 25;
13. int yBaseline = 100;
14. g.setColor(Color.lightGray);
15. g.drawLine(x, yBaseline, 125, yBaseline);
16. g.setColor(Color.black);
17. g.drawString("just a gaping quay", x, yBaseline);
18. }
19.
20.
21. public static void main(String[] args)
22. {
23. FontAndBaseline fabl = new FontAndBaseline();
24. fabl.setVisible(true);
25. }
26. }

Layout Managers

The java.awt package contains a very useful class called Container. Generally, you don't instantiate this class directly. Rather, you instantiate its various subclasses, which include Frame. All containers have the following properties:

  • They are rectangular.

  • They can contain other components, including other containers.

  • They use layout managers to determine the locations and positions of the components they contain.

The great thing about layout managers is that you don't have to think about the details of component layout. Each layout manager class imposes a different layout policy on the container it manages. All you have to do is become familiar with the various layout policies available to you. The layout manager will take care of the details.

Events

Event-Driven Programs

import java.awt.*;

public class Xxxx extends Frame
{
Xxxx()
{
setLayout(new FlowLayout());
Button btn = new Button("Push Me");
add(btn);
setSize(300, 300);
}

public void paint(Graphics g)
{
g.setColor(Color.blue);
g.fillOval(100, 100, 100, 100);
}

public static void main(String[] args)
{
(new Xxxx()).setVisible(true);
}
}

Threads

I was eight years old. I was waiting my turn to get my hair cut, and I was reading a Superman comic book. The Man of Steel was entertaining some kids at an orphanage by playing ping-pong against himself. He would serve the ball, and then run at super-speed to the other side of the table to return the ball. Then he would run back to the original side, and so on. Each time he changed sides, he ran so fast that nobody could see him, and he ended up exactly where he had been before. This created the illusion of two identical Supermen.

Computers do something similar. They create the illusion of doing several things simultaneously by rapidly switching from one task to another, many times each second, like Superman running from one side of the ping-pong table to the other. The individual tasks are called threads.

Action Listeners and Action Events

Java uses many types of events. The simplest is the action event, which is used by buttons and several other components to indicate that simple user input activity has occurred. The other event types are slightly more complicated than action events, but they are used in analogous ways. The nice thing about Java's event mechanism is that once you've learned how to handle one kind of event, it's easy to handle the other kinds.

Action Listeners and Action Events

Java uses many types of events. The simplest is the action event, which is used by buttons and several other components to indicate that simple user input activity has occurred. The other event types are slightly more complicated than action events, but they are used in analogous ways. The nice thing about Java's event mechanism is that once you've learned how to handle one kind of event, it's easy to handle the other kinds.

import java.awt.event.*;

class SimpleActionListener implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
System.out.println("The button was pushed.");
}
}

The following code creates a button that uses an instance of SimpleActionListener as its action listener:

import java.awt.*;

public class UsesListener extends Frame
{
UsesListener ()
{
setLayout(new FlowLayout());
Button btn = new Button("Push Me");
SimpleActionListener sal = new SimpleActionListener();
btn.addActionListener(sal);
add(btn);
setSize(300, 100);
}


public static void main(String[] args)
{
(new UsesListener ()).setVisible(true);
}
}

Events from other Components

  • Buttons

  • Check boxes

  • Choices

  • Labels

  • Menus and menu items

  • Scrollbars

  • Text areas

  • Text fields

Now you will learn how to respond to user input activity on each type of component. You already know how to respond to buttons. Labels do not send events. In the rest of this chapter, you will learn how to detect events from the other component types.