Course outline · 0% complete

0/29 lessons0%

Course overview →

Primitive types

lesson 2-1 · ~7 min · 4/29

From lesson 1-2, declaring int year = 1995; fixes the type of year permanently. A later line saying year = "hello" is rejected at compile time, and the program never runs.

A variable's declared type cannot change. Assigning text to an int is a type error, and lesson 1-2 showed that javac catches type errors before execution begins.

This lesson looks at what the available types actually are, and why choosing between them is a real decision.

The primitives

In Python every number is a flexible object. Java instead offers fixed-size boxes and asks you to pick one. The payoff is speed and predictability, because an int is always exactly 32 bits, so the compiler knows precisely how much memory a million of them need and exactly which CPU instruction adds them.

Choosing the type is a real engineering decision. Pick a box too small and values overflow, which has happened to databases full of user IDs at real companies.

Java has eight built-in primitive types, simple values stored directly in memory. Four cover almost everything you write:

TypeHoldsExample
intwhole numbers up to about ±2.1 billionint age = 21
doubledecimal numbersdouble price = 3.5
booleantrue or falseboolean open = true
charone character, in single quoteschar grade = 'A'

The others are long for huge whole numbers, written with an L suffix as in 9000000000L, short and byte for small numbers, both rare, and float, a less precise decimal that is also rare.

Declare once with the type, then reassign without it. Names use camelCase by convention, so totalScore rather than total_score.

One variable of each core type

Four declarations and four labeled lines of output.

public class Main {
  public static void main(String[] args) {
    int age = 21;
    double price = 3.5;
    boolean open = true;
    char grade = 'A';
    System.out.println("age: " + age);
    System.out.println("price: " + price);
    System.out.println("open: " + open);
    System.out.println("grade: " + grade);
  }
}

Output

age: 21
price: 3.5
open: true
grade: A

Notice two details in the output. The double prints as 3.5 rather than 3.50, since Java shows the shortest form that round-trips, and the boolean prints as lowercase true, unlike Python's True.

The char uses single quotes in the source and prints as a bare letter. Writing "A" with double quotes would be a String, which is a different type entirely and the subject of the next lesson.

Reassignment and inference

Once declared, a variable can take new values of the same type:

int score = 10;
score = 25;      // fine, still an int

The type appears only on the first line. Repeating it later is not a reassignment but a second declaration of the same name in one scope, which the compiler rejects.

Java also accepts var score = 10;, inferring the type from the right side, like Python but locked in at compile time. This course writes the explicit type so the type is always visible while you are learning to read it.

Two differences from Python are worth noting. True and False become lowercase true and false, and single quotes are only for char, never for strings.

Three declarations and three lines

The declaration pattern is the type, the name, then the value, as in int steps = 8500.

public class Main {
  public static void main(String[] args) {
    int steps = 8500;
    double miles = 4.2;
    boolean goalMet = true;
    System.out.println("steps: " + steps);
    System.out.println("miles: " + miles);
    System.out.println("goalMet: " + goalMet);
  }
}

Output

steps: 8500
miles: 4.2
goalMet: true

Each line is built by joining a label onto the variable with +, and Java converts each value to text automatically because the left side is a String.

The three types were chosen to fit the data rather than at random. A step count is a whole number, a distance needs decimals, and a goal is either met or not, which is exactly the reasoning to apply when declaring your own variables.

Overflow, and why the limits are real

An int uses 32 bits, which buys the range -2,147,483,648 to 2,147,483,647. Go past the edge and the value wraps around to the far end of the range, with no error and no warning, just a silently wrong number. This is called overflow.

int big = 2147483647;          // the maximum int
big + 1                        // -2147483648, wrapped
long safe = 2147483647L + 1L;  // 2147483648, long has room

A long uses 64 bits, which reaches about 9.2 quintillion, comfortably past anything a counter is likely to hit.

Real systems have broken this way. Counters of milliseconds and of video views held in an int have failed at 2.1 billion, in public and expensively.

The rule of thumb: counts of things a human enters fit in an int, while timestamps, IDs, and anything that grows forever get a long.

int, 32 bits -2147483648 2147483647 0 big + 1 wraps to the far end no error, no warning, just a negative total
An int is a fixed 32-bit range. Adding 1 to the maximum wraps around to the minimum instead of raising an error, which is why growing counters belong in a long.

Watching an int wrap

Adding 1 to the maximum int produces a large negative number, while the long version has room to spare.

public class Main {
  public static void main(String[] args) {
    int big = 2147483647;
    System.out.println(big);
    System.out.println(big + 1);
    long safe = 2147483647L + 1L;
    System.out.println(safe);
  }
}

Output

2147483647
-2147483648
2147483648

The second line is the whole problem in one number. No exception is thrown and nothing in the program looks wrong, so a total that has quietly turned negative flows onward into whatever depends on it.

The L suffix on the third line matters more than it appears. Without it the arithmetic would be done as int first and wrap before the result was ever widened to a long, so declaring the variable as long alone does not save you.