COMP 14, Fall 1998
1
5. Primitive Types
Reference: Chapter 2, Appendix A2.3.
In ordinary life we tend to “type” things and people so to get a better understanding of the world. For instance we, might distinguish the student type from the professor type. The distinction is based on the fact that, unlike a student, a professor is allowed to teach. Thus, there are rules regarding what a member of a type can do. The same rules apply to all members of a type.
The notion of programming language types is very similar. Consider the following values:
2 3 “hello world” “goodbye world” .
The values
2
and
3
are of the same type, int
, and the values
“hello world”
and
“goodbye world”
are of the same type, String . The reason two values are of the same type is because the exact same set of operations can be applied to them. If two values are of different types, there exists at least one operation that can be applied to one and not the other. Thus, we know that 3 and “hello world” are of different types because you can subtract an integer from the former but not the latter:
3 – 2 //legal operation
“hello world” – 1 //illegal operation
Thus a programming language type is a description of a set of Java values, called instances of the type, consisting of :
a name,
a set of predefined instances or constants ,
a set of operations that can be applied to the instances.
This definition of a type looks like our definition of a class. The concepts of a class and type are very similar. In fact, in some object-based languages such as Smalltalk, there is no difference between them. In Java, however, a type is a more general concept. The difference between the two is somewhat subtle 2 , and therefore, for now, we will consider them to be identical concepts.
Let us look at some of the basic or primitive types provided by Java. To distinguish them from the other types, Java begins their names with a lowercase letter. We will first look first at the names and constants of these types, and then look at the operations that can be applied to their instances.
1
Copyright Prasun Dewan, 1998.
2 Every class is a type but not every type is a class. A type can be a class, interface, or primitive type. To understand why the two are different, consider two different factories that produce identical products, that is, products that follow the same specification . A type corresponds to a product specification while a class, as we mentioned before, corresponds to a product factory.
This type corresponds to the set of integers you have studies in Mathematics; a Java value of type int is an integer.
However, not every integer is a Java int
. This is because integers are whole numbers in the range:
-
.. +
However, a Java int must fit in memory for the computer to process it, and computer memory is finite (though large).
The range of integers mapped to Java ints, is, thus, determined by the size of the memory slot allocated to each int . In Java, this slot consists of 32 binary digits or bits . This means that a range of 2 32 integers can be mapped to
Java ints . Half of the numbers in this range (2 31 ) are negative integers, one is zero, and remaining (2 31 - 1) are positive integers. Thus, Java ints are integers in the range:
-2 31 .. (2 31 – 1)
You do not have to memorize the values of the smallest and largest integers, since these are stored in the Java named constants,
Integer.MIN_VALUE
and
Integer.MAX_VALUE
, respectively.
An int is named by its decimal representation:
2 1 234 -3 +10 -2147483648 2147483647
The type double
describes a subset of the set of real numbers you have studied in Mathematics. Recall, that a real is a number that can have a fractional part The name of this type indicates that the memory slot allocated for its instances is twice the size of an int
memory slot, that is, it is 64 bits long. The Java predefined constants,
Double.MIN-
VALUE and Double.MAX_VALUE
define the absolute value of the smallest (non-zero) double value and largest double value, respectively.
Like int constants, double constants can be specified in Java by their decimal representation. However, we must ensure that their representations do not conflict with the representations of equivalent integers. Thus we cannot use, for instance, the representation:
2 for the double value
,2
, since it denotes the int
value,
2
, and Java must be able to uniquely type each value so that it knows what operations can be applied to it.
Therefore, in the decimal representation of doubles, a dot is required, even if there is no fractional part. The following are valid double constants:
2.2 2.0 0.22 02.20 -0.22 +2.2 .2 2.
We can also use a scientific representation of these numbers:
0.22E+1 2.2E0 22E-1 - 0.22E+1 where: xEy = x * 10 y
2
As shown above, the same real number can be represented in several ways. The standard or floating point representation is a scientific representation in which the number to the left of the exponent is a fraction beginning with a non-zero digit. Thus, the following is the standard representation of 2.2:
.22E1 and the following is the standard representation of .022:
.22E-1
The digits to the left of the exponent are called the mantissa and to the right are called the exponent.
3
Often your program must test if some condition has been met. The boolean type defines two literals, true and false , for indicating the result of such a test..
The computer must often process characters. Most programs accept character input and produce character output.
Moreover, some programs, such as a spelling checker, perform most of their computations in terms of characters.
Characaters are the building blocks for the strings we have seen before.
This type defines a variety of characters including the English letters (both lowercase, a..z, and uppercase, A..Z), the decimal digits 0..9; “whitespace” characters such as blank and tab; separators such as comma , semicolon, and newline; and other characters you see on your keyboard. We denote a character literal by including its print representation in quotes:
‘A’ ‘Z’ ‘1’ ‘0’ ‘ ‘
Two consecutive single-quotes denote a special character called the null character: 4
3 In Java, we can use a mantissa of up to seventeen significant decimal digits and exponents in the range -323..309.
The following numbers are in range:
.12345678901234567
.12345678901234568
0.1E309
0.1E-322 but not the following:
. 123456789012345678
0.1E+310
0.1E-324
Java disallows you from entering exponents that are not in range. Moreover, it rounds off the mantissa to the first seventeen digits. Thus,
System.out.println (.123456789012345678) prints:
.12345678901234568
4 It is used to mark the end of a string. It is not useful to print it since Java prints nothing for it.
3
‘’
How do we represent the single-quote character itself? We could enclose it in single-quotes:
‘’’
However, Java would match the first two single-quotes as the null character, and think you have an extra single-quote character. So we will use the following representation to denote a single-quote:
‘\’’
Thus, instead of enclosing one character within quotes, we have enclosed a two-character escape sequence . The first character, \, or backslash , is an escape character here, telling Java to escape from its normal rules and process the next character in a special way.
Java defines escape sequences to represent characters that cannot use the normal representation or for which the normal representation may not be readable. A literal cannot have a new line character in it, so
\n denotes the new line character. A backslash after the first quote denotes special processing, not the backlash character itself, so \\ denotes the backlash character. Typing a backspace after a single-quote would erase the single-quote, so \b denotes the backspace character. You can represent the tab character it by entering a tab between quotes:
‘ ‘ but this representation can be mistaken as the space character. So \t denotes a tab character. Similarly, you can represent the double quote character as
‘”’ but it may be mistaken for two null characters. So \” denotes the double quote character. The following table summarizes our discussion:
Escape Sequence Character Denoted
\’ ‘
\n new line
\b
\\
\t
\” back space
\ tab
“
Figure 1 Some Useful Java Escape Sequences
Java allocates 16 bits for storing a character. As a result, it can support as many as 2 16 different characters, which is useful since we would like to represent characters of all current languages; and some of them such as Chinese have a large character set. It stores a non-negative integer code for each character. As programmers, we do not have to concern ourselves with the exact integer assigned to each character. However, as discussed later, we need to know something about the relative order in certain subsets of the character set.
32 bits for an int and 64 bits for a double may be too little or too much for the integers and reals you wish to process in your program. Therefore, Java provides additional types with fewer and more bits. The following table summarizes the names of the Java primitive types, the kinds of values they store, and the size of the memory slot allocated to their instances:
Primitive Type int short char
Values Stored integers integers
Non-negative, or unsigned,
Size in bits
32
16
16
4
byte double float integer codes integers reals reals
8
64
32
Figure 2 Java Primitive Types
In this course, you can mostly ignore the additional types presented here: short , long , and float .
For each type we have mentioned so far, we have only described the predefined constants. To fully explain a type, we must also look at the operations that can be performed on its instances. Most of the operations we will see here are binary operations, that is, operations that take two operands. A few are unary operations, taking a single operand.
These are the operations we use in Mathematics for adding, subtracting, multiplying, and dividing integers. The following table describes the symbol used for naming each operation, the action it takes, and the types of the operand and result.
Name Action Operands & Result Type
(Signature)
+
-
-
*
/
% add subtract subtract from 0 multiply int quotient int remainder int, int -> int int, int -> int int -> int int, int -> int int, int -> int int, int -> int
Figure 3: Arithmetic Operations on ints
In this table: int, int -> int indicates that the operation takes two integer operands and produces one int result. A description such as this of the types of the operands and result of an operation is called the signature of the operation.
5
The difference between the two – operations is that one is a unary minus, returning the result of subtracting its operand from 0 while the other is the regular, binary -. Thus:
- i == 0 - i where i is an integer variable.
The only non-intuitive operation here is /. In Java:
5/2 == 2
Thus the result of the division is the integer quotient you get from the division. To find the remainder from the division, we use %:
5 In the previous handout (Figure 2), when looking at the various implementations of println , we saw a different way of representing a signature.
5
5 % 2 == 1
Thus, the following equality does not hold true: x == (x/y)*y
Instead, the following equality holds true: x == (x / y) * y + (x % y)
+
-
-
*
/
These are like the previous operations, except that they take double operands and produce double results:
Name Action Operands & Result Type
(Signature) add subtract (unary) subtract multiply divide double, double -> double
-> double double, double -> double double, double -> double double, double -> double
Figure 4: Arithmetic Operations on doubles
Thus, for every double operation here, there is a corresponding int operation that has the same name and performs
“same” computation. For instance, the + operation in both cases adds the two operands.
Why not treat each pair of corresponding operations as a single operation? These are different operations in that different instructions are executed to perform them. For instance, the two + operations process different representations of their operands and results and detect overflow differently. An overflow occurs when the result of an operation is too big or too small to fit in the slot allocated for it. To determine if an overflow occurs, the int + must compare the result with Integer.MAX_VALUE and Integer.MIN_VALUE, while the double + must compare it with
Double.MAX_VALUE and Double.MIN_VALUE. 6 The differences are even more evident when we consider the two
/ operations. The double. does a (more) precise division.
5/2 ==
5.0/2.0 ==
2
2.5
There is no % operation for doubles.
Thus, each of + ,-, *, and / is an overloaded operations with different implementations for ints and doubles rather than a single operation that handles both types.
6 In most cases, the value of the overflown result is set to the nearest legal value of that type. Thus,
Integer.MAX_VALUE + 1 == Integer.MAX_VALUE, Integer.MIN_VALUE – 1 == Integer.MIN_VALUE, and
Double.MAX_VALUE == Double.MAX_VALUE + 1. Division by zero is treated differently. Dividing an int by 0 raises an exception while dividing a positive (negative) double with 0.0 evaluates to a special value called
Double.POSTIVE_INFINITY (Double.NEGATIVE_INFINITY).
6
What if we wanted to invoke an arithmetic operation on an int
and a double
:
5 / 2.0
Since the arithmetic operations provided by Java do not accept mixed types, one of the two operands must be converted to the type of the other. Java automatically converts the operand whose type is narrower . A type T1 is narrower than another type T2 if:
Set of instances of T1
Set of instances of T2 or, in other words, if every instance of T1 can be mapped to an instance of T2. 7
Thus, in the example above, Java would convert the expression to:
5.0 / 2.0 and the call the double /.
What if we wanted integer division instead, that is, wanted to convert the double to the narrower type, int? In this case, we have to explicitly cast the double to an int by prefixing the name of the narrower type within parentheses:
5 / (int) 2.0
When you cast a double as an int, Java truncates the double, that is, gets rid of the fractional part.
8 Thus:
5 / (int) 2.0
5 / (int) 2.9
==
==
5 / 2
5 / 2
== 2
== 2
When you want to deal with whole numbers, truncation is desirable, but in other situations it is not. It is for this reason that Java does not automatically do the conversion for you. By explicitly casting, you are telling it you know what you are doing and you are willing to accept any negative consequences.
We can use mixed types also in assignment. Thus: double d = 5; int i = (int) 5.5;
In the first assignment, the rhs is automatically converted to the wider type of the lhs. In the second case, the explicit cast truncates the rhs and assigns it to the narrower type of the lhs. Assigning a value to a variable is very similar to passing a value as an argument, as we saw earlier, since an argument is just a special kind of variable.
Java also defines a set of overloaded relational operations with both int and double implementations. Each of these operations does a test involving two int or double operands and returns a boolean indicating the result of the test.
7 By this definition, byte is narrower than short , which is narrower than int , which is narrower than long.
However, there is no narrow relation between character and short, even if we treat a character as simply the integer code assigned to it (Why?).
8 It may also have to truncate the whole number part since it an int cannot be more than 10 decimal digits long. Thus:
(int) (Integer.MAX_VALUE + 1.0) == Integer.MAX_VALUE
Therefore, it is safer to cast a double to a long.
7
Name
==
!=
>
<
>=
<=
Action equal? not equal? greater than? less than? greater than or equal? less than or equal?
Signature of int implementation int, int -> boolean int, int -> boolean int, int -> boolean int, int -> boolean int, int -> boolean int, int -> boolean
Signature of double implementation double, double -> boolean double, double -> boolean double, double -> boolean double, double -> boolean double, double -> boolean double, double -> boolean
Figure 5: Relational Operations on ints and doubles
Thus:
5 == 5
5 == 4
5 >= 4
==
==
== true false true
5 != 5
5 <= 5
==
== false true
Note that we use the two equal symbols, pronounced “equal equal”, for equality rather than a single equal because the latter has been reserved for assignment. It is very easy to confuse them, so you have to be careful you use the right one.
Many subtle errors can be attributed to using = instead of ==.
9
The relational operations can be applied to values of types other than ints and booleans, as shown below.
Name
==
!=
>
Action equal? not equal? greater than?
Generalized Signature
T, T -> boolean
T, T -> boolean
OrderedT, OrderedT -> boolean
<
>=
<= less than? greater than or equal? less than or equal?
OrderedT, OrderedT -> boolean
OrderedT, OrderedT -> boolean
OrderedT, OrderedT -> boolean
Figure 6 Generalized Signatures
They == and != operations can be applied to values of any primitive type. Thus, the following are legal: true == true == true false != false == false
The result is always a boolean but both operands have to be of the same (arbitrary) primitive type 10 , T, as indicated by the signature:
9 It is unfortunate that Java took the FORTRAN and C approach of using the mathematical equality operator for assignment. Many languages use different operators such as := and <-. However, each of these operators requires us to type 2 characters. Since a large fraction of a program consists of assignment statements (avg. 40%), C-based languages use the one character operator.
10 In fact, these operations are also applicable to instances of non-primitive types such as String, but their semantics are not very intuitive or useful. When we study non-primitive types in more detail, we will discuss values of these types can be compared. The only non primitive type we know is String, and you can invoke the equals
method on a string to determine if it is the same as its argument string. Thus
8
T, T -> boolean
We cannot apply other relational operations to arbitrary types, since their operands must be ordered. Thus: true > false is illegal since boolean values in Java are not ordered.
11 Values of all other primitive types are considered ordered.
Clearly, it makes sense to order Java values that are numbers, but what why order characters? In ordinary life, we do order characters, when we learn the alphabet, and more important, when we search directories. It is to support such searches that programming languages order the elements in the character set. The integer code, or ordinal number , assigned to a character is its position in this set. We do not need to know the exact ordinal number assigned to a character. It is sufficient to know that:
The null character, ‘’, is assigned the ordinal number 0.
The digits are in order.
The uppercase letters, ‘A’ .. ‘Z’, are in order.
The lowercase letters, ‘a’ .. ‘z’, are in order.
Letters of other alphabets are in order.
Thus, we know that:
‘1’ > ‘0’
‘B’ > ‘A’
‘a’ > ‘b’ c >= ‘’
==
==
==
== true true false true where c is a character variable holding an arbitrary character value.
Based on the information above, we cannot compare elements from different ordered lists. Thus, we cannot say whether:
‘A’ > ‘a’
‘A’ > ‘0’
Like other programming languages, Java lets you find out the exact ordinal number of a character by casting it as an int. Thus:
System.out.println ( (int) ‘B’) will print out the ordinal number of ‘B’. This cast is always safe, since char (16 unsigned bits) is a narrower type than int (32 bits). Therefore, when context demands ints
, Java automatically performs the cast. Thus: int i = ‘A’;
true
11 In some languages such as Pascal booleans are ordered with true > false.
9
and:
‘B’ – ‘A’ computes the difference between the integer codes of the two characters, returning 1. Java lets you directly perform all the int
arithmetic operations on characters, and uses their ordinal numbers in the operations. Usually we do not look at absolute values or sums of ordinal number - the differences are more important, as we see below.
You can also convert an ordinal number to a character: char c = (char) intCodeOfB;
We had to perform a cast because not all integers are ordinal numbers of characters, just as not all doubles are integers.
For instance, the following assignment makes no sense: char c = (char) -1 since ordinal numbers are non-negative values. Java simply truncates the 32 bit signed value into a 16 unsigned value, much as it truncates a double with a fraction part to an int without a fraction. Again, by explicitly casting the value you are telling Java that you know what you are doing and are accepting the consequences of the truncation.
The two-way conversion between characters and ordinal numbers can be very useful. For instance, we can find the predecessor or successor of characters :
(char) (‘I’ – 1) ==
(char) (‘I’ + 1) ==
‘H’
‘J’
We can also convert between uppercase and lower case characters, which is useful, for instance, when processing languages in which case does not matter:
(char) (‘i’ – ‘a’ + ‘A’) ==
(char) (‘I’ – ‘A’ + ‘a’) ==
‘I’
‘i’
To understand why the above equalities hold, consider an equality we know his true based on the fact that letters are ordered:
‘i’ – ‘a’
==
‘I’ – ‘A’
Moving the ‘a’ to the right, we get:
‘i’ ==
Moving the ‘A’ to the left we get:
‘i’ – ‘a’ + ‘A’ ==
‘I’ – ‘A’ + ‘a’
‘I’
These are operations on booleans that produce booleans:
Name(s) Action
! not
&&, & and
Signature boolean -> boolean boolean, boolean-> boolean
10
||, | or boolean, boolean-> boolean
Figure 7 Logical Operations on Booleans
The ! operation is the logical not operation. It returns the opposite or negation of its operand. Thus:
!true == false
!false == true
The
&&
and
&
operations implement the logical and operation, which returns true if both of its operands are true .
Thus: true && true true && false false && true false && false
==
==
==
== true false false false
Similarly, the || and |operation is the logical or operation, which returns true if either of its operands is true. Thus: true || true true || false false || true false || false
== true
== true
== true
== false
Consider the following expression: false && (9654.34/323.13 > 32.34)
We do not have to evaluate the second boolean operand to determine the result since if the first operand is false, the result is false.
Now consider: true || (9654.34/323.13 > 32.34)
Again, we do not have to evaluate the second boolean operand to determine the result since if the first operand is true, the result is true.
Now we can explain the difference between the two implementations of and and or . The & and | operations always evaluate both operations; while the && and || operations, called short-circuit operations, sometimes only evaluate the first operand. The && operation does not evaluate is second operand if the first one is false, and the || operation does not evaluate the second operand if the first one is true. Which operation is used can influence the result if the second operation raises an exception. Consider: true || (10/0 > 1) true | (10/0 > 1)
In the second case we will get an error because we divided by 0 but in the first case we will not. We recommend that you always do short-circuit evaluation.
12
12 Why does Java provide & and |? When both operands have to be evaluated, the short-circuit evaluation is slower because it did the extra check to see if it could have bypassed the second evaluation. We recommend that you always
11
Java provides several additional useful operations given in the table below.
Operations (invoked on Math) abs
Signature double -> double, int -> int acos, asin, atan, cos, sin, tan pow exp, log round double > double double, double -> double double -> double double -> long random sqrt
-> double double -> double
They compute several standard mathematical functions, which must be invoked on Math .
It is important to understand the difference between truncation of reals, which we saw earlier, and rounding:
(int) 5.9 ==
Math.round(5.9) ==
5
6
Since the result of a round is a long , you must cast it before using it as an int : int i = (int) Math.round (5.9)
If you do not know what the other functions do, that is okay. You will not be required to use them.
Recall that an expression is a program fragment or “program phrase” that produces a value. We saw earlier three kinds of expressions:
1.
A literal such as “hello world.”
2.
A variable such as inputLine .
3.
An invocation of a function, such as dataIn.readLine()
By our study of primitive types, we have seen new ways of creating expressions. We have seen new kinds of literal expressions such as:
1.6
‘A’ true b1 || b2
! (1 < (i % 3)) use the short circuit operations because we are more interested in preventing errors rather than performance and in many cases it will indeed be faster.
Another reason for supporting & and | is that, unlike && and ||, they can take int
operands, doing a “bitwise” operation on the representation of the operands, with 1 behaving as true and 0 as false . Thus: 101 & 110 == 100 and
101 | 110 = 111.
12
Notice, that the last expression is different from others in that it involves the use of two different operations: !, <, and
%. The result of applying the % operation is used as an operand to the < operation, which in turn provides its result to the ! opertion. In this example:
(i % 3) is a sub-expression of
(1 < (i % 3)) which, in turn, is a sub-expression of: 13
! (1 < (i % 3))
In general, there can be arbitrary sub expression levels in an expression. Here is an example of an expression with a a particularly large number of levels:
!(Math.round(5.5 + (7.4*3.4))) > ((- 'a') + ('A' + ‘I’))
In general, it is a good idea to use parenthesis to clearly indicate the sub expressions of an expression. Otherwise, what you type can be ambiguous. For instance, does
5 + 5 * 3 mean
(5 + 5) * 3 or
5 + (5 * 3)
Java has default rules for disambiguating such ambiguous specifications. It divides the operators into precedence levels , applies operators in a higher precedence level before operators in lower precedence levels, and applies operators in the same precedence level in a left to right fashion. For instance, it puts * at a higher precedence level than +. As a result,
(5 + 5 * 3) = (5 + (5 * 3))
The following table describes the precedence levels of the operators we have seen so far:
! - (unary)
* / %
+ - (binary)
> < <= >=
== !=
&
|
13 This is essentially the method chaining we saw earlier, since there is no fundamental difference between methods and the operations we studied here.
13
&&
||
Figure 8 Precedence Levels
In this table, the – in the top level stands for the unary minus , while the – in the third level stands for the binary minus.
Thus
-5 - 4 is
(-5) - 4 and not
-(5 - 4) since the unary - has a higher precedence than the binary -
Similarly,
!
true && false is
!
true && false and not
! ( true && false) since ! has a higher precedence than &&.
5 / 4 * 3 is
(5 / 4) * 3 and not
5 / (4 * 3) since both / and * have the same precedence. true || false == false || true is true || ( false == false) || true and not:
14
(true || false) == ( false || true) 14 since == has higher precedence than ||.
Before we can evaluate an expression, we should be able to type their result. For most of the expressions we have seen so far, your mathematical intuition works fairly well in typing them. However, in general, you need to know the signatures of all the operations involved in the expression. For instance, given an expression such as:
5 > 3 we know the type is boolean because of the signature: int, int -> boolean of the operation.
Once we have computed an expression such as:
5 < 3
what can you do with it? As we saw earlier, we can use it in a larger expression such as:
(5 < 3) && (4 > 2)
We can also assign it to a variable. In fact, the general form of an assignment statement is:
<variable > = <expression>
We use < > brackets to a enclose description of the kind of program fragment that is used as a substitute for it in an actual statement. Thus, we can execute the following statement: boolean comparisonResult = 5 < 3;
Assigning a boolean expression may seem unintuitive to you. While you are used to “assigning” int and double expressions in formulas: fahrenheitTemp = centigradeTemp * 9/5 + 32 you are not used to doing so for boolean expressions. They will seem more natural to you when you use them in if statements: if (5 > 3> ) System.out.println (“comparison succeeded”);
By assigning them in variables, you are storing the results of tests to be used later, for instance in an if: if (comparisonResult) System.out.println (“comparison succeeded”);
Another important thing we can do with expressions is print their result.
14 In Pascal, this would be the valid interpretation!
15
As mentioned before, we can use the different implementations of println to output values of different types:
System.out.println (2)
System.out.println (2.0)
System.out.println (‘2’)
System.out.println ((int) ‘2’) output: 2 output: output: output:
System.out.println (char) 51) output:
System.out.println(5 > 0)
3 output:
2.0
2
50 true
Notice the use of the explicit cast:
(int) ‘2’
Contrast this with some of the previous that also converted chars to their integer codes: int i = ‘A’
‘B’ – ‘A’
In the previous examples, Java automatically does the cast for us since otherwise the program fragments would not be legal. It cannot do so in the println case, since a println without the cast is legal, as shown above.
Each of the examples above prints its value on a separate line. This is because println , after printing its argument, moves the cursor to the next line, causing subsequent output to appear on the next line. What if we wanted to print a series of values on a single line but not all of these value are available at the same time? In this case we can use print instead of println , which does not move the cursor to the next line. Thus:
System.out.print (s1)
… // compute s2
System.out.print (s2)
… // compute s3
System.out.println (s3) will print all three values on the same line.
Java also lets you input values of various types, but requires more work, as we have seen from the examples so far. We discussed earlier, how we could input a line of characters as a string, and also how we could use
Integer.parseInt
to parse a string as an int. The program below shows another method for doing the parsing: int int2 = new Integer (dataIn.readLine()).intValue(); import java.io.DataInputStream; class AnInputReader {
public static void main (String args[]) throws java.io.IOException {
DataInputStream dataIn = new DataInputStream(System.in);
System.out.println("Int1?");
int int1 = Integer.parseInt(dataIn.readLine());
16
System.out.println("Int2?");
int int2 = new Integer (dataIn.readLine()).intValue();
System.out.println("Double1?");
double double1 = new Double (dataIn.readLine()).doubleValue();
System.out.println("Double2?");
double double2 = new Double (dataIn.readLine()).doubleValue();
System.out.println("Bool1?");
boolean bool1 = new Boolean (dataIn.readLine()).booleanValue();
System.out.println("Bool2?");
boolean bool2 = new Boolean (dataIn.readLine()).booleanValue();
System.out.println("Char 1 and Char 2?");
char char1 = (char) System.in.read();
char char2 = (char) System.in.read();
char char3 = (char) System.in.read();
}
}
This is a more general approach in Java, since this pattern can also be used to parse a string as a double or boolean : double double1 = new Double (dataIn.readLine()).doubleValue(); boolean bool1 = new Boolean (dataIn.readLine()).booleanValue();
The general pattern is: primitive p1 = new Primitive (dataIn.readLine()).primitiveValue(); and you simply substitute primitive with different primitive types such as int , double , and boolean .
For char we use a simpler approach, which does not involve use of dataIn . We can directly use
System.in.read (). Unfortunately, this method returns the integer code of the character instead of the char directly. So we must convert this return value to a char using a cast: char char1 = (char) System.in.read();
We see below an in interaction with the program above and the values read into memory.
17
Figure 9 Inputting Variables of different Types
The memory contents are exactly as you expect, except perhaps in the case of char2 and char3 . You may expect these to be uninitialized, since we entered a single ‘a’ on the last input line. To complete the line though, we had to hit the Enter ( Return ) key. This key results in actually two characters being sent to the program, one to move the input cursor back to the start of the current line, and the other to move it to the next line. These two characters are ignored when reading an input line as a string, serving only as the delimiter for the string. However, when you do character input, Java assumes you are interested in them, and returns them in the next two calls to System.in.read().
Therefore, in our example, they get stored in char2
and char3.
They do not have a unique print representation, so the debugger simply draws a small square for them. The int codes assigned to them, thus, are more meaningful in identifying them. As you can see, the J++ debugger shows both the print representation and integer code for each char stored in memory.
18