Lesson 5 — Types

Data Types

Java is statically and strongly typed — every variable has a fixed type enforced at compile time. This prevents entire classes of runtime bugs and makes your code self-documenting.

Primitive Types vs Reference Types

Java has two categories of types. Primitive types store their value directly in memory. Reference types store a reference (memory address) to an object. Understanding this distinction matters when you pass variables to methods and compare values.

The Eight Primitive Types

Java has exactly eight built-in primitive types. Know them, their sizes, and when to use each:

JAVA — All eight primitive types
// INTEGER types (whole numbers, no decimals)
byte   b = 127;          // 8-bit  — range: -128 to 127
short  s = 32000;        // 16-bit — range: -32,768 to 32,767
int    i = 2_000_000;    // 32-bit — range: ±2.1 billion (most common)
long   l = 9_876_543_210L; // 64-bit — range: ±9.2 quintillion (suffix L required)

// FLOATING-POINT types (decimals)
float  f = 3.14f;        // 32-bit — 7 decimal digits of precision (suffix f required)
double d = 3.14159265;   // 64-bit — 15 decimal digits (standard choice for decimals)

// OTHER
char   c = 'A';          // 16-bit Unicode character — single quotes
boolean ok = true;       // true or false — no other values

System.out.println("Underscores in numbers: " + 2_000_000); // 2000000
// Underscores make large numbers readable — the compiler ignores them

Choosing the Right Type

  • Use int for whole numbers by default (age, count, index).
  • Use long when values can exceed ~2.1 billion (population figures, file sizes, financial totals in cents).
  • Use double for decimals by default (marks, prices, measurements). Never use float — its precision is insufficient for most real work.
  • Use boolean for flags and conditions.
  • Use char only when you specifically need a single character. For text, use String.

The String Type (Reference Type)

String is not a primitive — it is a class. But it is so fundamental that Java gives it special treatment: you can create Strings with literal syntax (double quotes) rather than new. Strings are immutable — once created, their content cannot be changed. Every operation that appears to modify a String actually creates a new one.

JAVA — String operations
String name    = "Thandi Mokoena";
String course  = "Java";
String combined = name + " studies " + course; // String concatenation with +

System.out.println(combined);
System.out.println("Length: " + name.length());
System.out.println("Uppercase: " + name.toUpperCase());
System.out.println("Contains Java: " + combined.contains("Java"));
System.out.println("First char: " + name.charAt(0));

Type Casting

Widening casting (smaller type to larger type) is automatic — no data is lost, so Java does it silently. Narrowing casting (larger to smaller) requires an explicit cast because data may be lost — Java makes you write it explicitly so you cannot claim ignorance.

JAVA — Type casting
// Widening — automatic (no data loss)
int    i = 42;
long   l = i;       // int → long: fine, no cast needed
double d = i;       // int → double: fine

// Narrowing — requires explicit cast
double avg    = 74.8;
int    floor  = (int) avg;  // 74 — decimal part is truncated (not rounded)
System.out.println(floor);

// Integer division — common source of bugs
int total   = 7;
int count   = 2;
double result = total / count;       // 3.0 — division happens as int first!
double fixed  = (double) total / count; // 3.5 — cast before dividing

System.out.println("Bug:   " + result); // 3.0
System.out.println("Fixed: " + fixed);  // 3.5

Checking Types at Runtime

JAVA — instanceof
Object obj = "Hello";

if (obj instanceof String) {
    String s = (String) obj;
    System.out.println("Length: " + s.length());
}

// Java 16+ pattern matching syntax (cleaner):
if (obj instanceof String s) {
    System.out.println("Length: " + s.length());
}

Practice Task

Your Turn

Write a program that stores: a learner's ID number as long, their mark as double, their grade symbol as char, their name as String, and a flag for whether they have submitted their final project as boolean. Print all five. Then cast the double mark to int and print both the original and the truncated value side by side. Explain in a comment why they differ.

Common Mistakes

  • int mark = 74.5; — cannot assign a double literal to an int without a cast.
  • Forgetting the L suffix on a long literal: long id = 9812315080082; causes a compile error — Java reads large integer literals as int by default.
  • Forgetting the f suffix on a float literal: float f = 3.14; — 3.14 is a double literal by default.
  • Integer division: 7 / 2 equals 3 in Java, not 3.5 — this is a silent, dangerous bug.
  • Comparing Strings with ==: this compares references (memory addresses), not content. Use .equals() instead.

Professional Tip

When in doubt about which numeric type to use: int for whole numbers, double for decimals. These cover 95% of real-world cases. Switch to long only when values might exceed 2.1 billion.

Mini Quiz

What happens when you cast a double to int in Java?