Java Data Types Tutorial

In this Java tutorial we learn about the basic primitive types in Java. We discuss the boolean, char, byte, short, int, long, float and double types.

Then, we take a look at type overflow and how to handle it with specific math methods, arithmetic exceptions and a try..catch block.

Finally, we learn how to convert from one type to another.

What are data types

Data types are the different types of values that can be stored in data containers by Java.

The type of a value is checked before the data is stored or manipulated, ensuring that the code will behave as we would expect it to.

Types in Java can be categorized as follows.

  • Primitive types
  • Reference types (non-primitive)

In this lesson we will discuss the primitive types. Each reference type will be discussed in its own dedicated lesson.

Primitive types in Java

Primitive types, also known as literals, store their values on the stack in memory. They aren’t objects or references to objects.

We discuss objects in the OOP: Classes & Objects lesson .

In Java, we have 8 types of primitive data types.

TypeDescription
booleanNamed constant with a binary value of true or false
charA single unicode character ranging from '\u0000' to '\uffff' (from 0 to 65535), inclusive
byteWhole number ranging from -128 to 127, inclusive
shortWhole number ranging from -32768 to 32767, inclusive
intWhole number (32 bit) ranging from -2147483648 to 2147483647, inclusive
longWhole number (64 bit) ranging from -9223372036854775808 to 9223372036854775807, inclusive
floatIEEE 754 Floating point number (32 bit) with a decimal precision of 7
doubleIEEE 754 Floating point number (64 bit) with a decimal precision of 15

In Java SE 7 and later, we may use underscores to separate thousands in a number. For example, 1_000_000 (one million) is a valid number. This is helpful to quickly read the value of large numbers.

Java boolean type

A boolean is a named constant that represents the logical values true and false.

Booleans are technically numbers themselves because the values true and false map to 1 and 0 respectively.

The boolean type uses the boolean keyword.

Example:
public class Program {
    public static void main(String[] args) {

        // boolean
        boolean auth = false;

        // usage example
        // if auth == false
        if (!auth) {

            System.out.println("Sorry, you are not allowed in the member area");
        } else {

            System.out.println("Welcome to the member area!");
        }

    }
}

In the example above, we initialize a boolean with one of the two values it can hold, false.

Then, in the conditional if statement we evaluate if the variable’s value is false or not. Depending on the outcome, the statement will print a message to the console.

When a boolean is declared without a value, Java automatically assigns the false value to it. It’s good practice however, to be explicit.

Java char type

A char is a single Unicode character such as ‘A’, or ☕ and uses only 1 byte of memory.

The char type uses the char keyword.

Example:
public class Program {
    public static void main(String[] args) {

        // char
        char letter = 'A';

        System.out.println(letter);
    }
}

The char value is a wrapped in single quotes. Also, we can’t assign multiple characters to the char type, the compiler will raise an error.

Example:
public class Program {
    public static void main(String[] args) {

        // char
        char letter = 'ABC';

        System.out.println(letter);
    }
}
Output:
Exception in thread "main" java.lang.Error: Unresolved compilation problem:
    Invalid character constant

If we want multiple characters, we have to use a string. The String type is a reference type (non-primitive), here is a simple example.

Example:
public class Program {
    public static void main(String[] args) {

        // string
        String message = "Hello there";

        System.out.println(message);
    }
}

A string allows us to store multiple characters wrapped in double quotes.

Technically, a string is an array of characters.

Java whole number types (byte, short, int, long)

The byte, short, int and long number types are for whole numbers, that’s to say numbers without a decimal point.

The numbers can be negative or positive and each has a specific range.

The byte type ranges from -128 to 127 and uses the byte keyword.

Example:
 byte num = 127;

The short type ranges from -32768 to 32767 and uses the short keyword.

Example:
 short num = -32768;

The int type ranges from -2147483648 to 2147483647 (-2.1 billion to 2.1 billion) and uses the int keyword.

Example:
 int num = 2147483647;

The long type ranges from -9223372036854775808 to 9223372036854775807 (-9.2 quintillion to 9.2 quintillion) and uses the long keyword.

The long type also needs the L post-fix directly after the number, otherwise the compiler will regard it as an int.

Example:
 long num = -9223372036854775808L;

If these ranges are exceeded, the compiler will raise an error.

Example:
public class Program {
    public static void main(String[] args) {

        long num = -92233720368547758089L;

        System.out.println(num);
    }
}
Output:
Exception in thread "main" java.lang.Error: Unresolved compilation problem:
    The literal 92233720368547758089L of type long is out of range

The error above means that the type overflowed and is now out of its designated range.

Java floating point types (float, double)

The float and double types are floating point types, that’s to say numbers with a decimal point.

The difference between them is the amount of digits available after the decimal point.

Floating point types require us to use a post-fix after the number value. If none is found, the compiler will assume the number is a double.

A float supports 7 digits after the decimal point (precision of 7), uses the float keyword and the postfix f .

Example:
 float num = 3.1415926f;

A double supports 15 digits after the decimal point (precision of 15), uses the double keyword and the postfix d .

As mentioned earlier, we can omit the postfix for a double.

Example:
// with postfix
double num = 3.141592653589793d;

// without postfix
double num = 3.141592653589793;

How to handle type overflow in Java

When a number goes beyond its range, its known as type overflow. The compiler will check for overflows at compile time, but overflow may still occur at runtime.

We can consider, as an example, newer massivly multiplayer online games (MMO’s) where bad actors can multiply overflowed ints, maximizing their damage output or income from in-game market trades, breaking the game.

Java 8 added a set of methods in the Math class that will throw an ArithmeticException on overflow.

The specific methods we want are:

Each of them can accept int and long types.

When these methods detect an overflow, they will throw a runtime error (exception) that we can handle with a try..catch statement.

A try..catch statement will try to execute code in its code block. If the code throws an exeception, we can catch that exception and handle it in the catch code block.

Syntax:
try {

    // code to try and execute

} catch (exception_type variable_to_store_exception_info) {

    // code to execute when exception is caught
}
Example:
public class Program {
    public static void main(String[] args) {

        int x = 2000000000;
        int y = 1000000000;

        try {

            // try to add numbers
            int result = Math.addExact(x, y);

            // if it works, print the result
            System.out.println("Result = " + result);

        } catch(ArithmeticException e) {

            // if an ArithmeticException
            // is caught, store it in the
            // 'e' variable and print it
            System.out.println(e);
        }
    }
}

If the two numbers in the example above are added together in the ‘addExact()’ method, they will produce an overflow.

We catch it in the catch statement and store it in the variable ‘e’. Then, for simplicity, we just print the exception message out to the console.

Output:
 java.lang.ArithmeticException: integer overflow

If you’re a beginner and this didn’t make any sense, don’t worry. We cover exceptions in more depth in the Errors & Exceptions lesson .

Type conversion and casting in Java

Type conversion is the act of converting one type to another.

In Java we have three conversion types:

  • Implicit conversion
  • Explicit conversion (also known as type casting)
  • Conversion between non-compatible types

Implicit conversion

Implicit conversions are done in a type-safe manner. If one type can safely be converted to another, it’s implicit, and the compiler will not raise an error.

For example, a byte can hold up to 255, and an int can hold up to 2 billion. We can implicitly convert a byte to an int because the value of 255 will always be able to fit into an int.

Example:
public class Program {
    public static void main(String[] args) {

        byte b = 25;

        // convert byte into int
        int i = b;

        System.out.println(i);
    }
}

An int is big enough to store any value that byte has, so we can easily copy a byte to an int.

In situations where the compiler is sure that no data loss will happen, it will allow implicit conversion.

Example:
public class Program {
    public static void main(String[] args) {

        int i = 1;

        // convert int into float
        float f = i;

        System.out.println(f);
    }
}

In the example above, the conversion is implicit because float is big enough to hold an int, and an int does not have decimal points with a precision greater than 7 digits.

Explicit conversion (type casting)

If the compiler knows that data loss may happen, it won’t allow implicit conversion.

For example, if we try to convert an int with a value greater than 255 to a byte, it will cause data loss because a byte can only store up to 255.

Example:
public class Program {
    public static void main(String[] args) {

        int i = 300;

        // try to convert
        // int into byte
        byte b = i;

        System.out.println(b);
    }
}

A byte is not big enough to store the value of 300 without data loss, so it can’t be implicitly converted and the compiler won’t allow it.

Output:
Exception in thread "main" java.lang.Error: Unresolved compilation problem:
    Type mismatch: cannot convert from int to byte

We need to explicitly tell the compiler that we’re aware of the data loss, and still want to go ahead with the conversion. Explicit type conversion is also known as casting.

When casting one type to another we specify the type between parentheses that we want to cast to.

Syntax:
 (type) variable_to_cast;
Example: cast int to byte
public class Program {
    public static void main(String[] args) {

        int i = 300;

        // cast i to a byte
        byte b = (byte)i;

        System.out.println(b);
    }
}

The compiler will now allow the conversion and compile the code.

Example: cast float to int
public class Program {
    public static void main(String[] args) {

        float f = 3.14f;

        // cast f to an int
        int i = (int)f;

        System.out.println(i);
    }
}

When the float converted into int it cut the decimal points completely, because an int doesn’t have decimal points.

Non-compatible type conversion

Sometimes we work with types that are not compatible, but we still need to convert them. For example, we can’t cast a string into an int, the compiler will raise an error.

In situations like these we need a different mechanism of conversion. Java provides us with handy class methods to convert con-compatible types.

1. TypeClass.parseType()

This class method converts a string to a numerical type. The parse type is the same as the type class.

Syntax:
 Integer.parseInt(string_to_convert);
Example:
public class Program {
    public static void main(String[] args) {

        // numbers in strings
        String strInt = "2020";
        String strLng = "-9223372036854775808";
        String strFlt = "3.14";
        String strDbl = "3.141592653589793";

        // string to int
        int parsedInt = Integer.parseInt(strInt);
        printVar(parsedInt);

        // string to long
        long parsedLng = Long.parseLong(strLng);
        printVar(parsedLng);

        // string to float
        float parsedFlt = Float.parseFloat(strFlt);
        printVar(parsedFlt);

        // string to double
        double parsedDbl = Double.parseDouble(strDbl);
        printVar(parsedDbl);
    }

    // function to print a variable's type
    public static void printVar(Object var) {

        System.out.println(var.getClass().getSimpleName() + ": " + var);
    }
}

In the example above, we’ve added a function to check and print the variable type as well as the value.

Output:
Integer: 2020
Long: -9223372036854775808
Float: 3.14
Double: 3.141592653589793

2. TypeClass.toString().

This class converts a numerical type to a string.

Syntax:
 Integer.toString(number_to_convert);
Example:
public class Program {

    public static void main(String[] args) {

        // numbers
        int numInt = 2020;
        long numLng = -9223372036854775808L;
        float numFlt = 3.14f;
        double numDbl = 3.141592653589793;

        // int to string
        String parsedInt = Integer.toString(numInt);
        printVar(parsedInt);

        // long to string
        String parsedLng = Long.toString(numLng);
        printVar(parsedLng);

        // float to string
        String parsedFlt = Float.toString(numFlt);
        printVar(parsedFlt);

        // double to string
        String parsedDbl = Double.toString(numDbl);
        printVar(parsedDbl);
    }

    // function to print a variable's type
    public static void printVar(Object var){

        System.out.println(var.getClass().getSimpleName() + ": " + var);
    }
}

Once again, our function will check and print the variable’s type as well as the value so we can see from the output that the conversion was successful.

Output:
String: 2020
String: -9223372036854775808
String: 3.14
String: 3.141592653589793

3. Math.toIntExact()

This function will convert a long to an int. If there is overflow, it will return an ArithmeticException that can be handled in a try..catch statement.

Syntax:
 Math.toIntExact(long_number_to_convert)
Example:
public class Program {
    public static void main(String[] args) {

        long num = 9248L;

        // long to int with
        // overflow protection
        int parsedNum = Math.toIntExact(num);
        printVar(parsedNum);
    }

    // function to print a variable's type
    public static void printVar(Object var) {

        System.out.println(var.getClass().getSimpleName() + ": " + var);
    }
}

In the example above, our long number isn’t bigger than an int can handle so it converts successfully and prints to the console.

Further reading

While not necessary, feel free to browse to the following external links for more information on some of the subjects of this lesson.

Official Java 13 Specification:

Other:

Summary: Points to remember

  • A data type specifies the type of data can be stored, or returned by a method. It also specifies the amount of space a value takes up in memory.
  • The primitive types in Java are boolean, char, byte, short, int, long, float and double.
    • Long, float and double each use the postfix L, f and d respectively.
  • Type overflow can be handled in a try..catch statement.
  • Java allows us to convert some types to others.
    • Implicit conversion is a type-safe conversion and converts up. For example, int to long.
    • Explicit converstion (casting) is not type-safe, may lose data, and converts down. For example, float to int.
    • Non-compatible conversion is done by special methods and converts types that aren’t compatible with each other. For example, int to String.