Java Variable Types: The Eight That Matter

Java has eight primitive types. Everything else is an object. Knowing what each primitive does, and which ones you should default to, prevents most of the silly bugs new Java developers ship.

Tech Talk News Editorial6 min readUpdated Jul 14, 2026
ShareXLinkedInRedditEmail
Java Variable Types: The Eight That Matter

Key takeaways

  • Java has exactly eight primitive types: byte, short, int, long, float, double, char, and boolean. Everything else, including String and arrays, is a reference type stored on the heap.
  • The practical Java defaults are int for whole numbers under about 2 billion, long above that, double for decimals, BigDecimal for money, boolean for true/false, and String for text.
  • An int is 32-bit and tops out around 2.1 billion, while a long is 64-bit and reaches roughly 9.2 quintillion, which is why timestamps and large IDs need long.
  • In Java, == compares values for primitives but compares memory references for objects, so two Integer objects holding 200 are not == to each other even though .equals() returns true.
  • A Java char is a 16-bit UTF-16 code unit, not a full Unicode character, so anything outside the Basic Multilingual Plane (most emoji) needs String and its code-point methods.

Java has exactly eight primitive types: byte, short, int, long, float, double, char, and boolean. Everything else (Strings, arrays, your own classes) is a reference type stored as an object on the heap. The eight primitives are the building blocks. Understanding what each one does, what its range is, and when to use it instead of a default int or double is one of those skills that separates Java developers who write working code from Java developers who write code without surprise overflow bugs.

The way I think about Java's primitives is that they're a performance trade-off the language insists on exposing. Most modern languages either hide the integer width entirely (Python, Ruby) or force you to think about it constantly (C, Rust). Java sits in the middle: you have eight options, but for most code you'll just use int and double and ignore the rest. Knowing why the others exist is useful even if you rarely choose them.

Plain English

Primitive types are stored directly in memory by value. Reference types (objects) are stored as pointers to a location on the heap. The distinction matters for performance, comparison semantics, and how you reason about mutation.

The Eight Primitives

Integer Types

Java integersjava
byte  b = 127;          // 8-bit, range -128 to 127
short s = 32767;        // 16-bit, range -32768 to 32767
int   i = 2147483647;   // 32-bit, range about ±2.1 billion
long  l = 9223372036854775807L;  // 64-bit, range about ±9.2 quintillion

The default integer type is int. Use long when your numbers can exceed roughly 2 billion (timestamps in milliseconds, IDs at scale, anything bigger than ~2.1B). Use byte for raw binary data (file I/O, network protocols). Use short almost never; the memory savings rarely justify the awkwardness, and most operations promote it to int anyway.

Floating-Point Types

Java floatsjava
float  f = 3.14f;     // 32-bit, ~7 significant decimal digits
double d = 3.141592653589793;  // 64-bit, ~15 significant decimal digits

The default floating-point type is double. Use float only when you have a specific reason (memory-constrained systems, graphics work where 32-bit floats are the standard). Both are IEEE 754 floating-point and produce the usual rounding errors at edges. For monetary calculations, neither is appropriate. Use BigDecimal instead.

Character Type

Java charjava
char c = 'A';
char unicode = '\u00E9';  // é

A single 16-bit Unicode character. The width was decided in the early 1990s when Unicode looked like it would fit in 16 bits, before they realized it wouldn't. Now char is technically half of a UTF-16 code unit, which causes endless confusion when working with characters outside the Basic Multilingual Plane (most emoji, some CJK characters). Don't use char for general text processing. Use String and its code-point methods.

Boolean Type

Java booleanjava
boolean isActive = true;
boolean hasPermission = false;

True or false. Java is strict: a boolean is not a number. You can't use 0 and 1 interchangeably as you can in C or JavaScript. if (1) is a compile error.

Reference Types

Everything that isn't one of the eight primitives is a reference type:

Reference typesjava
String name = "Alex";
int[] numbers = {1, 2, 3};
List<Integer> list = new ArrayList<>();
Person p = new Person("Alex", 30);

Reference types are stored as pointers on the stack pointing to objects on the heap. Two reference variables can point to the same object, and changes through one are visible through the other.

Boxing and Wrapper Classes

Each primitive has a wrapper class that turns it into an object: Byte, Short, Integer, Long, Float, Double, Character, Boolean. Wrappers are needed for collections (you can't store an int in List, but you can store an Integer).

Autoboxingjava
int primitive = 42;
Integer wrapper = primitive;     // autoboxed: int → Integer
int back = wrapper;              // unboxed: Integer → int

List<Integer> nums = new ArrayList<>();
nums.add(5);                     // autoboxed automatically

Java does this conversion automatically as needed (autoboxing and unboxing). The catch: a List<Integer> allocates a separate heap object for every element, while an int[] is one contiguous block of 4-byte slots. In tight numeric loops that costs you both memory and cache locality, and it shows up in profiles. The Stream API and primitive specializations (IntStream, LongStream) help reduce this.

The Default Values

Uninitialized fields (not local variables, which must be initialized before use) get default values:

  • byte, short, int, long: 0
  • float, double: 0.0
  • char: '\u0000' (the null character)
  • boolean: false
  • Reference types: null

These defaults are why Java is more predictable than C, where uninitialized variables contain whatever happened to be in memory. Java will crash with NullPointerException at runtime, but at least it won't silently use random data.

Comparing Primitives vs References

Equalityjava
int a = 5;
int b = 5;
a == b;  // true (value comparison for primitives)

String s1 = "hello";
String s2 = "hello";
s1 == s2;          // true or false depending on string interning
s1.equals(s2);     // true (the right way to compare strings)

Integer i1 = 200;
Integer i2 = 200;
i1 == i2;          // false (reference comparison, different objects)
i1.equals(i2);     // true

For primitives, == compares values. For reference types, == compares references (memory addresses), not contents. This is the source of half the confusing Java bugs new developers hit. Use .equals() for content comparison on objects.

What to Use by Default

  • Whole numbers under 2 billion: int.
  • Whole numbers above 2 billion (timestamps, IDs): long.
  • Decimal numbers: double.
  • Money: BigDecimal.
  • True/false: boolean.
  • Text: String (not char).
  • Lists of numbers: int[] for small/fixed, List<Integer> for dynamic.

That covers the overwhelming majority of normal Java code. The other primitives (byte, short, float, char) exist for specific cases and you'll know when you need them.

Takeaway

Eight primitives, each with a wrapper class, plus reference types for everything else. The defaults you actually use most of the time are int, long, double, boolean, and String. Knowing the ranges of int (~2 billion) and long (~9 quintillion) prevents the most common overflow bug. Knowing == vs .equals() prevents the most common comparison bug.

The Take

Java's primitive types are an artifact of a language designed in the early 1990s with C++ as its reference point and shipped in 1995. Modern JVM languages (Kotlin, Scala) hide most of this complexity, but they still compile down to the same eight primitives. Understanding them helps you read older Java code and reason about performance when it matters. For most application work, the distinction between primitives and reference types is one of those things that's technically important but rarely visible until something goes wrong.

Frequently asked questions

How many variable types does Java have?
Java has eight primitive types: byte, short, int, long, float, double, char, and boolean. Everything else in the language is a reference type, meaning an object on the heap. Strings, arrays, collections, and your own classes are all reference types. The eight primitives are stored directly by value, which is the core distinction that drives performance and comparison behavior.
Should I use int or long in Java?
Use int by default and switch to long when your numbers can exceed roughly 2.1 billion. An int is 32-bit, which caps out around plus or minus 2.1 billion. A long is 64-bit and handles about plus or minus 9.2 quintillion. Millisecond timestamps and IDs at scale both blow past the int limit, so they need long.
Why should you never use float or double for money in Java?
Both float and double are IEEE 754 floating-point types and produce rounding errors, which is unacceptable for monetary math. Use BigDecimal instead. Double is the sane default for general decimal numbers, and float is only worth reaching for in memory-constrained systems or graphics work where 32-bit floats are the standard.
What is the difference between == and .equals() in Java?
For primitives, == compares actual values. For reference types, == compares memory addresses, not contents, so it can return false for two objects holding identical data. Use .equals() for content comparison on objects. Two Integer objects both set to 200 will fail an == check but pass .equals(). This is the source of half the confusing bugs new Java developers hit.
What are wrapper classes and autoboxing in Java?
Each primitive has a wrapper class that turns it into an object: Byte, Short, Integer, Long, Float, Double, Character, and Boolean. Autoboxing is Java converting between the two automatically, which is what lets you add an int to a List of Integer. The cost is a heap object per element, so wrapper collections use significantly more memory than a raw int array and are slower in tight loops.

Written by

Tech Talk News Editorial

Computer engineering background. Writes about software, AI, markets, and real estate, and the places where the three meet.

More about the author
ShareXLinkedInRedditEmail