2.4. Primitive Data TypesJava supports eight basic data types known as primitive types. In addition, it supports classes and arrays as composite data types, or reference types. Classes and arrays are documented later in this chapter. The primitive types are: a boolean type, a character type, four integer types, and two floatingpoint types. The four integer types and the two floatingpoint types differ in the number of bits that represent them, and therefore in the range of numbers they can represent. Table 22 summarizes these primitive data types. Table 22. Java Primitive Data Types
2.4.1. The boolean TypeThe boolean type represents a truth value. There are only two possible values of this type, representing the two boolean states: on or off, yes or no, true or false. Java reserves the words true and false to represent these two boolean values. C and C++ programmers should note that Java is quite strict about its boolean type: boolean values can never be converted to or from other data types. In particular, a boolean is not an integral type, and integer values cannot be used in place of a boolean. In other words, you cannot take shortcuts such as the following in Java: if (o) { while(i) { } } Instead, Java forces you to write cleaner code by explicitly stating the comparisons you want: if (o != null) { while(i != 0) { } } 2.4.2. The char TypeThe char type represents Unicode characters. It surprises many experienced programmers to learn that Java char values are 16 bits long, but in practice this fact is totally transparent. To include a character literal in a Java program, simply place it between single quotes (apostrophes): char c = 'A'; You can, of course, use any Unicode character as a character literal, and you can use the \u Unicode escape sequence. In addition, Java supports a number of other escape sequences that make it easy both to represent commonly used nonprinting ASCII characters such as newline and to escape certain punctuation characters that have special meaning in Java. For example: char tab = '\t', apostrophe = '\'', nul = '\000', aleph='\u05D0'; Table 23 lists the escape characters that can be used in char literals. These characters can also be used in string literals, which are covered later in this chapter. Table 23. Java Escape Characters
char values can be converted to and from the various integral types. Unlike byte, short, int, and long, however, char is an unsigned type. The Character class defines a number of useful static methods for working with characters, including isDigit(), isJavaLetter(), isLowerCase(), and toUpperCase(). 2.4.3. Integer TypesThe integer types in Java are byte, short, int, and long. As shown in Table 22, these four types differ only in the number of bits and, therefore, in the range of numbers each type can represent. All integral types represent signed numbers; there is no unsigned keyword as there is in C and C++. Literals for each of these types are written exactly as you would expect: as a string of decimal digits. Although it is not technically part of the literal syntax, any integer literal can be preceded by the unary minus operator to indicate a negative number. Here are some legal integer literals: 0 1 123 42000 Integer literals can also be expressed in hexadecimal or octal notation. A literal that begins with 0x or 0X is taken as a hexadecimal number, using the letters A to F (or a to f) as the additional digits required for base16 numbers. Integer literals beginning with a leading 0 are taken to be octal (base8) numbers and cannot include the digits 8 or 9. Java does not allow integer literals to be expressed in binary (base2) notation. Legal hexadecimal and octal literals include: 0xff // Decimal 255, expressed in hexadecimal 0377 // The same number, expressed in octal (base 8) 0xCAFEBABE // A magic number used to identify Java class files Integer literals are 32bit int values unless they end with the character L or l, in which case they are 64bit long values: 1234 // An int value 1234L // A long value 0xffL // Another long value Integer arithmetic in Java is modular, which means that it never produces an overflow or an underflow when you exceed the range of a given integer type. Instead, numbers just wrap around. For example: byte b1 = 127, b2 = 1; // Largest byte is 127 byte sum = b1 + b2; // Sum wraps to 128, which is the smallest byte Neither the Java compiler nor the Java interpreter warns you in any way when this occurs. When doing integer arithmetic, you simply must ensure that the type you are using has a sufficient range for the purposes you intend. Integer division by zero and modulo by zero are illegal and cause an ArithmeticException to be thrown. Each integer type has a corresponding wrapper class: Byte, Short, Integer, and Long. Each of these classes defines MIN_VALUE and MAX_VALUE constants that describe the range of the type. The classes also define useful static methods, such as Byte.parseByte() and Integer.parseInt(), for converting strings to integer values. 2.4.4. FloatingPoint TypesReal numbers in Java are represented with the float and double data types. As shown in Table 23, float is a 32bit, singleprecision floatingpoint value, and double is a 64bit, doubleprecision floatingpoint value. Both types adhere to the IEEE 7541985 standard, which specifies both the format of the numbers and the behavior of arithmetic for the numbers. Floatingpoint values can be included literally in a Java program as an optional string of digits, followed by a decimal point and another string of digits. Here are some examples: 123.45 0.0 .01 Floatingpoint literals can also use exponential, or scientific, notation, in which a number is followed by the letter e or E (for exponent) and another number. This second number represents the power of ten by which the first number is multiplied. For example: 1.2345E02 // 1.2345 * 10^2, or 123.45 1e6 // 1 * 10^6, or 0.000001 6.02e23 // Avagadro's Number: 6.02 * 10^23 Floatingpoint literals are double values by default. To include a float value literally in a program, follow the number by the character f or F: double d = 6.02E23; float f = 6.02e23f; Floatingpoint literals cannot be expressed in hexadecimal or octal notation. Most real numbers, by their very nature, cannot be represented exactly in any finite number of bits. Thus, it is important to remember that float and double values are only approximations of the numbers they are meant to represent. A float is a 32bit approximation, which results in at least 6 significant decimal digits, and a double is a 64bit approximation, which results in at least 15 significant digits. In practice, these data types are suitable for most realnumber computations. In addition to representing ordinary numbers, the float and double types can also represent four special values: positive and negative infinity, zero, and NaN. The infinity values result when a floatingpoint computation produces a value that overflows the representable range of a float or double. When a floatingpoint computation underflows the representable range of a float or a double, a zero value results. The Java floatingpoint types make a distinction between positive zero and negative zero, depending on the direction from which the underflow occurred. In practice, positive and negative zero behave pretty much the same. Finally, the last special floatingpoint value is NaN, which stands for notanumber. The NaN value results when an illegal floatingpoint operation, such as 0/0, is performed. Here are examples of statements that result in these special values: double inf = 1/0; // Infinity double neginf = 1/0; // Infinity double negzero = 1/inf; // Negative zero double NaN = 0/0; // NaN Because the Java floatingpoint types can handle overflow to infinity and underflow to zero and have a special NaN value, floatingpoint arithmetic never throws exceptions, even when performing illegal operations, like dividing zero by zero or taking the square root of a negative number. The float and double primitive types have corresponding classes, named Float and Double. Each of these classes defines the following useful constants: MIN_VALUE, MAX_VALUE, NEGATIVE_INFINITY, POSITIVE_INFINITY, and NaN. The infinite floatingpoint values behave as you would expect. Adding or subtracting anything to or from infinity, for example, yields infinity. Negative zero behaves almost identically to positive zero, and, in fact, the = = equality operator reports that negative zero is equal to positive zero. The only way to distinguish negative zero from positive, or regular, zero is to divide by it. 1/0 yields positive infinity, but 1 divided by negative zero yields negative infinity. Finally, since NaN is notanumber, the = = operator says that it is not equal to any other number, including itself ! To check whether a float or double value is NaN, you must use the Float.isNan() and Double.isNan() methods. 2.4.5. StringsIn addition to the boolean, character, integer, and floatingpoint data types, Java also has a data type for working with strings of text (usually simply called strings). The String type is a class, however, and is not one of the primitive types of the language. Because strings are so commonly used, though, Java does have a syntax for including string values literally in a program. A String literal consists of arbitrary text within double quotes. For example: "Hello, world" "'This' is a string!" String literals can contain any of the escape sequences that can appear as char literals (see Table 23). Use the \" sequence to include a doublequote within a String literal. Strings and string literals are discussed in more detail later in this chapter. Chapter 4, "The Java Platform", demonstrates some of the ways you can work with String objects in Java. 2.4.6. Type ConversionsJava allows conversions between integer values and floatingpoint values. In addition, because every character corresponds to a number in the Unicode encoding, char types can be converted to and from the integer and floatingpoint types. In fact, boolean is the only primitive type that cannot be converted to or from another primitive type in Java. There are two basic types of conversions. A widening conversion occurs when a value of one type is converted to a wider typeone that is represented with more bits and therefore has a wider range of legal values. A narrowing conversion occurs when a value is converted to a type that is represented with fewer bits. Java performs widening conversions automatically when, for example, you assign an int literal to a double variable or a char literal to an int variable. Narrowing conversions are another matter, however, and are not always safe. It is reasonable to convert the integer value 13 to a byte, for example, but it is not reasonable to convert 13000 to a byte, since byte can only hold numbers between 128 and 127. Because you can lose data in a narrowing conversion, the Java compiler complains when you attempt any narrowing conversion, even if the value being converted would in fact fit in the narrower range of the specified type: int i = 13; byte b = i; // The compiler does not allow this The one exception to this rule is that you can assign an integer literal (an int value) to a byte or short variable, if the literal falls within the range of the variable. If you need to perform a narrowing conversion and are confident you can do so without losing data or precision, you can force Java to perform the conversion using a language construct known as a cast. Perform a cast by placing the name of the desired type in parentheses before the value to be converted. For example: int i = 13; byte b = (byte) i; // Force the int to be converted to a byte i = (int) 13.456; // Force this double literal to the int 13 Casts of primitive types are most often used to convert floatingpoint values to integers. When you do this, the fractional part of the floatingpoint value is simply truncated (i.e., the floatingpoint value is rounded towards zero, not towards the nearest integer). The methods Math.round(), Math.floor(), and Math.ceil() perform other types of rounding. The char type acts like an integer type in most ways, so a char value can be used anywhere an int or long value is required. Recall, however, that the char type is unsigned, so it behaves differently than the short type, even though both of them are 16 bits wide: short s = (short) 0xffff; // These bits represent the number 1 char c = '\uffff'; // The same bits, representing a Unicode character int i1 = s; // Converting the short to an int yields 1 int i2 = c; // Converting the char to an int yields 65535 Table 24 is a grid that shows which primitive types can be converted to which other types and how the conversion is performed. The letter N in the table means that the conversion cannot be performed. The letter Y means that the conversion is a widening conversion and is therefore performed automatically and implicitly by Java. The letter C means that the conversion is a narrowing conversion and requires an explicit cast. Finally, the notation Y* means that the conversion is an automatic widening conversion, but that some of the least significant digits of the value may be lost by the conversion. This can happen when converting an int or long to a float or double. The floatingpoint types have a larger range than the integer types, so any int or long can be represented by a float or double. However, the floatingpoint types are approximations of numbers and cannot always hold as many significant digits as the integer types. Table 24. Java Primitive Type Conversions
2.4.7. Reference TypesIn addition to its eight primitive types, Java defines two additional categories of data types: classes and arrays. Java programs consist of class definitions; each class defines a new data type that can be manipulated by Java programs. For example, a program might define a class named Point and use it to store and manipulate X,Y points in a Cartesian coordinate system. This makes Point a new data type in that program. An array type represents a list of values of some other type. char is a data type, and an array of char values is another data type, written char[]. An array of Point objects is a data type, written Point[]. And an array of Point arrays is yet another type, written Point[][]. As you can see, there are an infinite number of possible class and array data types. Collectively, these data types are known as reference types. The reason for this name will become clear later in this chapter. For now, however, what is important to understand is that class and array types differ significantly from primitive types, in that they are compound, or composite, types. A primitive data type holds exactly one value. Classes and arrays are aggregate types that contain multiple values. The Point type, for example, holds two double values representing the X and Y coordinates of the point. And char[] is obviously a compound type because it represents a list of characters. By their very nature, class and array types are more complicated than the primitive data types. We'll discuss classes and arrays in detail later in this chapter and examine classes in even more detail in Chapter 3, "ObjectOriented Programming in Java". Copyright © 2001 O'Reilly & Associates. All rights reserved. 
