java-foundations Lesson 1 15 min read

Data Types Deep Dive

All eight primitives, memory, and the type system

Reading: Reges & Stepp: Ch. 2 §2.3–2.4

Quick check before you start: Can you declare variables of the four core types? Do you know when to use int vs double?

Practice this topic: Lab 1 exercises on type selection

After this lesson, you will be able to:

  • Name all eight primitive types and explain when to use each
  • Describe the memory trade-offs between integer types
  • Explain what integer overflow is and why it happens
  • Distinguish between primitive types and reference types
  • Choose the correct type for real-world data

Why Eight Types?

In the previous lesson you learned the four types that cover 99% of CS1: int, double, boolean, and char. But Java has eight primitive types. Why?

Different data has different needs. A person’s age (0-120) does not require the same memory as the number of atoms in the universe. A price needs decimal precision; a student ID does not.

By choosing the right type, you communicate your intent to other programmers, optimize memory, and prevent overflow errors.

From CSCD 110: Python had essentially two numeric types: int (unlimited size) and float. Java exposes the details: four integer types, two decimal types. This is part of Java’s explicit philosophy — you control everything, and the compiler verifies your choices.


The Complete Primitive Type Menu

Integer Types: Four Sizes

All four store whole numbers, differing only in size and range:

Type Size Range When to Use
byte 1 byte -128 to 127 Rare in CS1. Tiny data like file bytes.
short 2 bytes -32,768 to 32,767 Rare in CS1. Compact integers.
int 4 bytes ±2.1 billion Default choice.
long 8 bytes ±9.2 quintillion Very large numbers.
public class Types {
    public static void main(String[] args) {
        byte smallCount = 100;
        short mediumCount = 30000;
        int studentCount = 150;
        long worldPopulation = 8_000_000_000L;  // L suffix required!
    }
}

Notice the L suffix on long literals. Without it, Java treats the number as an int, and 8 billion overflows.

Key insight: Default to int. Use long only when values exceed ±2.1 billion (timestamps, file sizes, population counts).

Floating-Point Types: Two Levels of Precision

Type Size Precision When to Use
float 4 bytes ~7 digits Rare. Graphics, memory-constrained.
double 8 bytes ~15 digits Default choice.
public class Floats {
    public static void main(String[] args) {
        float approximatePi = 3.14159f;     // f suffix required
        double precisePi = 3.141592653589793;
    }
}

A float literal requires the f suffix. Without it, Java treats it as a double.

Key insight: Default to double. The name means “double-precision.” You will not need float in this course.

Boolean and Char (Review)

Type Size Description
boolean 1 bit* Only true or false. Not an integer.
char 2 bytes One Unicode character in single quotes: 'A'

All Eight at a Glance

Integer Types:     byte → short → int → long
                   (1B)   (2B)   (4B)  (8B)

Floating-Point:    float → double
                   (4B)   (8B)

Other:             boolean    char

Shaded/italic types are your everyday choices in CS1.


Numeric Literal Suffixes

Java needs to know the type of every literal:

  • Whole numbers (like 42) are int by default
  • Decimals (like 3.14) are double by default
Suffix Type Example
L long 8_000_000_000L
f float 3.14f
(none) int / double 42, 3.14

You can use underscores for readability — the compiler ignores them:

int population = 8_000_000;           // easier to read than 8000000
long bigNumber = 9_223_372_036_854L;

Watch out: Always use uppercase L. The lowercase l looks like the digit 1 in many fonts.


Check Your Understanding
What happens if you forget the L suffix when assigning a large number to a long?
AIt works fine — Java figures it out
BCompiler error — the literal is too large for int
CIt automatically becomes a long
DIt compiles but runs incorrectly
Answer: B. By default, whole-number literals are int. If the value exceeds int's range (±2.1 billion), you need the L suffix to make it a long literal.

Integer Overflow: When Numbers Wrap Around

What happens when you exceed the maximum value of a type?

public class Overflow {
    public static void main(String[] args) {
        int maxInt = Integer.MAX_VALUE;  // 2,147,483,647
        System.out.println("Max int: " + maxInt);
        System.out.println("Max int + 1: " + (maxInt + 1));
    }
}

Output:

Max int: 2147483647
Max int + 1: -2147483648

Adding 1 to the maximum int does not cause an error. The value silently wraps around to the most negative int. This is integer overflow — one of the most insidious bugs because it happens silently. No crash, no error message, just a wrong answer.

The trick: Think of an odometer. When it hits 999,999, the next mile rolls to 000,000. Java’s int works the same way. The fix? Use a larger type.

long bigValue = (long) Integer.MAX_VALUE + 1;
System.out.println(bigValue);  // prints 2147483648 (correct!)

Floating-Point Precision: Not Quite Perfect

Floating-point numbers have a different limitation: precision. They cannot represent every decimal exactly.

public class Precision {
    public static void main(String[] args) {
        double result = 0.1 + 0.2;
        System.out.println(result);  // prints: 0.30000000000000004
    }
}

Wait — $0.1 + 0.2$ is not exactly $0.3$? Correct. This is not a Java bug. It is a fundamental limitation of binary floating-point representation. The number $0.1$ cannot be represented exactly in binary, just as $\frac{1}{3}$ cannot be represented exactly in decimal.

For CS1, this tiny error is usually insignificant. But be aware:

Watch out: Never compare floating-point numbers with ==. Because of precision errors, 0.1 + 0.2 == 0.3 evaluates to false.


Primitive Types vs. Reference Types

Java has two fundamentally different categories:

Primitive types store the value directly in the variable:

int age = 25;  // age contains the actual number 25

Reference types store a reference (a pointer) to data stored elsewhere:

String name = "Alice";  // name contains a reference to the String object
Category Types Naming Storage
Primitives 8 types: byte, short, int, long, float, double, boolean, char Lowercase Value directly
References String, Scanner, arrays, every class Uppercase (PascalCase) Reference to object

Key insight: Lowercase first letter = primitive. Uppercase = reference. This naming convention tells you instantly what category you are looking at. We will explore what “reference” really means in Week 9 when we study objects.


Default Values: Fields vs. Local Variables

Local variables (inside a method) have no default value. You must initialize them:

public class LocalVar {
    public static void main(String[] args) {
        int count;
        System.out.println(count);  // COMPILER ERROR: not initialized
    }
}

Fields (inside a class, outside any method) receive default values:

class Student {
    int age;          // default: 0
    double gpa;       // default: 0.0
    boolean enrolled; // default: false
    char grade;       // default: '\u0000' (null character)
    String name;      // default: null (no object)
}

For the next several weeks, every variable you write is a local variable. Initialize everything when you declare it.


Choosing the Right Type

Here is a decision guide for real-world data:

Data Type Why
Student’s age int Whole number
Room temperature double Has decimals
Passed exam? boolean Yes/no
Letter grade char Single character
Zip code String Leading zeros matter!
Annual salary double Money has decimals
World population long Exceeds 2.1 billion
Phone number String Dashes, parentheses
Number of credits int Small whole number
Checkbox checked? boolean On/off

The trick: If you would never do arithmetic on it (you would never add two zip codes), it is a String, not a number.


What you learned

  • Java has eight primitive types. Four integers (byte, short, int, long), two decimals (float, double), plus boolean and char.
  • Default to int and double. Use the others only when you have a specific reason.
  • Literal suffixes matter. L for long, f for float. Underscores improve readability.
  • Integer overflow wraps silently. Every type has a maximum. Choose a type large enough for your data.
  • Floating-point precision is limited. Never compare doubles with ==.
  • Primitives store values; references store addresses. Lowercase = primitive. Uppercase = reference.
  • Local variables must be initialized. No default value.

What Comes Next

Next: Scanner, integer division, % (modulus), and the nextInt()/nextLine() buffer trap.