Data Types in Java: Primitive vs Reference with Examples

Data types in Java with examples covering the 8 primitive types, sizes and ranges, reference types, literals, wrapper classes and choosing a type.

Java data types hierarchy with eight primitive types grouped into boolean, integral and floating-point types, and reference types grouped into class, interface, array, enum and record

Data types in Java define which values a variable can hold and which operations we can run on it, and they come in two groups, eight primitive types and reference types. A primitive variable stores the value itself, such as the number 4, whereas a reference variable stores a reference to an object, such as a String or an array.

We pick a data type every time we declare a variable, a field or a method parameter. The choice decides the range of values, the memory the value takes, and whether the variable can be null.

The following example declares one variable of the most common data types for a grocery app.

int servings = 4;                       // 32-bit whole number
long views = 3_000_000_000L;            // 64-bit, needs the L suffix
double weightKg = 1.25;                 // 64-bit floating point
char size = 'M';                        // one 16-bit character
boolean organic = true;                 // true or false
String item = "apple";                  // reference to a String object
int[] prices = {120, 80, 45};           // reference to an array object

The first five lines use lowercase primitive types, while String and int[] are reference types. We compare the two groups, list the size and range of every primitive, and cover literals, conversions, wrapper classes and how to pick a type for real data such as prices.

1. Primitive Types vs Reference Types

Java has eight primitive types, and they are built into the language. Every other type is a reference type, which includes classes, interfaces, arrays, enums and records, whether they come from the JDK or from our own code.

Java data types hierarchy with eight primitive types grouped into boolean, integral and floating-point types, and reference types grouped into class, interface, array, enum and record
Every Java type is either one of the eight primitives or a reference type that points to an object

The two groups behave differently when we copy a variable. Assigning a primitive copies the value, so the two variables are independent afterwards. Assigning a reference copies only the reference, so both variables point to the same object.

int a = 5;
int b = a;
b = 6;
int first = a;                          // 5, a did not change
int[] basket = {1, 2};
int[] same = basket;
same[0] = 9;
int changed = basket[0];                // 9, both point to one array

The same difference applies to the == operator. For primitives, == compares values. For references, it checks whether both sides point to the same object, so we compare the content of objects with equals().

String typed = new String("apple");
boolean sameObject = typed == "apple";      // false
boolean sameText = typed.equals("apple");   // true
FeaturePrimitive typeReference type
StoresThe value itselfA reference to an object
Can be nullNoYes
Default value of a field0, 0.0, false or ‘\u0000’null
Has methodsNoYes, such as item.length()
Usable as a generic type argumentNo, List<int> does not compileYes, List<Integer>
Number of typesFixed at 8Unlimited, every class is a type

2. The Eight Primitive Data Types

Each primitive type has a fixed size and range on every platform, which the Java Language Specification defines. A Java int is 32 bits on a phone, a laptop and a server, unlike int in C. The primitive data types article covers each type in more depth.

TypeSizeRangeField defaultLiteral example
byte8 bits-128 to 1270(byte) 10
short16 bits-32,768 to 32,7670(short) 300
int32 bits-231 to 231-1 (about 2.1 billion)042
long64 bits-263 to 263-10L42L
float32 bitsAbout 6 to 7 significant decimal digits0.0f1.5f
double64 bitsAbout 15 to 16 significant decimal digits0.01.5
char16 bits‘\u0000’ to ‘\uffff’ (0 to 65,535)‘\u0000’‘A’
booleanNot defined by the spectrue or falsefalsetrue

Two rows need a note. The JVM specification defines no size for boolean, and HotSpot uses one byte for a boolean field or array element, not one bit. A char holds one UTF-16 code unit, so characters outside the Basic Multilingual Plane, such as most emoji, take two char values in a String.

Integer types wrap around on overflow without any error, so Integer.MAX_VALUE + 1 is a large negative number. When a counter might pass about 2.1 billion, such as total page views, we use long from the start.

int max = Integer.MAX_VALUE;            // 2147483647
int wrapped = max + 1;                  // -2147483648
long safe = max + 1L;                   // 2147483648

3. Literals and Their Suffixes

A literal is a fixed value written in the source code, such as 42, 1.5 or ‘A’. A whole number without a suffix is an int, and a decimal number without a suffix is a double, so larger or smaller types need a suffix or a cast.

long views = 3_000_000_000L;            // 3000000000
float ratio = 0.75f;                    // 0.75
int mask = 0xFF;                        // 255, hexadecimal
int flags = 0b1010;                     // 10, binary
int octal = 010;                        // 8, a leading 0 means octal
char letter = 'A';                      // A
int code = letter;                      // 65

Underscores between digits, as in 3_000_000_000L, make long numbers readable and have no effect on the value. The octal line is a common trap, because a leading zero that looks like padding changes the number. The compiler rejects a literal that does not fit its target type.

// does not compile: integer number too large / possible lossy conversion from double to float
long views = 3_000_000_000;
float ratio = 0.75;

For the long line, javac reports integer number too large, and for the float line it reports incompatible types: possible lossy conversion from double to float. Adding the L or f suffix fixes both.

4. Reference Data Types

A reference type describes an object, and the variable holds a reference to that object or null. We meet five kinds of reference types in everyday code.

  • Classes, such as String, LocalDate or our own Order class.
  • Interfaces, such as List or Comparable, which a variable can use as its type while it holds an object of an implementing class.
  • Arrays, such as int[] or String[], which are objects even when their elements are primitives.
  • Enums and records, which are special kinds of classes for fixed constants and for immutable data carriers.
enum Unit { KG, PIECE }

record Product(String name, double price, Unit unit) {}
Product pear = new Product("pear", 0.5, Unit.KG);
String name = pear.name();              // "pear"
Unit unit = pear.unit();                // KG
Product none = null;                    // allowed for reference types

The String class gets special support from the compiler, because we can create a String from a literal such as “apple” and join strings with +. A String is still an object, and the Java String article explains its immutability and the string pool.

5. Converting Between Primitive Types

Java converts a smaller numeric type to a larger one without a cast, because no value can be lost. This is a widening conversion, such as int to long or int to double. A narrowing conversion, such as int to short or double to int, needs an explicit cast, because it can drop bits or the fraction.

int big = 20_000_000;
long wider = big;                       // 20000000, widening
short cut = (short) big;                // 11520, high bits dropped
double price = 9.99;
int whole = (int) price;                // 9, fraction dropped
int half = 7 / 2;                       // 3, int division
double exact = 7 / 2.0;                 // 3.5

A boolean never converts to or from a number, so if (1) does not compile in Java. The last two lines show that the types of the operands, not the target variable, decide the result of an expression. The Java operators article covers numeric promotion in expressions.

6. Wrapper Classes and Autoboxing

Each primitive type has a wrapper class in java.lang that holds the value in an object. The wrappers are Byte, Short, Integer, Long, Float, Double, Character and Boolean. We need them wherever Java expects an object, such as in collections, generics and fields that must allow null.

Since Java 5, the compiler converts between a primitive and its wrapper for us. Turning an int into an Integer is called autoboxing, and the opposite is unboxing.

List<Integer> stock = new ArrayList<>();
stock.add(5);                           // autoboxing int to Integer
int count = stock.get(0);               // 5, unboxing
Integer a = 127;
Integer b = 127;
boolean cached = a == b;                // true, small values are cached
Integer c = 128;
Integer d = 128;
boolean notCached = c == d;             // false, two objects
boolean equal = c.equals(d);            // true

The == result changes at 128 because Integer.valueOf() returns cached objects for the values from -128 to 127. Unboxing a null wrapper throws a NullPointerException, so we check for null or supply a default first.

Integer missing = null;
int boom = missing;                     // NullPointerException
int fallback = missing != null ? missing : 0;   // 0

The wrapper constructors such as new Integer(5) are deprecated for removal since Java 16 (JEP 390). We use autoboxing or Integer.valueOf(5) instead.

7. Choosing a Data Type for Real Data

Say a grocery app stores the quantity, the price and the stock status of each product. Quantities are whole numbers, so int works well. Prices look like decimals, but double stores binary fractions, so 0.1 and 0.2 cannot be stored without a rounding error and the totals are off by tiny amounts.

double sum = 0.1 + 0.2;                                          // 0.30000000000000004
BigDecimal total = new BigDecimal("0.10").add(new BigDecimal("0.20"));  // 0.30
long cents = 10 + 20;                                            // 30

For money, we use BigDecimal, created from a String, or we store whole cents in a long. The remaining choices follow from the data.

DataTypeReason
Counts, quantities, indexesintFast, and enough for values up to about 2.1 billion
Large counters, timestamps in millisecondslongAn int would overflow
Measurements such as weight or temperaturedoubleSmall rounding errors are acceptable
MoneyBigDecimal or long centsExact decimal arithmetic
Yes or no flagsbooleanClearer than 0 and 1 in an int
Optional numbers from a database columnInteger, LongCan hold null for a missing value
Raw file or network bytesbyte[]Matches the 8-bit data

Inside methods and loops, we prefer primitives, because a primitive needs no object allocation and can never be null. Wrappers belong where an object is required, such as a List<Integer>.

8. Java Data Types FAQs

Whether String counts as a primitive and how many types Java has are the two points that beginners mix up most.

8.1. Is String a primitive data type in Java?

No. String is a class in java.lang, so it is a reference type. It only looks like a primitive because Java supports string literals and the + operator for it.

8.2. How many data types are there in Java?

Java has eight primitive data types, namely byte, short, int, long, float, double, char and boolean. The number of reference types has no limit, because every class, interface, array type, enum and record is a type.

8.3. Why does Java have primitive types if it is object-oriented?

Primitives are faster and smaller. An int takes 4 bytes and needs no allocation, whereas an Integer object adds an object header and a reference to follow. Java keeps primitives for number crunching and offers wrapper classes where an object is needed.

8.4. Can we use primitive types in pattern matching in Java 25?

Only as a preview feature. Primitive types in patterns, instanceof and switch are in their third preview in Java 25 (JEP 507), so the code compiles only with –enable-preview. We do not use preview features in production code.

9. Conclusion

Java has eight primitive data types with fixed sizes and ranges, and everything else is a reference type that points to an object. Primitives copy their value and cannot be null, while references share one object between variables and are compared with equals().

Literals need the L or f suffix when the default int or double does not fit, narrowing needs a cast, and integer overflow wraps without an error. For real data, we pick int or long for counts, BigDecimal for money and wrapper types only where an object or null is needed. The Java tutorial lists the next core topics to read.

10. References

Happy Learning !!

Source Code on Github

About Us

HowToDoInJava provides tutorials and how-to guides on Java and related technologies.

It also shares the best practices, algorithms & solutions and frequently asked interview questions.