4.7.1 The Integer and Double Classes
Java has two kinds of types. Primitive types such as int and double are simple values: a variable of type int holds the number itself. Class types such as String, Scanner, and the classes you wrote in Unit 3 describe objects, and a variable of a class type holds a reference to an object. Most of the time numbers are happiest as primitives. But some parts of Java are built to hold objects only. The next topic’s ArrayList, a list that can grow, is one of them, and it cannot hold a plain int. So Java needs a way to package a number as an object.
The package is a wrapper class: a class whose objects each hold one primitive value, “wrapped” inside an object. Picture a number sealed inside a labeled envelope. The envelope can go anywhere an object is required, and the number inside is still there to be read. For the two primitive types this course uses most, the wrapper classes are:
| Primitive type | Wrapper class | A variable of the wrapper type holds… |
|---|---|---|
int |
Integer |
a reference to an object containing one int |
double |
Double |
a reference to an object containing one double |
Notice the capital letters. int and double are primitive types and are lowercase. Integer and Double are class names, and class names start with a capital letter. Here is the difference in memory:
int laps = 12; laps [ 12 ]
Integer lapCount = 12; lapCount [ ref ] ───► Integer object
value: 12
Because Integer and Double are class types, a variable of those types can also hold null (Topic 1.13), meaning it refers to no object. An int variable can never be null.
Both classes belong to the java.lang package, the group of classes that Java makes available to every program automatically. String and Math are in java.lang too, which is why you have never imported them. Compare that with File from Topic 4.6, which lives in java.io and needs an import statement.
Key idea
Integer and Double are wrapper classes: each object holds one int or one double value. They are in the java.lang package, so any program can use them without an import.
The Integer constants
You have already used the Integer class without knowing much about it. In Topic 1.5 you met Integer.MAX_VALUE (2,147,483,647, the largest int) and Integer.MIN_VALUE (−2,147,483,648, the smallest int). Now you can read those names fully: they are public static constants that belong to the Integer class, so you reach them through the class name, just like the class variables in Topic 3.7.
They are especially useful as starting values for the minimum and maximum algorithms from Topic 4.5. To find the smallest value, you need a starting “smallest so far” that any real value will beat. No int is larger than Integer.MAX_VALUE, so the first element is always less than or equal to it.
Worked example: The fastest lap
A runner’s lap times in seconds are stored in an array. Watch how fastest changes as the loop visits each time.
int[] lapTimes = {74, 69, 81, 72};
int fastest = Integer.MAX_VALUE;
for (int time : lapTimes)
{
if (time < fastest)
{
fastest = time;
}
}
System.out.println(fastest);| Visit | time |
time < fastest? |
fastest afterward |
|---|---|---|---|
| start | 2147483647 | ||
| 1 | 74 | yes | 74 |
| 2 | 69 | yes | 69 |
| 3 | 81 | no | 69 |
| 4 | 72 | no | 69 |
The first comparison is guaranteed to be true, so fastest takes on a real lap time right away. After that, only smaller times replace it. Line 10 prints:
69Now vary the goal: find the largest value in an array of overnight low temperatures, all below zero. The starting value flips to Integer.MIN_VALUE, because no int is smaller, so the first element always beats it.
int[] lows = {-12, -5, -9};
int warmest = Integer.MIN_VALUE;
for (int low : lows)
{
if (low > warmest)
{
warmest = low;
}
}
System.out.println(warmest);-5
warmest starts at −2,147,483,648. The first low, −12, is greater, so it replaces it. Then −5 is greater than −12, and −9 is not greater than −5. The warmest night was −5.
Watch out
Starting the maximum at 0. Zero feels like a natural “nothing yet” value, so students often write this:
int[] lows = {-12, -5, -9};
int warmest = 0;
for (int low : lows)
{
if (low > warmest)
{
warmest = low;
}
}
System.out.println(warmest);0Every low is negative, so no element is ever greater than 0, and warmest never changes. The program prints 0, a temperature that is not even in the array. This is a logic error: it compiles and runs, but the answer is wrong. Starting at 0 only works if you know every value is positive. Starting at Integer.MIN_VALUE (or at the first element of the array) works for any int values.
Pause and try
Question 1 · Tracing
Write the exact output.
int[] jumps = {-3, -8, -1, -6};
int biggest = Integer.MIN_VALUE;
for (int jump : jumps)
{
if (jump > biggest)
{
biggest = jump;
System.out.println(biggest);
}
}Check your reasoning
-3
-1| Visit | jump |
jump > biggest? |
biggest afterward |
Printed |
|---|---|---|---|---|
| start | −2147483648 | |||
| 1 | −3 | yes | −3 | -3 |
| 2 | −8 | no | −3 | |
| 3 | −1 | yes | −1 | -1 |
| 4 | −6 | no | −1 |
The println is inside the if, so it prints only when biggest changes. The first element always replaces Integer.MIN_VALUE.
Reread 4.7.1 The Integer and Double Classes if your output differed.
Question 2 · Short answer
A program that reads a text file needs import java.io.File; at the top, but it can use Integer.MAX_VALUE with no import at all. Explain why.
Check your reasoning
Model answer: Integer is in the java.lang package, which Java makes available to every program automatically, the same as String and Math. File is in the java.io package, which is not automatic, so it must be imported.
A complete answer names java.lang as the package of Integer and says that this package needs no import.
Reread 4.7.1 The Integer and Double Classes if your answer differed.
4.7.2 Autoboxing
You know what an Integer object is. How do you make one? You might expect to call a constructor with new, as with every other object so far. You do not need to. If you write Integer lapCount = 12;, the compiler sees an int value going into an Integer variable and quietly adds the code that creates an Integer object holding 12. You write a number, and Java seals it in the envelope for you.
This automatic conversion from a primitive value to an object of its wrapper class is called autoboxing (the value is put “in a box”). It converts an int to an Integer and a double to a Double. The compiler applies it in two situations:
- A primitive value is assigned to a variable of the matching wrapper type.
- A primitive value is passed as an argument to a method whose parameter has the matching wrapper type.
Key idea
Autoboxing is the compiler automatically turning an int into an Integer, or a double into a Double, when the value is assigned to a wrapper variable or passed to a wrapper parameter.
Worked example: Boxing by assignment
Lines 2 and 3 store primitive values in wrapper variables.
int laps = 12;
Integer lapCount = laps;
Double price = 4.75;
System.out.println(lapCount);
System.out.println(price);- Line 1 stores the primitive 12 directly in
laps. - Line 2 assigns an
intto anIntegervariable. The compiler autoboxes: it creates anIntegerobject holding 12, andlapCountholds a reference to it.lapsis unchanged; it still holds its own 12. - Line 3 does the same with a
doubleliteral: aDoubleobject holding 4.75 is created, andpricerefers to it. - Lines 4 and 5 print the wrapper variables. Printing a wrapper object (or joining it to a string with
+) shows the number inside, not an address.
12
4.75The second situation is a method call. Here two methods have wrapper parameters. Watch lines 6, 7, and 8.
public class AutoboxParameter
{
public static void main(String[] args)
{
int adaPoints = 15;
printBadge("Ada", adaPoints);
printBadge("Grace", 9);
printAverage("Ada", 87.5);
}
public static void printBadge(String name, Integer points)
{
System.out.println(name + " has " + points + " points");
}
public static void printAverage(String name, Double average)
{
System.out.println(name + " averages " + average);
}
}Ada has 15 points
Grace has 9 points
Ada averages 87.5
On line 6, the argument adaPoints is an int, but the parameter points on line 11 is an Integer. The compiler autoboxes the value 15 into a new Integer object, and points starts out referring to it. Line 7 shows that the argument can be a literal: 9 is boxed the same way. Line 8 boxes the double 87.5 into a Double for the parameter average. Inside the methods, line 13 and line 18 join the wrapper values into a string, which shows their numbers.
You may see older code that builds a wrapper with new Integer(12). That constructor is deprecated (marked for removal) in current Java and is not used on the AP exam; let autoboxing create wrapper objects instead.
Watch out
Boxing into the wrong wrapper. In Topic 1.5 you learned that Java happily widens an int to a double. So students expect an int to fit into a Double too:
public class BoxWrongType
{
public static void main(String[] args)
{
Double price = 5;
printPrice("pencil", 2);
}
public static void printPrice(String item, Double price)
{
System.out.println(item + " costs " + price);
}
}This does not compile. Java reports (wording can vary between versions):
BoxWrongType.java:5: error: incompatible types: int cannot be converted to Double
BoxWrongType.java:6: error: incompatible types: int cannot be converted to DoubleAutoboxing only goes to the matching wrapper: int to Integer, double to Double. The compiler will not widen and box in the same step, so 5 (an int) cannot become a Double. The fix is to supply a double value: write Double price = 5.0; and printPrice("pencil", 2.0);. Then the program compiles, and the call prints pencil costs 2.0. The same rule blocks the other direction: a double such as 7.5 cannot be boxed into an Integer.
Pause and try
Question 3 · Multiple choice
Which declaration compiles?
Double total = 10;Integer count = 3.0;Double average = 10.0;Integer score = 3.5;
Check your reasoning
C. 10.0 is a double, and a double autoboxes into its matching wrapper, Double.
- A is wrong:
10is anint, and anintboxes only into anInteger. Java reportsint cannot be converted to Double. This comes from expecting the Topic 1.5 widening rule to apply to wrappers. - B is wrong:
3.0is adoubleeven though its value is whole, and adoublecannot be boxed into anInteger. - D is wrong for the same reason as B: Java reports
double cannot be converted to Integer.
public class BoxOptions
{
public static void main(String[] args)
{
Double total = 10;
Integer count = 3.0;
Double average = 10.0;
Integer score = 3.5;
}
}Compiling all four together reports errors on lines 5, 6, and 8 only.
Reread 4.7.2 Autoboxing if you chose differently.
Question 4 · Short answer
In AutoboxParameter, line 7 calls printBadge("Grace", 9). The parameter points has type Integer, but 9 is an int. Explain what the compiler does so that this call works.
Check your reasoning
Model answer: Because the parameter’s type is Integer, the wrapper class that matches int, the compiler autoboxes the argument: it adds code that creates an Integer object holding 9. The parameter points then refers to that object, and line 13 prints Grace has 9 points.
A complete answer says that the conversion is automatic (no new in the code), that it happens because the value is passed to a parameter of the matching wrapper type, and that the parameter refers to a new Integer object.
Reread 4.7.2 Autoboxing if your answer differed.
4.7.3 Unboxing
Autoboxing goes from a number to an object. The reverse is just as common. You have an Integer and you want to do math with it, store it in an int, or pass it to a method such as Math.abs that expects a plain int. Opening the envelope by hand would be tedious, so the compiler does it for you.
Unboxing is the automatic conversion from a wrapper object to its primitive value: an Integer to an int, and a Double to a double. It mirrors autoboxing, and the compiler applies it when a wrapper object is:
- assigned to a variable of the matching primitive type, or
- passed as an argument to a method whose parameter has the matching primitive type.
Java also unboxes a wrapper whenever you use it in arithmetic or a comparison such as <, because those operators work on primitive values.
Key idea
Unboxing is the compiler automatically taking the int out of an Integer, or the double out of a Double, when the object is assigned to a primitive variable or passed to a primitive parameter.
Worked example: Unboxing by assignment
Each wrapper variable here is used where a primitive is expected.
Integer stored = 40;
int copy = stored;
int doubled = stored * 2;
Double rate = 1.5;
double rateCopy = rate;
System.out.println(copy);
System.out.println(doubled);
System.out.println(rateCopy);| Line | What happens | New variable |
|---|---|---|
| 1 | 40 is autoboxed; stored refers to an Integer holding 40 |
|
| 2 | stored is assigned to an int, so it is unboxed to 40 |
copy is 40 |
| 3 | * needs a primitive, so stored is unboxed; 40 × 2 is 80 |
doubled is 80 |
| 4 | 1.5 is autoboxed into a Double |
|
| 5 | rate is assigned to a double, so it is unboxed to 1.5 |
rateCopy is 1.5 |
40
80
1.5Now the second situation, method parameters, using Math methods from Topic 1.11:
Integer change = -8;
System.out.println(Math.abs(change));
Double area = 49.0;
System.out.println(Math.sqrt(area));8
7.0
Math.abs has a version with an int parameter. change is an Integer, so on line 2 it is unboxed to −8, and Math.abs returns 8. On line 4, Math.sqrt expects a double; area is unboxed to 49.0, and the square root is 7.0.
How do unboxing and the Topic 1.5 widening rule combine? Java can unbox and then widen, but it never narrows:
| Code | Compiles? | Why |
|---|---|---|
Integer count = 7; int n = count; |
yes | Unboxing to the matching type, int |
Integer count = 7; double d = count; |
yes, d is 7.0 |
Unbox to int 7, then widen to double (safe) |
Double rating = 4.0; int stars = rating; |
no | Unbox to double, but a double never narrows to int without a cast |
The second row prints 7.0:
Integer count = 7;
double asDouble = count;
System.out.println(asDouble);The third row gives the compile error incompatible types: Double cannot be converted to int, for the same reason int stars = 4.0; fails in Topic 1.5.
Watch out
Unboxing null. An Integer variable can hold null, for instance when a score has not been recorded yet. Unboxing needs an object to take the number out of:
Integer bestScore = null;
System.out.println("Looking up best score");
int score = bestScore;
System.out.println(score);Line 2 prints, and then line 3 crashes:
Looking up best score
Exception in thread "main" java.lang.NullPointerException: Cannot invoke "java.lang.Integer.intValue()" because "<local1>" is null(The exact message varies between Java versions.) Line 3 looks like an ordinary assignment, so students do not expect a crash there. But to unbox, Java calls a method on the Integer object to get its value, and calling a method through null throws a NullPointerException, just as in Topic 1.14. The compiler cannot catch this, because the type is correct; the problem is the value at run time. The fix is to make sure the variable refers to an object before it is unboxed, for example by checking first:
Integer bestScore = null;
System.out.println("Looking up best score");
if (bestScore != null)
{
int score = bestScore;
System.out.println(score);
}
else
{
System.out.println("No score yet");
}Looking up best score
No score yetPause and try
Question 5 · Tracing
Write the exact output.
Integer start = -6;
Integer end = 4;
int distance = Math.abs(start) + end;
Double scale = 2.5;
double scaled = scale * distance;
System.out.println(distance);
System.out.println(scaled);Check your reasoning
10
25.0| Line | What happens | Value |
|---|---|---|
| 3 | start unboxes to −6 for Math.abs, which returns 6; end unboxes to 4 for + |
distance is 10 |
| 5 | scale unboxes to 2.5; 2.5 × 10 is a double |
scaled is 25.0 |
scaled is a double, so it prints with a decimal point: 25.0.
Reread 4.7.3 Unboxing if your output differed.
Question 6 · Debugging
This program prints Base: 50 and then crashes with a NullPointerException on line 8. Explain why and correct line 5 so that the program prints a total of 50.
public class BonusNull
{
public static void main(String[] args)
{
Integer bonus = null;
int base = 50;
System.out.println("Base: " + base);
int total = base + bonus;
System.out.println("Total: " + total);
}
}Check your reasoning
bonus is an Integer that holds null. On line 8, base + bonus needs a primitive value, so Java tries to unbox bonus. There is no object to take a number from, so Java throws a NullPointerException. The compiler allowed it because Integer is a correct type for +; only the value is wrong.
Corrected line 5:
Integer bonus = 0;Now bonus refers to an Integer holding 0, line 8 unboxes it, and the program prints Base: 50 and Total: 50. (Declaring bonus as an int would also work, because an int can never be null.)
Reread 4.7.3 Unboxing if your fix differed.
4.7.4 Wrapper Objects Are Immutable
In Topic 3.6, a method that received a Player could call addPoints and the caller saw the change. Can the same thing happen with an Integer? Is there any way to change the number inside an Integer object?
There is not. An Integer object is immutable: once it is created, its value can never change. The same is true for every Double object. You saw this idea with String in Topic 1.15. Neither wrapper class has a single method that changes the stored number; the envelope is sealed for good.
But then how does visits = visits + 1; work when visits is an Integer? Break it into steps:
visitsis unboxed to itsintvalue.- 1 is added, giving a new
int. - That
intis autoboxed into a brand-newIntegerobject. visitsis changed to refer to the new object.
The old object is not changed. The variable just moves to a different object.
Key idea
An Integer or Double object can never change. Code that seems to change a wrapper variable really builds a new object and makes the variable refer to it.
Worked example: An alias that does not follow along
Two Integer variables start out referring to the same object.
Integer visits = 3;
Integer saved = visits;
visits = visits + 1;
System.out.println(visits);
System.out.println(saved);- Line 1: an
Integerholding 3 is created (call it object A), andvisitsrefers to it. - Line 2:
savedgets a copy of the reference, sovisitsandsavedare aliases of object A (Topic 2.6). - Line 3:
visits + 1unboxes A’s 3 and adds 1. The result, 4, is boxed into a new object B, andvisitsnow refers to B.
| After line | visits refers to |
saved refers to |
|---|---|---|
| 2 | A (3) | A (3) |
| 3 | B (4) | A (3) |
4
3Compare this with a mutable Player from Topic 3.6: if two variables referred to the same Player and you called addPoints through one, both would see the change. With an Integer there is no mutator, so the only way to get 4 is a new object, and saved keeps the old one.
The same reasoning explains what happens when an Integer is passed to a method:
public class IntegerParameter
{
public static void main(String[] args)
{
Integer score = 10;
addBonus(score);
System.out.println("Back in main: " + score);
}
public static void addBonus(Integer points)
{
points = points + 5;
System.out.println("Inside addBonus: " + points);
}
}Inside addBonus: 15
Back in main: 10
On line 6, points receives a copy of the reference to the Integer holding 10. Line 12 cannot change that object, so it builds a new Integer holding 15 and stores its reference in points. As Topic 3.6 showed, assigning to a parameter changes only the parameter. score in main still refers to the object holding 10.
Watch out
Expecting a method to update the caller’s Integer. Because an Integer is an object, students sometimes think it behaves like a Player and that addBonus(score) will change score. It cannot: the object is immutable, and the parameter is a separate variable. If a method needs to produce a new number, have it return the value and let the caller store it:
public static Integer addBonus(Integer points)
{
return points + 5;
}With the call written as score = addBonus(score);, the program prints Back in main: 15.
Comparing wrapper values
Because wrapper variables hold references, take care with ==. When one side of == is a primitive int, Java unboxes the Integer and compares the numbers. When both sides are Integer objects, == compares references, exactly like == on two String variables in Topic 2.6: it asks whether they are the same object, not whether they hold the same number. Whether two Integer variables holding equal numbers share one object depends on how Java decided to create the objects, which you cannot see in the code, so == between two wrapper objects is not a reliable test of their values. Compare the values instead, either with equals (as with strings in Topic 1.15) or by unboxing into int variables first:
Integer stored = 1000;
int goal = 1000;
System.out.println(stored == goal);
Integer first = 1000;
Integer second = 1000;
System.out.println(first.equals(second));
int firstValue = first;
int secondValue = second;
System.out.println(firstValue == secondValue);true
true
true
Line 3 compares an Integer with an int, so stored is unboxed. Line 6 uses equals to compare two Integer objects by value. Lines 7 and 8 unbox both objects, so line 9 compares two int values.
Pause and try
Question 7 · Tracing
Write the exact output.
public class ImmutableTrace
{
public static void main(String[] args)
{
Integer tickets = 8;
Integer backup = tickets;
tickets = tickets - 3;
sellOne(tickets);
System.out.println(tickets);
System.out.println(backup);
}
public static void sellOne(Integer count)
{
count = count - 1;
System.out.println(count);
}
}Check your reasoning
4
5
8| Line | What happens | tickets |
backup |
count |
|---|---|---|---|---|
| 5 | Integer holding 8 created |
8 | ||
| 6 | backup becomes an alias of the same object |
8 | 8 | |
| 7 | 8 − 3 is boxed into a new object; only tickets moves to it |
5 | 8 | |
| 8 | count gets a copy of tickets’s reference |
5 | 8 | 5 |
| 15 | 5 − 1 is boxed into a new object; only count moves |
5 | 8 | 4 |
| 16 | prints 4 |
5 | 8 | 4 |
| 9–10 | prints 5, then 8 |
5 | 8 |
Neither line 7 nor line 15 changes an existing object, so backup keeps 8 and the method cannot change tickets.
Reread 4.7.4 Wrapper Objects Are Immutable if your output differed.
Question 8 · Multiple choice
In the alias example, saved still prints 3 after visits = visits + 1;. Which explanation is correct?
- Line 2 made
saveda separate copy of theIntegerobject, so it was never connected tovisits. visits + 1built a newIntegerobject forvisits; the objectsavedrefers to was never changed.- Java changed the shared object to 4, but
savedremembers the value it had when it was assigned. - Wrapper variables hold primitive values, not references, so assigning one to another copies the number.
Check your reasoning
B. Integer objects are immutable. Line 3 unboxes, adds, and boxes the result into a new object, then moves only visits to it. saved still refers to the original object holding 3.
- A is wrong: assigning one reference variable to another copies the reference, not the object. Right after line 2, both variables refer to the same object.
- C is wrong: an
Integerobject can never be changed, and a reference variable does not “remember” old values; it shows whatever its object holds. - D is wrong:
Integeris a class type, so its variables hold references. That is why they can also holdnull.
Reread 4.7.4 Wrapper Objects Are Immutable if you chose differently.
4.7.5 Turning Strings into Numbers
In Topic 4.6 you read lines from a text file and used split to break each line into pieces. Every piece is a String, even when it looks like a number. If a line says Ada,12,88.5, the piece "12" is two characters, not the number twelve. You cannot do math with it: as Topic 1.15 showed, + with strings joins them.
String first = "12";
String second = "30";
System.out.println(first + second);
int sum = Integer.parseInt(first) + Integer.parseInt(second);
System.out.println(sum);1230
42
Line 3 joins "12" and "30" into "1230". Line 4 first converts each string to an int, so + adds: 12 + 30 is 42.
The conversion on line 4 is done by a static method of the Integer class:
public static int parseInt(String s)
Integer.parseInt takes a String that spells a whole number and returns that number as an int. The Double class has a partner method:
public static double parseDouble(String s)
Double.parseDouble takes a String that spells a number and returns it as a double. Both are class methods (Topic 3.7), so you call them through the class name, Integer.parseInt(...) and Double.parseDouble(...), never through an object. Use them whenever a number arrives as text: a piece of a file line, a word from a Scanner’s next(), or any other String.
Key idea
Integer.parseInt turns a String such as “12” into the int 12, and Double.parseDouble turns a String such as “88.5” into the double 88.5. Use them whenever numbers arrive as text.
Worked example: Taking apart a record
One line of a file holds a player’s name, goals, and shooting percentage. Watch the type of each piece.
String line = "Ada,12,88.5";
String[] parts = line.split(",");
String name = parts[0];
int goals = Integer.parseInt(parts[1]);
double percent = Double.parseDouble(parts[2]);
System.out.println(name + " next season: " + (goals + 1));
System.out.println(percent / 2);| Line | Expression | Type | Value |
|---|---|---|---|
| 2 | parts[0], parts[1], parts[2] |
String |
"Ada", "12", "88.5" |
| 4 | Integer.parseInt(parts[1]) |
int |
12 |
| 5 | Double.parseDouble(parts[2]) |
double |
88.5 |
| 6 | goals + 1 |
int |
13 |
| 7 | percent / 2 |
double |
44.25 |
The parentheses around goals + 1 on line 6 make Java add the numbers before joining the result to the string.
Ada next season: 13
44.25Now vary the text. parseDouble accepts a whole number, and parseInt accepts a minus sign:
double price = Double.parseDouble("90");
int count = Integer.parseInt("-5");
System.out.println(price);
System.out.println(count);90.0
-5
Double.parseDouble("90") returns the double 90.0, which prints with a decimal point. Integer.parseInt("-5") returns the negative int −5.
Watch out
Asking parseInt for something that is not a whole number. Hours worked are often written with a decimal, and it is easy to reach for parseInt out of habit:
String hours = "7.5";
System.out.println("Reading hours");
int worked = Integer.parseInt(hours);
System.out.println(worked);The program prints Reading hours, then crashes on line 3:
Exception in thread "main" java.lang.NumberFormatException: For input string: "7.5"(The wording can vary between Java versions.) parseInt does not round or truncate the way a cast does in Topic 1.5. If the string is not exactly a whole number, it throws a NumberFormatException at run time. The compiler cannot catch this, because any String is a legal argument; the problem is the text inside it. The same crash happens for text with a space in it, such as " 14", or a word such as "twelve". The fix is to use the method that matches the text: double worked = Double.parseDouble(hours); prints 7.5.
Pause and try
Question 9 · Tracing
Write the exact output.
String[] cells = {"8", "15", "2.5"};
System.out.println(cells[0] + cells[1]);
int total = Integer.parseInt(cells[0]) + Integer.parseInt(cells[1]);
System.out.println(total);
double result = total * Double.parseDouble(cells[2]);
System.out.println(result);Check your reasoning
815
23
57.5- Line 2 joins two strings:
"8" + "15"is"815". - Line 3 converts both to
intfirst: 8 + 15 is 23. - Line 5:
Double.parseDouble("2.5")is 2.5, and 23 × 2.5 is 57.5.
Reread 4.7.5 Turning Strings into Numbers if your output differed.
Question 10 · Debugging
This program prints Player: Lee and then crashes on line 8 with NumberFormatException: For input string: " 14". Explain the error and correct line 6.
public class SplitSpace
{
public static void main(String[] args)
{
String line = "Lee, 14";
String[] parts = line.split(",");
System.out.println("Player: " + parts[0]);
int points = Integer.parseInt(parts[1]);
System.out.println("Points: " + points);
}
}Check your reasoning
The line has a space after the comma. Splitting on "," alone gives parts[0] as "Lee" and parts[1] as " 14", with the space still attached. parseInt accepts only the characters of a whole number, so the space makes it throw a NumberFormatException.
Corrected line 6:
String[] parts = line.split(", ");Splitting on a comma followed by a space leaves parts[1] as "14", and the program prints Player: Lee and Points: 14.
Reread 4.7.5 Turning Strings into Numbers if your fix differed.
Putting it together
A weather station saves one line per day in weather.txt: the day, the overnight low in whole degrees, and the rainfall in inches.
Mon,-3,0.5
Tue,4,1.25
Wed,-7,0.0
Thu,2,2.75
This program finds the warmest night, adds up the rain, and prints each low through a method with an Integer parameter.
import java.io.File;
import java.io.IOException;
import java.util.Scanner;
public class WeatherLog
{
public static void main(String[] args) throws IOException
{
Scanner file = new Scanner(new File("weather.txt"));
int warmestLow = Integer.MIN_VALUE;
String warmestDay = "";
double totalRain = 0.0;
while (file.hasNext())
{
String[] parts = file.nextLine().split(",");
int low = Integer.parseInt(parts[1]);
double rain = Double.parseDouble(parts[2]);
printLow(parts[0], low);
if (low > warmestLow)
{
warmestLow = low;
warmestDay = parts[0];
}
totalRain += rain;
}
file.close();
System.out.println("Warmest night: " + warmestDay);
System.out.println("Total rain: " + totalRain);
}
public static void printLow(String day, Integer low)
{
if (low < 0)
{
System.out.println(day + ": " + Math.abs(low) + " below zero");
}
else
{
System.out.println(day + ": " + low + " above zero");
}
}
}Here is where each idea from the chapter appears:
| Line(s) | Idea |
|---|---|
| 10 | Integer.MIN_VALUE is the starting maximum, so even a night below zero can be the warmest (4.7.1) |
| 15–17 | Each piece of the line is a String; parseInt and parseDouble turn them into numbers (4.7.5) |
| 18, 31 | The int low is passed to an Integer parameter, so it is autoboxed (4.7.2) |
| 33, 35 | low < 0 unboxes low for the comparison; Math.abs(low) unboxes it for an int parameter (4.7.3) |
| 39 | Joining the Integer low to a string shows its number |
Tracing the loop:
| Line read | low |
rain |
Printed by printLow |
warmestLow |
warmestDay |
totalRain |
|---|---|---|---|---|---|---|
| (start) | −2147483648 | "" |
0.0 | |||
Mon,-3,0.5 |
−3 | 0.5 | Mon: 3 below zero |
−3 | Mon |
0.5 |
Tue,4,1.25 |
4 | 1.25 | Tue: 4 above zero |
4 | Tue |
1.75 |
Wed,-7,0.0 |
−7 | 0.0 | Wed: 7 below zero |
4 | Tue |
1.75 |
Thu,2,2.75 |
2 | 2.75 | Thu: 2 above zero |
4 | Tue |
4.5 |
Mon: 3 below zero
Tue: 4 above zero
Wed: 7 below zero
Thu: 2 above zero
Warmest night: Tue
Total rain: 4.5
Without parseInt and parseDouble, the program would have only strings, which cannot be compared with > or added as numbers. Without Integer.MIN_VALUE, a starting value of 0 would have worked for this file only by luck (Tuesday’s low happens to be positive); a week where every night was below zero would report 0.
Common mistakes
Every mistake in this chapter shows up as a message. Use this table to look one up.
| Message | Usual cause | Fix |
|---|---|---|
int cannot be converted to Double (compile) |
Boxing an int into a Double, such as Double price = 5; |
Use a double value (5.0), or an Integer variable |
double cannot be converted to Integer (compile) |
Boxing a double into an Integer |
Use a Double variable, or an int value |
Double cannot be converted to int (compile) |
Unboxing a Double into an int, which would narrow |
Store it in a double |
NullPointerException (run time) |
Unboxing a wrapper variable that holds null |
Make sure it refers to an object first |
NumberFormatException (run time) |
parseInt or parseDouble given text that is not a number (decimal point for parseInt, spaces, words) |
Use the matching method, and split so pieces have no extra spaces |
Vocabulary
| Term | Definition | Example |
|---|---|---|
| wrapper class | A class whose objects each hold one primitive value, so the value can be used where an object is needed | Integer for int, Double for double |
java.lang package |
The group of classes available to every program without an import | Integer, Double, String, Math |
| autoboxing | The compiler’s automatic conversion of an int to an Integer or a double to a Double |
Integer count = 12; |
| unboxing | The compiler’s automatic conversion of an Integer to an int or a Double to a double |
int n = count; |
| immutable (review) | Describes an object whose value can never change after it is created | An Integer holding 3 always holds 3 |
Integer.parseInt |
A class method that returns the int spelled by a String |
Integer.parseInt("12") is 12 |
Double.parseDouble |
A class method that returns the double spelled by a String |
Double.parseDouble("90") is 90.0 |
NumberFormatException |
The exception parseInt or parseDouble throws when the text is not a number of the right kind |
Integer.parseInt("7.5") |
Chapter summary
IntegerandDoubleare wrapper classes injava.lang: each object holds oneintordouble, so a number can go where only objects are allowed.Integer.MIN_VALUEandInteger.MAX_VALUEare class constants that make safe starting values for maximum and minimum algorithms.- Autoboxing turns an
intinto anIntegeror adoubleinto aDoublewhen the value is assigned to a wrapper variable or passed to a wrapper parameter. It only boxes into the matching wrapper. - Unboxing takes the primitive value out of a wrapper when the object is assigned to a primitive variable, passed to a primitive parameter, or used in arithmetic or a comparison. Unboxing
nullthrows aNullPointerException. - Wrapper objects are immutable. Arithmetic on a wrapper variable builds a new object, so aliases and the caller’s variables never see the change.
Integer.parseIntandDouble.parseDoubleconvert text into numbers; text that is not a number of the right kind throws aNumberFormatException.
Review questions
Question 11 · Multiple choice
Which statement is true?
- A method with an
Integerparameter can change the number inside the caller’sIntegerobject. - A program can call
Double.parseDoublewithout anyimportstatement. Integer.parseIntmust be called on anIntegerobject, such ascount.parseInt("5").- Autoboxing can turn an
intvalue into aDoubleobject.
Check your reasoning
B. Double is in the java.lang package, which every program can use without importing it.
- A is wrong:
Integerobjects are immutable. A method can only make its own parameter refer to a new object, which the caller never sees. - C is wrong:
parseIntis astatic(class) method, so it is called through the class name,Integer.parseInt("5"). - D is wrong: autoboxing goes only to the matching wrapper, so an
intbecomes anInteger, never aDouble.
Reread 4.7.1 The Integer and Double Classes, 4.7.2 Autoboxing, 4.7.4 Wrapper Objects Are Immutable, and 4.7.5 Turning Strings into Numbers for the choices you were unsure of.
Question 12 · Multiple choice
The program prints 81 and 40.5. Which description of the conversions on lines 6, 7, and 8 is correct?
public class ConversionSpot
{
public static void main(String[] args)
{
int raw = 9;
Integer boxed = raw;
int squared = square(boxed);
Double half = squared / 2.0;
System.out.println(squared);
System.out.println(half);
}
public static int square(int value)
{
return value * value;
}
}- Line 6 autoboxes; line 7 unboxes; line 8 autoboxes.
- Line 6 unboxes; line 7 autoboxes; line 8 unboxes.
- Line 6 autoboxes; line 7 autoboxes; line 8 does neither.
- Only line 6 converts; lines 7 and 8 do neither.
Check your reasoning
A.
- Line 6 assigns the
intrawto anIntegervariable: autoboxing. - Line 7 passes the
Integerboxedtosquare, whose parameter on line 13 is anint: unboxing. The method returns theint81. - Line 8 computes
81 / 2.0, adouble40.5, and assigns it to aDoublevariable: autoboxing.
The other choices:
- B is wrong: it reverses every direction. Boxing goes from primitive to object; unboxing goes from object to primitive.
- C is wrong: line 7 sends an object to a primitive parameter, which is unboxing, and line 8 does box a
doubleinto aDouble. - D is wrong: a conversion happens every time a value crosses between a primitive type and its wrapper, including at a method parameter.
Reread 4.7.2 Autoboxing and 4.7.3 Unboxing if you chose differently.
Question 13 · Tracing
The values arrive as strings. Write the exact output.
String[] entries = {"-4", "-9", "-1", "-6"};
System.out.println(entries[0] + entries[1]);
int highest = Integer.MIN_VALUE;
for (String entry : entries)
{
int value = Integer.parseInt(entry);
if (value > highest)
{
highest = value;
}
}
System.out.println(highest);Check your reasoning
-4-9
-1Line 2 joins two strings: "-4" followed by "-9" is "-4-9". No subtraction happens, because the operands are strings.
entry |
value |
value > highest? |
highest afterward |
|---|---|---|---|
| (start) | −2147483648 | ||
"-4" |
−4 | yes | −4 |
"-9" |
−9 | no | −4 |
"-1" |
−1 | yes | −1 |
"-6" |
−6 | no | −1 |
Every value is negative, so starting at Integer.MIN_VALUE matters; a start of 0 would print 0.
Reread 4.7.5 Turning Strings into Numbers and 4.7.1 The Integer and Double Classes if your output differed.
Question 14 · Debugging
This program does not compile. Java reports incompatible types: double cannot be converted to Integer for line 7. Explain the error and correct line 7.
public class HoursBug
{
public static void main(String[] args)
{
String line = "Lee,7.5";
String[] parts = line.split(",");
Integer hours = Double.parseDouble(parts[1]);
System.out.println(parts[0] + " worked " + hours);
}
}Check your reasoning
Double.parseDouble returns a double (7.5). Assigning it to an Integer variable would need autoboxing, but a double can be boxed only into its matching wrapper, Double.
Corrected line 7:
Double hours = Double.parseDouble(parts[1]);Now the double 7.5 is autoboxed into a Double, and the program prints Lee worked 7.5. (Declaring hours as a double would also work.) Do not “fix” it by switching to Integer.parseInt: that compiles, but "7.5" is not a whole number, so it would throw a NumberFormatException when the program runs.
Reread 4.7.2 Autoboxing and 4.7.5 Turning Strings into Numbers if your fix differed.
Question 15 · Short answer
In Topic 3.6, a method that received a Player could call addPoints and the caller’s player gained points. A method that receives an Integer can never make the caller’s Integer show a different number. Both parameters hold a copy of a reference. Explain why the results differ.
Check your reasoning
Model answer: In both cases the parameter refers to the caller’s object. A Player is mutable, so the method can call a mutator that changes that shared object, and the caller sees the change. An Integer is immutable: it has no method that changes its value. The only thing the method can do is compute a new number, which is boxed into a new Integer object and assigned to the parameter. Assigning to a parameter changes only the parameter, so the caller’s variable still refers to the original, unchanged object.
A complete answer includes:
- Both parameters hold a copy of the caller’s reference.
Playercan be changed through a mutator;Integeris immutable.- Arithmetic on the parameter creates a new object and changes only the parameter.
Reread 4.7.4 Wrapper Objects Are Immutable if your answer differed.
Question 16 · Multiple choice
What does this statement print?
System.out.println(Double.parseDouble("7") + Integer.parseInt("3"));10.010737.03
Check your reasoning
A. Double.parseDouble("7") returns the double 7.0 and Integer.parseInt("3") returns the int 3. Both operands are numbers, so + adds. A double plus an int is a double (Topic 1.5), so the result is 10.0.
- B is wrong: it forgets that one operand is a
double, which makes the sum adoublethat prints with a decimal point. - C is wrong: it treats the values as strings and joins them. After parsing, there are no strings left in the expression.
- D is wrong: it joins
"7.0"and"3"as text, which also assumes+is working on strings.
Reread 4.7.5 Turning Strings into Numbers if you chose differently.
Question 17 · Tracing
Write the exact output.
Integer total = 7;
Integer count = 2;
double average = total / count;
System.out.println(average);
double better = 1.0 * total / count;
System.out.println(better);Check your reasoning
3.0
3.5- Line 3:
total / countunboxes both tointvalues, 7 and 2. Dividing twointvalues truncates (Topic 1.3), so the result is theint3. Only then is it widened to thedouble3.0 foraverage. Wrapping the numbers in objects did not change howintdivision works. - Line 5:
1.0 * totalunboxestotaland multiplies it by adouble, giving 7.0. Then 7.0 / 2 is adoubledivision, 3.5.
Reread 4.7.3 Unboxing if your output differed.
Reflection
- I can explain how an Integer or Double object differs from an int or double, and why no import is needed. Reread
- I can choose Integer.MIN_VALUE or Integer.MAX_VALUE as the starting value for a maximum or minimum. Reread
- I can spot where Java autoboxes a primitive, and explain why an int cannot become a Double. Reread
- I can spot where Java unboxes a wrapper object, and explain why unboxing null crashes. Reread
- I can explain why a method or an alias never sees a change to an Integer or Double. Reread
- I can convert text to numbers with Integer.parseInt and Double.parseDouble, and predict a NumberFormatException. Reread