The author selected the Free and Open Source Fund to receive a donation as part of the Write for DOnations program.
Java is a statically typed programming language. This means that when you create a variable, you must also specify its data type, which is the type of information it stores. This is in contrast to dynamically typed languages, such as PHP. With dynamically typed languages, you don’t have to specify the data type of a variable, which may seem like a relief.
However, knowing the data types and using them appropriately allows developers to optimize their code because each data type has specific resource requirements. Also, if you specify one data type and try to store a different type, such as by mistake, you won’t be able to compile the code. Thus, with statically typed languages, you can detect errors even before any testing.
Java has two data types: primitive and reference (also known as non-primitive). In this tutorial, you will use variables to store and use information in a Java program to learn about some of the commonly used data types in Java. This is not an exhaustive overview of all data types, but this guide will help you become familiar with what options are available to you in Java.
To follow this tutorial, you will need:
An environment in which you can execute Java programs to follow along with the examples. To set this up on your local machine, you will need the following:
Familiarity with Java and object-oriented programming, which you can find in our tutorial, How To Write Your First Program in Java.
Java primitive types are the simplest and most basic data types in Java. They represent raw values such as numbers and characters. The most frequently used primitive data types are int
(integers), boolean
(boolean values), and char
(characters). You can find the rest at the official Java data types documentation.
Integers are both negative and positive whole numbers. In Java, you’ll use int
to store them. int
can accommodate large enough numbers for most purposes: from -2,147,483,648
to 2,147,483,647
.
Let’s look at how int
is used in an example:
int theAnswer = 42;
Primitive types always start with a lowercase letter (int
). Java syntax rules require that you first specify the data type (int
) and then its name (theAnswer
). After that, you assign the value 42
with the equals sign (=
) to the variable.
Regardless of its data type, you use a variable by directly specifying its name without prepending any special characters. This is because Java can recognize it as a variable.
Note: The name of the variable theAnswer
and all other variables in this tutorial are written in Camel case. Even though there is no strict requirement to use it, this is the accepted naming convention in Java.
Once you have declared the variable, you can use it by referring to it in a method like so:
int theAnswer = 42;
System.out.println("The answer to all questions is " + theAnswer);
In the second line, you print theAnswer
to the console using the built-in method println
from the package System.out
. This is the simplest way to test a variable to ensure it is declared as expected.
To see this code in action, use the Java Shell tool. After installing Java, open a terminal or command prompt on your local computer and type jshell
:
- jshell
Your output will look similar to the following:
Output| Welcome to JShell -- Version 11.0.16
| For an introduction type: /help intro
jshell>
You can paste the code examples from this tutorial into the console. Once you are done, you can exit jshell
by typing /exit
.
To declare and use int
, paste the following lines into the jshell
console:
- int theAnswer = 42;
- System.out.println("The answer to all questions is " + theAnswer);
You will see the following output:
OutputtheAnswer ==> 42
The answer to all questions is 42
This output confirms that you set the int
variable theAnswer
correctly to 42 (theAnswer ==> 42
). You also successfully used theAnswer
by passing it to a method, and the method produced the expected variable value.
Boolean values are true
or false
. In Java, you’ll use boolean
to store them. For example, let’s create a boolean
variable defining whether or not Java is fun:
boolean isJavaFun = true;
You define the variable isJavaFun
as true
. The alternative boolean
value is false
.
Using the above variable, you can print the sentence Java is fun: true
like this:
- boolean isJavaFun = true;
- System.out.println("Java is fun: " + isJavaFun);
Running these lines in jshell
will produce the following output:
OutputisJavaFun ==> true
Java is fun: true
Similar to the int
example, the method println
will print the argument provided in the parentheses. The plus sign (+
) concatenates or joins the string "Java is fun: " with the variable isJavaFun
so that in reality, it is just one argument — the string, Java is fun: true
.
To store a single alphanumeric character, you’ll use char
. For example:
char firstLetter = 'a';
Notice that the letter a
is surrounded by single quotes. Single quotes can be used only for char
values. Double quotes are used for strings, as you’ll learn later.
char
does not seem like a particularly useful type because it’s not likely that you will need a variable assigned to a single character. However, char
is used as the building block for character string classes such as String
, which are basically a collection of char
values.
As you have seen in this section, the declaration and use of primitive type variables is straightforward because they represent simple values such as integers. These values are ready to be used and do not require additional operations such as creating objects, invoking methods, and so on.
In the first tutorial in this series, How To Write Your First Program in Java, you learned that Java code is organized in classes and that these classes are used as templates to create objects. When such objects are assigned to variables, you are pointing or referring to these objects. In these cases, the variables are classified as reference types. These variables are also known as non-primitive because primitive type variables cannot point to objects.
Objects are powerful because they have advanced properties and are able to act when you trigger their methods. However, without variables pointing to them, these objects are inaccessible and practically unusable. That’s why reference type variables are essential to Java and object-oriented programming as a whole.
Note: Reference types point to objects created from classes. To avoid confusion, the reference type and the created object will be of the same class in the following examples.
However, in complex programs, this is rarely the case. In Java, an interface is a group of requirements for a specific behavior, and these requirements can be satisfied by one or more classes. A class that satisfies the requirements of an interface is said to implement this interface. Thus, in complex programs, it’s common to declare a variable with the reference type of an interface. This way, you specify the behavior that your variable should exhibit without tying it to a concrete implementation of this behavior. This allows you to easily change which implementation your variable points to without having to change the way the variable is used. This complex concept is part of a more advanced topic about inheritance and polymorphism, which will be a separate tutorial in our Java series.
While there are only a few primitive types, reference types are practically unlimited because there is no limit to the number of classes (and interfaces), and each class stands for a reference type. There are many built-in classes in Java that provide essential functionality. The most frequently used ones are found in the core java.lang
package. You’ll review some of them in this section.
String
ClassThe String
class represents a combination of characters that make up a string. To declare a String
, or any other reference type variable, you first specify its type followed by its name. After that, you assign a value to it with the equals sign. So far, it is similar to working with primitive types. However, reference types point to objects, so you have to create an object if there hasn’t been one created yet. Here is an example:
String hello = new String("Hello");
hello
is the name of the variable with reference type String
. You assign it to a new String
object. The new String
object is created with the new
keyword along with the name of the class — String
in this case. The class String
starts with a capital letter. By convention, all classes and therefore reference types start with a capital letter.
Each class has a special method called a constructor that is used for creating new objects. You can invoke this constructor by adding parentheses (()
) at the end of the class name. The constructor may accept parameters, as in the example above, where the parameter "Hello"
is applied to the constructor for String
.
To confirm the hello
variable behaves as expected, pass it again to the println
method like this:
- String hello = new String("Hello");
- System.out.println(hello);
Running these lines in jshell
will produce the following output:
Outputhello ==> "Hello"
Hello
This time, the output confirms that the variable hello
is set to Hello
. After that, the same Hello
is printed on a new line, confirming that the method println()
has processed it.
In the previous section, you worked with the String
reference type, which is frequently used. Other popular reference types are the so-called wrappers for primitive types. A wrapper class wraps or contains primitive data, hence its name. All primitive types have wrapper counterparts, and here are some examples:
Integer
: To wrap int
values.Character
: To wrap char
values.Boolean
: To wrap boolean
values.These wrappers exist so that you can upgrade a simple primitive value to a powerful object. Each wrapper has ready-to-use methods related to the values it is designed to store.
As an example, you’ll explore Integer
. In the previous section, you created a String
object with the new
keyword. However, some classes provide, and even encourage, the use of special methods for acquiring objects from them, and Integer
is one of them. In the case of Integer
, using a special method is mostly about resource optimization, but in other cases, it could be about simplifying the construction of complex objects.
In the following example, you create an Integer
variable called theAnswer
with the value 42
using the valueOf
method:
- Integer theAnswer = Integer.valueOf(42);
- System.out.println(theAnswer);
In jshell
, you’ll get the following output:
OutputtheAnswer ==> 42
42
By invoking the Integer
method valueOf(42)
, you instruct Java to give you an object with this value. Behind the scenes, Java will check whether there is already an object with such a value in its cache. If there is, the object will be linked to theAnswer
variable. If there isn’t, a new object will be created for the theAnswer
variable.
Many built-in classes provide such methods for performance reasons, and their use is recommended, if not compulsory. In the case of Integer
, you could still create an object with the new
keyword, but you will get a deprecation warning.
In addition to String
and wrappers, there are also other useful built-in reference types, which you can find at the java.lang package summary. To fully understand some of these more advanced reference types, additional explanation or prior knowledge is required. That’s why we’ll cover some of them in our next tutorials from the Java series.
Literals represent fixed values that can be used directly in the code and thus can be assigned both to primitive and reference types. There are a few types of literals, which can be categorized as follows.
You’ve already used a few literals in the section on primitive types. For each primitive type, there is a literal, such as the ones from our examples: 42
, 'a'
, and true
. Integers such as 42
are integer literals. Similarly, characters such as 'a'
are character literals, and true
and false
are boolean literals.
Primitive types literals can also be used to create values for reference types. The int
literal was used in creating an Integer
object with the code Integer.valueOf(42)
. There is also a shorthand for that, and you can assign the value directly like this:
Integer theAnswer = 42;
42
is an integer literal, just as any whole number, and you can assign it directly to the theAnswer
variable without any additional statements. It’s common to see an Integer
declared like this because it’s convenient.
This shorthand approach also works for other primitive types literals and their counterpart reference types such as Boolean
, for example:
Boolean isFun = true;
true
is the literal, which is directly assigned to the isFun
variable of type Boolean
. There is also a false
literal, which you can assign the same way.
There is also a special literal for the String
reference type, and it is recognized by the double quotes surrounding its value. In this example, it is "Hello, World!"
:
String helloWorld = "Hello, World!";
Using literals is simpler and shorter, and that’s many programmers prefer it. However, you can still declare a String
variable with a new String
object, as you’ve already done in the section for reference types.
There is one more important literal: null
, which represents the absence of a value or the non-existence of an object. Null
allows you to create a reference type and point it to null
instead of pointing it to an object. null
can be used for all reference types but not for any primitive types.
There is one caveat with the null
literal: you can declare variables with it, but you cannot use these variables until you reassign a suitable, non-null value. If you try to use a reference type variable with a null
value, you will get an error. Here is an example:
- String initiallyNullString = null;
- System.out.println("The class name is: " + initiallyNullString.getClass());
When you try to run this code in jshell
, you will see an error similar to the following:
OutputinitiallyNullString ==> null
| Exception java.lang.NullPointerException
| at (#4:1)
Depending on your operating system and Java version, your output may differ.
The error java.lang.NullPointerException
is thrown because you are trying to invoke the String
method getClass()
(which returns the name of the class) on the variable initiallyNullString
(which points to a null object).
Note: For simplicity, we are calling java.lang.NullPointerException
an error even though it’s technically an exception. For more on exceptions and errors, check the tutorial, Exception Handling in Java.
To address the error, you have to reassign the initiallyNullString
value like this:
- String initiallyNullString = null;
- initiallyNullString = "not null any longer";
- System.out.println("The class name is: " + initiallyNullString.getClass());
The new, fixed code will print the following output:
OutputinitiallyNullString ==> null
initiallyNullString ==> "not null any longer"
The class name is: class java.lang.String
The above output shows how initiallyNullString
is first null
, then it becomes a new String
object containing "not null any longer"
. Next, when the method getClass()
is invoked on the instantiated object, you get java.lang.String
, in which String
is the class name and java.lang
is its package. Finally, a full, meaningful message is printed: "The class name is: class java.lang.String"
.
Such null
value declarations are more common for legacy code. They have been used to create a variable first and then later to assign its real value, usually going through some logic that determines the latter. However, since Java version 8 there is a new reference type called Optional, which is more suitable for cases in which null
has been used before.
Until now, you’ve been using some of the common data types in Java to define variables. However, Java 10 introduced a new feature called local variable type inference, which allows you to use the keyword var
in front of a new variable. With this feature, Java will infer (that is, guess automatically) the data type from the local context. Type inference is controversial since it contrasts the previously explained verbosity of defining variables. The advantages and disadvantages of such a feature are disputable, but the fact is that other statically typed languages, such as C++, support type inference.
In any case, type inference cannot completely replace the use of data types because it works only with local variables, which are variables inside a method. Let’s look at an example with var
:
- var hello = "Hello";
- System.out.println(hello);
You declare the variable hello
with the var
keyword in order to instruct Java to detect its data type. After that, you print it to the console in the usual way to confirm it works as expected:
Ouputhello ==> "Hello"
Hello
This example will work as long as your Java installation (more specifically, the JDK) is above version 10. The var
keyword is not supported on older versions.
Type inference happens during the compilation process — that is, when you compile the code. The compilation process turns plain text source code into machine code and applies various optimizations, including type inference. This ensures that the correct amount of system memory is available for the type inferred variables. Thus, the machine code that you run after compiling is fully optimized, as if you have manually specified all the data types.
In this example, the var
keyword works because the variable is local, and the var
data type works only with local variables. Local variables are defined inside methods and are accessible only inside the methods, which is why they’re called “local”.
To show that var
can only be used for local variables, try placing it outside the main method, like so:
- public class Hello {
- var hello = "Hello";
- public static void main(String[] args) {
- // example code
- }
- }
When you paste the above code in jshell
, you will get the following error:
Output| Error:
| 'var' is not allowed here
| var hello = "Hello";
| ^-^
var
is not allowed there because hello
is outside a method and it is no longer considered local. Thus, type inference does not work for non-local variables because the context cannot be used reliably to detect the data type.
While using var
can be challenging and is not required, you will likely come across it so it is useful to know about it.
When declaring variables in Java, there is one more important rule to know. There are reserved keywords that you cannot use for variables names. For example, you cannot declare a primitive of type int
and name it new
like this:
- int new = 1;
If you try this example, you will get compilation errors because new
is a reserved keyword.
Output| Error:
| '.class' expected
| int new = 1;
| ^
| Error:
| <identifier> expected
| int new = 1;
| ^
| Error:
| '(' or '[' expected
| int new = 1;
| ^
| Error:
| unexpected type
| required: value
| found: class
| int new = 1;
| ^--^
| Error:
| missing return statement
| int new = 1;
| ^----------^
The keyword new
is used for creating new objects and Java is not expecting it at this position. In the list of errors in the previous output, the first part is the most important:
Output| Error:
| '.class' expected
| int new = 1;
| ^
The error '.class' expected
means that when you use the new
keyword, Java expects that a class will follow. At this point, Java is not able to interpret the statement and the rest of the errors follow.
The rest of the reserved keywords, such as abstract
, continue
, default
, for
, and break
, also have specific meanings in Java and cannot be used for variables names. The full list of the reserved keywords can be found on the Java Language Keywords page. Even if you don’t remember all the reserved keywords, you can use compilation errors to identify the issue.
In this tutorial, you learned about primitive and reference data types in Java, which is a complex but essential topic. Take your time to practice it and go through the examples more than once. Try changing some of the data types and values. Pay attention to when errors are thrown and when they are not in order to develop a sense for successful code execution.
For more on Java, check out our How To Code in Java series.
Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.
Java is a mature and well-designed programming language with a wide range of uses. One of its unique benefits is that it is cross-platform: once you create a Java program, you can run it on many operating systems, including servers (Linux/Unix), desktop (Windows, macOS, Linux), and mobile operating systems (Android, iOS).
This textbox defaults to using Markdown to format your answer.
You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!