Data Types
Every value in Java has a type. Data types tell the compiler how much memory to allocate and what operations are legal on a value. Java is a statically typed language, which means you must declare the type of every variable before you use it.
Java splits its type system into two broad families: primitive types (built into the language, stored by value) and reference types (objects and arrays, stored as heap references).
Primitive Data Types
Java has exactly 8 primitive types. They are not objects — they are raw binary values stored directly on the stack (or inlined into an object on the heap).
| Type | Size | Default | Range / Notes |
|---|---|---|---|
byte | 1 byte | 0 | −128 to 127 |
short | 2 bytes | 0 | −32,768 to 32,767 |
int | 4 bytes | 0 | −2,147,483,648 to 2,147,483,647 |
long | 8 bytes | 0L | −9.2 × 10¹⁸ to 9.2 × 10¹⁸ |
float | 4 bytes | 0.0f | ~7 significant decimal digits (IEEE 754) |
double | 8 bytes | 0.0d | ~15–16 significant decimal digits (IEEE 754) |
char | 2 bytes | '\u0000' | 0 to 65,535 (UTF-16 code unit) |
boolean | JVM-defined | false | true or false only |
Note: Defaults above apply only to instance and static fields. Local variables are not given defaults — the compiler forces you to initialize them before use.
Integer Types
public class IntegerTypes {
public static void main(String[] args) {
byte b = 100;
short s = 30_000; // underscore separators (Java 7+) improve readability
int i = 2_000_000;
long l = 9_000_000_000L; // 'L' suffix required for long literals
System.out.println("byte: " + b);
System.out.println("short: " + s);
System.out.println("int: " + i);
System.out.println("long: " + l);
}
}
Output:
byte: 100
short: 30000
int: 2000000
long: 9000000000
Tip: Prefer
intas your default integer type. Uselongwhen values might exceed ~2 billion (e.g., timestamps, large counts). Usebyteorshortonly when memory is critical (large arrays).
Floating-Point Types
public class FloatingPoint {
public static void main(String[] args) {
float f = 3.14f; // 'f' suffix is required
double d = 3.141592653589; // double is the default for decimal literals
System.out.println("float: " + f);
System.out.println("double: " + d);
// Beware: floating-point arithmetic is approximate
System.out.println(0.1 + 0.2); // prints 0.30000000000000004
}
}
Output:
float: 3.14
double: 3.141592653589
0.30000000000000004
Warning: Never use
floatordoublefor money or exact decimal arithmetic. Usejava.math.BigDecimalinstead.
char Type
char stores a single UTF-16 code unit. You can assign a character literal, a Unicode escape, or an integer in the range 0–65,535.
public class CharDemo {
public static void main(String[] args) {
char letter = 'A';
char unicode = 'A'; // same as 'A'
char digit = 65; // also 'A' — chars are unsigned 16-bit integers
System.out.println(letter); // A
System.out.println(unicode); // A
System.out.println(digit); // A
System.out.println((int) letter); // 65 — cast to see numeric value
}
}
Output:
A
A
A
65
See the Unicode System page for a deeper look at how Java handles character encoding.
boolean Type
public class BooleanDemo {
public static void main(String[] args) {
boolean isJavaFun = true;
boolean isHardToLearn = false;
System.out.println("Java is fun: " + isJavaFun);
System.out.println("Java is hard to learn: " + isHardToLearn);
}
}
Output:
Java is fun: true
Java is hard to learn: false
Note: In Java,
booleanis not numeric. You cannot writeif (1)— onlyif (someBoolean).
Reference Data Types
Reference types include classes, interfaces, arrays, and enums. A variable of reference type holds a reference (pointer) to an object on the heap, not the object itself.
public class ReferenceDemo {
public static void main(String[] args) {
String name = "DevCraftly"; // String is a class (reference type)
int[] nums = {1, 2, 3}; // array is also a reference type
System.out.println(name);
System.out.println(nums[0]);
String copy = name; // both variables point to the same object
System.out.println(copy == name); // true — same reference
}
}
Output:
DevCraftly
1
true
Tip: The default value for any reference variable is
null(field or array element). Accessing a member through anullreference throws aNullPointerException.
Type Casting
Sometimes you need to convert between types. Java supports two kinds of casting.
Widening (Implicit) Casting
Moving to a larger type is automatic and safe — no data is lost.
byte → short → int → long → float → double
int i = 42;
long l = i; // automatic widening
double d = l; // automatic widening again
System.out.println(d); // 42.0
Narrowing (Explicit) Casting
Moving to a smaller type requires an explicit cast — you may lose information.
double pi = 3.14159;
int rough = (int) pi; // truncates decimal portion
System.out.println(rough); // 3
Warning: Narrowing casts silently truncate or wrap values.
(byte) 130gives-126because 130 overflows a signed byte.
Literals
A literal is a fixed value written directly in source code.
int hex = 0xFF; // hexadecimal
int bin = 0b1010_1010; // binary (Java 7+)
long big = 100_000_000L;
float f = 1.5f;
double d = 1.5; // double by default
char c = '\n'; // escape sequence — newline
String s = "hello"; // String literal
Wrapper Classes
Every primitive type has a corresponding wrapper class in java.lang:
| Primitive | Wrapper |
|---|---|
byte | Byte |
short | Short |
int | Integer |
long | Long |
float | Float |
double | Double |
char | Character |
boolean | Boolean |
Wrappers are needed when you work with collections (which store objects, not primitives) and provide useful utility methods:
int max = Integer.MAX_VALUE; // 2147483647
int parsed = Integer.parseInt("42");
String s = Integer.toBinaryString(255); // "11111111"
System.out.println(max + " | " + parsed + " | " + s);
Output:
2147483647 | 42 | 11111111
Java automatically converts between primitives and their wrappers via autoboxing and unboxing. See Autoboxing & Unboxing for the full story.
Under the Hood
How Primitives Are Stored
When a primitive is a local variable, the JVM stores it on the operand stack or in a local variable slot of the current stack frame — no heap allocation happens. This is why primitives are fast.
When a primitive is an instance field, it is stored inline inside the object’s heap allocation. For example, an object with three int fields occupies roughly 12 bytes (object header) + 3 × 4 bytes = 24 bytes (aligned to 8 bytes).
Wrapper Objects and the Integer Cache
When you autobox an int between −128 and 127, Java reuses cached Integer objects rather than allocating new ones:
Integer a = 127;
Integer b = 127;
System.out.println(a == b); // true — same cached object
Integer c = 128;
Integer d = 128;
System.out.println(c == d); // false — different objects
This is a classic Java gotcha. Always compare Integer objects with .equals(), not ==.
float and double: IEEE 754
Both floating-point types follow the IEEE 754 standard. A double uses 1 sign bit, 11 exponent bits, and 52 mantissa bits. Because many decimal fractions (like 0.1) cannot be represented exactly in binary, rounding errors accumulate. This is expected behavior, not a bug.
boolean Size
The Java specification does not mandate a specific byte size for boolean. In practice, the JVM typically stores a boolean field as a 4-byte int (or 1 byte in arrays), since the x86 instruction set doesn’t natively operate on single bits efficiently.
Quick Reference: Choosing the Right Type
| Situation | Recommended type |
|---|---|
| Whole numbers (general use) | int |
| Very large whole numbers | long |
| Decimal numbers (general use) | double |
| Memory-critical large arrays of small integers | byte or short |
| Exact decimal arithmetic (money) | java.math.BigDecimal |
| True/false flags | boolean |
| Single character / Unicode code unit | char |
| Text | String (reference type) |
Related Topics
- Variables — declare and initialize variables using these types
- Operators — apply arithmetic, bitwise, and comparison operators to typed values
- Wrapper Classes — the object counterparts of primitives and their utility methods
- Autoboxing & Unboxing — how Java silently converts between primitives and wrappers
- Unicode System — deeper look at how
charandStringhandle Unicode - Type Casting with instanceof — safe reference-type casting using the
instanceofoperator