Fundamental types include the Java primitive types and their corresponding wrapper classes/reference types. There is provision for automatic conversion between these primitive and reference types through autoboxing and unboxing. Numeric promotion is applied to primitive types where appropriate.
There are eight primitive types in Java: each is a reserved keyword. They describe variables that contain single values of the appropriate format and size (see Table 3-1). Primitive types are always the specified precision, regardless of the underlying hardware precisions (e.g., 32- or 64-bit).
Type | Detail | Storage | Range |
---|---|---|---|
|
|
1 bit |
Not applicable |
|
Unicode character |
2 bytes |
u0000 to uFFFF |
|
Integer |
1 byte |
–128 to 127 |
|
Integer |
2 bytes |
–32768 to 32767 |
|
Integer |
4 bytes |
–2147483648 to 2147483647 |
|
Integer |
8 bytes |
–263 to 263 –1 |
|
Floating point |
4 bytes |
1.4e–45 to 3.4e+38 |
|
Floating point |
8 bytes |
5e–324 to 1.8e+308 |
All primitive types except boolean
can accept character, decimal, hexadecimal, octal, and Unicode literal formats, as well as character escape sequences. Where appropriate, the literal value is automatically cast or converted. Remember that bits are lost during truncation. The following is a list of primitive assignment examples.
The boolean
primitive’s only valid literal values are true
and false
:
boolean
isEndogamous
=
true
;
The char
primitive represents a single Unicode character. Literal values of the char
primitive that are greater than two bytes need to be explicitly cast.
// 'atDNA'
char
[]
cArray
=
{
'''
,
// '
'u0061'
,
// a
't'
,
// t
0x0044
,
// D
0116
,
// N
(
char
)
(
65
+
131072
)
,
// A
0
b00100111
};
// '
The byte
primitive has a four-byte signed integer as its valid literal. If an explicit cast is not performed, the integer is implicitly cast to one byte:
final
byte
CHROMOSOME_PAIRS
=
12
;
final
byte
CHROMOSOME_TOTAL
=
(
byte
)
48
;
The short
primitive has a four-byte signed integer as its valid literal. If an explicit cast is not performed, the integer is implicitly cast to two bytes:
short
firstCousins
=
6
;
short
secondCousins
=
(
short
)
18
;
The int
primitive has a four-byte signed integer as its valid literal. When char, byte
, and short
primitives are used as literals, they are automatically cast to four-byte integers, as in the case of the short
value within vipSeats
. Floating-point and long literals must be explicitly cast:
int
thirdCousins
=
104
;
int
forthCousins
=
(
int
)
648.0
D
;
int
fifthCousins
=
(
short
)
3_888
;
The long
primitive has an eight-byte signed integer as its valid literal. It is designated by an L
or l
postfix. The value is cast from four bytes to eight bytes when no postfix or cast is applied:
long
sixthCousins
=
23_000
;
long
seventhCousins
=
(
long
)
138_000
;
long
eighthCousins
=
828_000
l
;
long
ninthCousins
=
4_968_000L
;
The float
primitive has a four-byte signed floating point as its valid literal. An F
or f
postfix or an explicit cast designates it. Even though no explicit cast is necessary for an int
literal, an int
will not always fit into a float
where the value exceeds about 223:
float
totalSharedCentimorgansX
=
0
;
float
totalSharedCentimorgansAutosomal
=
(
float
)
285.5
;
float
largestSharedCentimorgansX
=
0.0f
;
float
largestSharedCentimorgansAutosomal
=
71
F
;
The double
primitive uses an eight-byte signed floating-point value as its valid literal. The literal can have a D, d
, or explicit cast with no postfix. If the literal is an integer, it is implicitly cast:
double
centimorgansSharedFloor
=
0
;
double
centimorgansSharedCeiling
=
6766.20
;
double
centimorgansShared
=
(
double
)
888
;
double
centimorgansUnShared
=
5878.0d
;
double
centimorgansPercentShared
=
13.12
D
;
See Chapter 2 for more details on literals.
Positive and negative floating-point infinities, negative zero, and not a number (NaN) are special entities defined to meet the IEEE 754-1985 standard (see Table 3-2).
The Infinity, –Infinity
, and –0.0
entities are returned when an operation creates a floating-point value that is too large to be traditionally represented.
Entity | Description | Examples |
---|---|---|
|
Represents the concept of positive infinity |
1.0 / 0.0, 1e300 / 1e–300, Math.abs (–1.0 / 0.0) |
|
Represents the concept of negative infinity |
–1.0 / 0.0, 1.0 / (–0.0), 1e300/–1e–300 |
|
Represents a negative number close to zero |
–1.0 / (1.0 / 0.0), –1e–300 / 1e300 |
|
Represents undefined results |
0.0 / 0.0, 1e300 * Float.NaN, Math.sqrt (–9.0) |
Positive infinity, negative infinity, and NaN entities are available as double and float constants:
Double
.
POSITIVE_INFINITY
;
// Infinity
Float
.
POSITIVE_INFINITY
;
// Infinity
Double
.
NEGATIVE_INFINITY
;
// –Infinity
Float
.
NEGATIVE_INFINITY
;
// –Infinity
Double
.
NaN
;
// Not-a-Number
Float
.
NaN
;
// Not-a-Number
The Double
and Float
wrapper classes have methods to determine if a number is finite, infinite, or NaN
:
Double
.
isFinite
(
Double
.
POSITIVE_INFINITY
);
// false
Double
.
isFinite
(
Double
.
NEGATIVE_INFINITY
);
// false
Double
.
isFinite
(
Double
.
NaN
);
// false
Double
.
isFinite
(
1
);
// true
// true
Double
.
isInfinite
(
Double
.
POSITIVE_INFINITY
);
// true
Double
.
isInfinite
(
Double
.
NEGATIVE_INFINITY
);
Double
.
isInfinite
(
Double
.
NaN
);
// false
Double
.
isInfinite
(
1
);
// false
Double
.
isNaN
(
Double
.
NaN
);
// true
Double
.
isNaN
(
1
);
// false
Table 3-3 shows the results of special entity operations where the operands are abbreviated as INF
for Double.POSITIVE_INFINITY, –INF
for Double.NEGATIVE_INFINITY
, and NAN
for Double.NaN
.
For example, column 4’s heading entry (–0.0) and row 12’s entry (* NAN
) have a result of NaN
, and could be written as follows:
// 'NaN' will be printed
System
.
out
.
((-
0.0
)
*
Double
.
NaN
);
INF | (–INF) | (–0.0) | |
---|---|---|---|
* INF |
|
|
|
+ INF |
|
|
|
– INF |
|
|
|
/ INF |
|
|
|
* 0.0 |
|
|
|
+ 0.0 |
|
|
|
+ 0.5 |
|
|
|
* 0.5 |
|
|
|
+ (–0.5) |
|
|
|
* (–0.5) |
|
|
|
+ NAN |
|
|
|
* NAN |
|
|
|
Numeric promotion consists of rules that are applied to the operands of an arithmetic operator under certain conditions. Numeric promotion rules consist of both unary and binary promotion rules.
When a primitive of a numeric type is part of an expression, as listed in Table 3-4, the following promotion rules are applied:
If the operand is of type byte, short
, or char
, the type will be promoted to type int
.
Otherwise, the type of the operand remains unchanged.
Expression |
---|
Operand of a unary plus operator |
Operand of a unary minus operator – |
Operand of a bitwise complement operator ~ |
All shift operators >>, >>>, or << |
Index expression in an array access expression |
Dimension expression in an array creation expression |
When two primitives of different numerical types are compared via the operators listed in Table 3-5, one type is promoted based on the following binary promotion rules:
If either operand is of type double
, the non-double
primitive is converted to type double
.
If either operand is of type float
, the non-float
primitive is converted to type float
.
If either operand is of type long
, the non-long
primitive is converted to type long
.
Otherwise, both operands are converted to int
.
Operators | Description |
---|---|
+ and – |
Additive operators |
*, /, and % |
Multiplicative operators |
<, <=, >, and >= |
Comparison operators |
== and != |
Equality operators |
&, ^, and | |
Bitwise operators |
? : |
Conditional operator (see next section) |
If one operand is of type byte
and the other is of type short
, the conditional expression will be of type short
:
short
=
true
?
byte
:
short
If one operand R is of type byte, short
, or char
, and the other is a constant expression of type int
whose value is within range of R, the conditional expression is of type R:
short
=
(
true
?
short
:
1967
)
Otherwise, binary numeric promotion is applied, and the conditional expression type will be that of the promoted type of the second and third operands.
Each of the primitive types has a corresponding wrapper class/reference type, which is located in package java.lang
. Each wrapper class has a variety of methods, including one to return the type’s value, as shown in Table 3-6. These integer and floating-point wrapper classes can return values of several primitive types.
Primitive types | Reference types | Methods to get primitive values |
---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Autoboxing and unboxing are typically used for collections of primitives. Autoboxing involves the dynamic allocation of memory and the initialization of an object for each primitive. Note that the overhead can often exceed the execution time of the desired operation. Unboxing involves the production of a primitive for each object.
Computationally intensive tasks using primitives (e.g., iterating through primitives in a container) should be done using arrays of primitives instead of collections of wrapper objects.
Autoboxing is the automatic conversion of primitive types to their corresponding wrapper classes. In this example, the diploid chromosome number for each species (e.g., 60, 46, and 38) are automatically converted to their corresponding wrappers class because collections store references, not primitive values:
// Create hash map of weight groups
HashMap
<
String
,
Integer
>
diploidChromosomeNumberMap
=
new
HashMap
<
String
,
Integer
>
();
diploidChromosomeNumberMap
.
put
(
"Canis latrans"
,
78
);
diploidChromosomeNumberMap
.
put
(
"Bison bison"
,
60
);
diploidChromosomeNumberMap
.
put
(
"Homo sapiens"
,
46
);
diploidChromosomeNumberMap
.
put
(
"Sus scrofa"
,
38
);
diploidChromosomeNumberMap
.
put
(
"Myrmecia pilosula"
,
2
);
The following example shows an acceptable but not recommended use of autoboxing:
// Set number of autosomal (atDNA) chromosomes
Integer
atDnaChromosomeSet
=
22
;
//improper
As there is no reason to force autoboxing, the preceding statement should instead be written as follows:
Integer
atDnaChromosomeSet
=
Integer
.
valueOf
(
22
);
Unboxing is the automatic conversion of the wrapper classes to their corresponding primitive types. In this example, a reference type is retrieved from the hash map. It is automatically unboxed so that it can fit into the primitive type:
// Get the DCN of a homo sapien, performing unboxing
int
homoSapienDcn
=
diploidChromosomeNumberMap
.
get
(
"Homo sapiens"
);
System
.
out
.
println
(
homoSapienDcn
);
$
46
The following example shows an acceptable but not recommended use of unboxing:
// Establish the total number of chromosomes
Integer
atDnaChromosomeSet
=
22
;
int
multiplier
=
2
;
int
xChromosomes
=
2
;
// 1 or 2
int
yChromosome
=
0
;
// 0 or 1
// Mixing int and Integer; not recommended
int
dcn
=
xChromosomes
+
yChromosome
+
(
multiplier
*
atDnaChromosomeSet
);
It is better to write this expression with the intValue()
method, as shown here:
int
dcn
=
xChromosomes
+
yChromosome
+
(
multiplier
*
atDnaChromosomeSet
.
intValue
());