1. List of primitive types

Java has 8 basic primitive types. They are called primitive because the values of these types are not objects and are stored directly inside variables.

Here is a table with some brief information about these types:

Type Size
in bytes
Value range Default value Description
byte 1 -128 .. 127 0 The smallest integer type is a single byte
short 2 -32,768 .. 32.767 0 Short integer, two bytes
int 4 -2*109 .. 2*109 0 Integer, 4 bytes
long 8 -9*1018 .. 9*1018 0L Long integer, 8 bytes
float 4 -1038 .. 1038 0.0f Floating-point number, 4 bytes
double 8 -10308 .. 10308 0.0d Double-precision floating point number, 8 bytes
boolean 1 true, false false Boolean type (only true and false)
char 2 0 .. 65.535 '\u0000' Characters, 2 bytes, all greater than 0
Default value

By the way, here's an important nuance. If you declare an instance variable (field) or a static class variable and do not immediately assign any value to it, then it is initialized with a default value. The table presents a list of these values.

Local variables in a method have no default value. If you do not assign a value to such variables, they are considered uninitialized and cannot be used.

But let's return to primitive types and take a closer look at them.



2. Integer types

Java has 4 integer types: byte, short, int and long. They differ in their size and the range of values they can store.

int type

The most commonly used is the int type. The name comes from the word integer (whole number). All integer literals (whole numbers) in code are ints (if they don't end in an L, F, or D).

Variables of this type can take values from -2,147,483,648 to +2,147,483,647.

That's a lot and is sufficient for almost every occasion. Almost every function that returns a number returns an int.

Examples:

Code Explanation
int n = "String".length();
The length() method returns the length of a string
String[] array = {"Tic", "Tac", "Toe"};
int n = array.length;
The length field contains the length of the array.

short type

The short type gets its name from short int. It is also often called a short integer. Unlike the int type, its length is only two bytes and the range of possible values is from -32,768 to +32,767.

That means you can't store the number one million in it. Or even 50,000. This is the most rarely used integer type in Java. The main motivation for using it is to conserve memory.

Suppose you have a situation where you know in advance that you will be working with values that never exceed 30,000, and there will be millions of these values.

For example, let's say you're writing an application that processes ultra-high definition pictures that use 10-bits per color. And you have a million pixels in your picture. This is a scenario where the decision to use int or short matters.

long type

This type gets its name from long int and is also called a long integer. Unlike the int type, it has a fabulously enormous range of values: from -9*1018 to +9*1018.

Why isn't it the basic integer type?

Because Java appeared in the mid-90s, when most computers were 32-bit. That means that all processors were optimized for working with numbers consisting of 32 bits. Processors could work with 64-bit integers, but operations with them were slower.

As a result, programmers reasonably decided to make int the standard integer type, and to use the long type only when truly necessary.

byte type

This is smallest integer type in Java, but far from the least used. Its name, byte, is also the word for the smallest addressable block of memory in Java.

There aren't that many valid values for the byte type: from -128 to +127. But that's not its strength. The byte type is most often used when you need to store a large blob data in memory. An array of bytes is ideal for this purpose.

Suppose you need to copy a file somewhere.

You don't need to process the contents of the file: you just want to create an area of memory (buffer), copy the contents of the file into it, and then write that data from the buffer to another file. A byte array is what you need for this.

Keep in mind that an array variable only stores a reference to an area of memory. When the variable is passed to some method, only the memory address is passed. The block of memory itself is not copied.

byte[] buffer = new byte[1024*1024];
FileInputStream sourceFile = new FileInputStream("c:\\data.txt");
FileOutputStream destFile = new FileOutputStream("c:\\output.txt");
while (true)
{
   int size = sourceFile.read(buffer); // Read data from a file into a buffer
   destFile.write(buffer, 0, size); // Write data from the buffer to a file

   // Stop copying if the buffer is not full
   if (size < buffer.length) break;
}
sourceFile.close();
destFile.close();


3. Real types

The primitive types include two types for real numbers. Though it isn't entirely accurate to use that term. When computers handle real numbers, we call them floating-point numbers. The name comes from a standard for representing numbers, in which the integer and fractional parts of a number are separated by a period (a point, not a comma).

Some helpful information:

Each country has its own standards for writing numbers (surprise!).

Many people are accustomed to using periods to separate thousands and commas as the decimal separator: for example, they would write one million ones and 153 thousandths as 1.000.000,153. But in the United States, where Java's creators lived, a different standard was adopted: 1000000.153

Java has two floating-point primitive types: double and float.

As we said earlier, these types have a very specific internal arrangement: in fact, inside each variable of these types is not one number, but two:

For example, the floating-point number 987654.321 can be represented as 0.987654321*106. Then in memory it will be represented as two numbers 987654321 (the mantissa, i.e. the significant part of the number) and 6 (exponent, i.e. a power of ten)

float type

The name of the float type comes from floating-point number. The size of this type is quite small — only 4 bytes (32 bits) — but it can store values from -3.4*1038 to 3.4*1038. 24 bits are allocated for representing the mantissa, and 8 bits for the exponent. This type is capable of storing only 8 significant digits.

This approach makes it possible to store much larger numbers than an int, while using the same 4 bytes. But to do so, we sacrifice accuracy. Because part of the memory stores the mantissa, these variables store only 6-7 decimal places while the rest are discarded.

Example:

Code Value
float a = (float) 123.456789;
123.45679
float a = (float) 12345.9999;
12346.0
float a = (float) -123.456789E-2;
-1.2345679

As you can see, this type's main drawback is the very small number of significant digits, and the loss of precision as soon as the eighth digit. That's why the float type is not very popular among Java programmers.

double type

The double type is the standard floating-point type. The name comes from double precision floating-point number. All real literals are doubles by default.

This type takes up 8 bytes of memory (64 bits) and can store values from -1.7*10308 to 1.7*10308. An important thing to know is that 53 bits are allocated for the mantissa, while the remaining 11 are for the exponent.

This allows 15-17 significant digits to be stored.

Example:

Code Value
double a = 1234567890.1234567890;
1234567890.1234567
double a = 1234567890.1234512345;
1234567890.1234512
double a = 1234567890.1357913579;
1234567890.1357913

This precision, especially in comparison with the float type, is decisive: 99% of all operations with real numbers are performed using the double type.

11 bits are allocated for the exponent, which means you can store powers of ten from -323 to +308 (that's a power of two from -1024 to +1023). The double type can easily store a number with hundreds of zeros after the decimal point:

Code Value
double a = 2E-300 * 3E+302
600.0


4. Infinity

Floating-point numbers have another interesting feature: they can store a special value denoting infinity. And you can represent positive infinity and negative infinity.

Examples:

Code Note
System.out.println( 100.0 / 0.0 );
Infinity
System.out.println( -100.0 / 0.0 );
-Infinity
double a = 1d / 0d;
double b = a * 10;
double c = b - 100;
a == Infinity
b == Infinity
c == Infinity

If infinity is multiplied by a number, you get infinity. If you add a number to infinity, you get infinity. That's super convenient.

Not a number (NaN)

Any operations involving infinity yield infinity. Well, most but not all.

Floating-point numbers can store another special value: NaN. It is short for Not a Number (not a number).

In mathematics, if you divide infinity by infinity, the result is undefined.

But, in Java, if you divide infinity by infinity, the result is NaN.

Examples:

Code Note
System.out.println(0.0 / 0.0);
NaN
double infinity = 1d / 0d;
System.out.println(infinity / infinity);

NaN
double a = 0.0 / 0.0;
double b = a * 10;
double c = b - 100;
double d = a + infinity;
a == NaN
b == NaN
c == NaN
d == NaN

Any operation with NaN yields NaN.



5. char type

Among Java's primitive types, one deserves some special attention: the char type. Its name comes from the word character, and the type itself is used to store characters.

Characters are what strings are made of, right? Strings are an array of characters.

But even more interesting is the fact that the char type is also a numeric type! It's a dual purpose type, so to speak.

The reality is that the char type doesn't actually characters. Instead, it stores character codes from the Unicode encoding. Each character corresponds to a number: the character's numeric code.

Each char variable occupies two bytes in memory (the same as the short type). But unlike the short type, the char integer type is unsigned and can store values from 0 to 65,535.

The char type is a hybrid type. Its values can be interpreted both as numbers (e.g. they can be added and multiplied) and as characters. This was done because although characters are visual representations, to a computer they are above all just numbers. And it's much more convenient to work with them as numbers.

Unicode

Unicode is a special table (encoding) that contains all the characters in the world. And each character has its own number. It looks approximately like this:

Primitive types in Java

There are different ways to assign a value to a char variable.

Code Description
char a = 'A';
The a variable will contain the Latin letter A.
char a = 65;
The a variable will contain the Latin letter A. Its code is 65.
char a = 0x41;
The a variable will contain the Latin letter A.
Its code is 65, which equals 41 in the hexadecimal system.
char a = 0x0041;
The a variable will contain the Latin letter A.
Its code is 65, which equals 41 in the hexadecimal system.
The two extra zeros don't change anything.
char a = '\u0041';
The a variable will contain the Latin letter A.
Another way to define a character by its code.

Most often, people simply indicate the character in quotation marks (as in the first row of the table). That said, the latter method is also popular. Its advantage is that it can be used in strings.

And as we said, the char type is also an integer type, so you can write something like this:

Code Console output
char a = 'A';
a++;
System.out.println(a);
The Latin letter B will be displayed on the screen.
Because:
A65
B66
C67

Working with chars

Each char is first of all a number (character code), and then a character. If you know a character code, you can always get the character in your program. Example:

Code Console output
char c = (char) 1128;
System.out.println(c);

Ѩ

Standard codes

Here are the most well-known character codes:

Characters Codes
0, 1, 2, ... 9 48, 49, 50, ... 57
a, b, c, ... z 97, 98, 99, ... 122
A, B, C, ... Z 65, 66, 67, ... 90


6. boolean type

And the last primitive type is boolean.

As you already know, it can only take two values: true and false.

And with that, you already know everything there is to know about this type.