left-icon

Java Succinctly® Part 1
by Christopher Rose

Previous
Chapter

of
A
A
A

CHAPTER 6

Operators and Expressions

Operators and Expressions


Computers can add, subtract, multiply, and divide numbers extremely quickly. But basic numerical arithmetic is only one of the types of computation that computers do well. In this section, we will look at various methods for computing results and calculating other values for variables by using operators and expressions.

We will also look at some very important differences between the way computers perform arithmetic and the way that arithmetic works in mathematics.

Operators and operands

Operators are symbols used to indicate some specific action between one or two operands (in Java, the operators all take one or two operands). The arithmetic operators are actions such as addition, subtraction, multiplication, and division. Each operator has a specific symbol, such as “+” for addition and “*” for multiplication.

Operands are the parameters or the data for the operators. Each operator requires some predefined number of operands. For instance, addition requires two numbers, such as 5+6. Operands can be variables, objects, or literals (depending on exactly what the operator is). Operands specify the data that an operator operates on. The JVM reads the operands and the operator, it performs some bit manipulations based on the specifications of the operator, and this results in some value—for example, 5+6 results in the value 11.

Operators and operands are combined together with brackets “(“ and “)” to form expressions. Expressions are similar to basic arithmetic, but in Java we have a lot more flexibility than basic arithmetic. We can form arithmetic, logical, Boolean, and string expressions.

Each operator has a predefined precedence that dictates the order in which the operators should be executed when they are combined in an expression, and brackets are used to override the normal precedence rules of the operators.

The general system we use in arithmetic and Java programming is called Infix Notation. In this system, operators with a higher precedence are performed first, followed by the operators with a lower precedence. For instance, 4+5*3 is an expression equaling 19. The precedence of the multiplication operator is higher than the precedence for the addition operator, so we perform 5*3 first, then add the 4, which results in 19. In Infix Notation, any operation surrounded by brackets occurs first, regardless of the precedence of the operators. So, we could write (4+5)*3, which has the same operators as before, but because of the brackets, the 4+5 is performed first and the final result is 27.


Arithmetic operators

Table 2: Arithmetic Operators

Name

Symbol

Operands

Precedence

Addition

+

2

1

Subtraction

-

2

1

Postfix Increment

i++

1

1

Postfix Decrement

i--

1

1

Multiplication

*

2

2

Division

/

2

2

Modulus

%

2

2

Unary Negative

-i

1

3

Unary Positive

+i

1

3

Prefix Increment

++i

1

4

Prefix Decrement

--i

1

4

Table 2 shows the arithmetic operators available in Java. Many will be familiar from regular arithmetic, but some require more explanation. First, I have included a dummy variable name i for the operators that require a variable in a certain position (e.g., the Postfix Decrement uses two minus symbols to the right of a variable i, like so: i--).

Table 2 also shows the relative precedence of the arithmetic operators. This is not the true precedence that the compiler uses when it parses expressions, but merely relative precedence so that you get an idea for which operations will occur first. You should always use brackets to directly specify the order of operations in complex expressions.

Addition, subtraction, multiplication, and division behave in similar ways to their arithmetic counterparts. But there is an important distinction between Java's operators and their arithmetic counter parts—all Java arithmetic is finite. This leads to some important points that are described in the sections on Integer Arithmetic and Rounding Error.

Postfix and Prefix increment and decrement take only a single parameter each. To increment is to add one, to decrement is subtract one. These operators can only be used with variables—you cannot increment a literal. The operators add one and subtract one from the value of the variable, and they set the variable accordingly (They are actually assignment operators, but they are generally introduced with the arithmetic operators.). For instance, if j is an integer with 5 as the value, then j++ will cause j to become 6. Postfix and Prefix increment differ only in precedence. Postfix increment occurs before other operations because it has a high precedence, and prefix increment occurs after because it has a low precedence. Code Listing 6.0 illustrates the difference between Postfix and Prefix.

Code Listing 6.0: Postfix vs. Prefix Increment

int j = 10;

int b = ++j;     // j increment to 11.

               // b is set to 11.

          

int p = 10;

int g = p++;     // b is set to 10.

               // p increments to 11.

Unary negative and unary positive differ from their standard mathematical definitions. In this context, unary simply means “single operand” and binary means “two operands.” Unary negative is the minus symbol when used beside a single number or variable, e.g., -9. In regular mathematics, there is no clear distinction between the precedence of unary negative -7 and binary negative 5-6, which can be read as a shorthand for 5+-6. In programming languages, the two are often implemented with the unary versions having a greater precedence than the binary versions. This is because programming languages such as Java have the concept of a negative number instead of applying a negative operator with some predefined precedence. When the compiler reads -7, it does not take 7 and subtract it from 0. It reads the value as a negative integer.

Also worth mentioning is the modulus operator: %. Modulus returns the remainder after division. For example, 67%5 is equal to 2, because 2 is left over when we divide 67 by 5. Modulus is a very important operator. If a variable j is evenly divisible by another k, then j%k=0. That is, if j divides k, then j%k leaves no remainder.

Code Listing 6.1 shows some examples of expressions using the arithmetic operators. I have included comments describing the values of the variables as the lines are executed.

Code Listing 6.1: Arithmetic Operators

public class MainClass {

     public static void main(String[] args) {

          int j = 190;          // j gets 190

          j++;                // j gets 191

          j = j - 89;           // j gets 102

          int b = 78 * j;       // b gets 7956

          int c = b + (9*j) / 12; // c gets 8032

          b--;                // b gets 7955

          c = c % b + (j / 2);    // c gets 128

          c = --b;             // c and b get 7954

          int h = (j + b) % 210;  // h gets 76

     }

}

Integer arithmetic: overflow

In Java, the integers do not have an infinite size. They wrap around when the results are too great in magnitude to fit into the designated variable. This is a very important difference between the arithmetic that computers use and the mathematics of the real world. And it can lead to some strange results if we are not careful.

Code Listing 6.2: Overflow Example

public class MainClass {

     public static void main(String[] args) {

          

          // Causing overflow of a byte:

          byte myByte = 127;

          myByte++; // Results in -128

          System.out.println("Byte says 127+1=" + myByte);

          myByte = -128;

          myByte--; // Results in 127

          System.out.println("Byte says -128-1=" + myByte);

          

          // Causing overflow of a short.

          short myShort = 32767; // This is the maximum for short!

          System.out.println("Short is " + myShort + " before ++.");

          myShort++;

          System.out.println("Short is " + myShort + " after ++.");

          myShort--;

          System.out.println("Short is " + myShort + " after --.");

     }

}

In Code Listing 6.2, we set the variable myByte to 127, which is the largest positive value that a byte can accommodate. In the very next line, we increment the variable using the ++ operator. Incrementing a variable set to 127 will normally result in the answer 128. But the value 128 is outside the range of a byte, which is from -128 to 127. Therefore, the result will actually wrap around, and what we get is 127+1=-128. Unless we are keenly aware of this wraparound behavior, it can quickly lead to bugs in our code as we count up in positive integers and suddenly pop out the other side with a very large negative value.

Similarly, on the next line we set the value of myByte to -128, and we then try to subtract 1 from this variable. Again, the result would normally be -129, but -129 does not fall within the range of the byte and the result will wrap around to positive 127.

The output from Code Listing 6.2 is as follows:

Byte says 127+1=-128

Byte says -128-1=127

Short is 32767 before ++.

Short is -32768 after ++.

Short is 32767 after --.

The range of byte and short is very limited, and this overflow can be easily illustrated. But we must remember that this wrapping around occurs when we use any integer type, even long. The range of an int is far greater than a byte, but it is not infinite.

Integer arithmetic: truncation

In addition to overflow, division of two integers will always round the result toward zero before storing. This may seem a poor choice at first, but it is a more useful way to perform arithmetic than regular rounding (i.e. rounding to the nearest whole number). The basic rounding rule is to chop off any digits right of the decimal point, which means 6.7 becomes 6, and 89.23678 becomes 89, and -2773.223 becomes -2773. This is a type of rounding called truncation.

Code Listing 6.3: Integer Rounding

int a = 24/5;    // Results in 4

int b = -24/5;   // Results in -4

Code Listing 6.3 illustrates the division of 24 and -24 by 5. On the first line, we divide 24 by 5 and store the result in the variable a. The result of 24/5 is actually 4.8—or 4 and 4/5. But because integers are whole numbers, the 4/5 is discarded. Notice the 4.8 is not rounded up to 5, but toward 0. The digits right of the decimal point are simply chopped off.

Likewise, on the second line of Code Listing 6.3, we see the division of -24 by 5 results in -4. Normally, the result would be -4.8, but again, integers can only store whole numbers and the digits to the left of the decimal point are chopped off (or truncated).

Truncation is particularly useful when dividing integers because we have the modulus operator to use in conjunction with the division. If we want to know exactly what 24/5 is, we cannot use floating-point numbers because (as we will see in a moment) floating point can only represent a handful of rational numbers accurately, and 4.8 is not one of them. But we can use division and modulus to create a fraction. Code Listing 6.4 shows a short example of how we can maintain the exact results of an integer division.

Code Listing 6.4: Integer Arithmetic and Fractions

public class MainClass {

     public static void main(String[] args) {

          int numerator = 24;

          int denominator = 5;

          

          int wholeResult = numerator / denominator;

          int fractionalResult = numerator % denominator;

     

          System.out.println("The result is " + wholeResult + " and " +

               fractionalResult + "/" + denominator);

     }

}

Floating-point arithmetic: rounding error

Floating-point variables (double and float) can seem limitless because they have a very large range, and they can seemingly represent very precise fractional values. But floating-point variables are as limited as integers, and, as with integers, you should be aware of several caveats regarding floating-point arithmetic. The most important thing to keep in mind when using floating-point arithmetic is this—the more operations performed on floating-point values, the more error there will be in the result.

Floating-point variables can store only a specific set of rational results. We might assume that any real number can be stored in a float or double, but this is not true. The only numbers a float or double can store are sums of perfect powers of two and the number zero (0.0). Numbers such as 256, ¼, or 4096.1875 are all stored perfectly with no rounding in floating-point variables. Other seemingly simple numbers, such as 1/5 or 0.3333…, are rounded to a near approximation of their true value. This means that the more times we compute with numbers such as 1/5 and 1/3, the more error is introduced into the final result.

Also keep in mind that floating-point variables (float and double) are limited in precision in another way. The higher the floating-point numbers, the less precision we have in computing exact results. Integers with up to six digits are all represented exactly using a float, such as 18.0f, 27837.0, etc. But beyond six digits, only every second integer has a representable value. Floating point uses a specification called IEEE 754 (https://en.wikipedia.org/wiki/IEEE_floating_point) that exchanges accuracy for range. All integers represent a number exactly, but high in the floating-point variables there are gaps and some integers cannot be stored.

All of this means that every computation we perform with floating-point variables potentially introduces and/or magnifies rounding errors. And floating-point variables typically only store approximations of real numbers—they almost never store a fraction exactly.

Code Listing 6.5: Rounding Error in Floating-Point Arithmetic

public class MainClass {

     public static void main(String[] args) {

          float j = 901.0f/13.0f;

          float q = (53.0f / 13.0f)*17.0f;

          

          System.out.println("J: " + j + " Q:" + q);

          if(j != q)

               System.out.println("The two are not equal!");

     }

}

Code Listing 6.5 illustrates two methods for computing 901/13 using floats. The listing uses an if statement, which we will cover when we look at control structures. Here the point is that both floats j and q should theoretically have exactly the same value. With some basic arithmetic, we could compute the same answer from the expression we used to set j and q:

901/13 = 69 and 4/13

(53/13)*17 = 69 and 4/13

However, there is no way to represent 4/13 as a finite sum of powers of two, therefore the computer will introduce some rounding errors in both of the above computations when using floats. This means that the two expressions will not result in exactly the same answer in Java, and the line "The two are not exactly equal!" will be printed to the console. The second result is likely to be less accurate than the first because it uses more operations, but neither result is correct. This is very important to remember when we are comparing floats, because it means we often need some small degree of error.

Code Listing 6.6: Comparing Floats with Error

public class MainClass {

     public static void main(String[] args) {

          float j = 901.0f/13.0f;

          float q = (53.0f / 13.0f)*17.0f;

          

          System.out.println("J: " + j + " Q:" + q);

          if(Math.abs(j - q) > 0.001f)

               System.out.println("The two are not equal!");

     }

}

Code Listing 6.6 illustrates the use of Math.abs method to allow a small margin of error between the two floating-point results. If the difference between the two floating-point values is less than 0.001, we assume they are “trying” to represent the same result. The function Math.abs takes a single numerical argument and returns the absolute value (i.e. if the argument is positive, then it is returned unchanged, and if it is negative, then it is changed to positive).

Tip: If you need to perform a common mathematical function, there is a good chance that the function is included in Java’s Math library. Code Listing 6.6 shows how to call a function from the Math library by calling Math.abs and passing a parameter in brackets. The Math library contains trigonometric functions such as Math.sin and Math.cos, logarithm functions, min, max sqrt, and many others. It also contains very accurate constants such as Math.PI, which is double constant for the value of Pi, and Math.E, which is another double constant for the value e.

Assignment operators

The assignment operators are used to alter or set the values of our variables. The most common assignment operator is the assignment operator itself, which is represented in Java by a single equals sign, “=”. This is very different from the double equal sign, “==”, which is a conditional operator and which asks if two values are equal. If we want to set a variable called someVariable to the integer 78, we would use “someVariable = 78;”.

After the equals operator, the remaining assignment operators (+=, -=, /=, *=, %=, &=, !, =, and ^=) are provided for convenience. They are sometimes called compound assignment operators, and they are a shorthand for a particularly common form of expression. When we have a variable, we often need to perform some operation on it and store the resulting value back into the same variable. This does not make sense mathematically, but it is perfectly normal in a programming language. Here is an example of a compound assignment operator:

Var1 += 1;

The above statement expands to mean:

Var1 = Var1 + 1;

So, the value of the variable Var1 has 1 added to it. If Var1 begins with the value of 25, it will be set to 26 after this line of code executes. The other assignment operators are similar only if the operation is specific to each operator. Instead of adding, -= subtracts from the variable and ^= perform bitwise exclusive OR, etc. Code Listing 6.7 shows some examples of compound assignment operators.

Code Listing 6.7: Compound Operators

Var1 -= 7; // Means Var1 = Var1 – 7;

Var1 ^= 7; // Means Var1 = Var1 ^ 7;

Var1 /= 7; // Means Var1 = Var1 / 7;

We will look at the exact meaning of each of the unadorned versions of these operators (i.e. ^ as opposed to the assignment version ^=). We can use the compound assignment operators with expressions, too:

Var1 += 5 + (2* Var2);

The above line of code will expand to the Var1 = Var1 + 5 + (2* Var2);. If Var1 is 10 and Var2 is 5, then Var1 will become 10+5+(2*5) or 25.

Detour: binary numbers

Before we look at the bitwise operators, we will take a brief detour and look at the way computers store integers. Computers perform all computation on collections of 0s and 1s. The smallest amount of memory in the computer is a bit that can be set to either 0 or 1 and that constitutes a single digit in binary. Variables, such as int and byte (even double and float) are made up of a collection of bits in binary.

Byte Containing 50

Figure 40: Byte Containing 50

Figure 40 illustrates a byte containing the decimal integer 50. The bits are blue boxes, and each bit is set to either 1 or 0 (the values inside the blue boxes). Each bit has an index. Bit 7, on the left, is the most significant bit and bit zero, on the right, is the least significant bit. This is exactly the same as with decimals—if we have a decimal number such as 345, the 3 on the left is more significant than the 5 on the right.

In order to work out the exact numerical value of a binary number, you must sum each power of 2 that corresponds to a 1 in the bits. In Figure 40, there is a 1 in the 25 position, another in the 24 position, and another in the 21 position. Therefore, this bit sequence represents 50, which is the result of adding the powers together: 25+24+21=50. Notice that this is the same in decimal except that the base of the powers in decimal is 10 rather than 2.

Negative numbers are slightly more complicated than positives. Java uses twos-complement (2’s complement). This means that if the left-most bit is set to 1, the number is negative. But the value of the negative number is the bitwise complement plus 1—e.g., the bit sequence 10000011 in a byte would normally be 27+21+20, but because bit 7 is the sign bit in a byte, and because it is set to 1, the actual value this sequence represents is the complement +1. The complement is the same string with every bit flipped (see the ~ operator below). Therefore, 10000011 actually means 01111100+1=01111101, or -125 in decimal. The left-most bit in each of the integer data types is called the sign bit for this reason. For byte, this left-most bit is bit 7, and in short it’s bit 15. In int, it is bit 31, and in long it is bit 63.

Bitwise operators

Several operators in Java are designed to directly manipulate the bits of our variables. These operators are often grouped together and called the bitwise operators. The bitwise operators are borrowed from a logical mathematics called Boolean algebra (which is also where we get the keyword “Boolean” for our true/false variables and the boolean operators that we will see shortly).

Boolean algebra consists of variables and operators, as in regular algebra, and we can use brackets to override the normal precedence of the operators, too. In Boolean algebra, the variables can only be true or false, 1 or 0—there are no other values. In many ways, Boolean algebra is much simpler than regular arithmetic, but it is easy to underestimate the power of this apparently simple set of logical rules. Boolean algebra can be used to compute anything that is computable. In fact, modern computers are nothing but billions of transistors all connected together and performing basic Boolean algebra.

There are only three basic operators in Boolean algebra—AND, OR, and NOT. In addition to these three, most programming languages also provide a fourth operator—XOR, which is short for Exclusive OR.

In Java, we use the symbols & for Boolean AND, | for Boolean OR, ~ for Boolean NOT (or the bitwise complement), and ^ for XOR.

Truth Tables for Boolean Operators

Figure 41: Truth Tables for Boolean Operators

Figure 41 shows the truth tables for the four bitwise operators available in Java. These tables are also the truth tables for the Boolean operators that we will look at shortly. Truth tables list the possible input states, and next to each state they list the result returned by the operator.

For instance, if we have the operator AND, and our two inputs (A and B) are set to 1 and 0, we can read along the third row of the AND truth table (which corresponds to A being 1 and B being 0) and see that the Out column says 0. This means 1 AND 0=0. Likewise, if we need to know what A^B is when A is 0 and B is 1, we can look along the second row of the XOR truth table and see that it will return 1 as the result.

In real Boolean algebra, the operations take only single bit operands, so we can compute the result from “True AND False” or “False OR False.” However, computers do not typically operate on single bits because this would be too slow. Instead, computers group bits together into strings to form a byte, short, int, etc. A byte in Java is eight bits wide. This means any binary value from 00000000 to 11111111 will fit into the eight bits of a byte. When we perform a bitwise operation between two variables, we actually perform the same Boolean operation eight times—once between each of the corresponding bits in two variables.

For instance, imagine we have two byte variables, a and b, that are set to 203 (assume the byte is unsigned, even though this would typically be read as a negative number using 2’s complement) and 166, respectively. In binary, the numbers 203 and 166 are represented by the bit patterns 11001011 and 10100110. If we perform the bitwise operations between these variables and store the results in a third variable, c, Code Listing 6.8 shows what will happen.

Code Listing 6.8: Bitwise Operations

c = a & b; // c will be set to 10000010

c = a | b; // c will be set to 11101111

c = a ^ b; // c will be set to 01101101

Note that whatever operation is being performed (&, |, or ^) the first bit on the right side of the resulting c value is the result of the operation performed between the right-most bits of the two input operands. And, as a final example, if we perform the bitwise complement of the a variable, we are simply flipping all of the 1s to 0s and all of the 0s to 1s:

c = ~a;// c will be set to 00110100, which is the complement of a

Shift operators

In addition to the bitwise operations based on Boolean algebra, we can also perform shifts in Java. The shifts are considered to be bitwise operators because they give us direct access to the bits of the variables. There are three shifts available in Java—shift left (the operator symbol is <<), shift right (the operator symbol is >>), and shift right with zero fill (the operator is >>>).

The shifts each take two operands. They shift the bits in the first operand to the left or right (depending on the operator) by the amount specified in the second operand. For instance, with a variable called a, we can shift the bits one space to the right by using the statement: a = a << 1;.

The effect of shifting the bits of integer to the left is the same as multiplying the value of the integer by the corresponding power of two. For instance, if we take the bit pattern three and we store it in a byte variable called a:

a = 3 << 4;// multiply 3 by 2 to the power 4, or 16, and a will become 00110000.

Likewise, shifting a variable to the right by some amount is the same as dividing the variable a power of two:

a = 48 >> 4;// divide 48 by 2 to the power 4, or 16, and a will become 00000011.

For the computer, shifting is much easier than division and multiplication, so it is used to optimize code (i.e. to make it run faster).

When we shift right, as bits shift out on the right side, new bits must enter on the left. When using the regular right shift (>>), the bits which enter on the left will be copies of the sign bit (i.e. the leftmost bit will be maintained). This enables us to divide negative numbers using the shifting technique described above and to maintain a negative answer. Whereas the shift right and zero fill operator (>>>) will shift in 0s on the left side. The shift right and zero fill operator is useful when we do not want Java to treat our numbers as being signed.

Relational operators

The relational operators allow us to compare the values of numerical variables against other variables or literal values. For instance, we might test if the integer variable A has a value less than 100. Or we could test if the variables B and C are equal to the statement B==C.

The relational operators take two operands each. They compare the operands and return a boolean result of true or false. The relational operators are most commonly used in conditional statements such as if statements and loops (which will be covered in the Control Structures chapter). We can string together many relational operators with parameters using the logical operators to form complex expressions (see the next section for a description of the logical operators).

Table 3: Relational Operators

Operator

Name

Example

==

Equal to

A==B; // Is A equal to B?

!=

Not equal to

A!=B; // Is A different from B?

<

Less than

A<B; // Is A less than B?

>

Greater than

A>B; // Is A greater than B?

<=

Less than or equal to

A<=B; // Is A less than or equal to B?

>=

Greater than or equal to

A>=B; // Is A greater than or equal to B?

Table 3 shows the relational operators available in Java. Notice that the double equals sign, ==, is the relational equal to operator. It asks if two variables have the same value. Whereas the single equals sign, =, is the assignment operator. It sets the value of the first operand to that of the second. Code Listing 6.9 shows some examples of how we can set boolean variables based on the results from logical. When we use relational operators in control structures, they generally do not set boolean values, but they execute code blocks when the results evaluate to true.

Code Listing 6.9: Relational Operators Examples

int var1 = 100, var2 = 30;

boolean ans1 = var1 == var2; // ans1 becomes false, 100 does not equal 30

boolean ans2 = var1 > var2; // ans2 becomes true, 100 is greater than 30

boolean ans3 = var2 <= var1; // ans3 becomes true, 30 is less than or equal to 100

boolean ans4 = var2 != var1; // ans4 becomes true, 30 is not equal 100

int v1 = 100, v2 = 30, v3 = 200;

boolean ans1 = (v1 < v2) || (v3 > v1);   // Using logical OR

Logical operators

The logical operators (sometimes called Boolean operators) work with boolean arguments. They are used to form logical statements. Each logical operator takes two operands, and both operands are boolean. We usually use logical operators in conjunction with if statements, loops, and other circumstances in which conditions are necessary.

The three logical operators are AND, represented by the symbol &&; OR, represented by the symbol ||; and NOT, represented by the exclamation point symbol !. The logical operators are used to string together multiple simple boolean conditions in order to create more complex expressions. Each of the logical operators works in exactly the same way as the Boolean truth tables for AND, OR, and NOT that we looked at for the bitwise operators. The only difference between the logical operators and the bitwise operators is that the logical operators are strictly meant for boolean parameters, whereas the bitwise operators are designed to work with all 32 bits of int, or all 64 bits of a long, etc.

As a short example of how we use the logical operators, imagine that we have a circumstance on which we wish to model in our program. We are in control of a garage door. The door can be open or closed. At night, the door should be closed in order to help prevent burglars, but if it is raining, the door should be closed so that the rain doesn’t get into the garage.

You could model the system above with a few variables: boolean doorClosed;, boolean nightTime;, and boolean raining;.

In order to determine whether or not the door should be closed, we might say something like doorOpen = nightTime || raining;.

This means the variable doorClosed should be set to the logical result of performing OR between the variables nightTime and raining. If it is nightTime, the nightTime variable will be 1, otherwise it will be false. If it is raining, the raining variable will be set to 1, otherwise it will be false.

We could add another condition. We could say that when it is nighttime, the door should be closed unless the light is on. Because if the light is on, perhaps that means that somebody is working the garage, which would look like this: doorOpen = (!nightTime && !raining) || (nighttime && lightIsOn);.

The preceding statement matches our conditions perfectly well, and the doorOpen variable will be set as we specified above. If the conversion from text to a Boolean expression looks daunting, keep in mind that there is no known easy way in mathematics or computer science to efficiently turn text into the simplest possible Boolean expression. Logical statements can be made arbitrarily complex, and it is deceptively difficult to accurately and efficiently describe the best possible method for representing some logical statements.

String concatenation

When we use + between two strings, it means to join them together. The term for this is concatenation.

Code Listing 6.10: String Concatenation

          String s1 = "This is";

          String s2 = " a string";

          String s3 = "!";

          String concatenated = s1 + s2 + s3;

          String str1 = "Concatenating integers is fine! " + 278;

          String str2 = "And floats/doubles too!" + 45.678;

          System.out.println(concatenated);

          System.out.println(str1);

          System.out.println(str2);

Code Listing 6.10 shows that concatenating integers, floating-point variables, and literals to strings is a matter of using the + operator. The int or floating-point literal will be converted to a string of characters before being added to the string. It is also possible to concatenate characters, such as A, or boolean literals such as false, to a string in the same way.

But, be careful: It is not possible to convert a string to an integer without calling the Integer.parseInt method!

Other operators

We will now examine two other operators that will make more sense when we look at control structures and inheritance hierarchies in the object-oriented section. Briefly, the conditional operator is simply a shorthand for a particularly common if statement; and the instanceof operator is a mechanism for testing whether or not an object is an instance of a particular class.

Conditional operator

The conditional operator (sometimes called the ternary operator) is a shorthand way of writing a condition with two possible outcomes. In the chapter on control structures, we will see how to write if statements that perform exactly the same function as the functional operator. However, for now note that the conditional operator requires less code.

Code Listing 6.11: Conditional Operator

public class MainClass {

     public static void main(String[] args) {

          int a = 25, b = 60;

          int larger = (a > b)? a : b;

          System.out.println(larger);

     }

}

Code Listing 6.11 shows the use of the conditional operator. The line int larger = (a > b)? a : b; is the invocation of the operator. The operator has three parts—a condition, a value when the condition is true, and a value when the condition is false. The operator will return either a or b, depending upon the result of the condition (a>b). In order to use the operator, we follow a conditional expression with a question mark, then place the outcome when the condition is true. After this, we use a colon, :, and place the outcome when the condition is false.

If we run the preceding program, we will see that the condition (a > b) is actually false because a is 25 and b is 60, and 25 is not greater than 60. Therefore, the value returned by the operator will be b, which we set to the larger variable and subsequently print to the screen.

The conditional operator above could easily be written out as an if statement, as in Code Listing 6.12 (we will look at if statements in the section on control structures).

Code Listing 6.12: Equivalent If Statement

public class MainClass {

     public static void main(String[] args) {

          int a = 25, b = 60;

          

          int larger;

          

          if(a > b)

               larger = a;

          else

               larger = b;

          

          System.out.println(larger);

     }

}

instanceof operator

The final operator in Java is the instanceof operator. When we devise a class hierarchy, testing whether or not a particular object is of some class will often be useful. For instance, if we have a control on the screen and the user clicks on the control, we might want to know if the control is a button or not.

The operator takes two operands that are of some class hierarchy and determines if the left operand is an instance of the class specified by the right operand.

Code Listing 6.13: instanceof Example

public class MainClass {

     public static void main(String[] args) {

          MyClass someInstance = new MyClass();

          if(someInstance instanceof MyClass)

               System.out.println("someInstance is of MyClass!");

          else

               System.out.println("someInstance is not of MyClass...");

     }

}

Code Listing 6.13 shows a basic example of how to test whether or not an object, someInstance, is an instance of a particular class, MyClass. Note that this code will not run as a standalone application because I have not included the declaration of the class MyClass.

Tip: Avoid using the instanceof operator if possible. There are better ways to program structures that otherwise might use the instanceof operator. Let’s say we need to program a method with this request: If the object is type t, do such-and-such; otherwise, if the object is of type q, do something else. In such a scenario, we might employ polymorphism in a more efficient manner. The compiler and JVM are able to detect and maintain object types, and we can specifically program our class hierarchies so that we never need to ask if object t is an instance of class c. We will examine class hierarchies in the section on object-oriented programming.

Challenges

Challenge 6.0: A farmer has collected 792,671 eggs from the chickens on his farm, and he wishes to sell them in cartons to commercial shopping outlets and supermarkets in regional north Queensland. How many full cartons worth of eggs does the farmer have (assuming 12 eggs fill a single carton)? How many eggs does he have left over for his own breakfast (hint: use the modulus operator)?

Challenge 6.1: Begin with the number four. Subtract two from it. Multiply this by 12. Increment your answer. Add your answer to itself. Divide it by seven and discard the remainder. Multiply your answer by itself. Cube your answer (i.e. raise it to the third power). Decrement your answer. What is the final result? The number you are left with is not divisible by one of the following primes—which one?

A) 2

B) 3

C) 17

D) 19

E) 43

Challenge solutions

Challenge 6.0

Code Listing 6.14: Solution to Challenge 6.0

public class MainClass {

     public static void main(String[] args) {

          int eggsTotal = 792671;

          int cartons = eggsTotal / 12;

          int eggsLeftOver = eggsTotal % 12;

          System.out.println("The farmer has " + cartons + " cartons of eggs, "

                    + "and " + eggsLeftOver + " eggs left over for breakfast.");

     }

}

Challenge 6.1

Code Listing 6.15: Solution to Challenge 6.1

public class MainClass {

     public static void main(String[] args) {

          int result = 4;

          result = result - 2;

          result = result * 12;

          result++;

          result = result + result;

          result = result / 7;

          result = result * result;

          result = result * result * result;

          result--;

          // To work out if a number evenly divides another, use mod:

          int mod2 = result % 2;

          int mod3 = result % 3;

          int mod17 = result % 17;

          int mod19 = result % 19;

          int mod43 = result % 43;

          System.out.println("The final result is " + result);

          System.out.println("Division by 2 leaves: " + mod2 + " remainder.");

          System.out.println("Division by 3 leaves: " + mod3 + " remainder.");

          System.out.println("Division by 17 leaves: " + mod17 + " remainder.");

          System.out.println("Division by 19 leaves: " + mod19 + " remainder.");

          System.out.println("Division by 43 leaves: " + mod43 + " remainder.");

     }

}

Code Listing 6.15 produces the following output to the console when run:

The final result is 117648.

Division by 2 leaves: 0 remainder.

Division by 3 leaves: 0 remainder.

Division by 17 leaves: 8 remainder.

Division by 19 leaves: 0 remainder.

Division by 43 leaves: 0 remainder.

We can see from this output that division of our final result by 17 leaves a remainder of 8, but division by any of the other four primes leaves no remainder at all. This means that the final result is evenly divisible by 2, 3, 19, and 43, but it is not divisible by 17. In programming, there are usually many good ways to solve any particular problem. It is also possible to compute the result for Challenge 6.1 as a single expression (in fact, if we were programming this expression in a real application, we would probably compute the entire thing first and only leave the literal answer). Code Listing 6.16 shows a few more examples of expressions that could solve this problem.

Code Listing 6.16: Other Solutions for Computing Challenge 6.1

// The first example shows computing the result without simplifying

// the expression:

int result1 = (((((((4-2)*12)+1)*2)/7)*(((((4-2)*12)+1)*2)/7))*

((((((4-2)*12)+1)*2)/7)*(((((4-2)*12)+1)*2)/7))*

((((((4-2)*12)+1)*2)/7)*(((((4-2)*12)+1)*2)/7)))-1;

System.out.println("Final result is " + result1);

          

// The second example shows that all we're really doing is raising

// 7 to the power of 6 and subtracting 1:

int result2 = 7*7*7*7*7*7-1;

System.out.println("Final result is " + result2);

Scroll To Top
Disclaimer
DISCLAIMER: Web reader is currently in beta. Please report any issues through our support system. PDF and Kindle format files are also available for download.

Previous

Next



You are one step away from downloading ebooks from the Succinctly® series premier collection!
A confirmation has been sent to your email address. Please check and confirm your email subscription to complete the download.