Java · Beginner
Variables and Data Types in Java
In short: A Java variable has a fixed type chosen when it is declared, such as int count = 0;, and can only ever hold values of that type. The eight primitive types store numbers, characters and booleans directly; every other type, including String, holds a reference to an object.
Typed variables and why they matter
Java is statically typed: every variable is declared with a type, and the compiler checks every use against it before the program runs. int seats = 42; creates a variable named seats that holds a whole number; a later seats = "many"; is refused at compile time rather than discovered as a crash in production. The type also fixes how much memory the value takes and which operations make sense on it.
Eight types are primitive, meaning the variable holds the value itself. Four hold whole numbers of increasing size: byte, short, int and long. Two hold floating-point numbers: float and double. char holds one UTF-16 character and boolean holds true or false. In everyday code int, long, double, boolean and char cover almost everything; the smaller types earn their place when data must be packed tightly.
Everything else is a reference type: the variable holds a reference to an object stored elsewhere. String is the first one you meet; arrays and every class you write are reference types too. A reference variable can hold null, meaning it points at nothing. A primitive cannot.
Literals carry types. 42 is an int; a whole number above 2,147,483,647 needs the suffix L to become a long, because it does not fit in an int at all. 2.75 is a double and 2.75f is a float. Characters use single quotes and strings double quotes: 'B' is a char, "B" is a String of length one. Underscores may separate digit groups, so 3_250_000_000L reads more easily than 3250000000L.
A declaration may include an initial value or not, but a local variable must be given a value before it is read; the compiler enforces this. Since Java 10, var lets the compiler infer the type from the initialiser: var count = 3; makes an int. The variable is still fixed to that type for its whole life; var saves typing, it does not add flexibility.
Moving a value between types is a conversion. Widening, such as int to long or int to double, happens automatically because nothing is lost. Narrowing needs an explicit cast such as (int) 35.75, which throws the fraction away rather than rounding. Arithmetic on two ints stays int, so 7 / 2 is 3; make one operand a double to get 3.5. The operators lesson covers this in detail.
Whole-number arithmetic that leaves the type's range wraps around silently instead of raising an error: two billion plus two billion in an int is a negative number. Use long for totals that may pass two billion. For money, avoid double: binary floating point cannot represent most decimal fractions exactly, so 0.1 + 0.2 is not quite 0.3. Keep money as whole cents in a long, or use BigDecimal.
Syntax
type name; // declare; must be assigned before use
type name = value; // declare and initialise
var name = value; // Java 10+: type inferred from the value
int seats = 42;
long odometerKm = 3_250_000_000L;
double fare = 2.75;
boolean electric = true;
char route = 'B';
String depot = "North Yard";
int truncated = (int) 35.75; // explicit cast: 35Names start with a letter, underscore or dollar sign and are case-sensitive; the convention is lowerCamelCase for variables and UPPER_SNAKE_CASE for constants declared with final.
The primitive types
| Type | Holds | Range or precision | Literal |
|---|---|---|---|
| byte | 8-bit integer | -128 to 127 | (byte) 7 |
| short | 16-bit integer | -32,768 to 32,767 | (short) 7 |
| int | 32-bit integer | -2,147,483,648 to 2,147,483,647 | 7 |
| long | 64-bit integer | about -9.2 to 9.2 quintillion | 7L |
| float | 32-bit floating point | about 7 significant decimal digits | 2.5f |
| double | 64-bit floating point | about 15 to 16 significant decimal digits | 2.5 |
| char | 16-bit UTF-16 code unit | 0 to 65,535 | 'B' |
| boolean | truth value | true or false | true |
Declaring the everyday types
A bus record using six different types, printed one per line.
public class Main {
public static void main(String[] args) {
int seats = 42;
long odometerKm = 3_250_000_000L;
double fareEuro = 2.75;
boolean electric = true;
char route = 'B';
String depot = "North Yard";
System.out.println("Route " + route + " from " + depot);
System.out.println("Seats: " + seats);
System.out.println("Odometer: " + odometerKm + " km");
System.out.println("Fare: " + fareEuro);
System.out.println("Electric: " + electric);
}
}Output
Route B from North Yard Seats: 42 Odometer: 3250000000 km Fare: 2.75 Electric: true
The odometer value is above the int limit, so it needs long and the L suffix; the underscores in the literal are dropped when it is printed. Joining any value to a String with + converts it to text, so the char, the boolean and both numbers print without any extra work.
Reading typed input, casting and integer division
The program reads a passenger count and a fare. It is run with the input lines 13 and 2.75.
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int passengers = in.nextInt();
double fare = in.nextDouble();
double total = passengers * fare;
int roundedDown = (int) total;
System.out.println("Passengers: " + passengers);
System.out.println("Total: " + total);
System.out.println("Rounded down: " + roundedDown);
int wholeShare = 7 / 2;
double exactShare = 7 / 2.0;
System.out.println(wholeShare);
System.out.println(exactShare);
}
}Input given to the program: 13 ↵ 2.75
Output
Passengers: 13 Total: 35.75 Rounded down: 35 3 3.5
nextInt() and nextDouble() return values of the named type, so the variables that receive them must match. Multiplying an int by a double widens the int automatically and gives a double. The cast (int) total drops the .75 without rounding. In the last two lines the only difference is 2 versus 2.0: two ints divide to an int, one double makes the whole expression a double.
Overflow, var and floating-point surprises
What happens when an int is pushed past its limit, and two things beginners are surprised by.
public class Main {
public static void main(String[] args) {
int big = 2_000_000_000;
int wrapped = big + big;
long safe = (long) big + big;
System.out.println(wrapped);
System.out.println(safe);
System.out.println(Integer.MAX_VALUE);
var label = "platform";
var count = 3;
System.out.println(label + " " + count);
System.out.println(0.1 + 0.2);
}
}Output
-294967296 4000000000 2147483647 platform 3 0.30000000000000004
Four billion does not fit in 32 bits, so the int sum wraps around to a negative number with no warning. Casting one operand to long first makes the addition happen in 64 bits, which is why safe is right. Integer.MAX_VALUE is the largest int. var infers String and int from the initialisers. The last line shows why doubles are wrong for money: 0.1 and 0.2 are not exactly representable in binary, and the tiny errors show up in the sum.
Common mistakes
Reading a local variable before assigning it
Why it goes wrong:
int total; System.out.println(total);fails to compile withvariable total might not have been initialized. Local variables have no automatic default; only fields and array elements do.Fix: Give the variable a starting value when you declare it, or make sure every path assigns it before the first read.
Java · fixint total = 0; System.out.println(total);Assigning a double to an int
Why it goes wrong:
int n = 3.7;fails withincompatible types: possible lossy conversion from double to int. Java refuses to throw away the fraction silently.Fix: Cast explicitly when truncation is what you want, or declare the variable as double.
Java · fixint n = (int) 3.7; // 3 double d = 3.7; // keeps the fractionWriting a large whole number without the L suffix
Why it goes wrong:
long km = 3250000000;fails withinteger number too large. The literal is parsed as an int before it is stored, and it does not fit.Fix: Add
L:long km = 3250000000L;.Calling nextLine() right after nextInt()
Why it goes wrong:
nextInt()reads the digits but leaves the line break after them in the input. The followingnextLine()returns that leftover, an empty string, instead of the next line of text.Fix: Call
in.nextLine()once to discard the rest of the number's line before reading text, or read everything withnextLine()and convert withInteger.parseInt.Java · fixint n = in.nextInt(); in.nextLine(); // consume the rest of that line String name = in.nextLine();
Where you use this
Choosing types is the first design decision in any program that stores data. A stock record for a hardware shop might hold the quantity on hand as an int, the unit price in whole cents as a long so that totals stay exact, a boolean for whether the line is discontinued, a char for a single-letter size code and a String for the name. Each choice states what the value can be and stops the compiler from accepting nonsense such as a fractional quantity. A mismatch the compiler finds costs seconds; one a customer finds costs far more.
int onHand = 140;
long unitPriceCents = 1299L;
boolean discontinued = false;
char sizeCode = 'M';
String name = "hex bolt M8";
long stockValueCents = onHand * unitPriceCents;Key points
- Every variable has one type for life, checked by the compiler.
- Primitives (
int,long,double,boolean,charand the smaller ones) hold values; everything else, includingString, holds a reference. - A local variable must be assigned before it is read.
- Whole-number literals are
intunless suffixed withL; decimal literals aredoubleunless suffixed withf. - Widening conversions are automatic; narrowing needs a cast, and a cast truncates.
- int arithmetic wraps around silently past 2,147,483,647; use
longfor large totals. var(Java 10+) infers the type from the initialiser but does not make the variable dynamic.
Try it yourself
After the existing print statement, declare a double named deposit set to 12.5 and a boolean named paid set to false, then print each of them on its own line.
public class Main {
public static void main(String[] args) {
int price = 9;
int quantity = 4;
System.out.println("Total: " + price * quantity);
}
}
Total: 36
12.5
falsepublic class Main {
public static void main(String[] args) {
int price = 9;
int quantity = 4;
double deposit = 12.5;
boolean paid = false;
System.out.println("Total: " + price * quantity);
System.out.println(deposit);
System.out.println(paid);
}
}
Practise this
Exercises for this lesson are in the Java practice set.
Related lessons
- Java Syntax and Your First ProgramWhat every Java program needs: a class, a main method, statements and semicolons. Compile and run a first program, read input, and avoid the usual setup errors.8 min
- Operators in JavaJava's arithmetic, comparison, logical and assignment operators: integer division, remainder, short-circuit evaluation and precedence, with runnable examples.9 min
- Strings in JavaHow Java strings work: immutability, the methods you use daily, why equals not == compares text, trimming and splitting input, StringBuilder and String.format.10 min
- Arrays in JavaHow Java arrays store a fixed number of values of one type: creating and indexing them, length, the for-each loop, java.util.Arrays helpers and 2D arrays.9 min
Frequently asked questions
What is the difference between int and Integer in Java?
int is a primitive that holds the number directly and can never be null. Integer is a class that wraps an int in an object, so it can be null and can be stored where an object is required, such as in an ArrayList. Java converts between them automatically (boxing and unboxing), but unboxing a null Integer throws a NullPointerException, so prefer int unless you need an object.
Should I use float or double?
Use double. It is the default type of decimal literals, has about 15 significant digits against float's 7, and modern hardware handles it just as fast. float is worth it only when memory is tight, for example in large graphics or audio buffers. Neither is exact for decimal money; use whole cents in a long or BigDecimal for that.
How this page was checked. Every program on it was run with OpenJDK 21 at build time by the publishing checks, and the output shown is what it printed. Running Java inside the browser is not available yet, so the Run button is absent rather than pretending; copy the code and run it with OpenJDK 21 locally.