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.
Type | Description |
---|---|
boolean | Named constant with a binary value of true or false |
char | A single unicode character ranging from '\u0000' to '\uffff' (from 0 to 65535), inclusive |
byte | Whole number ranging from -128 to 127, inclusive |
short | Whole number ranging from -32768 to 32767, inclusive |
int | Whole number (32 bit) ranging from -2147483648 to 2147483647, inclusive |
long | Whole number (64 bit) ranging from -9223372036854775808 to 9223372036854775807, inclusive |
float | IEEE 754 Floating point number (32 bit) with a decimal precision of 7 |
double | IEEE 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.
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.
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.
public class Program {
public static void main(String[] args) {
// char
char letter = 'ABC';
System.out.println(letter);
}
}
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.
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.
byte num = 127;
The short type ranges from -32768 to 32767 and uses the short keyword.
short num = -32768;
The int type ranges from -2147483648 to 2147483647 (-2.1 billion to 2.1 billion) and uses the int keyword.
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.
long num = -9223372036854775808L;
If these ranges are exceeded, the compiler will raise an error.
public class Program {
public static void main(String[] args) {
long num = -92233720368547758089L;
System.out.println(num);
}
}
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 .
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.
// 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.
try {
// code to try and execute
} catch (exception_type variable_to_store_exception_info) {
// code to execute when exception is caught
}
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.
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.
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.
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.
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.
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.
(type) variable_to_cast;
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.
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.
Integer.parseInt(string_to_convert);
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.
Integer: 2020
Long: -9223372036854775808
Float: 3.14
Double: 3.141592653589793
2. TypeClass.toString().
This class converts a numerical type to a string.
Integer.toString(number_to_convert);
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.
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.
Math.toIntExact(long_number_to_convert)
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.