☕ Java
Primitive Data Types
Java has eight primitive data types — the most basic building blocks for storing data. Unlike objects, primitives are stored directly in memory, making them fast and efficient. Understanding each type, its size, range, and when to use it is fundamental to writing correct Java programs.
What Are Primitive Data Types?
Java has two categories of data types: primitive types and reference types (objects). Primitive types are the foundation — they store simple values directly in memory, not as objects. There are exactly eight of them, and they're built into the language itself.
Unlike objects, primitives:
- Are stored on the stack (local variables) or inline in the object (fields) — no heap allocation
- Have a fixed size regardless of platform
- Cannot be null — they always have a default value
- Are compared by value, not by reference
Every other data type in Java — String, ArrayList, your own classes — is a reference type built on top of these eight primitives.
The Eight Primitive Types at a Glance
Here's the complete overview before diving into each one:
Java
// Type Size Default Range / Notes
// ───────────────────────────────────────────────────────────────
// byte 8-bit 0 -128 to 127
// short 16-bit 0 -32,768 to 32,767
// int 32-bit 0 -2,147,483,648 to 2,147,483,647
// long 64-bit 0L -9.2 quintillion to 9.2 quintillion
// float 32-bit 0.0f ~6-7 decimal digits of precision
// double 64-bit 0.0 ~15-16 decimal digits of precision
// char 16-bit '\u0000' 0 to 65,535 (Unicode characters)
// boolean 1-bit* false true or false only
//
// * boolean size is JVM-dependent; logically 1 bit, often stored as 1 bytebyte — 8-bit Integer
The smallest integer type. Holds values from -128 to 127. Rarely used for arithmetic — its main use is raw binary data: reading files, network streams, image pixel data, and anywhere you need to work with raw bytes.
Java
byte b = 100;
byte min = -128;
byte max = 127;
// Overflow wraps around:
byte overflow = (byte) 128; // becomes -128
byte overflow2 = (byte) 200; // becomes -56
// Common use — raw binary data:
byte[] fileData = new byte[1024]; // buffer for file reading
byte[] imagePixels = new byte[width * height * 3]; // RGB pixel data
// Reading bytes from a stream:
InputStream in = new FileInputStream("file.txt");
byte[] buffer = new byte[4096];
int bytesRead = in.read(buffer);
// byte arithmetic promotes to int automatically:
byte a = 10;
byte b2 = 20;
// byte sum = a + b2; // COMPILE ERROR — result is int, not byte
byte sum = (byte)(a + b2); // explicit cast required
int result = a + b2; // better — let it be intshort — 16-bit Integer
Holds values from -32,768 to 32,767. Rarely used in modern Java — it was more relevant when memory was extremely limited. Today it's occasionally seen in specific domains like audio processing (PCM audio samples are 16-bit) or legacy protocol parsing.
Java
short s = 30000;
short min = -32768;
short max = 32767;
// Like byte, arithmetic promotes to int:
short x = 100;
short y = 200;
// short sum = x + y; // COMPILE ERROR
short sum = (short)(x + y); // explicit cast required
// Practical use — 16-bit audio samples (PCM):
short[] audioSamples = new short[44100]; // 1 second at 44.1kHz
// In most cases, just use int instead of short:
// The memory saving (2 bytes vs 4 bytes per value) is rarely worth
// the extra casting complexity unless you have massive arraysint — 32-bit Integer (The Default)
The workhorse of Java integer types. When you need a whole number and don't have a specific reason to use another type, use int. It's the default type for integer literals and the type most Java APIs expect.
Java
int age = 25;
int year = 2024;
int temperature = -15;
int population = 1_400_000_000; // underscores for readability (Java 7+)
int min = Integer.MIN_VALUE; // -2,147,483,648
int max = Integer.MAX_VALUE; // 2,147,483,647
// Integer literals can be written in different bases:
int decimal = 255;
int hex = 0xFF; // 255 in hexadecimal
int binary = 0b11111111; // 255 in binary (Java 7+)
int octal = 0377; // 255 in octal
// Integer overflow — wraps silently (no exception):
int overflow = Integer.MAX_VALUE + 1; // becomes Integer.MIN_VALUE
System.out.println(overflow); // -2147483648
// Use Math.addExact() if overflow should throw an exception:
int safe = Math.addExact(Integer.MAX_VALUE, 1); // throws ArithmeticException
// int is the default for all integer arithmetic:
int result = 10 / 3; // 3 — integer division truncates, not rounds
int remainder = 10 % 3; // 1 — modulo operatorlong — 64-bit Integer
Use long when your values exceed int's range of ~2.1 billion. Common for timestamps, file sizes, database IDs, financial amounts in minor currency units (cents), and population counts.
Java
long population = 8_000_000_000L; // world population — exceeds int range
long fileSize = 10_737_418_240L; // 10 GB in bytes
long timestamp = System.currentTimeMillis(); // milliseconds since epoch
long nanoseconds = System.nanoTime(); // high-resolution timer
// The L suffix is required for long literals beyond int range:
long big = 3_000_000_000L; // L suffix required — 3 billion exceeds int max
long small = 100L; // L suffix optional for small values, but clear
// Without L, the literal is treated as int and may overflow:
long wrong = 3_000_000_000; // COMPILE ERROR — integer too large
long right = 3_000_000_000L; // correct
long lMin = Long.MIN_VALUE; // -9,223,372,036,854,775,808
long lMax = Long.MAX_VALUE; // 9,223,372,036,854,775,807
// Common use — timestamps and time calculations:
long start = System.currentTimeMillis();
// ... do some work ...
long elapsed = System.currentTimeMillis() - start;
System.out.println("Took " + elapsed + "ms");
// Financial amounts — store in cents to avoid floating point issues:
long priceInCents = 1999L; // $19.99 stored as 1999 centsfloat — 32-bit Floating Point
A single-precision floating-point number. Provides about 6-7 significant decimal digits of precision. Rarely used in modern Java — double is preferred for almost all floating-point work. float is still used in graphics programming (OpenGL, Android Canvas) and machine learning where memory efficiency matters more than precision.
Java
float pi = 3.14159f; // f suffix required — marks it as float
float price = 9.99f;
float temperature = -3.5f;
// f suffix is required — without it, 3.14 is a double literal:
float x = 3.14; // COMPILE ERROR — possible lossy conversion from double to float
float y = 3.14f; // correct
float fMin = Float.MIN_VALUE; // smallest positive value: ~1.4E-45
float fMax = Float.MAX_VALUE; // largest value: ~3.4E38
// Precision limitations — float only has ~7 significant digits:
float a = 1234567.89f;
System.out.println(a); // 1234568.0 — precision lost after 7 digits
// Special float values:
float inf = Float.POSITIVE_INFINITY; // result of 1.0f / 0.0f
float nan = Float.NaN; // Not a Number — result of 0.0f / 0.0f
System.out.println(1.0f / 0.0f); // Infinity
System.out.println(0.0f / 0.0f); // NaN
System.out.println(Float.isNaN(nan)); // true
// Where float is still used:
// Android graphics, OpenGL coordinates, neural network weights (memory efficiency)double — 64-bit Floating Point (The Default)
The default floating-point type in Java. Provides about 15-16 significant decimal digits of precision. Use double for all general-purpose decimal arithmetic unless you have a specific reason for float.
Java
double pi = 3.141592653589793;
double price = 19.99;
double gravity = 9.81;
double distance = 384_400.0; // distance to moon in km
// double is the default for decimal literals — no suffix needed:
double x = 3.14; // valid
double y = 3.14d; // d suffix is optional for double
double dMin = Double.MIN_VALUE; // ~4.9E-324
double dMax = Double.MAX_VALUE; // ~1.8E308
// Scientific notation:
double avogadro = 6.022e23; // 6.022 × 10^23
double electron = 9.109e-31; // electron mass in kg
// The critical floating-point pitfall — imprecise representation:
System.out.println(0.1 + 0.2); // 0.30000000000000004
System.out.println(0.1 + 0.2 == 0.3); // false!
// Why: 0.1 and 0.2 cannot be represented exactly in binary floating point.
// Fix 1 — compare with tolerance (epsilon):
double result = 0.1 + 0.2;
double epsilon = 1e-10;
System.out.println(Math.abs(result - 0.3) < epsilon); // true
// Fix 2 — use BigDecimal for exact decimal arithmetic (financial calculations):
import java.math.BigDecimal;
BigDecimal sum = new BigDecimal("0.1").add(new BigDecimal("0.2"));
System.out.println(sum); // 0.3 — exactchar — 16-bit Unicode Character
Stores a single Unicode character as a 16-bit unsigned integer (0 to 65,535). char is the only unsigned primitive in Java. It's used for individual characters, though in practice String handles most text work.
Java
char letter = 'A';
char digit = '7';
char space = ' ';
char newline = '\n';
char tab = '\t';
// Unicode escape — \uXXXX:
char omega = '\u03A9'; // Ω
char rupee = '\u20B9'; // ₹
// char is a 16-bit unsigned integer — arithmetic works on it:
char c = 'A';
System.out.println((int) c); // 65 — Unicode code point
System.out.println((char)(c + 1)); // B
System.out.println((char)(c + 25)); // Z
// Print the alphabet:
for (char ch = 'A'; ch <= 'Z'; ch++) {
System.out.print(ch); // ABCDEFGHIJKLMNOPQRSTUVWXYZ
}
// char vs String:
char singleChar = 'A'; // single quotes — exactly one character
String text = "A"; // double quotes — a String object
// char concatenated with int produces int:
char a = 'A';
System.out.println(a + 1); // 66 (int arithmetic)
System.out.println("" + a); // "A" (String concatenation)
// Testing character types:
Character.isLetter('A') // true
Character.isDigit('5') // true
Character.isWhitespace(' ') // true
Character.toUpperCase('a') // 'A'boolean — true or false
The simplest primitive — stores exactly two possible values: true or false. Used for conditions, flags, and any binary state. Cannot be cast to or from any other type in Java (unlike C/C++ where integers and booleans are interchangeable).
Java
boolean isActive = true;
boolean isLoggedIn = false;
boolean hasPermission = true;
// Default value for boolean fields is false:
class User {
boolean isVerified; // default: false
}
// boolean expressions — the result of comparisons:
boolean isAdult = age >= 18;
boolean isEmpty = list.size() == 0;
boolean isValid = email.contains("@") && email.contains(".");
// Used directly in control flow:
if (isActive) { ... }
while (isRunning) { ... }
// Logical operators:
boolean a = true;
boolean b = false;
System.out.println(a && b); // false — AND: both must be true
System.out.println(a || b); // true — OR: at least one must be true
System.out.println(!a); // false — NOT: inverts the value
System.out.println(a ^ b); // true — XOR: exactly one must be true
// Short-circuit evaluation:
// && stops at first false — right side not evaluated if left is false:
if (user != null && user.isActive()) { } // safe — won't NPE if user is null
// || stops at first true — right side not evaluated if left is true:
if (cache.contains(key) || loadFromDB(key)) { } // DB not hit if cache hits
// boolean CANNOT be cast to int (unlike C):
boolean flag = true;
int x = (int) flag; // COMPILE ERROR — not allowed in JavaDefault Values and Scope
Primitive fields (class-level variables) have default values. Primitive local variables (inside methods) do not — the compiler forces you to initialize them before use.
Java
public class Defaults {
// Fields — automatically initialized to defaults:
byte b; // 0
short s; // 0
int i; // 0
long l; // 0L
float f; // 0.0f
double d; // 0.0
char c; // '\u0000' (null character)
boolean flag; // false
void method() {
// Local variables — NO default value — must initialize before use:
int x;
System.out.println(x); // COMPILE ERROR: variable x might not have been initialized
int y = 0;
System.out.println(y); // fine — explicitly initialized
}
}Choosing the Right Type
A practical guide for picking the right primitive in common situations:
Java
// Whole numbers:
// → Default choice: int
// → Value might exceed ~2.1 billion: long
// → Raw binary / byte streams: byte
// → 16-bit audio samples (rare): short
// Decimal numbers:
// → Default choice: double
// → Graphics / ML / memory critical: float
// → Exact decimal arithmetic (money): BigDecimal (not a primitive)
// Text:
// → Single character: char
// → Any text longer than one char: String (not a primitive)
// Logic / flags:
// → Yes/no, true/false, on/off: boolean
// Quick decision tree:
int age = 30; // whole number — int
long fileSize = 5_000_000_000L; // exceeds int range — long
double price = 29.99; // decimal — double
boolean isAvailable = true; // flag — boolean
char grade = 'A'; // single character — char
byte[] data = new byte[1024]; // raw binary buffer — byte[]Related Topics in Java Basics
Variables in Java
A variable is just a named box in memory that holds a value. Java is strict about what goes in each box — you tell it the type upfront. Once you get this, the rest of Java clicks into place.
Data Types in Java
Java needs to know exactly what kind of data it's dealing with before it can store or process it. Integers, decimals, characters, true/false — each has its own type. Knowing which to use (and why) makes your programs efficient and bug-free.
Non-Primitive Data Types
Non-primitive data types — also called reference types — are everything beyond Java's eight primitives. Strings, arrays, classes, interfaces, enums, and records all fall into this category. They're more powerful than primitives, but they work differently in memory, comparison, and nullability. Understanding the distinction is essential for writing correct Java.
Literals
A literal is a fixed value written directly in your source code — 42, 3.14, 'A', true, "Hello". Every literal has a type, and understanding how Java interprets each one prevents subtle bugs around type mismatches, overflow, and precision loss.