Skip to content

Lesson 02 · Numeric Values — Wrappers, Math & BigDecimal

Objectives

After this lesson you will be able to:

  • Use the wrapper classes and explain autoboxing/unboxing — including the null-unbox NPE and the Integer cache == trap.
  • Parse and format numbers (parseInt, valueOf, radix, String.format).
  • Predict integer division, modulo sign, and silent overflow, and use Math correctly.
  • Choose BigInteger / BigDecimal for exactness, and avoid the BigDecimal equals/double constructor traps.

Wrapper classes

Each primitive has an immutable wrapper class in java.lang that lets a number live as an object (so it can go into collections, generics, and null):

PrimitiveWrapperPrimitiveWrapper
byteBytelongLong
shortShortfloatFloat
intIntegerdoubleDouble
charCharacterbooleanBoolean

Wrappers expose useful constants and statics: Integer.MAX_VALUE, Integer.MIN_VALUE, Long.BYTES, Double.NaN, Integer.parseInt(...), Integer.compare(a, b).

Prefer the static valueOf over the deprecated constructors (new Integer(1) was deprecated for removal in Java 9) — valueOf can reuse cached objects, new never does.

Autoboxing & the Integer cache

Autoboxing converts a primitive to its wrapper automatically; unboxing does the reverse.

java
Integer boxed = 42;      // autobox: int → Integer  (Integer.valueOf(42))
int back = boxed;        // unbox:   Integer → int
List<Integer> xs = new ArrayList<>();
xs.add(7);               // autoboxed

Exam trap

Unboxing null throws NullPointerException — it tries to call .intValue() on null:

java
Integer i = null;
int x = i;               // NullPointerException at runtime (compiles fine)

Exam trap

Integer.valueOf caches boxed values from −128 to 127, so == (reference compare) is true in range and false outside it. Always compare values with .equals(), not ==.

java
Integer a = 127, b = 127;
a == b;                  // true   — same cached object
Integer c = 128, d = 128;
c == d;                  // false  — distinct objects, outside the cache
c.equals(d);             // true   — compare values

But if either side is a primitive, the wrapper unboxes and == compares numeric values:

java
Integer big = 1000;
big == 1000;             // true  — right side is int, so big is unboxed
What prints? — mixed ternary
java
Object o = true ? Integer.valueOf(1) : Double.valueOf(2.0);
System.out.println(o);

Answer: 1.0. A conditional with Integer and Double branches applies binary numeric promotion: both are unboxed and widened to double, so even the taken branch becomes 1.0 (re-boxed to a Double). The chosen value matters, but its type is decided by both branches.

Parsing & formatting

parseXxx returns a primitive; valueOf returns a wrapper. Bad input throws the unchecked NumberFormatException.

java
int n      = Integer.parseInt("42");        // 42  (primitive int)
Integer w  = Integer.valueOf("42");         // 42  (Integer)
int hex    = Integer.parseInt("ff", 16);    // 255 (radix/base argument)
double d   = Double.parseDouble("3.14");    // 3.14
Integer.parseInt("4.0");                     // NumberFormatException (not an int)

Format the other way with String.format / printf patterns, or the radix helpers:

java
String.format("%d", 42);          // "42"
String.format("%05d", 42);        // "00042"  (zero-padded width 5)
String.format("%,d", 1_000_000);  // "1,000,000"  (grouping)
String.format("%.2f", 3.14159);   // "3.14"   (2 decimals, rounded)
Integer.toBinaryString(10);       // "1010"
Integer.toHexString(255);         // "ff"

Integer arithmetic: division, modulo, overflow

Integer division truncates toward zero (drops the fraction); % is the remainder, and its sign follows the left operand (the dividend):

java
7 / 2;       //  3     (not 3.5 — integer division)
-7 / 2;      // -3     (truncates toward zero, not -4)
7 % 3;       //  1
-7 % 3;      // -1     (sign matches the dividend, -7)
7 % -3;      //  1
1 / 0;       // ArithmeticException: / by zero  (integer only)

Exam trap

int/long math overflows silently — it wraps around with no error:

java
int max = Integer.MAX_VALUE;     // 2_147_483_647
max + 1;                          // -2147483648  (Integer.MIN_VALUE) — wraps!
Math.abs(Integer.MIN_VALUE);      // still MIN_VALUE — no positive counterpart

Use the Math.*Exact methods to fail fast instead of wrapping:

java
Math.addExact(Integer.MAX_VALUE, 1);  // throws ArithmeticException

Math you should know: abs, max, min, pow (returns double), sqrt, ceil, floor, and round. Math.round returns a long for a double argument and rounds half up toward positive infinity:

java
Math.round(2.5);     // 3
Math.round(-2.5);    // -2    (half rounds toward +∞, so -2.5 → -2)
Math.floorDiv(-7, 2);// -4    (rounds toward -∞, unlike / )
Math.floorMod(-7, 2);//  1    (sign follows the divisor, unlike % )

Gotcha

Floating-point division does not throw on divide-by-zero. 1.0 / 0 is Infinity, -1.0 / 0 is -Infinity, and 0.0 / 0.0 is NaN. NaN is not equal to anything — even itself (Double.NaN == Double.NaN is false); test with Double.isNaN(x).

BigInteger & BigDecimal

When long overflows or you need exact decimals (money!), reach for the arbitrary-precision types in java.math. Both are immutable — every operation returns a new object.

java
BigInteger huge = BigInteger.valueOf(2).pow(100);   // 1267650600228229401496703205376
BigDecimal price = new BigDecimal("0.10")
        .add(new BigDecimal("0.20"));               // exactly 0.30
0.10 + 0.20;                                          // 0.30000000000000004  (double!)

Exam trap

Use the String BigDecimal constructor, not the double one — new BigDecimal(0.1) captures the binary approximation (0.1000000000000000055…), while new BigDecimal("0.1") is exact.

Exam trap

BigDecimal.equals compares value and scale, so different scales are unequal. Use compareTo for numeric equality:

java
new BigDecimal("1.0").equals(new BigDecimal("1.00"));      // false — scales 1 vs 2
new BigDecimal("1.0").compareTo(new BigDecimal("1.00"));   // 0     — equal value

Gotcha

divide throws ArithmeticException when the result is a non-terminating decimal — you must supply a scale and a RoundingMode: a.divide(b, 2, RoundingMode.HALF_UP).

SDET note

For money and any business assertion, compare with BigDecimal (via compareTo) and never with double — floating-point drift makes tests flaky and wrong by cents. When a test must round, state the RoundingMode explicitly so the expected value is unambiguous.

Key Takeaways

  • Each primitive has an immutable wrapper; prefer valueOf over new. Autoboxing is automatic, but unboxing null is an NPE.
  • The Integer cache (−128…127) makes == true only in range; compare wrapper values with .equals(). If either operand is primitive, the wrapper unboxes and == compares values.
  • parseInt → primitive, valueOf → wrapper; bad input → NumberFormatException. Format with String.format/radix helpers.
  • Integer division truncates toward zero; % sign follows the dividend; int math overflows silently (use Math.addExact); integer /0 throws, but float /0 gives Infinity/NaN.
  • Math.round returns long and rounds half toward +∞; floorDiv/floorMod differ from //% for negatives.
  • Use BigInteger/BigDecimal for range/exactness; build BigDecimal from a String, and compare with compareTo (not equals, which is scale-sensitive).

Lesson Quiz

Lesson Quiz · Numeric Values0 / 10
  1. What happens at runtime?

    Integer i = null;
    int x = i;
    • Ax is 0
    • Bx is null
    • CNullPointerException
    • DCompile error
  2. Which comparisons are TRUE? (select all) (select all that apply)

    Integer a = 127, b = 127;
    Integer c = 128, d = 128;
    Integer e = 1000;
    • Aa == b
    • Bc == d
    • Cc.equals(d)
    • De == 1000
  3. What is the value of -7 % 3 ?

    • A1
    • B-1
    • C2
    • D-2
  4. What does this print?

    int max = Integer.MAX_VALUE;
    System.out.println(max + 1);
    • A2147483648
    • B-2147483648
    • CArithmeticException
    • D0
  5. What is Math.round(-2.5) ?

    • A-3
    • B-2
    • C-2.5
    • D2
  6. What is the result of 7 / 2 ?

    • A3.5
    • B3
    • C4
    • D3.0
  7. Which throws an exception?

    • A1.0 / 0
    • B0.0 / 0.0
    • C1 / 0
    • DMath.sqrt(-1)
  8. What does Integer.parseInt("4.0") do?

    • AReturns 4
    • BReturns 4.0
    • CNumberFormatException
    • DReturns 0
  9. What does this evaluate to?

    new BigDecimal("1.0").equals(new BigDecimal("1.00"))
    • Atrue
    • Bfalse
    • CCompile error
    • DArithmeticException
  10. Which expression gives EXACTLY 0.3?

    • A0.1 + 0.2
    • Bnew BigDecimal(0.1).add(new BigDecimal(0.2))
    • Cnew BigDecimal("0.1").add(new BigDecimal("0.2"))
    • D(float)0.1 + (float)0.2

Next: Boolean & Conditions. Run the matching code in labs/src/main/java/com/jse21/m01_values/Numeric.java.