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:
// 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
intfor whole numbers by default (age, count, index). - Use
longwhen values can exceed ~2.1 billion (population figures, file sizes, financial totals in cents). - Use
doublefor decimals by default (marks, prices, measurements). Never usefloat— its precision is insufficient for most real work. - Use
booleanfor flags and conditions. - Use
charonly when you specifically need a single character. For text, useString.
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.
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.
// 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
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 adoubleliteral to anintwithout a cast.- Forgetting the
Lsuffix on a long literal:long id = 9812315080082;causes a compile error — Java reads large integer literals as int by default. - Forgetting the
fsuffix on a float literal:float f = 3.14;— 3.14 is a double literal by default. - Integer division:
7 / 2equals3in Java, not3.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?