Chapter 2

Programs, Data, Variables, and Calculation

WHAT YOU WILL LEARN IN THIS CHAPTER:

  • How to declare and define variables of the basic integer and floating-point types
  • How to write an assignment statement
  • How integer and floating-point expressions are evaluated
  • How to output data from a console program
  • How mixed integer and floating-point expressions are evaluated
  • What casting is and when you must use it
  • What boolean variables are
  • What determines the sequence in which operators in an expression are executed
  • How to include comments in your programs

In this chapter you look at the entities in Java that are not objects — numbers and characters. This gives you all the elements of the language you need to perform numerical calculations, and you apply these in a few working examples.

DATA AND VARIABLES

A variable is a named piece of memory that you use to store information in your Java program — an item of data of some description. Each named piece of memory that you define can only store data of one particular type. If you define a variable to store integers, for example, you can’t use it to store a value that is a decimal fraction, such as 0.75. If you’ve defined a variable that you use to refer to a Hat object, you can only use it to reference an object of type Hat (or any of its subclasses, as you will see in Chapter 6). Because the type of data that each variable can store is fixed, the compiler can verify that each variable you define in your program is not used in a manner or a context that is inappropriate to its type. If a method in your program is supposed to process integers, the compiler is able to detect when you inadvertently try to use the method with some other kind of data, for example, a string or a numerical value that is not integral.

Explicit data values that appear in your program are called literals. Each literal is also of a particular type: 25, for example, is an integer literal of type int. I will go into the characteristics of the various types of literals that you can use as I discuss each variable type.

Before you can use a variable you must specify its name and type in a declaration statement. First consider what flexibility you have in choosing a name for a variable.

Naming Your Variables

The name that you choose for a variable, or indeed the name that you choose for anything in Java, is called an identifier. An identifier can be any length, but it must start with a letter, an underscore (_), or a dollar sign ($). The rest of an identifier can include any characters except those used as operators in Java (such as +, −, or *), but you are generally better off if you stick to letters, digits, and the underscore character.

Java is case sensitive, so the names republican and Republican are not the same. You must not include blanks or tabs in the middle of a name, so Betty May is out, but you could have BettyMay or even Betty_May. Note that you can’t have 6Pack as a name because you cannot start a name with a numeric digit. Of course, you could use sixPack as an alternative.

Subject to the restrictions I have mentioned, you can name a variable almost anything you like, except for two additional restraints: you can’t use a keyword as a name for something, and a name can’t be anything that could be interpreted as a constant value — as a literal in other words. Keywords are words that are part of the Java language. You saw some keywords in the previous chapter, and you will learn a few more in this chapter. If you’d like to know what they all are now, see the complete list in Appendix A. The restriction on constant values is there because, although it is obvious why a name can’t be 1234 or 37.5, constants can also be alphabetic, such as true and false, which are literals of the type boolean. Of course, the basic reason for these rules is that the compiler has to be able to distinguish between your variables and other things that can appear in a program. If you try to use a name for a variable that makes this impossible, then it’s not a legal name.

Clearly, it makes sense to choose names for your variables that give a good indication of the sort of data they hold. If you want to record the size of a hat, for example, hatSize is a good choice for the variable name whereas qqq would be a bad choice. It is a common convention in Java to start variable names with a lowercase letter and, where you have a name that combines several words, to capitalize the first letter of each word, as in hatSize or moneyWellSpent. You are in no way obliged to follow this convention, but because almost all the Java world does, it helps to do so.

image

NOTE If you feel you need more guidance in naming conventions (and coding conventions in general) take a look at www.oracle.com/technetwork/java/codeconv-138413.html.

Variable Names and Unicode

Even though you may be entering your Java programs in an environment that stores ASCII characters, all Java source code is in Unicode. Although the original source code that you create may be ASCII, it is converted to Unicode characters internally, before it is compiled. Although you can write any Java language statement using ASCII, the fact that Java supports Unicode provides you with immense flexibility. It means that the identifiers that you use in your source program can use any national language character set that is defined within the Unicode character set, so your programs can use French, Greek, or Russian variable names, for example, or even names in several different languages, as long as you have the means to enter them in the first place. The same applies to character data that your program defines.

image

NOTE The Java compiler assumes that your source code is in the default character encoding for the environment in which you are working. If it is not, you must specify the -encoding option to tell the compiler about the character encoding for you source code. For example, if you have created the source file Test.java in Unicode, you can compile it with the following command:

javac.exe -encoding unicode JavaTest.java

Variables and Types

As I mentioned earlier, each variable that you declare can store values only of a type consistent with the data type of that variable. You specify the type of a particular variable by using a type name in the variable declaration. For instance, here’s a statement that declares a variable that can store integers:

int numberOfCats;
 

The data type in this case is int and the variable name is numberOfCats. The semicolon marks the end of the statement. The variable, numberOfCats, can only store values of type int. Of course, int is a keyword.

Many of your variables will be used to reference objects, but let’s leave those on one side for the moment as they have some special properties. The only things in Java that are not objects are variables that correspond to one of eight basic data types, defined within the language. These fundamental types are referred to as primitive types, and they enable you to define variables for storing data that fall into one of three categories:

  • Numeric values, which can be either integer or floating-point
  • A single Unicode character
  • Logical values that can be true or false

All of the type names for the basic variable types are keywords in Java so you must not use them for other purposes. Let’s take a closer look at each of the primitive data types and get a feel for how you can use them.

INTEGER DATA TYPES

There are four types of variables that you can use to store integer data. All of these are signed — that is, they can store both negative and positive values. The four integer types differ in the range of values they can store, so the choice of type for a variable depends on the range of data values you are likely to need.

The four integer types in Java are shown in Table 2-1:

TABLE 2-1: Java Integer Types

DATA TYPE DESCRIPTION
byte Variables of this type can have values from −128 to +127 and occupy 1 byte (8 bits) in memory
short Variables of this type can have values from −32768 to 32767 and occupy 2 bytes (16 bits) in memory
int Variables of this type can have values from −2147483648 to 2147483647 and occupy 4 bytes (32 bits) in memory
long Variables of this type can have values from −9223372036854775808 to 9223372036854775807 and occupy 8 bytes (64 bits) in memory

Although I said that the choice of type depends on the range of values that you want to be able to store, in practice you’ll be using variables of type int or type long to store integers most of the time, for reasons that I explain a little later. Let’s take a look at declarations for variables of each of these types:

byte  smallerValue;
short pageCount;
int   wordCount;
long  bigValue;
 

Each of these statements declares a variable of the type specified.

The range of values that can be stored by each integer type in Java is shown in the preceding table and this is always the same, regardless of what kind of computer you are using. This is also true of the other primitive types that you will see later in this chapter. This has the rather useful effect that your program executes in the same way on computers that might be quite different. This is not necessarily the case with other programming languages.

Of course, although I have expressed the range of possible values for each type as decimal values, integers are stored internally as binary numbers, and it is the number of bits available to store each type that determines the maximum and minimum values, as shown in Figure 2-1.

For each of the binary numbers shown in Figure 2-1, the leftmost bit is the sign bit, marked with an s. When the sign bit is 0 the number is positive, and when it is 1 the number is negative. Negative binary numbers are represented in what is called 2s complement form. If you are not familiar with this, you can find an explanation of how it works in Appendix B.

Integer Literals

Before you use integer variables you need to understand how you write integer values of various types. As I said earlier, a value of any kind in Java is referred to as a literal. So 1, 10.5, and “This is text” are all examples of literals.

Integer literals can be of type int or type long. Any integer literal that you specify as a sequence of decimal digits is of type int by default. Thus 1, −9999, and 123456789 are all literals of type int. If you want to define an integer literal of type long, you need to append an L to the value. The values 1L, −9999L, and 123456789L are all of type long. You could use a lowercase letter l, but don’t — it is too easily confused with the digit 1.

Very large integer literals with lots of digits can be difficult to read; you can make such literals more readable by separating groups of digits using the underline character in the way you use a comma to separate groups of digits when writing numbers outside of programming. For example, you can write the value 1,234,567,890 as the literal 1234567899L or as the literal 1_234_567_890L. You could also use multiple underscores in sequence if you feel it is necessary: 1__234__567__890L. Underscores can only appear interior to a literal, so each set of one or more underlines in a literal must be bounded by digits both sides; putting them anywhere else results in a compiler error message. As you will see later in this chapter, you can use underscores in other kinds of numeric literals too.

You are perhaps wondering how you specify literals of type byte or short. You can’t. Because of the way integer arithmetic works in Java, they just aren’t necessary in the main. You see a couple of instances where an integer literal may be interpreted by the compiler as type byte or short later in this chapter, but these situations are the exception.

You can write integer literals in four different ways:

  • As decimal values that are numbers to base 10
  • As hexadecimal values that are numbers to base 16
  • As binary values that are numbers to base 2
  • As octal values that are numbers to base 8

You have seen how to write decimal integer literals. Let’s look at the others.

Hexadecimal Literals

Hexadecimal literals in Java have 0x or 0X in front of them and follow the usual convention of using the letters A to F (or a to f) to represent digits with values 10 to 15, respectively. In case you are a little rusty on hexadecimal values, here are some examples:

0x100 is (1 × 162) + (0 × 161) + (0 × 160) which is 256 in decimal.

0x1234 is (1 × 163) + (2 × 162) + (3 × 161) + (4 × 160) which is 4660 in decimal.

0xDEAF is (13 × 163) + (14 × 162) + (10 × 161) + (15 × 160) which is 57007 in decimal.

0xCAB is (12 × 162) + (10 × 161) + (11 × 160) which is 3243 in decimal.

If you are not familiar with hexadecimal numbers, you can find an explanation of how these work in Appendix B. All the hexadecimal literals in the preceding table are of type int. If you want to specify a hexadecimal literal of type long, you must append L to the literal just as with decimal literals. For example, 0xFL is a hexadecimal literal that is equivalent to the decimal value 15. Of course, you can write a literal such as 0xAABBCCD9L as 0xAABB_CCD9L. The underscore character here separates the hexadecimal digits into groups of four. Each group of four hexadecimal digits corresponds to 2 bytes in memory. As with decimal integer literals, underscores can only appear between digits in hexadecimal literals so 0x_3ABC and 0x3ABC_ are both errors.

Binary Literals

It is sometimes convenient to specify an integer literal as a binary value. Placing 0b or 0B in front of a literal identifies it as a binary number. In this case the digits can only be 0 or 1. For example, 0b110010101011 or 0B110010101011 is the same value as 0xCAB or the decimal value 3243. You can use the underline character in binary literals too, so you could also write the value as 0b1100_1010_1011, which is much easier to read. Each group of four binary digits corresponds to one hexadecimal digit. Of course, binary literals can also be of type long; you just append an L to the number. 0b_1000 and 0b1000_ are both errors because underscores can only appear between digits.

Octal Literals

You write literals that are octal numbers with a leading zero so 035 and 067 are examples of octal numbers of type int and 0777777L is an octal literal of type long. The latter could also be written 0777_777L. Octal numbers can only use the digits 0 to 7 and each octal digit defines 3 bits. Octal numbers were used frequently in the days when machines used words of lengths that were a multiple of 3 bits to store a number.

You will rarely find it necessary to use octal numbers these days, but you should take care not to use them by accident. If you put a leading zero at the start of an integer literal, the Java compiler thinks you are specifying an octal value. Unless one of the digits is greater than 7, which results in the compiler flagging it as an error, you won’t know that you have done this and the value will not be what you think it is.

Declaring Integer Variables

As you saw earlier, you can declare a variable of type long with the statement:

long bigOne;
 

This statement is a declaration for the variable bigOne. This specifies that the variable bigOne stores a value of type long. When this statement is compiled, 8 bytes of memory are allocated for the variable bigOne. Java does not automatically initialize a variable such as this. If you want your variables to have an initial value rather than a junk value left over from when the memory was last used, you must specify your own value in the declaration. To declare and initialize the variable bigOne to 2,999,999,999, you just write:

long bigOne = 2_999_999_999L;
 

The variable is set to the value following the equal sign. It is good practice to always initialize your variables when you declare them. I inserted underlines to make the literal easier to read. Note that if you try to use a variable in a calculation that has not had a value assigned to it, your program does not compile. There are also circumstances where the compiler cannot determine whether or not a variable has been initialized before it is used if you don’t initialize it when you declare it, even though it may be obvious to you that it has been. This is also flagged as an error, but if you get into the habit of always initializing variables when you declare them, you can avoid all of these problems.

You can declare a variable just about anywhere in your program, but you must declare each variable before you use it in a calculation. The placement of the declaration has an effect on whether a particular variable is accessible at a given point in a program, and we look deeper into the significance of this in the next chapter. Broadly, you should group related variable declarations together, immediately before the code that uses them.

You can declare and define multiple variables in a single statement. For example:

long bigOne = 999_999_999L, largeOne = 100_000_000L;
 

Here I have declared two variables of type long. A comma separates each variable from the next. You can declare as many variables as you like in a single statement, although it is usually better to stick to declaring one variable in each statement as it helps to make your programs easier to read. A possible exception occurs with variables that are closely related — an (x,y) coordinate pair representing a point, for example, which you might reasonably declare as the following:

int xCoord = 0, yCoord = 0;       // Point coordinates
 

On the same line as the declaration of these two variables, I have a comment following the double slash, explaining what the variables are about. The compiler ignores everything from the double slash (//) until the end of the line. Explaining the purpose of your variables in comments is a good habit to get into, as it can be quite surprising how something that was as clear as crystal when you wrote it transmogrifies into something as clear as mud a few weeks later. You can add comments to your programs in other ways that you see a little later in this chapter.

You can also spread a single declaration over several lines if you want. This also can help to make your program more readable. For example:

int miles    = 0,          // One mile is 8 furlongs
    furlongs = 0,          // One furlong is 220 yards
    yards    = 0,          // One yard is 3 feet
    feet     = 0;
 

This defines four variables of type int in a single statement with the names miles, furlongs, yards, and feet. Each variable has 0 as its initial value. Naturally, you must be sure that an initializing value for a variable is within the range of the type concerned; otherwise, the compiler complains. Your compiler is intelligent enough to recognize that you can’t get a quart into a pint pot or, alternatively, a very large long constant into a variable of type int, short, or byte. Because the statement is spread over four lines, I am able to add a comment on each of the first three lines to explain something about the variable that appears on it.

To complete the set of variables that store integers, you can declare and initialize a variable of type byte and one of type short with the following two statements:

byte luckyNumber = 7;
short smallNumber = 1234;
 

Here the compiler can deduce that the integer literals are to be of type byte and short, respectively, and can convert the literals to the appropriate type. It is your responsibility to make sure the initial value fits within the range of the variable that you are initializing. If it doesn’t, the compiler rejects the statement and outputs an error message.

Most of the time you will find that variables of type int will cover your needs for dealing with integers, with type long being necessary now and again when you have some really big integer values to deal with. Variables of type byte and short do save a little memory, but unless you have a lot of values of these types to store, that is, values with a very limited range, they won’t save enough to be worth worrying about. They also introduce complications when you use them in calculations, as you will see shortly, so generally you should not use them unless it is absolutely necessary. Of course, when you are reading data from some external source, a disk file for instance, you need to make the type of variable for each data value correspond to what was written originally.

FLOATING-POINT DATA TYPES

Numeric values that are not integral are stored as floating-point numbers. A floating-point number has a fixed number of digits of accuracy but a very wide range of values. You get a wide range of values, even though the number of digits is fixed, because the decimal point can “float.” For example the values 0.000005, 500.0, and 5,000,000,000,000.0 can be written as 5 × 10-6, 5 × 102, and 5 × 1012 respectively — you have just one significant digit 5, but you get three different numbers by moving the decimal point around.

There are two primitive binary floating-point types in Java: type float and type double, described in Table 2-2. These give you a choice in the number of digits available to represent your data values, and in the range of values that can be accommodated.

TABLE 2-2: Java Primitive Binary Floating-Point Types

DATA TYPE DESCRIPTION
float Variables of this type can have values from −3.4E38 (−3.4 × 1038) to +3.4E38 (+3.4 × 1038) and occupy 4 bytes in memory. Values are represented with approximately 7 decimal digits accuracy. The smallest non-zero float value that you can have is approximately 1.4 × 10−45.
double Variables of this type can have values from −1.7E308 (−1.7 × 10308) to +1.7E308 (+1.7 × 10308) and occupy 8 bytes in memory. Values are represented with approximately 17 decimal digits accuracy. The smallest non-zero double value that you can have is approximately 4.9 × 10−324.

The number of digits in representing a floating-point value is called its precision.

image

NOTE All floating-point operations and the definitions for values of type float and type double conform to the IEEE 754 standard.

As with integer calculations, floating-point calculations in Java produce the same results on any computer.

Floating-Point Literals

Floating-point literals are of type double by default, so 1.0 and 345.678 are both of type double. When you want to specify a value of type float, you just append an f, or an F, to the value, so 1.0f and 345.678F are both literals of type float. A literal of type double can have d or D appended to it, but this is rarely used in practice. You can use underlines to make floating point literals clearer so you can write 12345678.75 as 12_345_678.75. You can also use underlines to the right of the decimal point if you think it helps, so you can write 7.123456789 as 7.123_456_789. However, 1_.234 and 1._234 are errors.

When you need to write very large or very small floating-point values, you usually want to write them with an exponent — that is, as a decimal value multiplied by a power of 10. You can do this in Java by writing the number as a decimal value followed by an E, or an e, preceding the power of 10 that you require. For example, the distance from Earth to the sun is approximately 149,600,000 kilometers, more conveniently written as 1.496E8. Because the E (or e) indicates that what follows is the exponent, this is equivalent to 1.496 × 108. At the opposite end of the scale, the mass of an electron is around 0.0000000000000000000000000009 grams. This is much more convenient, not to say more readable, when it is written as 9.0E−28 grams.

Declaring Floating-Point Variables

You declare floating-point variables in a similar way to what you’ve already used for integers. You can declare and initialize a variable of type double with the following statement:

double sunDistance = 1.496E8;
 

This declares the variable with the name sunDistance and initializes it with the appropriate value.

Declaring a variable of type float is much the same. For example:

float electronMass = 9E-28F;
 

This defines and initializes the variable electronMass.

You can, of course, declare more than one variable of a given type in a single statement:

float hisWeight = 185.2F, herWeight = 108.5F;
 

Remember that you must put the F or f at the end of literals of type float. If you leave it out, the literal is of type double, and the compiler won’t automatically convert it to type float.

FIXING THE VALUE OF A VARIABLE

Sometimes you declare and initialize a variable with a value that should never change, for example:

int feet_per_yard = 3;
double mm_per_inch = 25.4;
 

Both these values should be fixed. There are always 3 feet to a yard, and an inch is always 25.4 millimeters. Although they are fixed values for which you could use a literal in calculations, it is very convenient to store them in a variable because using suitable names makes it clear in your program what they mean. If you use the value 3 in your program code it could mean anything — but the name feet_per_yard leaves no doubt as to what it is.

Generally you want to prevent the values of these variables from being modified. Accidental changes to the number of feet in a yard could make the results of your program suspect, to say the least. Java provides you with a way to fix the value of any variable by using the final keyword when you declare it, for example:

final int FEET_PER_YARD = 3;           // Constant values
final double MM_PER_INCH = 25.4;       // that cannot be changed
 

The final keyword specifies that the value of a variable is final and must not be changed. The compiler checks your code for any violations of this and flags them as errors. I’ve used uppercase letters for the names of the variables here because it is a convention in Java to write constants in this way. This makes it easy to see which variables are defined as constant values. Obviously, any variable you declare as final must have an initial value assigned as you can’t specify it later.

Now that you know how to declare and initialize variables of the basic types, you are nearly ready to write a program. You just need to look at how you express the calculations you want carried out, and how you store the results.

ARITHMETIC CALCULATIONS

You store the result of a calculation in a variable by using an assignment statement. An assignment statement consists of three elements: the name of the variable where you want the result stored; the assignment operator, =, that indicates that this is indeed an assignment operation; and an arithmetic expression that defines the calculation you want to perform to produce the result. The whole thing is terminated by a semicolon that marks the end of the assignment statement. Here’s a simple example of an assignment statement:

numFruit = numApples + numOranges;       // Calculate the total fruit
 

When this statement executes, the value of the expression to the right of the assignment operator, =, is calculated, and the result is stored in the variable that appears to the left of the = sign. In this case, the values stored in the variables numApples and numOranges are added together, and the result is stored in the variable numFruit. Of course, you have to declare and initialize all three variables before using this assignment statement.

Incrementing a variable by a given amount is a common requirement in programming. Look at the following assignment statement:

numApples = numApples + 1;
 

The result of evaluating the expression on the right of the = is one more than the value of numApples. This result is stored back in the variable numApples, so the overall effect of executing the statement is to increment the value in numApples by 1. You will see an alternative, more concise, way of producing the same effect shortly.

You can write multiple assignments in a single statement. Suppose you have three variables a, b, and c that you have defined to be of type int, and you want to set all three to 777. You can do this with the following statement:

a = b = c = 777;
 

Note that an assignment is different from initializing a variable in a declaration. Initialization causes a variable to have the value of the constant that you specify when it is created. An assignment involves copying data from one place in memory to another. For the preceding assignment statement, the compiler allocates some memory (4 bytes) to store the constant 777 as type int. This value is then copied to the variable c. The value in c is extracted and copied to b. Finally the value in b is copied to a. (However, strictly speaking, the compiler may optimize these assignments when it compiles the code to reduce the inefficiency of performing successive assignments of the same value in the way I have described.)

With simple assignments of a constant value to a variable of type short or byte, the constant is stored as the type of the variable on the left of the =, rather than type int. For example:

short value = 0;
value = 10;
 

Here you have a declaration statement for the variable value, followed by an assignment statement. When the declaration executes, it allocates space for the variable value and arranges for its initial value to be 0. The assignment statement that follows the declaration statement needs to have 10 available as an integer literal of type short, occupying 2 bytes, because value is of type short. The compiler converts the literal value 10, which is of type int, to type short. The value 10 is then copied to the variable value.

Now let’s look in more detail at how you perform calculations with integers.

Integer Calculations

The basic operators you use in calculations involving integers are +, −, *, and /, and these have the usual meanings — add, subtract, multiply, and divide, respectively. (The % operator is also a basic operator and I’ll discuss that later in this chapter.) Each of these is a binary operator; that is, they combine two operands to produce a result. 2 + 3, for example, results in 5. An operand is just the term for a value to which an operator is applied. The priority or precedence that applies when an expression using these operators is evaluated is the same as you learned at school, so multiplication and division operations are executed before any addition or subtraction. Evaluating the expression

20 - 3 * 3 - 9 / 3
 

produces the value 8 because it is equivalent to 20 − 9 − 3.

As you also have learned in school, you can use parentheses in arithmetic calculations to change the sequence of operations. Expressions within parentheses are always evaluated first, starting with the innermost set of parentheses when they are nested. You use parentheses to override the default sequence of operations. Therefore the expression

(20 -3) x (3 -9) / 3
 

is equivalent to 17 * (−6) / 3, which results in −34.

Of course, you use these operators with variables that store integer values as well as integer literals. You could calculate a value to be stored in a variable, area, of type int from values stored in the variables length and width, also of type int, by writing the following:

area = length * width;
 

As I said earlier, these arithmetic operators are binary operators, so called because they require two operands. There are also unary versions of the + and - operators that apply to a single operand to the right of the operator. Note that the unary - operator is not just a sign, as in a literal such as -345; it is an operator that has an effect. When applied to a variable, it results in a value that has the opposite sign to that of the value stored in the variable. For example, if the variable count has the value -10, the expression -count has the value +10. Of course, applying the unary + operator to the value of a variable results in the same value.

Let’s try out some simple arithmetic in a working application.

TRY IT OUT: Apples and Oranges

Key in the code for this example and save it in a file with the name Fruit.java. Remember from the previous chapter that each source file contains a class definition, and that the name of the file is the same as that of the class with the extension .java. Store the file in a directory that is separate from the hierarchy containing the JDK. You can give the directory any name that you want, even the name Fruit if that helps to identify the program that it contains.

image
public class Fruit {
  public static void main(String[] args) {
    // Declare and initialize three variables
    int numOranges = 5;                     // Count of oranges
    int numApples = 10;                     // Count of apples
    int numFruit = 0;                       // Count of fruit
 
    numFruit = numOranges + numApples;      // Calculate the total fruit count
 
    // Display the result
    System.out.println("A totally fruity program");
    System.out.println("Total fruit is " + numFruit);
  }
}

code snippet Fruit.java

In some Java development environments, the output may not be displayed long enough for you to see it when you run the program. If this is the case, you can add a few lines of code to get the program to wait until you press Enter before it ends. The additional lines to do this are shown bolded in the following listing:

image
import java.io.IOException;  // For code that delays ending the program
public class FruitWait {
  public static void main(String[] args) {
    // Declare and initialize three variables
    int numOranges = 5;                 // Count of oranges
    int numApples = 10;                 // Count of apples
    int numFruit = 0;                   // Count of fruit
 
    numFruit = numOranges + numApples;  // Calculate the total fruit count
    // Display the result
    System.out.println("A totally fruity program");
    System.out.println("Total fruit is " + numFruit);
    // Code to delay ending the program
    System.out.println("(press Enter to exit)");
    try {
      System.in.read();               // Read some input from the keyboard
    } catch (IOException e) {         // Catch the input exception
      return;                         // and just return
    }
  }
}

code snippet FruitWait.java

I have changed the class name from Fruit to FruitWait to distinguish it from the previous version of the program. The file FruitWait.java is in the code download for the book. I won’t go into this extra code here. If you need to, just put it in for the moment. You will learn exactly how it works later in the book.

How It Works

The stuff between the parentheses following main — that is String[] args — provides a means of accessing data that passed to the program from the command line when you run it. I go into this in detail later in the book so you can just ignore it for now, though you must always include it in the first line of main(). If you don’t, the program compiles but doesn’t execute.

All that additional code in the body of the main() method in the FruitWait.java version just waits until you press Enter before ending the program. If necessary, you can include this in all of your console programs at each point in the code where the program terminates to make sure they don’t disappear before you can read the output. It won’t make any difference to how the rest of the program works. I will defer discussing in detail what is happening in the bit of code that I have added until I get to explaining exceptions in Chapter 7.

If you run the FruitWait.java version of this program, the output is the following:

A totally fruity program
Total fruit is 15
(press Enter to exit)

The original version does not output the third line.

The basic elements of the code in the original version of the program are shown in Figure 2-2:

The original version of the program consists of just one class, Fruit, and just one method, main(). Execution of an application always starts at the first executable statement in the method main(). There are no objects of the class Fruit defined, but the method main() can still be executed because I have specified it as static. The method main() is always specified as public and static and with the return type void. The effects of these three keywords on the method are as follows:

public Specifies that the method is accessible from outside the Fruit class.
static Specifies that the method is a class method that is to be executable, even though no class objects have been created. (Methods that are not static can be executed only for a particular object of the class, as you will see in Chapter 5.)
void Specifies that the method does not return a value.

Don’t worry if these are not completely clear to you at this point — you see them all again later.

The first three statements in main() declare the variables numOranges, numApples, and numFruit to be of type int and initialize them to the values 5, 10, and 0 respectively. The next statement adds the values stored in numOranges and numApples, and stores the result, 15, in the variable numFruit. We then generate some output from the program.

Producing Output

The next two statements use the println() method, which displays text output. The statement looks a bit complicated, but it breaks down quite simply, as Figure 2-3 shows.

The text between double quotes, "A totally fruity program", is a character string. Whenever you need a string constant, you just put the sequence of characters that you want in the string between double quotes.

You can see from the annotations in Figure 2-3 how you execute methods that belong to an object. Here you execute the method println(), which belongs to the object out, which, in turn, is a variable that is a static member of the class System. Because the object out is static, it exists even if there are no objects of type System in existence. This is analogous to the use of the keyword static for the method main().

Most objects in a program are not static members of a class though, so calling a method for an object typically just involves the object name and the method name. For instance, if you guessed based on the last example that to call the putHatOn() method for an object cowboyHat of the type Hat that I introduced in Chapter 1, you would write

cowboyHat.putHatOn();

you would be right. Don’t worry if you didn’t though. I go into this again when we look at classes in detail. For the moment, any time you want to output something as text to the console, you just write

System.out.println( whateverYouWantToDisplay );

with whatever character string you want to display plugged in between the parentheses.

Thus, the second statement in the example

System.out.println("Total fruit is " + numFruit);
 

outputs the character string “Total fruit is” followed by the value of numFruit converted to a character string, that is “15”. So what’s the + doing here — it’s obviously not arithmetic we are doing, is it? The addition operator has a special effect when used with operands that are character strings — it joins them together to produce a single string. But numFruit is not a string, is it? No, but the left operand, "Total fruit is " is, and this causes the compiler to decide that the whole thing is an expression working on character strings. Therefore, the compiler inserts code that converts the value of the right operand, numFruit, to a character string to be compatible with the left operand. The effect of the + operation is to tack the string representation of the value of numFruit on to the end of the string "Total fruit is ". The composite string is then passed to the println() method to display it on your screen. Dashed clever, these compilers.

If you want to output the value of numOranges as well, you could write the following:

System.out.println("Total fruit is " + numFruit + " and oranges = " + numOranges);

Try it out by adding it to the program if you like. You should get this output:

Total fruit is 15 and oranges = 5

Integer Division and Remainders

When you divide one integer by another and the result is not exact, any remainder is discarded, so the final result is always an integer. The division 3 / 2, for example, produces the result 1, and 11 / 3 produces the result 3. This makes it easy to divide a given quantity equally among a given number of recipients. To divide numFruit equally between four children, you could write:

int numFruitEach = 0;        // Number of fruit for each child
numFruitEach = numFruit/4;
 

The result of division when the operands are positive is fairly obvious. It’s the result of dividing the right operand, called the divisor, into the left operand, referred to as the dividend, a whole number of times. The situation when either or both operands are negative deserves a little more exploration.

If you divide 7 by −3, the result is −2. Similarly, if you divide −10 by 4 the result is −2. If you divide −5 by −3 the result is +1. The magnitude of the result of dividing a value a by a value b is the same, regardless of the sign of the operands, but the sign of the result depends on the sign of the operands. The sign of the result is positive when the operands both have the same sign and negative when the operands are of different signs and the divisor is not greater than the dividend (in which case the result is zero). There is one peculiar exception to this. When the divisor is a negative integer of the largest possible magnitude and the divisor is −1, the result is the same as the dividend, which is negative and therefore violates the rule. You can see why this is so by considering specifics.

The value −2_147_483_648 is the negative value of type int that has the largest magnitude. Dividing this by −1 should result in the value +2_147_483_648, but the largest positive integer you can have as type int is 2_147_483_647, so this result cannot be represented as type int. Therefore, the result is arbitrarily the original dividend, −2_147_483_648.

Dividing by zero is something you should avoid. If you accidentally cause this to be attempted, your program terminates because an exception of type ArithmeticException is thrown. You will learn what exceptions are and what you can do about them in Chapter 7.

Of course, there are circumstances where you may want to obtain the remainder after a division, and on these occasions you can calculate the remainder using the modulus operator, %. If you wanted to know how many fruit were left after dividing the total by 4, you could write the following:

int remainder = 0;
remainder = numFruit%4;      // Calculate the remainder after division by 4
 

When either or both operands to the remainder operator are negative, the result may not seem to be obvious. Keep in mind, though, that it is related to the divide operation, so if you can work out what the result of a division is, you can deduce the result of the remainder operation. You can get a clear idea of what happens by considering a few examples.

The result of the operation 8 % (−3) is +2. This is evident if you recall that from the earlier discussion of division you know that the result of 8 / (−3) is −2. If you multiply the result of the division by the divisor, (−2) * (−3), the result is +6, so a remainder of +2 makes sense. The expression (−8) % 3 produces −2, which again, you can deduce from the result of (−8) / 3 being −2. You have to add −2 to the result of (−2) * 3 to get the original value, −8. Lastly (−8) % (−3) results in −2, which is also consistent with the divide operation applied to the same operands.

The modulus operator has the same precedence as multiplication and division and therefore executes before any add or subtract operations in the same expression. You could add these statements to the program, too, if you want to see the modulus operator in action. The following statement outputs the following results:

System.out.println("The number of fruit each is " + numFruitEach
                                 + " and there are " + remainder + " left over.");
 

The Increment and Decrement Operators

If you want to increment an integer variable by one, you can use the increment operator instead of using an assignment. You write the increment operator as two successive plus signs, ++. For example, if you have an integer variable count that you’ve declared as

int count = 10;
 

you can then write the statement

++count;               // Add 1 to count
 

This statement increases the value of count to 11. To decrease the value of count by 1, you can use the decrement operator, --:

--count;               // Subtract 1 from count
 

At first sight, apart from reducing the typing a little, this doesn’t seem to have much of an advantage over writing the following:

count = count - 1;     // Subtract 1 from count
 

However, a big advantage of the increment and decrement operators is that you can use them in an expression. Try changing the arithmetic statement calculating the sum of numApples and numOranges in the previous example:

public class Fruit {
  public static void main(String[] args)   {
    // Declare and initialize three variables
    int numOranges = 5;
    int numApples = 10;
    int numFruit = 0;
 
    // Increment oranges and calculate the total fruit
    numFruit = ++numOranges + numApples; 
    System.out.println("A totally fruity program");
    // Display the result
    System.out.println("Value of oranges is " + numOranges);
    System.out.println("Total fruit is " + numFruit);
  }
}
 

I bolded the lines that have been altered or added. In addition to the change to the numFruit calculation, I added an extra statement to output the final value of numOranges. The value of numOranges is increased to 6 before the value of numApples is added, so the value of numFruit is 16. Thus, the statement changes the value stored in numOranges as well as the value stored in numFruit. You could try the decrement operation in the example as well.

A further property of the increment and decrement operators is that they work differently in an expression depending on whether you put the operator in front of the variable to which it applies or following it. When you put the operator in front of a variable, as in the example you have just seen, it’s called the prefix form. The converse case, with the operator following the variable, is called the postfix form. If you change the statement in the example to

numFruit = numOranges++ + numApples;
 

and run it again, you find that numOranges still ends up with the value 6, but the total stored in numFruit has remained 15. This is because the effect of the postfix increment operator is to change the value of numOranges to 6 after the original value, 5, has been used in the expression to supply the value of numFruit. The postfix decrement operator works similarly, and both operators can be applied to any type of integer variable.

As you see, no parentheses are necessary in the expression numOranges++ + numApples. You could even write it as numOranges+++numApples and it still means the same thing, but it is certainly a lot less obvious that this is the case. Someone who doesn’t have all the rules for evaluating Java expressions at his fingertips might guess, wrongly, that the expression executes as numOranges+(++numApples). Such potential confusion is really the programmer’s fault. You can write it as

(numOranges++) + numApples

to make it absolutely clear where the ++ operator belongs. It is a good idea to always add parentheses to clarify things when there is some possibility of misinterpretation.

Computation with Shorter Integer Types

I have deliberately used variables of type int in all the previous examples. Computations with variables of the shorter integer types introduce some complications. This is because all binary integer operations in Java work only with both operands of type int or both operands of type long. With arithmetic expressions using variables of type byte or short, the values of the variables are first converted to type int, and the calculation is carried out using 32-bit arithmetic. The result is therefore type int — a 32-bit integer. This has an interesting effect that you can see in the context of the previous example. Try changing the types of the variables numOranges, numApples, and numFruit in the original version of the program to type short. For example:

short numOranges = 5;
short numApples = 10;
short numFruit = 0;
 

You will find that the program no longer compiles. The problem is with this statement:

numFruit = numOranges + numApples;
 

Because the expression numOranges + numApples produces a 32-bit result, the compiler cannot store this value in numFruit, as the variable numFruit is only 16 bits long. To make the code acceptable to the compiler, you must modify the assignment statement so that the 32-bit result of the addition is converted back to a 16-bit number. You do this by changing the statement to:

numFruit = (short)(numOranges + numApples);
 

The statement now calculates the sum of numOranges and numApples and then converts, or casts, the 32-bit result to type short before storing it in numFruit. This is called an explicit cast, and the conversion process is referred to as casting. The cast to type short is the expression (short), and the cast applies to whatever is immediately to the right of (short), so the parentheses around the expression numOranges + numApples are necessary. Without them the cast applies only to the variable numOranges, which is type short anyway, and the code still does not compile.

If the variables here were of type byte, you would need to cast the result of the addition to type byte. You would write such a cast as (byte). This is a strong clue to how you write casts to other types. In general, you write a cast to any given type, typename, as the typename between parentheses — thus (typename).

The effect of the cast to type short in the example is just to take the least significant 16 bits of the result, discarding the most significant 16 bits. The least significant bits are those at the right-hand end of the number because the bits in a binary number in Java increase in value from right to left. Thus, the most significant bits are those at the left-hand end. For the cast to type byte only the least significant 8 bits are kept. This means that if the magnitude of the result of the addition is such that more than 16 bits are necessary to represent it (or 8 bits in the case of a cast to byte), your answer will be wrong. You get no indication from the compiler that this has occurred because it was you, after all, that expressly specified the cast, and the compiler assumes that you know what you are doing. To minimize the possibility for such hidden and mystifying errors, you should avoid explicit casts in your programs unless they are absolutely essential.

An integer arithmetic operation involving a value of type long is always carried out using 64-bit values. If the other number in such an operation is not of type long, the compiler arranges for it to be cast to type long before the operation is executed. For example:

long result = 0;
long factor = 10L;
int number = 5;
result = factor*number;
 

To execute the last statement, the multiplication is carried out using long values because the variable factor is of type long. The value stored in number is converted to type a, and that is multiplied by the value of factor.

All other integer arithmetic operations involving types other than long are carried out with 32-bit values. Thus, you really need to consider only two kinds of integer literals:

  • Type long for operations with 64-bit values where the value has an L appended
  • Type int for operations with 32-bit values for all other cases where there is no L at the end of the number

Errors in Integer Arithmetic

As I mentioned earlier, if you divide an integer value by zero, no sensible result can be produced so an exception is thrown. An exception is the way of signaling errors in Java that I will discuss in detail in Chapter 7. Using the % operator with a variable or expression for the right-hand operand that has a zero value also causes an exception to be thrown.

If an integer expression results in a value that is outside the range of the type of the result, the result is truncated to the number of bits for the type you are using and therefore is incorrect, but this is not indicated in any way. It is up to you to make sure that the integer types that you are using in your program are always able to accommodate any value that might be produced by your calculations.

Problems can arise with intermediate results in some situations. Even when the ultimate result of an expression is within the legal range, the result of any intermediate calculation that is outside the range is truncated, thus causing an incorrect result to be produced. To take a trivial example — if you multiply 1000000 by 2000000 and divide by 500000 using type int, you do not obtain the correct result if the multiplication is executed first. This is because the result of the multiplication exceeds the maximum that can be stored as type int. Obviously where you know this sort of problem can occur, you may be able to circumvent it by using parentheses to make sure the division takes place first — but you need to remember that integer division produces an integer result, so a different sequence of execution can produce a different answer.

Floating-Point Calculations

The four basic arithmetic operators (+, -, *, /) are also available for use in floating-point expressions, as is %, which I will come to shortly. You can try some of these out in another version of the Fruit program, which I’m calling AverageFruit.

TRY IT OUT: Average Fruit

Make the following changes to the Fruit.java file and save this as AverageFruit.java. If necessary, you can add the code you used earlier to make the program wait for the Enter key to be pressed before finishing.

image
public class AverageFruit {
  public static void main(String[] args) {
    // Declare and initialize three variables
    double numOranges = 50.0E-1;         // Initial value is 5.0
    double numApples = 1.0E1;            // Initial value is 10.0
    double averageFruit = 0.0;
    averageFruit = (numOranges + numApples)/2.0;    
    System.out.println("A totally fruity program");
    System.out.println("Average fruit is " + averageFruit);
  }
}

code snippet AverageFruit.java

This produces the following output:

A totally fruity program
Average fruit is 7.5
 

How It Works

The program just computes the average number of fruits of different kinds by dividing the total by 2.0.

As you can see, I have used various representations for the initializing values for the variables in the program, which are now of type double. It’s not the ideal way to write 5.0, but at least it demonstrates that you can write a negative exponent value.

Other Floating-Point Arithmetic Operators

You can use ++ and -- operators with floating point variables, and they have the same effect as with integer variables, incrementing or decrementing the floating-point variable to which they are applied by 1.0. You can use them in prefix or postfix form, and their operation in each case is the same as with integer variables.

You can apply the modulus operator, %, to floating-point values, too. For an operation of the form:

floatOperand1 % floatOperand2
 

The result is the floating-point remainder after dividing floatOperand2 into floatOperand1 an integral number of times. For example, the expression 12.6 % 5.1 gives the result 2.4 (actually 2.4000006 because decimal values that are not integral do not always have an exact representation as binary floating-point values). In general, the sign of the result of applying the modulus operator to floating-point values is the sign of the dividend. The magnitude of the result of a floating-point remainder operation is the largest integral value such that the magnitude of the result of multiplying the divisor by the result of the remainder operation does not exceed the dividend.

Error Conditions in Floating-Point Arithmetic

There are two error conditions that can occur with floating-point operations that are signaled by a special result value being generated. One occurs when a calculation produces a value that is outside the range that can be represented by the floating-point type you are using, and the other arises when the result is mathematically indeterminate, such as when your calculation is effectively dividing zero by zero.

To illustrate the first kind of error you could use a variable to specify the number of types of fruit. You could define the variable

double fruitTypes = 2.0;
 

and then rewrite the calculation as

averageFruit = (numOranges + numApples)/fruitTypes;
 

This in itself is not particularly interesting, but if you happened to set fruitTypes to 0.0, the output from the program would be the following:

A totally fruity program
Average fruit is Infinity
 

The value Infinity indicates a positive but effectively infinite result, in that it represents a value that is greater than the largest number that can be stored as type double. An effectively infinite result that was negative would be output as -Infinity. You don’t actually need to divide by zero to produce this effect; any calculation that generates a value that exceeds the maximum value that can be represented as type double has the same effect. For example, repeatedly dividing by a very small number, such as 1.0E-300, yields an out-of-range result.

If you want to see what an indeterminate result looks like, you can replace the statement to calculate averageFruit with the following:

averageFruit = (numOranges - 5.0)/(numApples - 10.0);
 

This statement doesn’t make much sense, but it produces an indeterminate result. The value of averageFruit is output as NaN. This value is referred to as Not-a-Number, indicating an indeterminate value. A variable with an indeterminate value contaminates any subsequent expression in which it is used, so any operation involving an operand value of NaN produces the same result of NaN.

A value that is Infinity or -Infinity is unchanged when you add, subtract, or multiply by finite values, but if you divide any finite value by Infinity or -Infinity the result is zero.

image

NOTE If a floating-point operation results in a negative value that is so small it cannot be represented, the result is0.0. Although0.0 is considered to be numerically identical to 0.0, dividing a positive value by0.0 produces -Infinity whereas dividing a positive value by 0.0 produces Infinity.

Mixed Arithmetic Expressions

You have probably guessed from earlier discussions that you can mix values of the basic types together in a single expression. The way mixed expressions are treated is governed by some simple rules that apply to each operator in such an expression. The rules, in the sequence in which they are checked, follow:

1. If either operand is of type double, the other is converted to double before the operation is carried out.

2. If either operand is of type float, the other is converted to float before the operation is carried out.

3. If either operand is of type long, the other is converted to long before the operation is carried out.

The first rule in the sequence that applies to a given operation is the one that is carried out. If neither operand is double, float, or long, they must be int, short, or byte, so they are converted to type int where necessary and use 32-bit arithmetic to produce the result, as you saw earlier in the chapter.

Explicit Casting

It may well be that the default treatment of mixed expressions listed in the preceding section is not what you want. For example, suppose you have defined a double variable result, and two variables, three and two, of type int with the values 3 and 2 respectively. If you compute the value of result with the statement

result = 1.5 + three/two;
 

the value stored is 2.5, because three/two is executed as an integer operation and produces the result 1. You may have wanted the term three/two to produce the value 1.5 so the overall result would be 3.0. You could do this using an explicit cast:

result = 1.5 + (double)three/two;
 

This causes the value stored in three to be converted to type double before the divide operation takes place. Then rule 1 applies for the divide operation, and the operand two is also converted to type double before the divide operation is executed. Hence, the value of result in this case is 3.0.

image

NOTE You can cast a value from any primitive type to any other, but you need to take care that you don’t unintentionally lose information when you do so. Obviously casting from one integer type to another with a more limited range has the potential for losing information, as does casting any floating-point value to an integer. Casting from type double to type float can also produce an effective infinity when the original value is greater than the maximum value for a value of type float.

Automatic Type Conversions in Assignments

When the type of the result of an arithmetic expression on the right of an assignment operator differs from the type of the variable on the left, an automatic cast is applied to the result as long as there is no possibility of losing information. If you think of the basic types that you have seen so far as being in the sequence

byte ⇒ short ⇒ int ⇒ long ⇒ float ⇒ double

then an automatic conversion is made as long as it is upward through the sequence of types — that is, from left to right. If you want to go in the opposite direction, from type double to type float or long, for example, then you must insert an explicit cast into your code for the result of the expression on the right of the assignment operator.

THE OP= OPERATORS

The op= operators are used in statements of the form

lhs op= rhs;
 

where op can be any of the arithmetic operators (+, -, *, /, %). It also works with some other operators you haven’t seen yet. The preceding statement is basically a shorthand representation of this statement:

lhs = lhs op (rhs);
 

The right-hand side (rhs) is in brackets because it is worked out first — then the result is combined with the left-hand side (lhs) using the operation op. Let’s look at a few examples of this to make sure it’s clear. To increment an int variable count by 5 you can write:

count += 5; 
 

This has the same effect as the following statement:

count = count + 5;
 

Of course, the expression to the right of the op= operator can be anything that is legal in the context, so the statement

result /= a % b/(a + b);
 

is equivalent to

result = result/(a % b/(a + b));
 

What I have said so far about op= operations is not quite the whole story. If the type of the result of the rhs expression is different from the type of lhs, the compiler automatically inserts a cast to convert the rhs value to the same type as lhs. This would happen with the last example if result was of type int and a and b were of type double, for example. This is quite different from the way the normal assignment operation is treated. A statement using the op= operator is really equivalent to:

lhs = (type_of_lhs)(lhs op (rhs)); 
 

The automatic conversion is inserted by the compiler regardless of what the types of lhs and rhs are. Of course, this can result in information being lost due to the cast, and you get no indication that it has occurred. This is different from ordinary assignment statements where an automatic cast is allowed only when the range of values for the type of lhs is greater that the range for the type of rhs.

The complete set of op= operators are the following:

image

You will learn about the operators on the second row later in the book.

MATHEMATICAL FUNCTIONS AND CONSTANTS

Sooner or later you are likely to need mathematical functions in your programs, even if it’s only to obtain an absolute value or calculate a square root. Java provides a range of methods that support such functions as part of the standard library that is stored in the package java.lang, and all these are available in your program automatically.

The methods that support various additional mathematical functions are implemented in the Math class as static methods, so to reference a particular function you can just write Math and the name of the method you want to use separated by a period. For example, the sqrt() method calculates the square root of whatever you place between the parentheses. To use the sqrt() method to produce the square root of the floating-point value that you’ve stored in a variable, aNumber, you write Math.sqrt(aNumber).

The class Math includes a range of methods for standard trigonometric functions. The most commonly used are shown in Table 2-3:

TABLE 2-3: Class Math Trigonometric Functions

image

As with all methods, the arguments that you put between the parentheses following the method name can be any expression that produces a value of the required type. The toRadians() method in the Math class converts a double argument that is an angular measurement in degrees to radians. There is a complementary method, toDegrees(), to convert in the opposite direction. The Math class also defines double values for e and π, which you can access as Math.E and Math.PI respectively. If you are not familiar with these trigonometric operations you can safely ignore them.

You also have a range of numerical functions implemented as static methods in the Math class and at least some of these are useful to you. These are shown in Table 2-4:

TABLE 2-4: Math Class Numerical Functions

image

Where more than one type of argument is noted in the table, there are actually several methods, one for each type of argument, but all have the same name. You will see how this is possible in Java implementing class methods, covered in Chapter 5.

Several methods implement mathematical functions in the Math class. You’ll probably be surprised at how often you find uses for some of these. The mathematical methods you have available are shown in Table 2-5:

TABLE 2-5: Math Class Mathematical Functions

image

image

I have not discussed all the methods in the Math class here, just the most commonly used ones. You may want to explore the JDK documentation to get a feeling for the rest.

You can try out a sample of the contents of the Math class in an example to make sure you know how they are used.

TRY IT OUT: The Math Class

You are planning a new circular pond in which you want to keep fish. Your local aquatics supplier tells you that they can stock the pond with fish at the rate of 2 inches of fish length per square foot of pond surface area. Your problem is to calculate the radius of the pond that will accommodate 20 fish averaging 10 inches in length. The solution, of course, is to write a Java program — what else? The following program calculates the radius of a pond in feet and inches that can provide a home for the number of fish you would like to keep:

public class PondRadius {
  public static void main(String[] args) {
    // Calculate the radius of a pond
    // which can hold 20 fish averaging 10 inches long
    int fishCount = 20;             // Number of fish in pond
    int fishLength = 10;            // Average fish length
    int inchesPerFoot = 12;         // Number of inches in one foot
    int lengthPerSqFt = 2;          // Fish length per square foot of surface
    double radius = 0.0;            // Pond radius in feet
 
    int feet = 0;                   // Pond radius - whole feet
    int inches = 0;                 //             - and whole inches
 
    double pondArea = (double)(fishCount*fishLength)/lengthPerSqFt;
    radius = Math.sqrt(pondArea/Math.PI);
 
    // Get the whole feet and nothing but the feet
    feet = (int)Math.floor(radius); 
    inches = (int)Math.round(inchesPerFoot*(radius - feet));  // Get the inches
 
    System.out.println(
                "To hold " + fishCount + " fish averaging " + fishLength +
                " inches long you need a pond with an area of 
" +
                pondArea + " square feet.");
    System.out.println("The radius of a pond with area " + pondArea +
                       " square feet is " + 
                       feet + " feet " + inches + " inches.");
  }
}
 

Save the program source file as PondRadius.java. When you compile and run it, you should get the following output:

To hold 20 fish averaging 10 inches long you need a pond with an area of 
100.0 square feet.
The radius of a pond with area 100.0 square feet is 5 feet 8 inches.
 

How It Works

You first define the variables that specify initial data followed by the variables feet and inches that you will use to store the result. You then calculate the pond surface area in feet with this statement:

double pondArea = (double)(fishCount*fishLength)/lengthPerSqFt;
 

You cast the total length of fish to be in the pond, fishCount*fishLength, to type double to force the division by the number of inches per square foot of pond surface to be done using floating-point values rather than integers.

The next calculation uses the sqrt() method to calculate the radius. Because the area of a circle with radius r is given by the formula πr2, the radius must be image, so you specify the argument to the sqrt() method as the expression pondArea/Math.PI, where Math.PI references the value of π that is defined in the Math class:

radius = Math.sqrt(pondArea/Math.PI);

The result is in feet as a value of type double.

To get the number of whole feet you use the floor() method:

feet = (int)Math.floor(radius);  // Get the whole feet and nothing but the feet

Note that the cast to type int of the value that is produced by the floor() method is essential in this statement; otherwise, you get an error message from the compiler. The value returned from the floor() method is type double, and the compiler will not cast this to type int automatically because the process potentially loses information.

Finally, you get the number of inches by subtracting the value for whole feet from the original radius, multiplying the fraction of a foot by 12 to get the equivalent inches, and then rounding the result to the nearest integer using the round() method:

inches = (int)Math.round(inchesPerFoot*(radius - feet));  // Get the inches
 

To output the result, you specify a combination (or concatenation) of strings and variables as an argument to the two println() method calls:

    System.out.println(
                "To hold " + fishCount + " fish averaging " + fishLength +
                " inches long you need a pond with an area of 
" +
                pondArea + " square feet.");
    System.out.println("The radius of a pond with area " + pondArea +
                       " square feet is " + 
                       feet + " feet " + inches + " inches.");
 

Each statement is spread over three lines for convenience here. The that appears in the first output statement specifies a newline character, so the output is on two lines. Any time you want the next bit of output to begin a new line, just add to the output string. You can’t enter a newline character just by pressing Enter because when you do that the cursor just moves to the next line. That’s why it’s specified as . There are other characters like this that you cannot enter directly that are covered a little later in this chapter.

Importing the Math Class Methods

It would be a lot more convenient if you were able to avoid having to qualify the name of every method in the Math class that you use with the class name. The code would be a lot less cluttered if you could write floor(radius) instead of Math.floor(radius), for example. Well, you can. All you need to do is put the following statement at the beginning of the source file:

import static java.lang.Math.*;        // Import static class members

This statement makes the names of all the static members of the Math class available for use in your program code without having to qualify them with the class name. This includes constants such as PI as well as static methods. You can try this statement in the PondRadius example. With this statement at the beginning of the source file, you are able to remove the qualification by the class name Math from all the members of this class that the program uses.

The * in the statement indicates that all static names are to be imported. If you want to import just the names from the Math class that the PondRadius program uses, you write the following:

import static java.lang.Math.floor;    // Import floor
import static java.lang.Math.sqrt;     // Import sqrt
import static java.lang.Math.round;    // Import round
import static java.lang.Math.PI;       // Import PI
 

These statements import individually the four names from the Math class that the program references. You could use these four statements at the beginning of the program in place of the previous import statement that imports all the static names. I will discuss this form of the import statement further in Chapter 5.

STORING CHARACTERS

Variables of type char store a single character code. They each occupy 16 bits (2 bytes) in memory because all characters in Java are stored as Unicode. To declare and initialize a character variable myCharacter you could use the following statement:

char myCharacter = 'X';
 

This initializes the variable with the Unicode character representation of the letter X. You must always put single quotes as delimiters for a character literal in a statement as in this example, 'X'. This is necessary to enable the compiler to distinguish between the character 'X' and a variable with the name X. Note that you can’t use double quotes as delimiters here because they are used to delimit a character string. A character string such as "X" is quite different from the literal of type char, 'X'.

Character Escape Sequences

In general, the characters that you are able to enter directly from your keyboard are a function of the keys you have available and the set of character codes they map to according to your operating system. Whatever that is, it is a small subset of the characters defined by the Unicode encoding. To enable you to enter any Unicode character as part of your program source code you can define Unicode characters by specifying the hexadecimal representation of the character codes in an escape sequence. An escape sequence is simply an alternative means of specifying a character that is often, but not exclusively, by its code. A backslash indicates the start of an escape sequence, so you have already met the escape sequence for a newline character, .

You create an escape sequence for a Unicode character by preceding the four hexadecimal digits of the character code by u. Because the Unicode coding for the letter X is the hexadecimal value 0x0058 (the low order byte is the same as the ASCII code), you could also declare and define myCharacter with this statement:

char myCharacter = 'u0058';
 

You place the escape sequence between single quotes to define the character literal. The result is the same as the previous statement where you used 'X' as the initial value for myCharacter. You can enter any Unicode character in this way, as long as you know its code of course.

image

NOTE You can get more information on the full Unicode character set on the Internet by visiting www.unicode.org/.

Because the backslash indicates the beginning of an escape sequence, you must always use the escape sequence, \, to specify a backslash character as a character literal or in a text string.

As you have seen, you write a character string (a String literal as shown in Chapter 4) enclosed between double quotes and a character literal between single quotes. For this reason you also need the escape sequences ' and " to specify these characters. For example, to produce the output

"It's freezing in here", he said coldly.
 

you could write

System.out.println(""It's freezing in here", he said coldly.");
 

In fact, it’s not strictly necessary to use an escape sequence to specify a single quote within a string, but obviously it is when you want to specify a single quote as a character literal. Of course, it is always necessary to specify a double quote within a string using an escape sequence, otherwise it would be interpreted as the end of the string.

There are other escape sequences that you use to define control characters (shown in Table 2-6).

TABLE 2-6: Escape Sequences

CHARACTER DESCRIPTION
 Backspace
f Form feed
New line
Carriage return
Tab

Character Arithmetic

You can perform arithmetic on char variables. With myCharacter containing the character 'X', the statement

myCharacter += 1;    // Increment to next character
 

results in the value of myCharacter being changed to 'Y'. This is because the Unicode code for 'Y' is one more than the code for 'X'. You could use the increment operator ++ to increase the code stored in myCharacter by just writing the following:

++myCharacter;       // Increment to next character
 

When you use variables of type char in an arithmetic expression, their values are converted to type int to carry out the calculation. It doesn’t necessarily make a whole lot of sense, but you could write the following statements that calculate with values of type char:

char aChar = 0;
char bChar = 'u0028';
aChar = (char)(2*bChar + 8);
 

These statements leave the aChar variable holding the code for the letter X — which is 0x0058.

TRY IT OUT: Arithmetic with Character Codes

This example demonstrates arithmetic operations with values of type char:

public class CharCodeCalcs {
  public static void main(String[] args){
    char letter1 = 'A';                // letter1 is 'A'
    char letter2 = (char)(letter1+1);  // letter2 is 'B'
    char letter3 = letter2;            // letter3 is also 'B'
    System.out.println("Here's a sequence of letters: "+ letter1 + letter2 +
                                                           (++letter3));
    // letter3 is now 'C'
    System.out.println("Here are the decimal codes for the letters:
"+
                               letter1 + ": " + (int)letter1 + 
                        "  " + letter2 + ": " + (int)letter2 +
                        "  " + letter3 + ": " + (int)letter3); 
  }
}
 

This example produces the output:

Here's a sequence of letters: ABC
Here are the decimal codes for the letters:
A: 65  B: 66  C: 67

How It Works

The first three statements in main() define three variables of type char:

    char letter1 = 'A';                // letter1 is 'A'
    char letter2 = (char)(letter1+1);  // letter2 is 'B'
    char letter3 = letter2;            // letter3 is also 'B'
 

The cast to type char of the initial value for letter2 is essential. Without it, the code does not compile. The expression letter1+2 produces a result of type int, and the compiler does not insert an automatic cast to allow the value to be used as the initial value for letter2.

The next statement outputs three characters:

    System.out.println("Here's a sequence of letters: "+ letter1 + letter2 +
                                                                      (++letter3));
 

The first two characters displayed are those stored in letter1 and letter2. The third character is the value stored in letter3 after the variable has been incremented by 1.

By default the println() method treats a variable of type char as a character for output. You can still output the value stored in a char variable as a numerical value simply by casting it to type int. The next statement demonstrates this:

    System.out.println("Here are the decimal codes for the letters:
"+
                               letter1 + ": " + (int)letter1 + 
                        "  " + letter2 + ": " + (int)letter2 +
                        "  " + letter3 + ": " + (int)letter3);

This statement outputs the value of each of the three variables as a character followed by its decimal value.

Of course, you may prefer to see the character codes as hexadecimal values. You can display any value of type int as a hexadecimal string by enlisting the help of a static method that is defined in the Integer class in the standard library. Add an extra output statement to the example as the last statement in main():

    System.out.println("Here are the hexadecimal codes for the letters:
"+
                               letter1 + ": " + Integer.toHexString(letter1) + 
                        "  " + letter2 + ": " + Integer.toHexString(letter2) +
                        "  " + letter3 + ": " + Integer.toHexString(letter3));
 

This statement outputs the character codes as hexadecimal values so you see this additional output:

Here are the hexadecimal codes for the letters:
A: 41  B: 42  C: 43
 

The toHexString() method generates a string representation of the argument you supply. Here you just have the name of a variable of type char as the argument in each of the three uses of the method, but you could put in any expression that results in a value of type int. Because the method requires an argument of type int, the compiler inserts a cast to type int for each of the arguments letter1, letter2, and letter3.

The Integer class is related to the primitive type int in that an object of type Integer “wraps” a value of type int. You will understand the significance of this better when you investigate classes in Chapter 5. There are also classes of type Byte, Short, and Long that relate to values of the corresponding primitive types. The Long class also defines a static method toHexString() that you use to obtain a string that is a hexadecimal representation of a value of type long. These classes also contain other useful utility methods that I introduce later when a suitable context arises.

Of course, you can use the static import statement that I introduced in the context of the Math class to import the names of static members of other classes such as Integer and Long. For example, the following statement at the beginning of a source file enables you to use the toHexString() method without having to qualify it with the Integer class name:

import static java.lang.Integer.toHexString;

BITWISE OPERATIONS

As you already know, all these integer variables I have been talking about are represented internally as binary numbers. A value of type int consists of 32 binary digits, known to us computer fans as bits. You can operate on the bits that make up integer values using the bitwise operators, of which there are four available, as shown in Table 2-7:

TABLE 2-7: Bitwise Operators

BIT OPERATOR
& AND
| OR
^ Exclusive OR
~ Complement

Each of these operators operates on the individual bits in its operands as follows:

  • The bitwise AND operator, &, combines corresponding bits in its two operands such that if both bits are 1, the result is 1 — otherwise the result is 0.
  • The bitwise OR operator, |, combines corresponding bits such that if either or both bits are 1, then the result is 1. Only if both bits are 0 is the result 0.
  • The bitwise exclusive OR (XOR) operator, ^, combines corresponding bits such that if both bits are the same the result is 0; otherwise, the result is 1.
  • The complement operator, ~, takes a single operand in which it inverts all the bits, so that each 1 bit becomes 0, and each 0 bit becomes 1.

Table 2-8 shows examples of the effect of each of these operators:

TABLE 2-8: Operator Examples

OPERATOR EXAMPLE
a 0b0110_0110_1100_1101
b 0b0000_0000_0000_1111
a & b 0b0000_0000_0000_1101
a | b 0b0110_0110_1100_1111
a ^ b 0b0110_0110_1100_0010
~a 0b1001_1001_0011_0010

This shows the binary digits that make up the operands a and b and the results of four bitwise operations. The three binary bitwise operations produces the result by applying the operation to each corresponding pair of bits from its operands in turn. The complement operator just flips the state of each bit in its operand so that 0 changes to 1 and 1 changes to 0 in the value that results.

Because you are concerned with individual bits when using bitwise operations, writing a constant as a normal decimal value is not going to be particularly convenient, and a binary literal is the obvious choice. If you are comfortable working with hexadecimal literals and can convert from binary to hexadecimal and vice versa very quickly, these provide a more compact way of representing bit patterns. There’s more on working with binary and hexadecimal values in Appendix B.

Using the AND and OR Operators

If you think of the variable b in the previous section as a mask that is applied to the value of a, you can view the & operator as keeping bits unchanged where the mask is 1 and setting the other bits to 0. Mask is a term used to refer to a particular configuration of bits designed to operate on specific bits when it is combined with a variable using a bitwise operator. So, if you want to select a particular bit out of an integer variable, to determine whether it is 1 or 0 for example, just AND it with a mask that has that bit set to 1 and all the others as 0.

You can also envisage what the & operator does from another perspective. The operation forces a bit to 0 if the corresponding mask bit is 0 and leaves a bit unchanged if the mask bit is 1. Thus the & operator provides you with a way to switch off specific bits in a word leaving the rest as they were. Just create a mask with 0 bits in the positions that you want to make 0 and with 1 bits everywhere else.

The | operator forces a bit to be 1 in a variable when the corresponding mask bit is 1, and each mask bit that is 0 leaves the corresponding bit unchanged. Thus you can use the | operator to set particular bits in a variable to 1.

The & and | operators are the most frequently used of the bitwise operators, mainly for dealing with variables where the individual bits are used as state indicators of some kind — for things that can be either true or false, or on or off. You could use a single bit as a state indicator determining whether something should be displayed, with the bit as 1, or not displayed, with the bit as 0. To take a simple example, to select the third bit from the right in the int variable indicators, you can write the following:

thirdBit = indicators & 0b0100;     // Select the 3rd bit, 0x4 in hexadecimal
 

The third bit of the variable thirdBit is the same as the third bit in indicators and all the other bits is zero. I can illustrate how this works by assuming the variable indicators contains the binary value 0b1111_1111_0000_1111, which you could also write as the hexadecimal value 0xFF07:

indicators 0b1111_1111_0000_1111
mask 0b0000_0000_0000_0100
indicators & mask 0b0000_0000_0000_0100

All these values should have 32 bits, and I am only showing 16 bits here, but you see all you need to know how it works. The mask value sets all the bits in the result to zero except for the third bit, which is set to that of the indicators variable. Here, the result of the expression is non-zero because the third bit in indicators is 1.

On the other hand, if the variable indicators contained the value 0xFF09 the result is different:

indicators 0b1111_1111_0000_1001
mask 0b0000_0000_0000_0100
indicators & mask 0b0000_0000_0000_0000

The result of the expression is now zero because the third bit of indicators is zero.

As I said, you can use the | operator to set a particular bit on. For example, to set the third bit in indicators on, you can write the following:

indicators = indicators | mask;   // Set the 3rd bit on
 

You can see how this applies to the last value you had for indicators:

indicators 0b1111_1111_0000_1001
mask 0b0000_0000_0000_0100
indicators | mask 0b1111_1111_0000_1101

As you can see, the effect is just to switch the third bit of indicators on, leaving all the other bits unchanged. Of course, if the third bit was already on, it would stay on.

You can also use the bitwise operators in the op= form. Setting the third bit in the variable indicators is usually written as:

indicators |= mask;
 

Although there is nothing wrong with the original statement, the preceding one is just a bit more concise.

To set a bit off you need to use the & operator again, with a mask that has 0 for the bit you want as 0, and 1 for all the others. To set the third bit of indicators off you could write:

indicators &= ~mask;                 // Set the 3rd bit off, mask value 0x4
 

The ~ operator provides a useful way of specifying a value with all bits 1 apart from one. The mask variable contains a value with the third bit as 1 and the other bits as 0. Applying the ~ operator to this flips each bit, so that the 0 bits are 1 and the 1 bit is zero. With indicators having the value 0xFF07, this would work as follows:

indicators 0b1111_1111_0000_0111
mask 0b0000_0000_0000_0100
~mask 0b1111_1111_1111_0100
indicators & ~mask 0b1111_1111_0000_0011

Let’s see some of these bitwise operations in action.

TRY IT OUT: Bitwise AND and OR Operations

This example exercises some of the operations that you saw in the previous section:

import static java.lang.Integer.toBinaryString;
 
public class BitwiseOps {
  public static void main(String[] args) {
    int indicators = 0b1111_1111_0000_0111;  // Same as 0xFF07
    int selectBit3 = 0b0000_0000_0000_0100;  // Mask to select the 3rd bit, 0x4
    
    // Try the bitwise AND to select the third bit in indicators
    System.out.println("indicators               = " +
                                                    toBinaryString(indicators));
    System.out.println("selectBit3               = " +
                                                    toBinaryString(selectBit3));
    indicators &= selectBit3;
    System.out.println("indicators & selectBit3  = " + 
                                                    toBinaryString(indicators));
 
    // Try the bitwise OR to switch the third bit on
    indicators = 0b1111_1111_0000_1001;      // Same as 0xFF09  
    System.out.println("
indicators               = "+
                                                    toBinaryString(indicators));
    System.out.println("selectBit3               = "+
                                                    toBinaryString(selectBit3));
    indicators |= selectBit3;
    System.out.println("indicators | selectBit3  = " + 
                                                    toBinaryString(indicators));
 
    // Now switch the third bit off again
    indicators &= ~selectBit3;
    System.out.println("
The third bit in the previous value of indicators" +
                                                       " has been switched off");
    System.out.println("indicators & ~selectBit3 = " +
                                                    toBinaryString(indicators));
  }
}
 

This example produces the following output:

indicators               = 1111111100000111
selectBit3               = 100
indicators & selectBit3  = 100
 
indicators               = 1111111100001001
selectBit3               = 100
indicators | selectBit3  = 1111111100001101
 
The third bit in the previous value of indicators has been switched off
indicators & ~selectBit3 = 1111111100001001
 

How It Works

The example uses the code fragments that I discussed in the previous section so you can see from the output that they work as described. One new capability introduced here is the use of the static toBinaryString() method that is defined in the Integer class. There’s a static import statement for the name of this method, so its use is not qualified by the class name in the example. The toBinaryString() method produces a string containing a binary representation of the value of type int that is passed as the argument to the method. You can see from the output for the value of selectBit3 that the string does not include leading zeros. Obviously, the output would be better with leading zeros displayed but you need to know more about handling strings to be able to fix this. By the end of Chapter 4, you will be in a position to do so.

Using the Exclusive OR Operator

The ^ operator has the slightly surprising ability to interchange two values without moving either value somewhere else. The need for this turns up most frequently in tricky examination questions. Suppose you execute the following three statements with integer variables a and b:

a ^= b;
b ^= a;
a ^= b;
 

The effect of these statements is to interchange the values of a and b, but remember this works only for integers. You can try this out with a couple of arbitrary values for a and b, 0b1101_0000_0000_1111 and 0b1010_1011_1010_1101, respectively — again, I am just showing 16 bits for each variable. The first statement changes a to a new value:

a 0b1101_0000_0000_1111
b 0b1010_1011_1010_1101
a from a^b 0b0111_1011_1010_0010

The next statement calculates a new value of b using the new value of a:

a 0b0111_1011_1010_0010
b 0b1010_1011_1010_1101
b from b^a 0b1101_0000_0000_1111

So b now has a value that looks remarkably like the value that a started out with. Let’s look at the last step, which calculates a new value for a using the new value of b:

a 0b0111_1011_1010_0010
b 0b1101_0000_0000_1111
a from a^b 0b1010_1011_1010_1101

Lo and behold, the value of a is now the original value of b. When you get to do some graphics programming later in the book, you see that this application of the exclusive OR operator is quite useful.

Don’t forget — all of these bitwise operators can be applied only to integers. They don’t work with any other type of value. As with the arithmetic expressions, the bitwise operations are carried out with 32 bits for integers of type short and of type byte, so a cast to the appropriate type is necessary for the result of the expression on the right of the assignment operator.

One note of caution — special care is needed when initializing variables of type byte and type short with hexadecimal values to avoid being caught out. For example, you might be tempted to initialize a variable of type byte to 1111 1111 with the statement:

byte allBitsOne = 0xFF;    // Wrong!!
 

In fact, this results in a compiler error message, and it doesn’t help to use 0b1111_1111 as the value. The literal 0xFF is 1111 1111, so what’s the beef here? The beef is that neither 0xFF nor 0b1111_1111 are 1111 1111 at all. These literals are type int, so they are the binary value 0000 0000 0000 0000 0000 0000 1111 1111. This happens to be equivalent to the decimal value 255, which is outside the range of type byte. The byte value you are looking for, 1111 1111, is equivalent to the decimal value −1, so the correct way to initialize allBitsOne to 1s is to write:

byte allBitsOne = 0xFFFFFFFF;    // Correct - well done!!
 

Now the compiler happily chops off the high order bits to produce the result you are looking for.

Shift Operations

Another mechanism that you have for working with integer variables at the bit level is shifting. You can shift the bits in an integer to the right or the left. You can also envisage the process of shifting binary digits right or left as dividing or multiplying by powers of two, respectively. Shifting the binary value of 3, which is 0011, to the left one bit multiplies it by 2. It becomes binary 0110, which is decimal 6. Shifting it to the right by 1 bit divides it by 2. It becomes binary 0001, which is 1.

Java has three shift operators:

<< Shift left, filling with zeros from the right
>> Shift right, propagating the sign bit from the left
>>> Shift right, filling with zeros from the left

The effect of each of the shift operators is shown in Figure 2-4.

image

Of course, if the high order bit in the >> operation in Figure 2-4 were zero, there would be three zeros at the leftmost end of the result.

Shift operations are often used in combination with the other bitwise operators I have discussed to extract parts of an integer value. In many operating systems a single 32-bit value is sometimes used to store multiple values. For example, you could store two 16-bit screen coordinates in a single 32-bit word. This is illustrated in Figure 2-5.

Figure 2-5 shows how you can use the shift operations to extract either the left or the right 16 bits from the variable value. You can see here why you have an extra shift right operation that propagates the leftmost bit. It is related to the notion of a shift as multiplying or dividing by a power of 2, and the implications of that in the context of negative integers that are represented in 2’s complement form (see Appendix B). When the sign bit is not propagated, the shift right operation does not have a numerical interpretation for negative values because the sign bit is treated the same as any other bit, and zeros are inserted from the right. When the sign bit is propagated, the effect for negative values is the same as for positive values — namely that each bit position shifted is a division by 2.

TRY IT OUT: Using Shift Operations

This example uses the shift operators with the bitwise operators to pack four values of type char into a variable of type long. Here’s the code:

import static java.lang.Long.toHexString;
 
public class PackingCharacters {
  public static void main(String[] args) {
    char letterA = 'A';
    char letterB = 'B';
    char letterC = 'C';
    char letterD = 'D';
    long packed = 0L;
    packed = letterD;                      // Store D
    packed = (packed << 16) | letterC;     // Shift and add the next letter - C
    packed = (packed << 16) | letterB;     // Shift and add the next letter - B
    packed = (packed << 16) | letterA;     // Shift and add the next letter - A
    System.out.println("packed now contains 0x" + toHexString(packed));
 
    // Now unpack the letters and output them
    long mask = 0xFFFF;                    // Rightmost 16 bits as 1
    char letter = (char)(packed & mask);   // Extract the rightmost letter
    System.out.println("From right to left the letters in packed are:");
    System.out.println("  " + letter + "  0x" + toHexString(letter));
    packed >>= 16;                         // Shift out the rightmost letter
    letter = (char)(packed & mask);        // Extract the new rightmost letter
    System.out.println("  " + letter + "  0x" + toHexString(letter));
    packed >>= 16;                         // Shift out the rightmost letter
    letter = (char)(packed & mask);        // Extract the new rightmost letter
    System.out.println("  " + letter + "  0x" + toHexString(letter));
    packed >>= 16;                         // Shift out the rightmost letter
    letter = (char)(packed & mask);        // Extract the new rightmost letter
    System.out.println("  " + letter + "  0x" + toHexString(letter));
  }
}
 

The output from this example is the following:

packed now contains 0x44004300420041
 

From right to left the letters in packed are:

  A  0x41
  B  0x42
  C  0x43
  D  0x44
 

How It Works

The first four statements in main() define variables initialized with the letters to be packed into the variable, packed, of type long that is defined in the fifth statement in main(). The packing process begins by storing the first character in packed:

    packed = letterD;                       // Store D
 

The rightmost 16 bits in packed now contain the character code D. This eventually ends up in the leftmost 16 bits of packed. The next statement inserts the next letter, C, into packed:

    packed = (packed << 16) | letterC;      // Shift and add the next letter - C
 

The letter is inserted by first shifting the contents of packed left by 16 bits, and then ORing the value of letterC with the result. At this point, the leftmost 32 bits of packed are zero and the rightmost 32 bits contain D followed by C.

The next two statements repeat the same process to insert B and then A:

    packed = (packed << 16) | letterB;     // Shift and add the next letter - B
    packed = (packed << 16) | letterA;     // Shift and add the next letter - A
 

Now the variable packed holds the codes for all four characters in the sequence D, C, B, and A.

The output produced by the next statement confirms this:

    System.out.println("packed now contains 0x" + toHexString(packed));
 

This statement uses the toHexString() method defined in the Long class to generate a string containing a hexadecimal representation of the value of packed. Because you have a static import statement for the name of this method, you don’t need to qualify it with the class name. You can see from the output that this consists of the character code values 0x44, 0x43, 0x42, and 0x41, which are the codes for the letters D through A.

The program then demonstrates how you can use the shift operators combined with the bitwise AND to extract the four char values from packed. The first step is to define a mask to select the rightmost 16 bits in a value of type long:

    long mask = 0xFFFF;                     // Rightmost 16 bits as 1
 

The next statement uses mask to pick out the rightmost character code in packed:

    char letter = (char)(packed & mask);    // Extract the rightmost letter
 

The cast to type char of the value that results from ANDing mask with packed is necessary because the compiler does not insert an automatic cast from type long to type char.

The next two statements output a heading followed by the first letter as a letter and its code:

    System.out.println("From right to left the letters in packed are:");
    System.out.println("  " + letter + "  0x" + toHexString(letter));
 

To get at the next character, you can shift out the character just extracted and AND the result with mask once again:

    packed >>= 16;                          // Shift out the rightmost letter
    letter = (char)(packed & mask);         // Extract the new rightmost letter
 

The result of the shift right operation is stored in packed, so ANDing mask with packed extracts the next letter. Extraction of the next two letters is achieved by repeating exactly the same process of shifting and then ANDing with mask. From the output you can see that it all works as it should.

Methods for Bitwise Operations

In addition to the basic Java language facilities for operations on integers at the bit level, you also have some methods available in library classes that provide you with a few extra capabilities. I won’t go into great detail on these as they’re rather specialized, but I’ll outline the methods and explain what they do so you are aware of them.

The methods that implement bitwise operations are defined in the Integer and Long classes in the java.lang package. The methods in the Integer class apply to values of type int, and the methods in the Long class apply to values of type long. Both classes define the static methods for bitwise operations, shown in Table 2-9:

TABLE 2-9: Static Methods for Bitwise Operations

METHOD DESCRIPTION
bitCount(arg) Returns the number of 1 bits in the binary integer that you supply as arg. The count is returned as a value of type int.
highestOneBit(arg) Returns an integer with a single 1 bit in the position corresponding to the leftmost 1 bit in arg. The value is returned as the same type as arg.
lowestOneBit(arg) Returns an integer with a single 1 bit in the position corresponding to the rightmost 1 bit in arg. The value is returned as the same type as arg.
numberOfLeadingZeros(arg) Returns the number of 0 bits preceding the leftmost 1 bit in arg. The value is returned as type int. If arg is zero then the method returns the total number of bits in arg, which is 32 for type int and 64 for type long.
numberOfTrailingZeros(arg) Returns the number of 0 bits following the rightmost 1 bit in arg. The value is returned as type int. If arg is zero then the method returns the total number of bits in arg, which is 32 for type int and 64 for type long.
reverse(arg) Returns the value that is obtained by reversing the order of bits in arg. The value is returned as the same type as arg.
rotateLeft(arg, distance) Returns the value obtained by rotating the bits in arg left by distance bits positions, where distance is a value of type int. Rotating left means that bits shifted out on the left are shifted into vacated bit positions on the right. The value is returned as the same type as arg.
rotateRight(arg, distance) Returns the value obtained by rotating the bits in arg right by distance bits positions, where distance is a value of type int. Rotating right means that bits shifted out on the right are shifted into vacated bit positions on the left. The value is returned as the same type as arg.

The return value is of the same type as the argument in each case where the result is a transformed version of the argument. Where it is simply a count, the value returned is of type int.

If you think about what you would need to do yourself to implement what these methods do, you’ll realize they can save a lot of effort. To count how many 1 bits there are in an integer, you would need to work through each of the bits in a loop checking for a 1 bit in each case. With the bitCount() method, you get the result with a single operation. It may well be faster than you could implement it for yourself, too. Let’s consider some examples of how you use these methods.

First, suppose you define an integer variable as follows:

int data = 0x0F00;          // data is:  0b0000_0000_0000_0000_0000_1111_0000_0000
 

You can now apply the bitCount() method to this. You must use the method in the Integer class because data is of type int:

int bits = Integer.bitCount(data);     // Result is 4     
 

The variable bits is set to 4 because data contains four 1 bits.

Here’s a definition of another integer variable, this time of type long:

long number = 0xF000_0000_0000_000FL;    
 

The bit pattern in number has the first byte as 1111 0000 and the last byte as 0000 1111; all the other bytes are zero. Note that the L on the end of the literal is essential here. Without it you are specifying a literal of type int, and type int only has 32 bits so you get an error message from the compiler.

You could rotate the bits in number left by two with the following statement:

long result = Long.rotateLeft(number, 2);
 

The variable result are set to a value where the first byte is 0xC0, the last byte is 0x3F, and all the other bits are zero. The bits in number are shifted left by two bit positions, and the two 1 bits that are shifted out on the left are shifted in on the right as this is a rotation operation on the bits.

Let’s see some of these methods working for real.

TRY IT OUT: Methods for Operations on Bits

You can see the effects of some of the methods I have discussed by just outputting the results of some of the operations. The example also makes use of another static method that is defined in both the Integer and Long classes that you’ve seen in an earlier example — the toBinaryString() method that creates a string representation of a binary integer. Here’s the code:

import static java.lang.Long.*;
 
public class TryBitMethods {
  public static void main(String[] args) {
    long number = 0xF000_0000_0000_000FL;
    System.out.println("number:
" + toBinaryString(number));
    long result = rotateLeft(number,2);    
    System.out.println(
                    "number rotated left 2 bits:
" + toBinaryString(result));
    result = rotateRight(number, 3);
    System.out.println(
                   "number rotated right 3 bits:
" + toBinaryString(result));
    result = reverse(result);
    System.out.println("Previous result reversed:
" + toBinaryString(result));
    System.out.println("Bit count in number:" + bitCount(number));
  }
}
 

This program produces the following output:

number:
1111000000000000000000000000000000000000000000000000000000001111
number rotated left 2 bits:
1100000000000000000000000000000000000000000000000000000000111111
number rotated right 3 bits:
1111111000000000000000000000000000000000000000000000000000000001
Previous result reversed:
1000000000000000000000000000000000000000000000000000000001111111
Bit count in number: 8
 

I inserted characters in the output to put the binary value on the line following its description because it would not fit within the page width in the book. You might find it more convenient to remove the newlines but insert spaces to make the binary values align vertically. They are easier to compare that way.

How It Works

The program applies a variety of the methods for bit operation in the Long class to the value in number. The toBinaryString() method in the Long class creates a string representation of the binary value that is passed to the method, and you output that using the println() method. By comparing the bit patterns produced by each method with the original, you can clearly see what the methods do. You might like to try the same thing with the methods in the Integer class. Because there is an import statement for all the static members of the Long class, none of the methods from the Long class that the program uses need to be qualified with the class name.

VARIABLES WITH A FIXED SET OF INTEGER VALUES

You often need variables that can have values only from a predefined fixed set. For example, suppose you want to define an integer variable with the name weekday, which stores an integer value representing a day of the week. The variable ideally needs to be limited to seven possible values, one for each of Monday through Sunday. This is a situation where a facility called an enumerated type, or simply an enumeration, is a natural choice. You could define an enumeration for this situation with the following declaration statement:

enum Day {Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday }
 

This defines a new type, Day, for variables that can store only one or other of the values specified between the braces. The names Monday, Tuesday, and so on through to Sunday are called enumeration constants, and they identify the only values that are allowed for variables of type Day. In fact these names correspond to integer values, starting from 0 in this case, but they are not the same as integer variables because they exist only within the context of the enumeration, Day.

Note the absence of a semicolon at the end of the definition of the Day enumeration. Because you are defining a type here, no semicolon is required after the closing brace. I used a capital D at the beginning of the type name, Day, because by convention types that you define begin with a capital letter. The names for the enumeration constants would usually be written beginning with a small letter, but in this case I used a capital letter at the beginning because that’s how the days of the week are usually written. You could just as well write the enumeration constants with a small letter.

With this new type, you can now define the variable weekday like this:

Day weekday = Day.Tuesday;
 

This declares the variable weekday to be of type Day and initializes it with the value, Tuesday. Note that the enumeration constant that provides the initial value for weekday must be qualified with the name of the enumeration type here. If you leave the qualifier out, the compiler does not recognize the constant. There is a way to get around this, but you have to wait until Chapter 5 to find out about it. A variable of a given enumeration type can only be set to one or other of the enumeration constants that you defined for the type.

An enumeration can contain as many or as few enumeration constants as you need. Here’s an enumeration type for the months in the year:

enum Month { January, February, March    , April  , May     , June, 
             July   , August  , September, October, November, December }
 

You could define a variable of this type like this:

Month current = Month.September;       // Initialize to September
 

If you later want to change the value stored in the variable, you can set it to a different enumeration constant:

current = Month.October;
 

The current variable now contains the enumeration constant, October.

Let’s see an enumeration in action in an example.

TRY IT OUT: Using an Enumeration

Here’s a program that defines the Day enumeration and some variables of that type.

public class TryEnumeration {
  // Define an enumeration type for days of the week
  enum Day {Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday}
 
  public static void main(String[] args) {
    // Define three variables of type Day
    Day yesterday = Day.Thursday;
    Day today = Day.Friday;
    Day tomorrow = Day.Saturday;
 
    // Output the values of the Day variables
    System.out.println("Today is " + today);
    System.out.println("Tomorrow will be " + tomorrow);
    System.out.println("Yesterday was " + yesterday);
  }
}
 

This produces the output:

Today is Friday
Tomorrow will be Saturday
Yesterday was Thursday
 

How It Works

The code itself is essentially what you saw in the previous section. There is the declaration of the enumerated type, Day, with definitions of three variables of that type in main(). You then have output statements for the values of the three variables. Note that enum types cannot be local to a method. If you put the definition for the Day type within main(), the example does not compile. Here the Day type is local to the class TryEnumeration that contains the main() method. You could put the definition for Day outside the TryEnumeration class, in which case it is global.

The output is very interesting. It doesn’t display the numerical values of the variables of type Day but their names. This is the default way in which a value of an enumeration type is represented as a string because the names are more important than the values in most enumeration types. After all, the values that they have serve only to differentiate one enumeration constant from another.

This is just a small fraction of the capabilities of enumerations. I introduced the concept at this point because enumeration constants — the values that a variable of an enumeration type may have — are always integers. You will find out more about how you can use them as you progress through subsequent chapters, but you have to wait until Chapter 6 for the full story.

BOOLEAN VARIABLES

Variables of type boolean can have only one of two values, true or false. The values true and false are boolean literals. The boolean type is named after the mathematician, George Boole, who invented Boolean algebra, and variables of this type are described as boolean variables. You can define a variable of type boolean called state with the following statement:

boolean state = true;
 

This statement also initializes the variable state with the value true.

You can also set the value of a boolean variable in an assignment statement. For example, the statement

state = false;
 

sets the value of the variable state to false.

At this point you can’t do much with a boolean variable, other than to set its value to true or false, but as you see in the next chapter, boolean variables become much more useful in the context of decision making in a program, particularly when you can use expressions that produce a result of type boolean.

Several operators combine boolean values, including operators for boolean AND, boolean OR, and boolean negation (these are &&, ||, and !, respectively), as well as comparison operators that produce a boolean result. Rather than go into these here in the abstract, I defer discussion until the next chapter where I also go into how you can apply them in practice to alter the sequence of execution in a program.

image

NOTE Note that variables of type boolean differ from the other primitive data types in that they cannot be cast to any other basic type, and the other primitive types cannot be cast to type boolean.

OPERATOR PRECEDENCE

I have already introduced the idea of a pecking order for operators that determines the sequence in which they are executed in a statement. A simple arithmetic expression such as 3 + 4 * 5 results in the value 23 because the multiply operation is executed first — it takes precedence over the addition operation. I can now formalize the position by classifying all the operators present in Java according to their precedence. Each operator in Java has a set priority or precedence in relation to the others, as shown in the following table. Operators with a higher precedence are executed before those of a lower precedence. Precedence is highest for operators in the top line in the table, down through to the operators in the bottom line, which have the lowest precedence. Operators that appear on the same line of the table have the same precedence (see Table 2-10):

TABLE 2-10: The Associativity of Operator Precedence Groups

OPERATOR PRECEDENCE GROUP ASSOCIATIVITY
(), [], postfix ++, postfix −− left
unary +, unary −, prefix ++, prefix −−, ~, ! right
(type), new left
*, /, % left
+, − left
<<, >>, >>> left
< ,<= , >, >=, instanceof left
==, != left
& left
^ left
| left
&& left
|| left
?: left
=, +=, −=, *=, /=, %=, <<=, >>=, >>>=, &=, |=, ^= right

Most of the operators that appear in the table you have not seen yet, but you will learn about them all in this book eventually, and it is handy to have them all gathered together in a single precedence table that you can refer to when necessary.

By definition, the postfix ++ operator changes the value of its operand after the other operators in the expression in which it appears have been executed, despite its high precedence. In this case, precedence determines what it applies to; in other words, the postfix ++ acts only on the variable that appears immediately before it. For this reason the expression numOranges+++numApples that we saw earlier in the chapter is evaluated as (numOranges++) + numApples rather than numOranges + (++numApples).

The sequence of execution of operators with equal precedence in a statement is determined by a property called associativity. The operators that appear on the same line in Table 2-10 form a group of operators that are either left-associative or right-associative. A left-associative operator attaches to its immediate left operand. This results in an expression involving several left-associative operators with the same precedence in the same expression being executed in sequence starting with the leftmost and ending with the rightmost. Right-associative operators of equal precedence in an expression bind to their right operand and consequently are executed from right to left. For example, if you write the following statement

a = b + c + 10;
 

the left associativity of the group to which the + operator belongs implies that this is effectively

a = (b + c) + 10;
 

On the other hand, = and op= are right-associative, so if you have int variables a, b, c, and d each initialized to 1, the statement

a += b = c += d = 10;
 

sets a to 12, b and c to 11, and d to 10. The statement is equivalent to:

a += (b = (c += (d = 10)));
 
image

NOTE Note that these statements are intended to illustrate how associativity works and are not a recommended approach to coding.

You will probably find that you will learn the precedence and associativity of the operators in Java by just using them in your programs, so don’t spend time trying to memorize them. You may need to refer to the table from time to time, but as you gain experience you will gain a feel for where the operators sit and eventually you will automatically know when you need parentheses and when not.

PROGRAM COMMENTS

I have been adding comments in all the examples so far, so you already know that everything following // in a line is ignored by the compiler (except when the // appears in a character string between double quotes of course). Another use for // is to change lines of codes into comments so that they aren’t executed — to “comment them out” in other words. If you want to remove some code from a program temporarily, you just add // at the beginning of each line that you want to eliminate. Removing the // later restores the line of code.

It is often convenient to include multiple lines of comment in a program — for example, at the beginning of a method to explain what it does. An alternative to using // at the beginning of each line in a block of comments is to put /* at the beginning of the first comment line and */ at the end of the last comment line. Everything between the /* and the */ is ignored. By this means you can annotate your programs, like this for example:

/***************************************
 * This is a long explanation of       *
 * some particularly important         *
 * aspect of program operation.        *
 ***************************************/
 

Here I have used asterisks to highlight the comment. Of course, you can frame blocks like this in any way that you like, or even not at all, just so long as there is /* at the beginning and */ at the end.

Documentation Comments

You can also include comments in a program that are intended to produce separate documentation for the program. These are called documentation comments. A program called javadoc processes the documentation comments in the source code for a program to generate separate documentation for the code. All the documentation that you get with the SDK is produced in this way.

The documentation that is generated by javadoc is in the form of HTML web pages that can be viewed using a browser such as Firefox or Internet Explorer. A full discussion of documentation comments is outside the scope of this book — not because they are difficult; they aren’t. However, it would need a lot of pages to cover them properly, and there are already a lot of pages in the book. Here I just describe them sufficiently so that you will recognize documentation comments when you see them.

A documentation comment begins with /** and ends with */. An example of a simple documentation comment is the following:

/**
 *    This is a documentation comment.
 */
 

Any asterisks at the beginning of each line in a documentation comment are ignored, as are any spaces preceding the first asterisk.

A documentation comment can also include HTML tags, as well as special tags beginning with @ that are used to document methods and classes in a standard form. The @ character is followed by a keyword that defines the purpose of the tag. Table 2-11 shows some of the keywords that you can use:

TABLE 2-11: HTML Tag Keywords

KEYWORD DESCRIPTION
@author Used to define the author of the code. For example, I could specify that I am the author by adding the tag:
/**
* @author Ivor Horton
*/
@deprecated Used in the documentation of library classes and methods to indicate that they have been superseded and generally should not be used in new applications. This is primarily used within the class libraries to identify obsolete methods.
@exception Used to document exceptions that the code can throw and the circumstance that can cause this to occur. For example, you might add the following documentation comment preceding your definition of a method to indicate the type of exception that the method may throw:
/**
* @exception IOException When an I/O error occurs.
*/
{@link} Generates a link to another part of the documentation within the documentation that is produced. You can use this tag to embed a link to another class or method within descriptive text for your code. The curly brackets are used to separate the link from the rest of the in-line text.
@param Used to describe the parameters for a method.
@return Used to document the value returned from a method.
@see Used to specify cross-references to some other part of the code such as another class or a method. For example:
/**
* @see Object#clone()
*/
It can also just specify a string that represents an external reference not identifiable by a URL. For example:
/**
* @see "Beginning Java 7"
*/
It can also reference a URL.
@throws A synonym for @exception.
@version Used to describe the current version of the code.

You can use any HTML tags within a documentation comment except for header tags. The HTML tags you insert are used to structure and format the documentation appropriately when it is viewed, and javadoc adds HTML tags to format the comments that include the special @ tags that I mentioned in the preceding table.

The outline here really only gives you a hint as to what documentation comments are and doesn’t do justice to the power and scope of javadoc. For that you need to look into it in detail. If you want to see real examples of javadoc comments, take a look at one or other of the source code files for the library classes. The SDK comes with the javadoc program and its documentation. javadoc also has its own home page on the Javasoft website at www.oracle.com/technetwork/java/javase/documentation/javadoc-137458.html.

SUMMARY

In this chapter you have seen all of the basic types of variables that are available in Java. All other types that you define for yourself are ultimately defined in terms of these primitive types. The discussion of boolean variables will be more meaningful in the context of the next chapter because their primary use is in decision making and modifying the execution sequence in a program.

EXERCISES

1. Write a console program to define and initialize a variable of type byte to 1, and then successively multiply it by 2 and display its value 8 times. Explain the reason for the last result.

2. Write a console program to declare and initialize a double variable with some value such as 1234.5678. Retrieve the integral part of the value and store it in a variable of type long, and retrieve the first four digits of the fractional part and store them in an integer of type short. Display the value of the double variable by outputting the two values stored as integers.

3. Write a program that defines a floating-point variable initialized with a dollar value for your income and a second floating-point variable initialized with a value corresponding to a tax rate of 35 percent. Calculate and output the amount of tax you must pay with the dollars and cents stored as separate integer values (use two variables of type int to hold the tax, perhaps taxDollars and taxCents).

4. The diameter of the sun is approximately 865,000 miles. The diameter of Earth is approximately 7600 miles. Use the methods in the class Math to calculate the following:

  • The volume of Earth in cubic miles
  • The volume of the sun in cubic miles
  • The ratio of the volume of the sun to the volume of Earth
image

• WHAT YOU LEARNED IN THIS CHAPTER

TOPIC CONCEPT
Primitive types Primitive types are types that are built in to the Java language. Variables of the primitive types store numerical values that are integers or floating-point values, boolean values, or Unicode characters.
Integer types The integer types are byte, short, int, and long, and variables of these types occupy 1, 2, 4, and 8 bytes, respectively.
Storing characters Variables of type char occupy 2 bytes and can store a single Unicode character code.
Storing logical values Variables of type boolean can have only the value true or the value false.
Floating-point types The floating-point types are float and double, occupying 4 and 8 bytes respectively.
Integer calculations Integer expressions are evaluated using 64-bit operations for variables of type long and using 32-bit operations for all other integer types. You must, therefore, add a cast for all assignment operations storing a result of type byte, type short, or type char.
Floating-point calculations A floating-point operation can produce a result that is outside the range of the type of the result. Values that are outside the range of a floating-point type are represented by a special value that is displayed as either Infinity or -Infinity. The result of a floating-point operation can also be indeterminate, which is displayed as NaN.
Casting You can convert a value of one type to another by using an explicit cast. A cast is automatically supplied where necessary for op= assignment operations. You cannot cast to or from values of type boolean.
Order of operations The order of execution of operators in an expression is determined by their precedence. Where operators are of equal precedence, the order of execution is determined by their associativity.
Enumeration types You use an enumeration type to define variables that can only be assigned values from a fixed set that you specified as part of the enumeration. An enumeration type cannot be defined within a method.
image
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.149.231.128