Chapter 5: Data Types and Variables
On 4 June 1996, a newly built launch vehicle named Ariane 5 lifted off on its first flight and broke apart less than a minute later. The vehicle was healthy. The engines were healthy. What failed was a single line of arithmetic inside the guidance software.
Part of that software had been carried over unchanged from an earlier rocket, Ariane 4, where it had run for years without trouble. Deep inside it, a measurement of horizontal velocity was held as a large real number occupying 64 bits of memory. That number was copied into a much smaller store — a signed integer of only 16 bits, able to hold whole numbers between about −32,768 and 32,767. On Ariane 4 the velocity never grew large enough for this to matter. Ariane 5 was a faster machine. Within seconds of launch the real value climbed past what 16 bits could represent, the conversion failed, and the guidance computers shut down.
Nothing here was exotic. A value of one type was moved into a variable of another type, and the second type was too small to hold it. That is a decision a Class XII student makes in the very first line of almost every program: which type, how wide, and what happens at the edges.
Diagram
Fig. 5.0-1 – a wide box labelled “64-bit real value” on the left, an arrow pointing right into a much narrower box labelled “16-bit integer variable”; the arrow is labelled “conversion”; bits that do not fit are shown spilling past the edge of the narrow box, marked “value out of range”
This chapter will answer each of these questions.
5.1 Data and Data Types
Every program you write does the same three things in some order: it takes in values, it works on them, and it gives values back. Those values have to live somewhere while the program runs, and that somewhere is the computer’s memory.
Data is the collection of raw facts and figures given to a computer for processing — the marks of a student, the fare on a bus route, the number of books issued from a library counter. On its own, a fact is only a fact. It becomes information once the program has organised it and drawn something out of it.
Memory, however, does not store marks or fares or names. It stores bits — tiny switches, each holding one of two states, written 0 and 1. Eight bits together make one byte. Everything a program keeps in memory, without exception, is finally a pattern of 0s and 1s. The question that this whole chapter grows out of is a simple one: how does a program know what a particular pattern of bits is supposed to mean?
5.1.1 Why Every Value Needs a Type
Consider the eight-bit pattern 01000001. Read as an ordinary binary whole number, it is 65. Read as a character under the coding scheme that computers use for text, it is the letter A. Read as part of a larger pattern, it may be a fragment of a much bigger number and mean nothing at all by itself. The pattern in Fig. 5.1-1 does not change; only the reading of it changes.
Diagram
Fig. 5.1-1 – a single row of eight boxes holding the bits 0, 1, 0, 0, 0, 0, 0, 1; three arrows leaving the row to the right, labelled “read as a whole number → 65”, “read as a character → ‘A’”, and “read as part of a wider value → incomplete”; caption line: the same bits, three meanings
Memory cannot settle that question for itself. Something outside the memory has to record, once and for all, how each stored pattern is to be read. That something is the data type.
A data type fixes three things at the same time. It fixes how much space the value occupies, so the compiler knows exactly how many bytes to set aside. It fixes what set of values may legally be stored there, so a value outside that set is rejected or altered. And it fixes what operations are meaningful on the value, so that dividing one number by another is allowed while dividing one letter by another is not.
Java takes this seriously. It is a statically typed language, which means the type of every variable is written down in the program text and checked by the compiler before the program ever runs. You cannot store a value of one type in a variable of another type unless the language can see that the move is safe, or unless you state in writing that you want it anyway. This is why a Java program begins with declarations such as:
int marks = 78;
char grade = 'B';
double fare = 12.50;Each line does two jobs at once. It reserves memory of the right size, and it stamps that memory with a reading — whole number, single character, real number — that will hold for the whole life of the variable.
The discipline costs you a little writing. It buys back a great deal. A mistake such as storing a fare in a variable meant for a count of books is caught by the compiler on your desk, not by a user months later; and because the compiler knows the exact size of every value in advance, it can allocate memory tightly instead of guessing.
5.1.2 The Two Families of Data Types
The data types available in Java fall into two families, shown in Fig. 5.1-2. The division is not a matter of naming convenience: the two families are stored differently, and knowing which family a type belongs to tells you what really sits inside the variable.
Diagram
Fig. 5.1-2 – classification tree; root box “Data Types in Java” branching into two boxes, “Primitive” and “Non-Primitive”; the Primitive box branches into “Numeric” and “Non-Numeric”; Numeric branches into “Integer types: byte, short, int, long” and “Floating-point types: float, double”; Non-Numeric branches into “boolean” and “char”; the Non-Primitive box branches into three boxes labelled “class”, “array” and “interface”
A primitive data type is one of the eight types built into the Java language itself. They are not made out of anything simpler, which is why they are also called basic types. A variable of a primitive type holds the value itself, directly, in the memory set aside for that variable.
The eight primitive types, in the order the syllabus fixes them, are byte, int, short, long, float, double, boolean and char. Six of them are numeric and two are not. Section 5.2 takes each of them in turn.
A non-primitive data type is built up from other types rather than supplied ready-made — a class, an array or an interface. A variable of a non-primitive type does not hold the object itself. It holds a reference: the address at which the object has been placed in memory. For this reason non-primitive types are also called reference types.
The practical consequence is worth seeing early. When you declare int count;, the memory for the whole number is reserved at that moment. When you declare a variable of a class type, only the space for a reference is reserved; the object is created separately, at run time, and the reference is then made to point at it. Until it points at something, the reference holds the special value null, meaning “no object”.
| Feature | Primitive type | Non-primitive type |
|---|---|---|
| Origin | Built into the Java language; there are exactly eight | Constructed from other types — classes, arrays, interfaces — and unlimited in number |
| What the variable holds | The value itself | A reference (address) to an object stored elsewhere |
| Size | Fixed by the language, known at compile time | The reference is fixed in size; the object it points to may be of any size |
| Default value | A defined value such as 0 or false | null, meaning it refers to no object |
| Operations available | The operators of the language, such as + and < |
The methods defined by the class, called using the dot operator |
| Name convention | Written entirely in lowercase — int, double |
Begins with a capital letter by convention — String, Scanner |
The last row is a small thing that saves a great deal of confusion later. Wherever you meet int, you are looking at a primitive type; wherever you meet Integer, you are looking at a class. They are related, and section 5.4 explains exactly how, but they are not the same thing and the capital letter is the visible clue.
Arrays and interfaces are studied in their own chapters. What matters at this point is only the shape of the division in Fig. 5.1-2, because everything that follows in this chapter — ranges, defaults, conversion, casting — behaves one way for the eight primitive types and another way for everything else.
5.2 The Primitive Data Types
Java gives you exactly eight primitive types and no more. Six of them hold numbers; the remaining two hold a truth value and a single character. They are, in the order the syllabus fixes them, byte, int, short, long, float, double, boolean and char.
Eight may sound like a lot for something as ordinary as a number. It is not extravagance. Each numeric type occupies a different amount of memory, and the amount of memory decides the range of values the type can hold. Choosing a type is therefore a genuine engineering decision: enough room for every value the program will ever meet, and not much more.
5.2.1 The Integer Types: byte, short, int, long
An integer is a whole number, positive, negative or zero, with no fractional part. Integer types are also called fixed point types, because the decimal point never moves — it sits permanently after the last digit.
Java stores integers in two’s complement form, a scheme in which the leftmost bit indicates the sign: 0 for a value that is zero or positive, 1 for a negative value. You do not need the full arithmetic of two’s complement here, but one consequence of it explains every range in this section. With \(n\) bits there are \(2^n\) different patterns available. One of them is used for zero, which leaves an odd number to share between the positive and the negative side. The negative side gets the extra one. That is why the ranges below always run from \(-2^{n-1}\) up to \(2^{n-1} - 1\), and never quite balance.
The four integer types differ only in how many bits they are given.
byte
The byte type occupies 8 bits — one byte, which is where the name comes from. Its range is −128 to 127, both values included. It is the smallest integer type Java offers, and it is used where memory genuinely matters: a long array of small values, or data read from a file or device a byte at a time.
byte count = 20;
byte offset = -50;short
The short type occupies 16 bits, giving a range of −32,768 to 32,767. This is the type that failed in the launch described at the opening of this chapter, and the two numbers above are exactly the boundaries the velocity reading crossed. A short is useful when values are known to stay modest and space is at a premium.
short seats = 123;
short delta = -624;int
The int type occupies 32 bits, with a range of −2,147,483,648 to 2,147,483,647. This is the everyday integer type of Java. Unless you have a definite reason to choose otherwise, whole numbers in a program go into an int: counts, marks, quantities, index positions, years.
int booksIssued = 1234;
int balance = -64532;long
The long type occupies 64 bits. Its range runs from −9,223,372,036,854,775,808 to 9,223,372,036,854,775,807 — large enough for population figures, distances in metres across a country, or a count of milliseconds spanning centuries.
A long needs one piece of care in writing. A whole number written plainly in a program, such as 9500000000, is treated by the compiler as an int first, and that value will not fit in 32 bits, so the program fails to compile before it ever reaches the long variable. The suffix L tells the compiler to read the literal as a long from the start.
long population = 9500000000L;
long shortfall = -5435673452L;Fig. 5.2-1 shows how the four ranges sit inside one another. Every value a byte can hold, a short can hold; every value a short can hold, an int can hold. Widening never loses anything. Moving the other way is where trouble begins, and section 5.8 returns to it.
Diagram
Fig. 5.2-1 – four concentric horizontal bars centred on 0 on a number line; innermost and shortest bar labelled “byte −128 to 127”; next “short −32,768 to 32,767”; next “int −2,147,483,648 to 2,147,483,647”; outermost “long −9,223,372,036,854,775,808 to 9,223,372,036,854,775,807”; each bar labelled with its bit width 8, 16, 32, 64 at its right end
5.2.2 The Floating-Point Types: float and double
Numbers such as a bus fare of 12.50 or an average of 67.4 have a fractional part, and the position of the decimal point varies from one value to the next. Types that hold such values are called floating-point types, because the point is free to float rather than sitting in a fixed place.
A floating-point value is not stored as a string of digits with a dot in it. It is stored in three parts: a sign bit, a group of bits holding an exponent, and the remaining bits holding the fractional digits, called the mantissa. Fig. 5.2-2 shows the arrangement for the smaller of the two types.
Diagram
Fig. 5.2-2 – a horizontal bar of 32 cells divided into three labelled regions: leftmost 1 cell shaded and labelled “sign (1 bit)”, next 8 cells labelled “exponent (8 bits)”, remaining 23 cells labelled “mantissa (23 bits)”; below the bar a caption line: layout of a float value
float
The float type uses 32 bits, an arrangement called single precision: one sign bit, eight exponent bits and twenty-three mantissa bits. It carries roughly seven significant decimal digits of accuracy.
A plain number written with a decimal point, such as 234.5, is treated by the compiler as a double. To store it in a float you must add the suffix F or f.
float rate = 234.5f;double
The double type uses 64 bits — double precision — with one sign bit, eleven exponent bits and fifty-two mantissa bits. It carries roughly fifteen significant decimal digits, which is why it is the normal choice for real numbers in Java and the type a decimal literal takes by default. The suffix D or d may be written for clarity but is not required.
double average = 12.3567834456;
double fare = 12.50D;Both floating-point types share a limitation that is easy to overlook and important to understand. Most decimal fractions cannot be represented exactly in a finite number of binary digits, in the same way that one-third cannot be written exactly as a terminating decimal. The stored value is therefore extremely close to the intended one, but not always equal to it.
5.2.3 The Non-Numeric Types: boolean and char
Two primitive types hold something other than a number.
boolean
The boolean type holds exactly one of two values, written true and false — nothing else, and never in quotes. It is used wherever a program must record whether a condition holds: whether a book is overdue, whether a seat is free, whether a search succeeded. A variable used in this way is often called a flag.
boolean overdue = true;
boolean seatFree = false;The information content of a boolean is one bit, but Java does not promise any particular amount of storage for it, and the amount is not something a program can inspect or rely on.
A boolean is not a number in disguise. Java will not let you use true where a 1 is expected, add two boolean values together, or store an int in a boolean variable. If you have met a language where zero means false, set that habit aside here — Java keeps the two ideas strictly apart, and the compiler enforces the separation.
char
The char type holds a single character, written between single quotation marks: 'B', '7', '*'. It occupies 16 bits and stores a Unicode value, so its range runs from '\u0000' (0) through '\uffff' (65,535). Unicode is a coding scheme that assigns a number to every character used in the world’s writing systems, and its first 128 codes are exactly the older ASCII codes, so the familiar values still hold: 'A' is 65, 'Z' is 90, 'a' is 97, 'z' is 122, '0' is 48, '9' is 57, and a blank space is 32.
char grade = 'B';
char symbol = '#';Because a char really does hold a number, it can take part in arithmetic. Adding 1 to 'A' produces the code for 'B'; subtracting '0' from a digit character produces the digit’s numeric value. This is a genuinely useful idea and it appears repeatedly in later chapters on strings.
Some characters cannot be typed directly between quotes — a newline, a tab, or a quotation mark itself. These are written as escape sequences, a backslash followed by one letter or symbol: '\n' for a new line, '\t' for a tab, '\\' for a backslash, '\'' for a single quote and '\"' for a double quote.
5.2.4 Sizes, Ranges and Default Values
Table 5.2 collects the eight primitive types in the order the syllabus lists them, with the memory each occupies, the values it may take, and the value it starts with if none is supplied.
| Type | Size | Range of values | Default value |
|---|---|---|---|
byte |
8 bits (1 byte) | −128 to 127 | 0 |
int |
32 bits (4 bytes) | −2,147,483,648 to 2,147,483,647 | 0 |
short |
16 bits (2 bytes) | −32,768 to 32,767 | 0 |
long |
64 bits (8 bytes) | −9,223,372,036,854,775,808 to 9,223,372,036,854,775,807 | 0L |
float |
32 bits (4 bytes) | single precision, about 7 significant decimal digits | 0.0f |
double |
64 bits (8 bytes) | double precision, about 15 significant decimal digits | 0.0d |
boolean |
not precisely defined | true or false only |
false |
char |
16 bits (2 bytes) | '\u0000' (0) to '\uffff' (65,535) |
'\u0000' |
The default value column needs one qualification, and it is a qualification students are regularly caught by. These defaults are supplied automatically only to variables declared inside a class — the instance variables you will meet in the chapter on objects. A variable declared inside a method gets no default at all. It must be given a value in the program before it is read, or the compiler refuses to accept the program. Section 5.6.2 returns to this.
The following program uses four different primitive types in one small task, which is how they actually appear in practice: a whole number for a count, a floating-point number for money, a boolean for a condition and a char for a code.
5.3 Classes as Types
The eight primitive types are fixed. Java will never add a ninth, and no program can invent one. Yet the values a real program deals with are rarely single numbers. A book in a library is a code, a title and a price together. A bus route is a number, a starting point, a destination and a fare. None of these is an int or a double, and splitting each one into loose variables scattered through a program quickly becomes unmanageable.
This is the gap the class fills. Where the primitive types are handed to you ready-made, a class lets you build a type of your own.
5.3.1 The Class as a Mechanism for User Defined Types
A class is a description written by the programmer that names a new type and states what a value of that type consists of. Once the class Book exists, Book is a type in that program exactly as int is a type, and variables may be declared of it.
Here is a class describing a book for a library register. Each line inside it names one piece of data and gives that piece a primitive or class type of its own.
class Book
{
int code;
String title;
double price;
}Three things are worth noticing. The class is built entirely out of types that already exist — two primitive, one a class — so nothing new had to be invented at the level of storage. The class has a name that begins with a capital letter, following the convention for every type that is not primitive. And the class describes a book; it is not itself a book, in the same way that the word int is not itself a number.
An actual book comes into being only when the program asks for one. A value of a class type is called an object, and an object is created with the keyword new. The object is stored in memory, and the variable is then made to refer to it.
5.3.2 Reference Types Compared with Primitive Types
The declaration int count; and the declaration Book entry; look alike and behave quite differently, and Fig. 5.3-1 shows why.
Diagram
Fig. 5.3-1 – two labelled memory boxes on the left; the upper box labelled “count (int)” contains the value 7; the lower box labelled “entry (Book)” contains an arrow, and the arrow points to a separate three-part box on the right labelled “Book object” whose parts hold 104, “Atlas of India” and 245.5; caption line: a primitive variable holds the value, a reference variable holds the way to it
A variable of a primitive type holds its value. The eight bits, or thirty-two, or sixty-four are the value. There is nothing else anywhere.
A variable of a class type holds a reference — the address at which the object has been placed in memory. The object lives elsewhere and may be far larger than the variable that reaches it. This is why class, array and interface types are called reference types.
Two consequences follow directly, and both matter in practice.
The first concerns the starting state. A reference variable that has not been made to point at anything holds null, a special value meaning “refers to no object”. A null reference is a perfectly valid thing for a variable to hold; what is not valid is following it, because there is nothing at the other end.
The second consequence concerns copying. Assigning one primitive variable to another copies the value, and the two variables go their separate ways afterwards. Assigning one reference variable to another copies the address, so both now refer to the same single object; a change made through either variable is visible through the other. There is one object, reached by two names.
This distinction is the reason the chapter on objects treats primitive and object arguments separately, and the reason a program that never sees an object misbehave can still produce puzzling results once objects arrive. For the present chapter, one rule carries the whole idea: a primitive variable holds a value, a reference variable holds the way to a value.
5.4 Wrapper Classes
Section 5.3 drew a firm line: a primitive value is a value, and an object is something else entirely. That line is useful, but it creates a difficulty. A good deal of the Java library is written to work with objects and only with objects. Anything that stores a mixed collection of items, or that is written once to work with values of any type at all, expects references — and a plain int is not a reference.
There is also a second, more everyday difficulty. Values arriving from outside a program — typed at a keyboard, read from a file, passed on the command line — arrive as text. The characters 4 and 5 standing side by side are not the number 45, and something must perform the conversion.
Both difficulties are answered by the same set of eight classes.
5.4.1 The Eight Wrapper Classes
A wrapper class is a class whose object holds a single primitive value inside it. The value is unchanged; it is simply enclosed in an object, so that wherever a reference is required, the reference can be supplied.
Diagram
Fig. 5.4-1 – on the left, a small box labelled “int” containing the value 45; an arrow labelled “wrapping” pointing right to a larger rounded box labelled “Integer object” that contains the same small box with 45 inside it; a second arrow labelled “unwrapping” pointing back to the left
Java provides exactly one wrapper class for each primitive type, so there are eight of them. Table 5.5 pairs them.
| Primitive type | Wrapper class |
|---|---|
byte |
Byte |
int |
Integer |
short |
Short |
long |
Long |
float |
Float |
double |
Double |
boolean |
Boolean |
char |
Character |
Six of the wrapper names are simply the primitive name with a capital first letter. Two are not: int becomes Integer and char becomes Character. These two are the ones students misremember, and the compiler will not accept Int or Char.
All eight belong to the package java.lang, which every Java program can use without any import statement. That is why you have already been able to write System.out.println and use String without importing anything — the same package supplies them.
From Java 5.0 onwards, the wrapping and unwrapping happen by themselves where the meaning is clear. Writing Integer n = 45; creates an Integer object holding 45, and writing int m = n; takes the value back out. These automatic conversions are called autoboxing and unboxing. They are a convenience of notation only; the object is still an object and the primitive is still a primitive.
Integer count = 45;
int plain = count;5.4.2 Conversion Between Strings and Primitive Values
Everything a program reads from the outside world begins life as text. The string "45" is three objects’ worth of nothing to arithmetic: it is a sequence of two characters, and "45" + 1 does not give 46.
The numeric wrapper classes each supply a method that reads such a string and returns the corresponding primitive value. The method is named after the type it produces, and it is used through the class name rather than through an object:
Integer.parseInt(String)returns anintLong.parseLong(String)returns alongDouble.parseDouble(String)returns adoubleFloat.parseFloat(String)returns afloatBoolean.parseBoolean(String)returns aboolean
Conversion in the other direction, from a value to its printed form, is provided by toString: Integer.toString(45) returns the string "45", and the other wrapper classes follow the same pattern.
One wrapper stands apart. Character has no parse method, because a string that is meant to be a single character does not need parsing — the character can be taken directly out of the string using the methods studied in the chapter on strings.
The next program shows the conversion doing real work. Its two values are supplied on the command line, which means they reach the program as strings and must be converted before any arithmetic is possible.
5.5 Literals, Identifiers and Reserved Words
Before a variable can be declared, three kinds of word have to be told apart. Consider one short statement:
int marks = 78;Four different things are happening in those few characters. int is a word the language has already claimed for itself. marks is a name invented by the programmer. 78 is a value written out in full. And = and ; are symbols that hold the statement together.
Each of these smallest meaningful pieces of a program is called a token. A compiler reads a program by first breaking it into tokens, exactly as a reader breaks a sentence into words, and every token belongs to one of a small number of categories. Fig. 5.5-1 shows the statement above taken apart.
Diagram
Fig. 5.5-1 – the statement “int marks = 78;” written across the top; five downward arrows from its parts to five labelled boxes below reading, in order, “reserved word”, “identifier”, “operator”, “literal”, “punctuator”
Operators are the subject of section 5.7. Punctuators — the semicolon, the comma, the braces and brackets that mark off parts of a program — need no separate study; you have been using them since your first program. The three categories that must be settled now are literals, identifiers and reserved words.
5.5.1 Literals
A literal is a value written directly into the program text. It is also called a constant, because the value cannot change while the program runs: the 78 in the statement above is 78 on every run, whatever else the program does.
Java recognises several kinds of literal, and the kind decides the type the compiler gives the value.
Integer literals are whole numbers written without a decimal point — 986, 2009, −645. A plain integer literal is an int; the suffix L or l makes it a long.
Real literals, also called floating-point literals, carry a decimal point or an exponent — 924.6, 0.00892, −65.50082, 1.0E-03, where E-03 means multiplied by ten to the power −3. A plain real literal is a double; the suffix F or f makes it a float.
Character literals are single characters in single quotation marks — 'P', '8', '*' — including the escape sequences met in section 5.2.3, such as '\n'.
String literals are runs of characters in double quotation marks — "Computer Science", "Bus route 27". A string literal is not a primitive value at all: its type is the class String.
Boolean literals are the two words true and false, written in lowercase and never in quotes. "true" in quotes is a string of four characters, which is an entirely different thing.
The null literal, written null, is the value of a reference that refers to no object, as section 5.3.2 described.
| Kind of literal | Examples | Type given by the compiler |
|---|---|---|
| Integer | 986, −645, 0 | int, or long with the suffix L |
| Real | 924.6, 1.0E-03 |
double, or float with the suffix f |
| Character | 'P', '8', '\n' |
char |
| String | "Bus route 27" |
String, a class type |
| Boolean | true, false |
boolean |
| Null | null |
any reference type |
5.5.2 Identifiers and the Rules for Naming Them
An identifier is a name invented by the programmer to identify something in the program — a class, a variable, a method or an interface. Book, marks, daysLate and main are all identifiers.
Java is strict about the form of an identifier, and the rules are few enough to learn once and keep.
- An identifier may be made of letters, digits, the underscore
_and the dollar sign$. - It must not begin with a digit.
mark2is legal;2markis not. - It must not contain a space. Where a name needs more than one word, the words are run together or joined with an underscore.
- It must not be a reserved word.
classandintare already taken. - It is case sensitive.
marks,MarksandMARKSare three different identifiers.
Beyond the rules there are conventions, and they are followed so consistently in Java that breaking them makes a program look wrong even when it compiles. Class names begin with a capital letter — Book, TicketTotal. Variable and method names begin with a small letter, and where they run several words together each later word is capitalised — daysLate, ratePerDay. Named constants are written entirely in capitals with underscores between words, a convention section 5.6.3 puts to use.
One habit is worth forming now, and it costs nothing. A name should say what the thing is. d and x2 tell a reader nothing; daysLate and totalFare tell a reader everything, and they still cost one line to type. A program has to be read by someone who has never seen it before — and in a program of your own, six months later, you will be that someone.
5.5.3 Reserved Words
A reserved word, also called a keyword, is a word that Java has given a fixed meaning of its own. Reserved words are written entirely in lowercase, and none of them may be used as an identifier.
Table 5.8 lists them. You have already used a fair number without thinking of them as a group: int, double, char, boolean, class, new, public, static, void.
abstract |
assert |
boolean |
break |
byte |
case |
catch |
char |
class |
const |
continue |
default |
do |
double |
else |
enum |
extends |
final |
finally |
float |
for |
goto |
if |
implements |
import |
instanceof |
int |
interface |
long |
native |
new |
package |
private |
protected |
public |
return |
short |
static |
strictfp |
super |
switch |
synchronized |
this |
throw |
throws |
transient |
try |
void |
volatile |
while |
Two of these, const and goto, are reserved but have no meaning in Java at all. They are held back so that a program cannot use them as names, and so that a compiler can give a clear message rather than a confusing one to a programmer arriving from a language where they do have a meaning.
5.6 Variables
A program that could only work with literals would be useless. It would compute the same answer every time it ran, because every value in it would be written into its own text. What makes a program general is its ability to hold a value that is not known when the program is written — a value read in, calculated, or changed as the work proceeds.
A variable is a named location in memory that holds a value, where the value may change while the program runs. The name is the point of it: instead of thinking about an address in memory, you think about daysLate or totalFare, and the compiler keeps track of where that actually is.
5.6.1 Declaring a Variable
A variable comes into existence through a declaration, which states its type and its name:
int marks;
double average;
char grade;Several variables of the same type may be declared together in one statement, separated by commas:
int english, hindi, science;The declaration does two things at once, as Fig. 5.6-1 shows. It reserves memory of the size that the type demands — four bytes for an int, eight for a double — and it records the name and the type so that every later use of that name can be checked.
Diagram
Fig. 5.6-1 – on the left the text “int marks;” with an arrow pointing right to a rectangle in a strip labelled “memory”; the rectangle is labelled above with the name “marks”, labelled below with “4 bytes reserved”, and its interior is empty; caption line: a declaration reserves space and fixes a name and a type for it
This is why the type must be written and cannot be worked out from the value: the space has to be reserved before there is any value to look at.
Where the declaration is written matters. A variable declared inside a method exists only while that method is running and is called a local variable. A variable declared inside a class but outside every method belongs to each object of that class and is called an instance variable. The chapter on objects develops the difference; one consequence of it appears immediately below.
5.6.2 Initialising a Variable
Reserving space does not put anything useful in it. Initialisation is the act of giving a variable its first value, and it may be done in the declaration itself or in a later statement:
int marks = 78;
double average;
average = 86.2;The two forms differ only in convenience. Initialising in the declaration is preferred where the first value is already known, because it keeps the name and its value in one place and leaves no gap in which the variable exists without meaning anything.
Where the first value is a plain constant, the initialisation is called static initialisation — the value is fixed and visible in the program text. Where the first value must be worked out while the program runs, from other variables or from a call to a method, it is called dynamic initialisation:
int width = 12;
int height = 9;
int area = width * height;Here width and height are initialised statically, and area dynamically: its value cannot be known until the multiplication is performed.
Now the consequence promised above. An instance variable that is never initialised is given the default value of its type from Table 5.2 — 0, false or '\u0000' as the case may be. A local variable is given nothing at all. If a program reads a local variable before assigning to it, the program does not compile.
5.6.3 Named Constants Using final
Some values in a program are fixed by the problem itself and must not drift: the number of subjects in a report card, the fine charged per day, the number of paise in a rupee. Writing such a value as a literal wherever it is needed works, but it has two weaknesses. The reader of the program has to guess what a bare 500 means, and a change to the value has to be made correctly in every place it occurs.
The keyword final solves both. A variable declared final must be given a value, and that value can never be changed afterwards. Such a variable is called a named constant.
final int SUBJECTS = 5;
final double FINE_PER_DAY = 2.50;By convention a named constant is written entirely in capitals, with underscores between words, so that a reader can tell at a glance which names may change and which may not.
5.6.4 The Assignment Operation: Left Hand Side and Right Hand Side
The symbol = in Java does not mean “is equal to”. It is an instruction: work out the value on the right, and store it in the place named on the left. This one sentence removes most of the confusion that surrounds assignment.
The general form is
variable = expression;and the order of events is fixed. The right hand side is evaluated first, producing a single value. That value is then stored into the left hand side. The two sides therefore have quite different requirements, set out in Table 5.10.
| Feature | Left hand side | Right hand side |
|---|---|---|
| What it must be | A variable — a place that can be written to | Any expression that produces a value |
| What it contributes | A destination | A value |
| When it is used | After the right hand side has been evaluated | First |
| May it be a literal? | No — 78 = marks; is not a statement |
Yes — marks = 78; is ordinary |
| May it contain operators? | No | Yes — marks = english + hindi; |
Once the order is clear, a statement that looks impossible in mathematics becomes obvious:
int count = 7;
count = count + 1;Read as an equation, the second line is false for every value of count. Read as an instruction, it is perfectly sensible: take the current value of count, which is 7, add 1 to get 8, and store 8 back into count. The old value is overwritten and is gone.
Java also allows a value to be assigned to several variables in one statement, a form called chain assignment:
int m, n, p;
m = n = p = 245;This works because assignment groups from the right: p receives 245 first, then n receives the value of that assignment, then m. Section 5.9.2 gives the general rule that makes this happen.
5.7 Expressions and Operators
Declaring variables gives a program somewhere to keep values. Doing anything with those values requires expressions.
An expression is any combination of variables, literals and operators that produces a single value. The values worked upon are called operands, and the symbols that act upon them are the operators. In total * 100.0 / maximum, three operands are joined by two operators, and the whole thing yields one number.
An expression on its own does nothing. It becomes a statement when its value is put to use, most often by storing it in a variable. Fig. 5.7-1 separates the two ideas in a single line of code.
Diagram
Fig. 5.7-1 – the line “area = length * breadth;” written across the middle; a brace under “length * breadth” labelled “expression”; a wider brace under the whole line labelled “assignment statement”; small downward arrows labelling “length” and “breadth” as operands and “*” as an operator
Operators are classified by how many operands they take. A unary operator takes one, a binary operator takes two, and Java has exactly one ternary operator, which takes three. The subsections that follow work through the operators the syllabus requires: arithmetic, increment and decrement, relational, logical, conditional and shorthand assignment.
5.7.1 Arithmetic Operators
The five arithmetic operators are the familiar ones, with one addition. Table 5.11 shows each of them applied to a = 23 and b = 4.
| Operator | Meaning | Expression | Result |
|---|---|---|---|
+ |
Adds the two operands | a + b |
27 |
- |
Subtracts the right operand from the left | a - b |
19 |
* |
Multiplies the two operands | a * b |
92 |
/ |
Divides the left operand by the right | a / b |
5 |
% |
Gives the remainder of that division | a % b |
3 |
The fourth row is the one to look at twice. Twenty-three divided by four is 5.75, but the result printed is 5. When both operands of / are integer types, Java performs integer division: it divides and discards the fractional part entirely. Nothing is rounded — 23 / 4 is 5, and so is 23 / 5 and 29 / 5.
This is not a defect; it is exactly what is wanted when you are asking how many complete buses are needed for a group of students, or how many whole rupees a value contains. But it is a frequent source of wrong answers, because it is silent. If a fractional result is wanted, at least one operand must be a floating-point value, as the percentage calculation in section 5.6.3 arranged.
The modulus operator % gives what integer division threw away — the remainder. It answers questions of a distinctive shape: is a number even (n % 2 == 0), what is the last digit of a number (n % 10), how many rupees are left over after making up complete notes of fifty. When the left operand is negative the remainder is negative too, so -7 % 3 gives −1, because the sign of the result follows the sign of the value being divided.
Division by zero deserves one honest sentence. With integer operands, n / 0 stops the program at run time with an ArithmeticException. With floating-point operands the program does not stop: the result is the special value Infinity, or NaN — not a number — when zero is divided by zero. Neither outcome is useful, so a program that divides must know that the divisor cannot be zero, or must check.
5.7.2 Increment and Decrement Operators
Adding one to a variable and storing the result back is so common that Java gives it a dedicated unary operator. The increment operator ++ increases its operand by 1; the decrement operator -- decreases it by 1. Both come in two forms.
In the prefix form the operator is written before the operand — ++n. The variable is changed first, and the changed value is the value of the expression. Change, then use.
In the postfix form it is written after the operand — n++. The value of the expression is the value the variable had before the change, and the change happens afterwards. Use, then change.
| Feature | Prefix (++n) |
Postfix (n++) |
|---|---|---|
| Order of events | Variable is changed, then the value is supplied | Value is supplied, then the variable is changed |
| Value of the expression | The new value | The old value |
| Effect on the variable | Increased by 1 | Increased by 1 |
Result of n = 5; System.out.println(++n); |
prints 6 | — |
Result of n = 5; System.out.println(n++); |
— | prints 5 |
The third row is the point students most often miss. Both forms change the variable by exactly the same amount. They differ only in what the surrounding expression receives. If the operator stands alone as a complete statement, n++; and ++n; do precisely the same thing, and either is fine.
| Statement | Value handed to println | n after |
m after |
|---|---|---|---|
int n = 15; |
— | 15 | — |
println(... + n) |
15 | 15 | — |
println(... + n++) |
15 | 16 | — |
println(... + n) |
16 | 16 | — |
int m = 22; |
— | 16 | 22 |
println(... + m) |
22 | 16 | 22 |
println(... + --m) |
21 | 16 | 21 |
println(... + m) |
21 | 16 | 21 |
Result: the output is the six lines shown above. The trace shows that in a postfix expression the variable is already changed by the time the next statement runs, even though the earlier statement printed the older value.
5.7.3 Relational Operators
A relational operator compares two values and produces a boolean result — true or false, never a number. These operators are what conditions in later chapters are built from. Table 5.14 shows the six of them for a = 22 and b = 15.
| Operator | Meaning | Expression | Result |
|---|---|---|---|
< |
Is the left value less than the right? | a < b |
false |
> |
Is the left value greater than the right? | a > b |
true |
<= |
Is the left value less than or equal to the right? | a <= b |
false |
>= |
Is the left value greater than or equal to the right? | a >= b |
true |
== |
Are the two values equal? | a == b |
false |
!= |
Are the two values unequal? | a != b |
true |
Two of these need care. The equality operator is written with two equal signs, ==, and asks a question. The single = is assignment and gives an order. They are entirely different operators and the resemblance between them is unfortunate.
The second point of care concerns objects. On primitive values, == compares the values themselves, which is what you expect. On reference types it compares the references — it asks whether two variables point at the same object, not whether two objects hold the same contents. Comparing the contents of two String values therefore calls for a method rather than ==, and the chapter on strings takes this up in full.
5.7.4 Logical Operators
Relational operators produce single boolean values. Logical operators combine them, so that a program can ask a compound question: is the student’s attendance above 75 per cent and the total above the pass mark?
Java has three: && (logical AND), || (logical OR) and ! (logical NOT). The first two are binary; ! is unary. Table 5.15 gives the complete behaviour for two boolean operands p and q.
| p | q | p && q |
p \|\| q |
!p |
|---|---|---|---|---|
false |
false |
false |
false |
true |
false |
true |
false |
true |
true |
true |
false |
false |
true |
false |
true |
true |
true |
true |
false |
Read down the third column: && gives true in exactly one row, the one where both operands are true. Read down the fourth: || gives false in exactly one row, the one where both are false. The ! operator simply reverses whatever it is given.
These operators have a property that goes beyond the table, and it has practical consequences. Java evaluates && and || by short-circuit evaluation: it stops as soon as the answer is certain. For p && q, if p turns out false the result must be false whatever q is, so q is never evaluated at all. For p || q, if p is true the result must be true, and again q is skipped. Fig. 5.7-2 traces the decision.
Diagram
Fig. 5.7-2 – flow: evaluate p → decision diamond “is p false?” → branch labelled “yes” leading directly to a box “result is false, q not evaluated” → branch labelled “no” leading to a box “evaluate q” → box “result is the value of q”; whole figure titled “short-circuit evaluation of p && q”
This is not merely a saving of time. It lets the first operand act as a guard for the second. A condition such as count != 0 && total / count > 50 is safe precisely because the division is never attempted when count is zero.
5.7.5 The Conditional Operator
The conditional operator, also called the ternary operator, is the only Java operator taking three operands. It chooses between two values according to a condition:
variable = condition ? value if true : value if false;The condition is evaluated first. If it is true the operator yields the value written before the colon; if it is false, the one after it. The whole thing is an expression, so it produces a value that can be stored, printed or passed on.
Conditional operators may be nested, with a whole conditional expression appearing as one of the two values:
String band = (age < 12) ? "child" : (age < 60) ? "adult" : "senior";Read left to right, this asks the questions in order and stops at the first that is answered true. Nesting more than two deep, however, produces a line that has to be decoded rather than read, and at that point the if else if statement is the better tool.
5.7.6 Shorthand Assignment Operators
The pattern variable = variable operator something occurs constantly. Java shortens it by joining the operator to the assignment sign. Table 5.17 lists the five that arise from the arithmetic operators.
| Operator | Written | Equivalent to |
|---|---|---|
+= |
a += b |
a = a + b |
-= |
a -= b |
a = a - b |
*= |
a *= b |
a = a * b |
/= |
a /= b |
a = a / b |
%= |
a %= b |
a = a % b |
The shorthand form names the variable once instead of twice, which shortens a long name and removes a real opportunity for error: in totalFare = totalFere + amount; the misspelling on the right is caught by the compiler, but in a pair of similar variable names an accidental substitution is not.
One difference from the long form is worth knowing. The shorthand operators perform the necessary narrowing conversion by themselves, so byte b = 10; b += 5; compiles, while b = b + 5; does not. Section 5.8 explains why the second one fails.
5.7.7 Pure and Impure Expressions
Expressions are also classified by the types of the operands appearing in them, and this classification is what section 5.8 rests on.
A pure expression is one in which every operand is of the same data type.
int a = 7, b = 3, c;
c = a + b * 2;Every operand here — a, b and the literal 2 — is an int, so the whole expression is evaluated in int arithmetic and yields an int.
An impure expression, also called a mixed mode expression, is one in which the operands are of different data types.
int a = 7;
double b = 2.5;
double d = a + b;Here an int and a double meet in a single addition. The arithmetic hardware works on one type at a time, so something must give way before the addition can happen: one of the two values has to be converted to the type of the other. Which one, and on whose authority, is the whole business of the next section.
5.8 Type Conversion and Casting
An impure expression cannot be evaluated as it stands. Arithmetic works on one type at a time, so when operands of different types meet, one of them must be turned into the other type first. That turning is called type conversion.
Java performs some conversions on its own, without being asked, and refuses to perform others unless the programmer states in writing that they are wanted. The whole of this section rests on why that division exists, and the reason is simple: some conversions cannot lose anything, and some can.
5.8.1 Automatic Type Coercion
Where the conversion is safe — where the destination type can hold every value the source type can hold — Java performs it silently. This is called automatic type coercion, or implicit type conversion, or widening.
Fig. 5.8-1 gives the order of widths. Each type on the ladder can hold every value of every type below it, so a conversion upward is always permitted.
Diagram
Fig. 5.8-1 – a vertical ladder of six boxes, from bottom to top: byte, short, int, long, float, double; a single upward arrow running alongside the ladder labelled “widening — always allowed”; a seventh box labelled char placed to the left of short with an arrow joining the ladder at int; caption line: the widening order of the numeric types
The rule Java applies to a binary expression is short and worth learning exactly:
- If either operand is a
double, the other is converted todouble. - Otherwise, if either operand is a
float, the other is converted tofloat. - Otherwise, if either operand is a
long, the other is converted tolong. - Otherwise, both operands are converted to
int.
Table 5.18 shows the rule at work.
| Left operand | Right operand | Type of the result |
|---|---|---|
int |
long |
long |
int |
double |
double |
long |
float |
float |
char |
int |
int |
byte |
byte |
int |
short |
byte |
int |
The last two rows are the surprise, and they follow from rule 4. There is no arithmetic on byte or short values in Java at all: both are widened to int before anything is added, and the result of adding two byte values is an int. This is precisely why byte b = 10; b = b + 5; is rejected by the compiler — the right hand side has become an int, and an int will not fit back into a byte without being asked to. The shorthand form b += 5; succeeds because it performs that narrowing on your behalf.
Consider a larger mixed expression:
char c = 'A';
int i = 5;
float f = 2.5f;
double d = 1.5;
double result = c + i * f / d;Working outward from the highest-priority operators: i * f combines an int with a float and gives a float; that float divided by a double gives a double; and c added to a double gives a double, the char being promoted first to int and then widened. The type of the whole expression is double, which is why the variable holding it must be a double too.
Widening is safe in the sense that no value is put out of range. It is not always exact. A long carries up to nineteen significant digits, while a float carries about seven and a double about fifteen, so converting a very large long to a floating-point type can round the last digits away. This is a limitation of precision, not of range, and it is the only case in which a widening conversion loses anything at all.
5.8.2 Explicit Type Casting
The conversions Java will not perform for you are the ones that can lose a value. To obtain them you must ask, and the asking is called type casting: the destination type is written in brackets before the value.
double average = 86.7;
int rounded = (int) average;| Feature | Automatic type coercion | Explicit type casting |
|---|---|---|
| Who requests it | The compiler, on its own | The programmer, in writing |
| Direction | From a narrower type to a wider one | Any direction, including wider to narrower |
| Risk of loss | None in range; possible loss of precision from long to float or double |
Fractional part or high-order bits may be lost |
| Also called | Implicit conversion, widening | Type casting, narrowing when the target is smaller |
| Appearance in the program | Nothing is written | The target type appears in brackets |
Casting is not only for narrowing. It is equally often used to force an expression into a type it would not otherwise take, and the commonest case by far is making a division come out as a fraction:
int total = 431;
int subjects = 5;
double average = (double) total / subjects;Because a cast is a unary operator, it binds more tightly than /. So total becomes 431.0 first, and the division then has one floating-point operand, which by rule 1 makes the whole division a double. The result is 86.2.
One type stands entirely outside all of this. A boolean cannot be cast to any other type, and no other type can be cast to a boolean. true is not 1 and false is not 0; they are values of a type that has nothing to do with numbers.
5.8.3 Casting to a Narrower Type and What It Costs
When the target type is narrower than the value being cast, something has to give. What is given up depends on which pair of types is involved, and in both cases the loss is silent — the program does not stop, warn or complain.
Casting a floating-point value to an integer type discards the fractional part. It does not round. (int) 9.87 is 9, and (int) 9.99 is also 9. For a negative value the truncation is toward zero, so (int) -9.87 is −9, not −10.
Casting an integer value to a narrower integer type keeps only as many low-order bits as the destination has room for and throws the rest away, as Fig. 5.8-2 shows. The bits that remain are then read as a value of the new type, sign bit and all — which is how a positive number can come back negative.
Diagram
Fig. 5.8-2 – a long horizontal bar of 32 cells labelled “int value 130”; the rightmost 8 cells shaded and labelled with the bits 1, 0, 0, 0, 0, 0, 1, 0; the leftmost 24 cells greyed out and marked with a cross labelled “discarded”; below the shaded group an arrow down to a box labelled “byte: leading 1 read as sign bit → −126”
5.9 Evaluation of Expressions: Precedence and Associativity
An expression with one operator has only one possible reading. An expression with several has many, and they do not all give the same answer. Consider
int result = 2 + 3 * 4;Multiplying first gives 14; adding first gives 20. Both are arithmetically sound readings of the symbols; only one of them is what Java does. The language therefore fixes two rules that between them settle every expression uniquely: precedence decides which operator acts first when different operators meet, and associativity decides which acts first when the same operator meets itself.
5.9.1 Operator Precedence
Precedence is the rank that decides which of two different operators is applied first.
Multiplication has higher precedence than addition, so 2 + 3 * 4 is read as 2 + (3 * 4) and gives 14. Table 5.21 sets out the ranking for the operators studied in this chapter, highest at the top.
| Rank | Category | Operators | Associativity |
|---|---|---|---|
| 1 | Brackets and member access | () [] . |
Left to right |
| 2 | Postfix | expr++ expr-- |
Left to right |
| 3 | Unary | ++expr --expr + - ! (type) |
Right to left |
| 4 | Multiplicative | * / % |
Left to right |
| 5 | Additive | + - |
Left to right |
| 6 | Relational | < > <= >= |
Left to right |
| 7 | Equality | == != |
Left to right |
| 8 | Logical AND | && |
Left to right |
| 9 | Logical OR | \|\| |
Left to right |
| 10 | Conditional | ?: |
Right to left |
| 11 | Assignment | = += -= *= /= %= |
Right to left |
Three readings of the table are worth making explicit.
Brackets sit at the top. Anything written inside brackets is evaluated first, whatever it contains, and this is the one tool that lets a programmer override every other rule in the table.
Assignment sits at the bottom. This is what makes an ordinary statement work: in average = total / count; the entire right hand side is evaluated before the assignment is even considered, exactly as section 5.6.4 described.
The relational operators rank above the equality operators but below the arithmetic ones, and the logical operators rank below all of them. That ordering is what allows a condition such as total / count > 50 && count != 0 to be written without a single bracket: the division happens first, then the comparisons, then the &&.
5.9.2 Associativity
Precedence says nothing about two operators of the same rank. In 100 / 5 * 2, both / and * sit at rank 4, so the ranking alone cannot decide. Associativity settles it.
Most operators in Java associate from left to right. So 100 / 5 * 2 is read as (100 / 5) * 2, which is 40. Had the rule been right to left, it would have been 100 / (5 * 2), which is 10 — the same symbols, a different answer.
Three levels associate from right to left, and each of them makes sense once you see why.
The unary operators do, because they attach to what stands immediately beside them. In - (int) x the cast is applied first and the minus sign then acts on the result; reading in the other direction would be meaningless.
The conditional operator does, which is what makes the nested form in section 5.7.5 work: the second conditional expression is treated as a single value belonging to the first.
Assignment does, which is what makes chain assignment work. In m = n = p = 245; the rightmost assignment is performed first, giving p the value 245, and the value of that assignment then travels leftward to n and to m.
5.9.3 Working Through a Mixed Expression
The two rules, applied together with the conversion rules of section 5.8, settle any expression. The method is always the same, and it is worth writing down as a habit rather than improvising each time.
- Evaluate whatever stands inside brackets, innermost first.
- Among what is left, apply the operator of highest precedence from Table 5.21.
- Where two operators are of equal precedence, apply them in the order given by their associativity.
- At every step, apply the conversion rules: if the two operands differ in type, the narrower is widened before the operation.
- Repeat until a single value remains, and note its type as well as its value.
Take the expression in the program below. Fig. 5.9-1 shows the order in which its parts are reduced.
Diagram
Fig. 5.9-1 – an evaluation tree for the expression c / b + a * 2.0 - 5 % 2; leaves at the bottom holding c=12, b=3, a=4, 2.0, 5, 2; three lower internal nodes labelled “c / b → 4 (int)”, “a * 2.0 → 8.0 (double)” and “5 % 2 → 1 (int)”; above them a node labelled “4 + 8.0 → 12.0 (double)”; at the top a node labelled “12.0 − 1 → 11.0 (double)”; the three lowest nodes marked “step 1”, the next “step 2”, the top “step 3”
A second expression brings in the operators that change their operands, where the order of evaluation decides not only the answer but the state of the variables afterwards.
int p = 5, q = 8;
boolean r = p++ < q && --q > p;| Step | Operation | Value produced | p after |
q after |
|---|---|---|---|---|
| 1 | p++ supplies the old value, then increases p |
5 | 6 | 8 |
| 2 | 5 < q, that is 5 < 8 |
true |
6 | 8 |
| 3 | && — left operand is true, so the right operand must be evaluated |
— | 6 | 8 |
| 4 | --q decreases q first, then supplies the new value |
7 | 6 | 7 |
| 5 | 7 > p, that is 7 > 6 |
true |
6 | 7 |
| 6 | true && true |
true |
6 | 7 |
Result: r holds true, and the two variables end at p = 6 and q = 7. The trace shows why a single line can be hard to reason about: the value of the condition and the final values of p and q all depend on the order in which the operators were applied, and on the fact that short-circuit evaluation did not cut the expression short.
That last point is the honest limitation of writing expressions this way. Everything in this section is exact and predictable, but predictability is not the same as clarity. Splitting the line above into three plain statements would cost nothing at run time and would remove any need for a trace. Precedence and associativity exist so that you can read what someone else has written; brackets and short statements exist so that no one has to.
5.10 Summary
Data and types
- Data is the raw facts given to a computer for processing. In memory, all of it is finally a pattern of bits, and the same pattern can be read in more than one way.
- A data type fixes three things at once: how much memory a value occupies, which values it may take, and which operations are legal on it. Java is statically typed, so every variable’s type is written in the program and checked by the compiler.
- Types fall into two families. A primitive type is built into the language and its variable holds the value itself. A non-primitive, or reference, type is built from other types, and its variable holds a reference to an object stored elsewhere. An unset reference holds
null.
The eight primitive types
- The primitive types are
byte,int,short,long,float,double,booleanandchar. Six are numeric, two are not. - The integer types are stored in two’s complement, which is why their ranges run from \(-2^{n-1}\) to \(2^{n-1}-1\):
byte8 bits, −128 to 127;short16 bits, −32,768 to 32,767;int32 bits, −2,147,483,648 to 2,147,483,647;long64 bits, with the suffixLneeded on a literal too large for anint. floatis 32-bit single precision, about seven significant decimal digits, and needs the suffixfon a literal;doubleis 64-bit double precision, about fifteen digits, and is the type a plain decimal literal takes. Most decimal fractions cannot be stored exactly in either, so floating-point values should not be tested for exact equality.booleanholds onlytrueorfalseand is not a number in disguise; it cannot be converted to or from any other type.charholds one 16-bit Unicode character from'\u0000'to'\uffff', whose first 128 codes are the ASCII codes, and it can take part in arithmetic.- Instance variables receive the default value of their type. Local variables receive nothing, and a program that reads one before assigning to it does not compile.
Classes, references and wrapper classes
- A class is a user defined type: it names a new type and states what a value of that type consists of. An object is a value of a class type, created with
new, and the class is the type of which the object is an instance. - Assigning one primitive variable to another copies the value; assigning one reference variable to another copies the address, so both then refer to the same single object.
- Each primitive type has one wrapper class, which holds that value as an object:
Byte,Integer,Short,Long,Float,Double,BooleanandCharacter. All eight are injava.langand need no import. From Java 5.0 onward, autoboxing and unboxing perform the wrapping and unwrapping automatically. - Values arriving from outside a program arrive as text, and the wrapper classes convert them:
Integer.parseInt,Long.parseLong,Double.parseDouble,Float.parseFloatandBoolean.parseBooleanreturn primitive values, whiletoStringconverts in the other direction.Characterhas no parse method.
Tokens, variables and constants
- A program is read by the compiler as a sequence of tokens: literals, identifiers, reserved words, operators and punctuators.
- A literal is a fixed value written directly in the program. Integer literals are
intunless suffixedL; real literals aredoubleunless suffixedf; character literals use single quotes and string literals double quotes;true,falseandnullare literals and cannot be used as names. - An identifier is a programmer-supplied name. It may use letters, digits, underscore and dollar sign, must not begin with a digit, must contain no space, must not be a reserved word, and is case sensitive. Java has fifty reserved words, two of which —
constandgoto— have no meaning in the language and exist only to be reserved. - A variable is a named memory location of a stated type whose value may change. A declaration reserves the space and fixes the name and type; initialisation gives the first value, statically from a constant or dynamically from a computation.
- A named constant is a variable declared
final. It must be given a value and can never be changed afterwards, which documents the value, confines it to one place, and turns an accidental change into a compile error. - In an assignment the right hand side is evaluated first and must be an expression producing a value; the left hand side must be a variable, a place that can be written to. This is why
count = count + 1;is sensible andcount + 1 = count;is not.
Expressions and operators
- An expression is operands joined by operators, evaluated to a single value. Operators are unary, binary, or the one ternary operator.
- Where both operands of
/are integers, the division is integer division and the fractional part is discarded silently;%supplies the discarded remainder and takes the sign of the value being divided. Integer division by zero stops the program; floating-point division by zero producesInfinityorNaN. - The prefix form
++nchanges the variable and then supplies the new value; the postfix formn++supplies the old value and then changes the variable. Both change the variable by the same amount, and they differ only in what the surrounding expression receives. - Relational operators produce a
boolean.=assigns and==compares; on reference types==compares references, not contents. &&yieldstrueonly when both operands aretrue,||yieldsfalseonly when both arefalse, and!reverses its operand. Both&&and||use short-circuit evaluation, stopping as soon as the answer is certain — which lets the first operand guard the second.- The conditional operator chooses between two values according to a condition. The shorthand assignment operators shorten
a = a + btoa += b, and perform any needed narrowing themselves. - An expression whose operands are all of one type is pure; one whose operands differ in type is impure, or mixed mode.
Conversion, precedence and associativity
- Automatic type coercion widens the narrower operand without being asked, because widening cannot put a value out of range. The rule is: if either operand is
doublethe other becomesdouble; else if either isfloatthe other becomesfloat; else if either islongthe other becomeslong; else both becomeint. The last clause means there is nobyteorshortarithmetic at all — the result of adding twobytevalues is anint. - Explicit type casting writes the target type in brackets and forces the conversion. Casting a floating-point value to an integer type truncates toward zero rather than rounding; casting to a narrower integer type keeps only the low-order bits, so 130 cast to a
bytegives −126. Both losses are silent. - Because a cast is a unary operator it binds tighter than
/, which is why casting one operand before a division gives a fractional result while casting the whole division afterwards does not. - Precedence decides which of two different operators acts first; associativity decides the order among operators of equal precedence. Brackets rank highest and assignment lowest. Most operators associate left to right; the unary operators, the conditional operator and assignment associate right to left, which is what makes chain assignment work.
5.11 Key Terms
- ASCII (American Standard Code for Information Interchange) — the older character coding scheme whose 128 codes form the first 128 codes of Unicode.
- Associativity — the rule that decides the order in which operators of equal precedence are applied, left to right or right to left.
- Autoboxing — the automatic wrapping of a primitive value into an object of its wrapper class, available from Java 5.0 onward.
- Automatic type coercion — conversion of a value to a wider type, performed by the compiler without being asked, in an expression whose operands differ in type.
- Binary operator — an operator that acts on two operands.
- Bit — a single binary digit, holding 0 or 1; the smallest unit of storage.
- Byte — a group of eight bits; also the name of the 8-bit primitive type
byte. - Chain assignment — an assignment such as
m = n = p = 245;that gives one value to several variables, working from the right. - Class — a user defined type: a named description of the data a value of that type holds and the operations allowed on it.
- Conditional operator — the ternary operator
?:, which chooses between two values according to a condition. - Data — the raw facts and figures supplied to a computer for processing.
- Data type — a named category of values that fixes the memory a value occupies, the values it may take and the operations legal on it.
- Declaration — the statement that brings a variable into existence by giving its type and its name.
- Decrement operator — the unary operator
--, which reduces its operand by 1. - Double precision — the 64-bit floating-point format used by
double, carrying about fifteen significant decimal digits. - Dynamic initialisation — giving a variable its first value from a computation performed while the program runs.
- Escape sequence — a character written as a backslash followed by one symbol, such as
'\n', used for characters that cannot be typed directly. - Expression — operands joined by operators, evaluated by the program to produce a single value.
- Flag — a
booleanvariable used to record whether some condition holds. - Floating-point — a way of storing real numbers in which the position of the decimal point may vary, held as a sign, an exponent and a mantissa.
- Identifier — a programmer-supplied name for a class, variable, method or interface.
- Impure expression — an expression whose operands are of two or more different data types; also called a mixed mode expression.
- Increment operator — the unary operator
++, which raises its operand by 1. - Initialisation — the act of giving a variable its first value.
- Instance variable — a variable declared inside a class but outside every method, belonging to each object of that class.
- Integer — a whole number, positive, negative or zero, with no fractional part.
- Integer division — division in which both operands are of integer type, so the fractional part of the result is discarded.
- Left hand side — the part of an assignment that names the destination; it must be a variable.
- Literal — a fixed value written directly in the program text.
- Local variable — a variable declared inside a method, existing only while that method runs, and receiving no default value.
- Logical operator — an operator that combines
booleanvalues:&&,||or!. - Mantissa — the group of bits in a floating-point value that holds its significant digits.
- Modulus — the operator
%, which gives the remainder left by a division. - Named constant — a variable declared
final, which must be given a value and can never be changed afterwards. - Non-primitive data type — a type built from other types, whose variable holds a reference to an object rather than the object itself; also called a reference type.
- Object — a value of a class type, created at run time; an instance of that class.
- Operand — a value acted upon by an operator.
- Operator — a symbol that performs an operation on one, two or three operands.
- Postfix form — the form
n++, which supplies the old value of the variable and then changes it. - Precedence — the ranking that decides which of two operators of different kinds is applied first.
- Prefix form — the form
++n, which changes the variable first and then supplies the new value. - Primitive data type — one of the eight types built into Java, whose variable holds the value itself.
- Pure expression — an expression in which every operand is of the same data type.
- Reference — the address of an object, held by a variable of a non-primitive type.
- Relational operator — an operator that compares two values and produces a
booleanresult. - Reserved word — a word with a fixed meaning in Java, which may not be used as an identifier; also called a keyword.
- Right hand side — the part of an assignment that is evaluated first to produce the value being stored.
- Short-circuit evaluation — the behaviour of
&&and||in which the second operand is not evaluated once the result is already certain. - Single precision — the 32-bit floating-point format used by
float, carrying about seven significant decimal digits. - Statement — a complete instruction, most often an expression whose value is put to use.
- Static initialisation — giving a variable its first value from a constant written in the program text.
- Statically typed — describing a language in which the type of every variable is written in the program and checked before it runs.
- Ternary operator — an operator taking three operands; Java has exactly one, the conditional operator.
- Token — the smallest meaningful unit of a program: a literal, identifier, reserved word, operator or punctuator.
- Two’s complement — the scheme used to store integers, in which the leftmost bit marks the sign and the range runs from \(-2^{n-1}\) to \(2^{n-1}-1\).
- Type casting — an explicit conversion, written as the target type in brackets before an expression.
- Type conversion — the changing of a value of one data type into a value of another.
- Unary operator — an operator that acts on a single operand.
- Unboxing — the automatic extraction of a primitive value from an object of its wrapper class.
- Unicode — the character coding scheme used by
char, assigning a number to every character of the world’s writing systems. - Variable — a named memory location of a stated type, holding a value that may change while the program runs.
- Wrapper class — a class holding a single primitive value as an object, and supplying methods for converting that type to and from other representations.
5.12 Points to Ponder
Java insists that you state the type of every variable, even when the value being stored makes the type obvious. Some other languages work the type out from the value instead. What does a program gain by having the type written down, and what does it lose?
Widening happens automatically because it is called safe, yet a very large
longwidened to afloatcan lose its last digits, while anintcast to abyteloses whole bits. If both conversions can lose something, on what grounds does the language treat only one of them as needing the programmer’s permission?Arithmetic on
byteandshortvalues is always carried out asintarithmetic, and the result is anint. If no calculation is ever performed in those two types, what work are they actually doing in the language?Integer division discards the fractional part without any warning, and this silence is the cause of a great many wrong answers. Would Java be a better language if it refused to divide two integers unless the programmer said what should happen to the remainder — or is the silence part of what makes the operator useful?
A
finalvariable cannot be changed, but a programmer who did not want to change a value could simply refrain from changing it. What does asking the compiler to enforce that promise buy, that self-discipline does not?The prefix and postfix forms of
++change the variable in exactly the same way and differ only in the value handed to the surrounding expression. Would anything of substance be lost if a language provided only one of the two forms?
5.13 Objective Questions
- Which of the following is not a primitive data type? [1 mark]
shortcharStringboolean
- The default value of a
booleaninstance variable is: [1 mark]
- 0
truefalse'\u0000'
- The value of the expression
17 % 5is: [1 mark]
- 3
- 3.4
- 2
- 0
- Which suffix must be written on a decimal literal that is to be stored in a
floatvariable? [1 mark]
LDFI
- The wrapper class corresponding to the primitive type
charis: [1 mark]
CharChrCharacterCharType
- In the assignment
area = length * breadth;, the partlength * breadthis: [1 mark]
- the left hand side
- the right hand side
- a named constant
- a punctuator
- Assertion (A): A local variable must be given a value before any statement reads it. Reason (R): The compiler supplies every local variable with the default value of its type. Choose the correct option: [1 mark]
- Both Assertion (A) and Reason (R) are true and Reason (R) is the correct explanation of Assertion (A).
- Both Assertion (A) and Reason (R) are true but Reason (R) is not the correct explanation of Assertion (A).
- Assertion (A) is true but Reason (R) is false.
- Assertion (A) is false but Reason (R) is true.
- Assertion (A): With
int aandint b, the expression(double)(a / b)does not produce a fractional result. Reason (R): A cast is a unary operator and binds more tightly than the division operator. Choose the correct option: [1 mark]
- Both Assertion (A) and Reason (R) are true and Reason (R) is the correct explanation of Assertion (A).
- Both Assertion (A) and Reason (R) are true but Reason (R) is not the correct explanation of Assertion (A).
- Assertion (A) is true but Reason (R) is false.
- Assertion (A) is false but Reason (R) is true.
Fill in the blank: A variable declared with the keyword ____________ must be given a value and can never be changed afterwards. [1 mark]
State whether true or false: In Java, a
booleanvalue may be cast to anint. [1 mark]Match each type in column A with its description in column B. [1 mark]
| Column A | Column B |
|---|---|
a. byte |
i. 16-bit Unicode character |
b. char |
ii. 64-bit floating-point value |
c. long |
iii. 8-bit integer |
d. double |
iv. 64-bit integer |
- Give the full form of ASCII. [1 mark]
5.14 Check Your Concepts
Define a data type. State the three things a data type fixes about a value. [2 marks]
Name the eight primitive data types of Java. [2 marks]
What is a named constant? Write one declaration as an example. [2 marks]
Differentiate between a pure expression and an impure expression, giving one example of each. [3 marks]
Differentiate between the prefix and the postfix form of the increment operator. Use a variable holding 5 in your answer to show the difference clearly. [3 marks]
Give reasons: with
byte b = 10;, the statementb = b + 5;is rejected by the compiler whileb += 5;is accepted. [3 marks]Explain automatic type coercion and explicit type casting, and state one situation in which each of them is needed. [4 marks]
What is short-circuit evaluation? Explain with one example how it can be used to prevent a run-time error. [4 marks]
Explain the terms declaration, initialisation, static initialisation and dynamic initialisation, giving an example of each. [6 marks]
A student writes
double average = total / count;where bothtotalandcountare of typeint, and is surprised that the average never has a fractional part. Explain the fault, give two different ways of correcting it, and state which correction you would prefer and why. [6 marks]
5.15 Output & Debugging
- Predict the output of the following fragment and explain why the two printed values differ. [3 marks]
int a = 7, b = 2;
double x = a / b;
double y = (double) a / b;
System.out.println(x);
System.out.println(y);- Write the four lines printed by the following fragment, and state the value held by
nafter the last statement. [4 marks]
int n = 10;
System.out.println(n++);
System.out.println(++n);
System.out.println(n--);
System.out.println(n);- The following class contains one error. Name the error, state at which stage it is detected, and rewrite the corrected class. [3 marks]
class Area
{
public static void main(String args[])
{
int length = 12;
int breadth;
System.out.println("Area = " + length * breadth);
}
}- The following class does not compile. Identify the single fault, explain why the compiler rejects it, and give the corrected line. [2 marks]
class Temperature
{
public static void main(String args[])
{
float reading = 36.6;
System.out.println(reading);
}
}- Predict the two values printed by the following fragment, and give the reason for the second one. [3 marks]
int p = 6, q = 4;
boolean r = p > q || q++ > 10;
System.out.println(r);
System.out.println(q);- Write the two values printed by the following fragment, and state in one sentence what the two calculations do to any positive whole number, not just to this one. [3 marks]
int n = 4728;
int d = n % 10;
n = n / 10;
System.out.println(d);
System.out.println(n);5.16 Programming Practice
A shop’s billing counter charges a fixed packing charge of ₹15.00 on every bill. Write a class that stores the quantity of an item as a whole number and its unit price as a real number, and prints the quantity, the cost of the items, the packing charge and the amount payable. The packing charge must be written as a named constant. Submit the complete class and a variable description table. Test with a quantity of 7 and a unit price of ₹42.50. [4 marks]
Two values are supplied to a program as command-line arguments: the first is a whole number and the second is a real number. Write a class that converts both arguments to numeric values using the appropriate wrapper class methods, and prints their sum and their product. Submit the complete class, and name the two conversion methods you have used and the type each returns. [5 marks]
Write a class that stores a four-digit whole number and prints its four digits separately, from the units digit to the thousands digit, followed by the sum of the four digits. Use only the arithmetic operators of this chapter; no loop is required. Submit an algorithm in pseudocode followed by the complete class. Test with 4728, and then with an unseen input of 9105. [6 marks]
A temperature is recorded in degrees Celsius as a real number. Write a class that prints the temperature in degrees Fahrenheit as a real number, and also prints the whole-number part of that Fahrenheit value obtained by means of a cast. State clearly in a comment which conversion is automatic and which is explicit. Submit the complete class and a variable description table. Test with 36.6 degrees Celsius. [6 marks]